Skip to content

Commit

Permalink
Merge pull request #4 from mcg-web/removed-all-event-references
Browse files Browse the repository at this point in the history
Removed all eventLoop references
  • Loading branch information
mcg-web authored Nov 11, 2016
2 parents ba33d6f + c4dd048 commit 0c84e81
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 232 deletions.
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,4 @@ install: composer update --prefer-dist --no-interaction
script: if [ "$TRAVIS_PHP_VERSION" == "5.6" ]; then phpunit -d xdebug.max_nesting_level=1000 --debug --coverage-clover build/logs/clover.xml; else phpunit --debug; fi

after_success:
- composer require "satooshi/php-coveralls:^1.0"
- travis_retry php vendor/bin/coveralls -v
- if [ "$TRAVIS_PHP_VERSION" == "5.6" ]; then composer require "satooshi/php-coveralls:^1.0" && travis_retry php vendor/bin/coveralls -v; fi
22 changes: 10 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ data sources such as databases or web services via batching and caching.

## Requirements

* This library require [React/Promise](https://github.com/reactphp/promise) and PHP >= 5.5 to works.
* The [React/EventLoop](https://github.com/reactphp/event-loop) component are **totally optional** (see `await` method for more details).
This library require [React/Promise](https://github.com/reactphp/promise) and PHP >= 5.5 to works.

## Getting Started

Expand All @@ -33,15 +32,15 @@ use Overblog\DataLoader\DataLoader;

$myBatchGetUsers = function ($keys) { /* ... */ };

$userLoader = new DataLoader(new BatchLoadFn($myBatchGetUsers));
$userLoader = new DataLoader($myBatchGetUsers);
```

A batch loading instance accepts a callable callback that accepts an Array of keys, and returns a Promise which
A batch loading callable / callback accepts an Array of keys, and returns a Promise which
resolves to an Array of values.

Then load individual values from the loader. DataLoaderPHP will coalesce all
individual loads which occur within a single frame of execution (a single tick
of the event loop if install or using `await` method) and then call your batch function with all requested keys.
individual loads which occur within a single frame of execution (using `await` method)
and then call your batch function with all requested keys.

```php
$userLoader->load(1)
Expand Down Expand Up @@ -80,7 +79,7 @@ your application:

```php
$promise1A = $userLoader->load(1);
$promise1B = userLoader->load(1);
$promise1B = $userLoader->load(1);
var_dump($promise1A === $promise1B); // bool(true)
```

Expand All @@ -94,7 +93,7 @@ Here's a simple example using SQL UPDATE to illustrate.
```php
$sql = 'UPDATE users WHERE id=4 SET username="zuck"';
if (true === $conn->query($sql)) {
$userLoader->clear(4);
$userLoader->clear(4);
}
```

Expand Down Expand Up @@ -123,12 +122,11 @@ Each `DataLoaderPHP` instance contains a unique memoized cache. Use caution when
used in long-lived applications or those which serve many users with different
access permissions and consider creating a new instance per web request.

##### `new DataLoader(batchLoadFn $batchLoadFn [, Option $options])`
##### `new DataLoader(callable $batchLoadFn [, Option $options])`

Create a new `DataLoaderPHP` given a batch loading instance and options.

- *$batchLoadFn*: A object which accepts a callable callback that accepts an Array of keys,
and returns a Promise which resolves to an Array of values.
- *$batchLoadFn*: A callable / callback which accepts an Array of keys, and returns a Promise which resolves to an Array of values.
- *$options*: An optional object of options:

- *batch*: Default `true`. Set to `false` to disable batching, instead
Expand Down Expand Up @@ -164,7 +162,7 @@ list($a, $b) = DataLoader::await($myLoader->loadMany(['a', 'b']);
This is equivalent to the more verbose:

```js
list($a, $b) = await DataLoader::await(\React\Promise\all([
list($a, $b) = DataLoader::await(\React\Promise\all([
$myLoader->load('a'),
$myLoader->load('b')
]);
Expand Down
43 changes: 0 additions & 43 deletions src/BatchLoadFn.php

This file was deleted.

102 changes: 33 additions & 69 deletions src/DataLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
class DataLoader
{
/**
* @var BatchLoadFn
* @var callable
*/
private $batchLoadFn;

Expand All @@ -35,26 +35,15 @@ class DataLoader
*/
private $queue = [];

/**
* @var null|\React\EventLoop\LoopInterface
*/
private $eventLoop;

/**
* @var Promise
*/
private $resolvedPromise;

/**
* @var self[]
*/
private static $instances = [];

public function __construct(BatchLoadFn $batchLoadFn, Option $options = null)
public function __construct(callable $batchLoadFn, Option $options = null)
{
$this->batchLoadFn = $batchLoadFn;
$this->options = $options ?: new Option();
$this->eventLoop = class_exists('React\\EventLoop\\Factory') ? \React\EventLoop\Factory::create() : null;
$this->promiseCache = $this->options->getCacheMap();
self::$instances[] = $this;
}
Expand Down Expand Up @@ -97,12 +86,7 @@ function ($resolve, $reject) use (&$promise, $key, $shouldBatch) {
// A single dispatch should be scheduled per queue at the time when the
// queue changes from "empty" to "full".
if (count($this->queue) === 1) {
if ($shouldBatch) {
// If batching, schedule a task to dispatch the queue.
$this->enqueuePostPromiseJob(function () {
$this->dispatchQueue();
});
} else {
if (!$shouldBatch) {
// Otherwise dispatch the (queue of one) immediately.
$this->dispatchQueue();
}
Expand All @@ -124,11 +108,11 @@ function (callable $resolve, callable $reject) {
/**
* Loads multiple keys, promising an array of values:
*
* [$a, $b] = $myLoader->loadMany(['a', 'b']);
* list($a, $b) = $myLoader->loadMany(['a', 'b']);
*
* This is equivalent to the more verbose:
*
* [$a, $b] = \React\Promise\all([
* list($a, $b) = \React\Promise\all([
* $myLoader->load('a'),
* $myLoader->load('b')
* ]);
Expand All @@ -139,7 +123,7 @@ function (callable $resolve, callable $reject) {
public function loadMany($keys)
{
if (!is_array($keys) && !$keys instanceof \Traversable) {
throw new \InvalidArgumentException(sprintf('The %s function must be called with Array<key> but got: %s.', __METHOD__, gettype($keys)));
throw new \InvalidArgumentException(sprintf('The "%s" method must be called with Array<key> but got: %s.', __METHOD__, gettype($keys)));
}
return \React\Promise\all(array_map(
function ($key) {
Expand Down Expand Up @@ -207,7 +191,9 @@ public function __destruct()
if ($this->needProcess()) {
foreach ($this->queue as $data) {
try {
$data['promise']->cancel();
/** @var Promise $promise */
$promise = $data['promise'];
$promise->cancel();
} catch (\Exception $e) {
// no need to do nothing if cancel failed
}
Expand Down Expand Up @@ -243,51 +229,45 @@ public static function await($promise = null, $unwrap = true)
{
self::awaitInstances();

if (null !== $promise) {
$resolvedValue = null;
$exception = null;
if (null === $promise) {
return null;
}
$resolvedValue = null;
$exception = null;

if (!is_callable([$promise, 'then'])) {
throw new \InvalidArgumentException('Promise must have a "then" method.');
}
if (!is_callable([$promise, 'then'])) {
throw new \InvalidArgumentException(sprintf('The "%s" method must be called with a Promise ("then" method).', __METHOD__));
}

$promise->then(function ($values) use (&$resolvedValue) {
$resolvedValue = $values;
}, function ($reason) use (&$exception) {
$exception = $reason;
});
if ($exception instanceof \Exception) {
if (!$unwrap) {
return $exception;
}
throw $exception;
$promise->then(function ($values) use (&$resolvedValue) {
$resolvedValue = $values;
}, function ($reason) use (&$exception) {
$exception = $reason;
});
if ($exception instanceof \Exception) {
if (!$unwrap) {
return $exception;
}

return $resolvedValue;
throw $exception;
}

return $resolvedValue;
}

private static function awaitInstances()
{
$dataLoaders = self::$instances;
if (empty($dataLoaders)) {
return;
}
if (!empty($dataLoaders)) {
$wait = true;

$wait = true;

while ($wait) {
foreach ($dataLoaders as $dataLoader) {
try {
while ($wait) {
foreach ($dataLoaders as $dataLoader) {
if (!$dataLoader || !$dataLoader->needProcess()) {
$wait = false;
continue;
}
$wait = true;
$dataLoader->process();
} catch (\Exception $e) {
$wait = false;
continue;
}
}
}
Expand All @@ -305,27 +285,11 @@ private function checkKey($key, $method)
{
if (null === $key) {
throw new \InvalidArgumentException(
sprintf('The %s function must be called with a value, but got: %s.', $method, gettype($key))
sprintf('The "%s" method must be called with a value, but got: %s.', $method, gettype($key))
);
}
}

/**
* @param $fn
*/
private function enqueuePostPromiseJob($fn)
{
if (!$this->resolvedPromise) {
$this->resolvedPromise = \React\Promise\resolve();
}

if ($this->eventLoop) {
$this->resolvedPromise->then(function () use ($fn) {
$this->eventLoop->nextTick($fn);
});
}
}

/**
* Given the current state of a Loader instance, perform a batch load
* from its current queue.
Expand Down
Loading

0 comments on commit 0c84e81

Please sign in to comment.