From 2022921c3518cd08e8e21e58fe232a73d10f8e77 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Mon, 21 Oct 2013 11:27:52 +0200 Subject: [PATCH 01/29] Require PHP >= 5.4.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 9f12fc1b..95bf9633 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ {"name": "Jan Sorgalla", "email": "jsorgalla@googlemail.com"} ], "require": { - "php": ">=5.3.3" + "php": ">=5.4.0" }, "autoload": { "psr-0": { From 95fb5782517a93f831d89dda708b891ccb0c177b Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Mon, 21 Oct 2013 12:07:04 +0200 Subject: [PATCH 02/29] Change variable names to follow the A+ specs --- README.md | 51 ++++++++++---------- src/React/Promise/Deferred.php | 24 ++++----- src/React/Promise/DeferredPromise.php | 4 +- src/React/Promise/FulfilledPromise.php | 20 ++++---- src/React/Promise/LazyPromise.php | 4 +- src/React/Promise/PromiseInterface.php | 2 +- src/React/Promise/RejectedPromise.php | 10 ++-- src/React/Promise/ResolverInterface.php | 2 +- src/React/Promise/When.php | 30 ++++++------ tests/React/Promise/DeferredProgressTest.php | 2 +- tests/React/Promise/DeferredRejectTest.php | 2 +- tests/React/Promise/DeferredResolveTest.php | 2 +- tests/React/Promise/LazyPromiseTest.php | 12 ++--- 13 files changed, 82 insertions(+), 83 deletions(-) diff --git a/README.md b/README.md index 44a30666..80bd9ade 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ should be given to consumers and producers. ``` php $deferred = new React\Promise\Deferred(); -$deferred->then(callable $fulfilledHandler = null, callable $errorHandler = null, callable $progressHandler = null); +$deferred->then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); $deferred->resolve(mixed $promiseOrValue = null); $deferred->reject(mixed $reason = null); $deferred->progress(mixed $update = null); @@ -109,23 +109,23 @@ The Promise represents the eventual outcome, which is either fulfillment reason. The Promise provides mechanisms for arranging to call a function on its value or reason, and produces a new Promise for the result. -A Promise has a single method `then()` which registers new fulfilled, error and -progress handlers with this Promise (all parameters are optional): +A Promise has a single method `then()` which registers new fulfilled, rejection +and progress handlers with this Promise (all parameters are optional): ``` php -$newPromise = $promise->then(callable $fulfilledHandler = null, callable $errorHandler = null, callable $progressHandler = null); +$newPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); ``` - * `$fulfilledHandler` will be invoked once the Promise is fulfilled and passed + * `$onFulfilled` will be invoked once the Promise is fulfilled and passed the result as the first argument. - * `$errorHandler` will be invoked once the Promise is rejected and passed the + * `$onRejected` will be invoked once the Promise is rejected and passed the reason as the first argument. - * `$progressHandler` will be invoked whenever the producer of the Promise + * `$onProgress` will be invoked whenever the producer of the Promise triggers progress notifications and passed a single argument (whatever it wants) to indicate progress. Returns a new Promise that will fulfill with the return value of either -`$fulfilledHandler` or `$errorHandler`, whichever is called, or will reject with +`$onFulfilled` or `$onRejected`, whichever is called, or will reject with the thrown exception if either throws. Once in the fulfilled or rejected state, a Promise becomes immutable. @@ -134,11 +134,11 @@ Neither its state nor its result (or error) can be modified. A Promise makes the following guarantees about handlers registered in the same call to `then()`: - 1. Only one of `$fulfilledHandler` or `$errorHandler` will be called, + 1. Only one of `$onFulfilled` or `$onRejected` will be called, never both. - 2. `$fulfilledHandler` and `$errorHandler` will never be called more + 2. `$onFulfilled` and `$onRejected` will never be called more than once. - 3. `$progressHandler` may be called multiple times. + 3. `$onProgress` may be called multiple times. #### See also @@ -148,19 +148,18 @@ the same call to `then()`: ### Resolver The Resolver represents the responsibility of fulfilling, rejecting and -notifying the associated Promise. +progressing the associated Promise. A Resolver has 3 methods: `resolve()`, `reject()` and `progress()`: ``` php -$resolver->resolve(mixed $result = null); +$resolver->resolve(mixed $value = null); ``` -Resolves a Deferred. All consumers are notified by having their -`$fulfilledHandler` (which they registered via `$promise->then()`) called with -`$result`. +Resolves a Deferred. All consumers are notified by having `$onFulfilled` +(which they registered via `$promise->then()`) called with `$value`. -If `$result` itself is a promise, the Deferred will transition to the state of +If `$value` itself is a promise, the Deferred will transition to the state of this promise once it is resolved. ``` php @@ -168,8 +167,8 @@ $resolver->reject(mixed $reason = null); ``` Rejects a Deferred, signalling that the Deferred's computation failed. -All consumers are notified by having their `$errorHandler` (which they -registered via `$promise->then()`) called with `$reason`. +All consumers are notified by having `$onRejected` (which they registered via +`$promise->then()`) called with `$reason`. If `$reason` itself is a promise, the Deferred will be rejected with the outcome of this promise regardless whether it fulfills or rejects. @@ -181,8 +180,8 @@ $resolver->progress(mixed $update = null); Triggers progress notifications, to indicate to consumers that the computation is making progress toward its result. -All consumers are notified by having their `$progressHandler` (which they -registered via `$promise->then()`) called with `$update`. +All consumers are notified by having `$onProgress` (which they registered via +`$promise->then()`) called with `$update`. ### When @@ -192,7 +191,7 @@ mapping and reducing collections of Promises. #### When::all() ``` php -$promise = React\Promise\When::all(array|React\Promise\PromiseInterface $promisesOrValues, callable $fulfilledHandler = null, callable $errorHandler = null, callable $progressHandler = null); +$promise = React\Promise\When::all(array|React\Promise\PromiseInterface $promisesOrValues, callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); ``` Returns a Promise that will resolve only once all the items in @@ -203,7 +202,7 @@ will be an array containing the resolution values of each of the items in #### When::any() ``` php -$promise = React\Promise\When::any(array|React\Promise\PromiseInterface $promisesOrValues, callable $fulfilledHandler = null, callable $errorHandler = null, callable $progressHandler = null); +$promise = React\Promise\When::any(array|React\Promise\PromiseInterface $promisesOrValues, callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); ``` Returns a Promise that will resolve when any one of the items in @@ -216,7 +215,7 @@ rejected. The rejection value will be an array of all rejection reasons. #### When::some() ``` php -$promise = React\Promise\When::some(array|React\Promise\PromiseInterface $promisesOrValues, integer $howMany, callable $fulfilledHandler = null, callable $errorHandler = null, callable $progressHandler = null); +$promise = React\Promise\When::some(array|React\Promise\PromiseInterface $promisesOrValues, integer $howMany, callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); ``` Returns a Promise that will resolve when `$howMany` of the supplied items in @@ -334,8 +333,8 @@ function getAwesomeResultPromise() getAwesomeResultPromise() ->then( - function ($result) { - // Deferred resolved, do something with $result + function ($value) { + // Deferred resolved, do something with $value }, function ($reason) { // Deferred rejected, do something with $reason diff --git a/src/React/Promise/Deferred.php b/src/React/Promise/Deferred.php index ef084aee..7e58900f 100644 --- a/src/React/Promise/Deferred.php +++ b/src/React/Promise/Deferred.php @@ -10,33 +10,33 @@ class Deferred implements PromiseInterface, ResolverInterface, PromisorInterface private $handlers = array(); private $progressHandlers = array(); - public function then($fulfilledHandler = null, $errorHandler = null, $progressHandler = null) + public function then($onFulfilled = null, $onRejected = null, $onProgress = null) { if (null !== $this->completed) { - return $this->completed->then($fulfilledHandler, $errorHandler, $progressHandler); + return $this->completed->then($onFulfilled, $onRejected, $onProgress); } $deferred = new static(); - if (is_callable($progressHandler)) { - $progHandler = function ($update) use ($deferred, $progressHandler) { + if (is_callable($onProgress)) { + $progHandler = function ($update) use ($deferred, $onProgress) { try { - $deferred->progress(call_user_func($progressHandler, $update)); + $deferred->progress(call_user_func($onProgress, $update)); } catch (\Exception $e) { $deferred->progress($e); } }; } else { - if (null !== $progressHandler) { - trigger_error('Invalid $progressHandler argument passed to then(), must be null or callable.', E_USER_NOTICE); + if (null !== $onProgress) { + trigger_error('Invalid $onProgress argument passed to then(), must be null or callable.', E_USER_NOTICE); } $progHandler = array($deferred, 'progress'); } - $this->handlers[] = function ($promise) use ($fulfilledHandler, $errorHandler, $deferred, $progHandler) { + $this->handlers[] = function ($promise) use ($onFulfilled, $onRejected, $deferred, $progHandler) { $promise - ->then($fulfilledHandler, $errorHandler) + ->then($onFulfilled, $onRejected) ->then( array($deferred, 'resolve'), array($deferred, 'reject'), @@ -49,13 +49,13 @@ public function then($fulfilledHandler = null, $errorHandler = null, $progressHa return $deferred->promise(); } - public function resolve($result = null) + public function resolve($value = null) { if (null !== $this->completed) { - return Util::promiseFor($result); + return Util::promiseFor($value); } - $this->completed = Util::promiseFor($result); + $this->completed = Util::promiseFor($value); $this->processQueue($this->handlers, $this->completed); diff --git a/src/React/Promise/DeferredPromise.php b/src/React/Promise/DeferredPromise.php index 6ca2f4fc..e09c0702 100644 --- a/src/React/Promise/DeferredPromise.php +++ b/src/React/Promise/DeferredPromise.php @@ -11,8 +11,8 @@ public function __construct(Deferred $deferred) $this->deferred = $deferred; } - public function then($fulfilledHandler = null, $errorHandler = null, $progressHandler = null) + public function then($onFulfilled = null, $onRejected = null, $onProgress = null) { - return $this->deferred->then($fulfilledHandler, $errorHandler, $progressHandler); + return $this->deferred->then($onFulfilled, $onRejected, $onProgress); } } diff --git a/src/React/Promise/FulfilledPromise.php b/src/React/Promise/FulfilledPromise.php index 0d5aa144..20689e77 100644 --- a/src/React/Promise/FulfilledPromise.php +++ b/src/React/Promise/FulfilledPromise.php @@ -4,25 +4,25 @@ class FulfilledPromise implements PromiseInterface { - private $result; + private $value; - public function __construct($result = null) + public function __construct($value = null) { - $this->result = $result; + $this->value = $value; } - public function then($fulfilledHandler = null, $errorHandler = null, $progressHandler = null) + public function then($onFulfilled = null, $onRejected = null, $onProgress = null) { try { - $result = $this->result; + $value = $this->value; - if (is_callable($fulfilledHandler)) { - $result = call_user_func($fulfilledHandler, $result); - } elseif (null !== $fulfilledHandler) { - trigger_error('Invalid $fulfilledHandler argument passed to then(), must be null or callable.', E_USER_NOTICE); + if (is_callable($onFulfilled)) { + $value = call_user_func($onFulfilled, $value); + } elseif (null !== $onFulfilled) { + trigger_error('Invalid $onFulfilled argument passed to then(), must be null or callable.', E_USER_NOTICE); } - return Util::promiseFor($result); + return Util::promiseFor($value); } catch (\Exception $exception) { return new RejectedPromise($exception); } diff --git a/src/React/Promise/LazyPromise.php b/src/React/Promise/LazyPromise.php index 308f7e95..e9932e10 100644 --- a/src/React/Promise/LazyPromise.php +++ b/src/React/Promise/LazyPromise.php @@ -12,7 +12,7 @@ public function __construct($factory) $this->factory = $factory; } - public function then($fulfilledHandler = null, $errorHandler = null, $progressHandler = null) + public function then($onFulfilled = null, $onRejected = null, $onProgress = null) { if (null === $this->promise) { try { @@ -22,6 +22,6 @@ public function then($fulfilledHandler = null, $errorHandler = null, $progressHa } } - return $this->promise->then($fulfilledHandler, $errorHandler, $progressHandler); + return $this->promise->then($onFulfilled, $onRejected, $onProgress); } } diff --git a/src/React/Promise/PromiseInterface.php b/src/React/Promise/PromiseInterface.php index 9d23be2e..8af5d15c 100644 --- a/src/React/Promise/PromiseInterface.php +++ b/src/React/Promise/PromiseInterface.php @@ -4,5 +4,5 @@ interface PromiseInterface { - public function then($fulfilledHandler = null, $errorHandler = null, $progressHandler = null); + public function then($onFulfilled = null, $onRejected = null, $onProgress = null); } diff --git a/src/React/Promise/RejectedPromise.php b/src/React/Promise/RejectedPromise.php index ef507a53..0b943c7c 100644 --- a/src/React/Promise/RejectedPromise.php +++ b/src/React/Promise/RejectedPromise.php @@ -11,18 +11,18 @@ public function __construct($reason = null) $this->reason = $reason; } - public function then($fulfilledHandler = null, $errorHandler = null, $progressHandler = null) + public function then($onFulfilled = null, $onRejected = null, $onProgress = null) { try { - if (!is_callable($errorHandler)) { - if (null !== $errorHandler) { - trigger_error('Invalid $errorHandler argument passed to then(), must be null or callable.', E_USER_NOTICE); + if (!is_callable($onRejected)) { + if (null !== $onRejected) { + trigger_error('Invalid $onRejected argument passed to then(), must be null or callable.', E_USER_NOTICE); } return new RejectedPromise($this->reason); } - return Util::promiseFor(call_user_func($errorHandler, $this->reason)); + return Util::promiseFor(call_user_func($onRejected, $this->reason)); } catch (\Exception $exception) { return new RejectedPromise($exception); } diff --git a/src/React/Promise/ResolverInterface.php b/src/React/Promise/ResolverInterface.php index 32d735e2..f4a93610 100644 --- a/src/React/Promise/ResolverInterface.php +++ b/src/React/Promise/ResolverInterface.php @@ -4,7 +4,7 @@ interface ResolverInterface { - public function resolve($result = null); + public function resolve($value = null); public function reject($reason = null); public function progress($update = null); } diff --git a/src/React/Promise/When.php b/src/React/Promise/When.php index 321f0bc0..b55b301f 100644 --- a/src/React/Promise/When.php +++ b/src/React/Promise/When.php @@ -19,29 +19,29 @@ public static function lazy($factory) return new LazyPromise($factory); } - public static function all($promisesOrValues, $fulfilledHandler = null, $errorHandler = null, $progressHandler = null) + public static function all($promisesOrValues, $onFulfilled = null, $onRejected = null, $onProgress = null) { $promise = static::map($promisesOrValues, function ($val) { return $val; }); - return $promise->then($fulfilledHandler, $errorHandler, $progressHandler); + return $promise->then($onFulfilled, $onRejected, $onProgress); } - public static function any($promisesOrValues, $fulfilledHandler = null, $errorHandler = null, $progressHandler = null) + public static function any($promisesOrValues, $onFulfilled = null, $onRejected = null, $onProgress = null) { - $unwrapSingleResult = function ($val) use ($fulfilledHandler) { + $unwrapSingleResult = function ($val) use ($onFulfilled) { $val = array_shift($val); - return $fulfilledHandler ? $fulfilledHandler($val) : $val; + return $onFulfilled ? $onFulfilled($val) : $val; }; - return static::some($promisesOrValues, 1, $unwrapSingleResult, $errorHandler, $progressHandler); + return static::some($promisesOrValues, 1, $unwrapSingleResult, $onRejected, $onProgress); } - public static function some($promisesOrValues, $howMany, $fulfilledHandler = null, $errorHandler = null, $progressHandler = null) + public static function some($promisesOrValues, $howMany, $onFulfilled = null, $onRejected = null, $onProgress = null) { - return When::resolve($promisesOrValues)->then(function ($array) use ($howMany, $fulfilledHandler, $errorHandler, $progressHandler) { + return When::resolve($promisesOrValues)->then(function ($array) use ($howMany, $onFulfilled, $onRejected, $onProgress) { if (!is_array($array)) { $array = array(); } @@ -100,7 +100,7 @@ public static function some($promisesOrValues, $howMany, $fulfilledHandler = nul } } - return $deferred->then($fulfilledHandler, $errorHandler, $progressHandler); + return $deferred->then($onFulfilled, $onRejected, $onProgress); }); } @@ -112,21 +112,21 @@ public static function map($promisesOrValues, $mapFunc) } $toResolve = count($array); - $results = array(); + $values = array(); $deferred = new Deferred(); if (!$toResolve) { - $deferred->resolve($results); + $deferred->resolve($values); } else { - $resolve = function ($item, $i) use ($mapFunc, &$results, &$toResolve, $deferred) { + $resolve = function ($item, $i) use ($mapFunc, &$values, &$toResolve, $deferred) { When::resolve($item) ->then($mapFunc) ->then( - function ($mapped) use (&$results, $i, &$toResolve, $deferred) { - $results[$i] = $mapped; + function ($mapped) use (&$values, $i, &$toResolve, $deferred) { + $values[$i] = $mapped; if (0 === --$toResolve) { - $deferred->resolve($results); + $deferred->resolve($values); } }, array($deferred, 'reject') diff --git a/tests/React/Promise/DeferredProgressTest.php b/tests/React/Promise/DeferredProgressTest.php index 838105da..6df0ee01 100644 --- a/tests/React/Promise/DeferredProgressTest.php +++ b/tests/React/Promise/DeferredProgressTest.php @@ -343,7 +343,7 @@ public function shouldIgnoreNonFunctionsAndTriggerPhpNotice($var) $d->progress(1); - $errorCollector->assertCollectedError('Invalid $progressHandler argument passed to then(), must be null or callable.', E_USER_NOTICE); + $errorCollector->assertCollectedError('Invalid $onProgress argument passed to then(), must be null or callable.', E_USER_NOTICE); $errorCollector->unregister(); } } diff --git a/tests/React/Promise/DeferredRejectTest.php b/tests/React/Promise/DeferredRejectTest.php index f10d9beb..b3fef741 100644 --- a/tests/React/Promise/DeferredRejectTest.php +++ b/tests/React/Promise/DeferredRejectTest.php @@ -154,7 +154,7 @@ public function shouldIgnoreNonFunctionsAndTriggerPhpNotice($var) $d->reject(1); - $errorCollector->assertCollectedError('Invalid $errorHandler argument passed to then(), must be null or callable.', E_USER_NOTICE); + $errorCollector->assertCollectedError('Invalid $onRejected argument passed to then(), must be null or callable.', E_USER_NOTICE); $errorCollector->unregister(); } } diff --git a/tests/React/Promise/DeferredResolveTest.php b/tests/React/Promise/DeferredResolveTest.php index 3ee7027c..055e07a1 100644 --- a/tests/React/Promise/DeferredResolveTest.php +++ b/tests/React/Promise/DeferredResolveTest.php @@ -190,7 +190,7 @@ public function shouldIgnoreNonFunctionsAndTriggerPhpNotice($var) $d->resolve(1); - $errorCollector->assertCollectedError('Invalid $fulfilledHandler argument passed to then(), must be null or callable.', E_USER_NOTICE); + $errorCollector->assertCollectedError('Invalid $onFulfilled argument passed to then(), must be null or callable.', E_USER_NOTICE); $errorCollector->unregister(); } } diff --git a/tests/React/Promise/LazyPromiseTest.php b/tests/React/Promise/LazyPromiseTest.php index 52a2517f..34d87d9a 100644 --- a/tests/React/Promise/LazyPromiseTest.php +++ b/tests/React/Promise/LazyPromiseTest.php @@ -40,15 +40,15 @@ public function shouldReturnPromiseFromFactory() ->method('__invoke') ->will($this->returnValue(new FulfilledPromise(1))); - $fulfilledHandler = $this->createCallableMock(); - $fulfilledHandler + $onFulfilled = $this->createCallableMock(); + $onFulfilled ->expects($this->once()) ->method('__invoke') ->with($this->identicalTo(1)); $p = new LazyPromise($factory); - $p->then($fulfilledHandler); + $p->then($onFulfilled); } /** @test */ @@ -75,14 +75,14 @@ public function shouldReturnRejectedPromiseIfFactoryThrowsException() ->method('__invoke') ->will($this->throwException($exception)); - $errorHandler = $this->createCallableMock(); - $errorHandler + $onRejected = $this->createCallableMock(); + $onRejected ->expects($this->once()) ->method('__invoke') ->with($this->identicalTo($exception)); $p = new LazyPromise($factory); - $p->then($this->expectCallableNever(), $errorHandler); + $p->then($this->expectCallableNever(), $onRejected); } } From a4d6358a26579a4c2b1840bff0ffa5bb8bbe1134 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Mon, 21 Oct 2013 15:00:29 +0200 Subject: [PATCH 03/29] Add callable typehints Also removes trigger_error's for non-function arguments and related tests --- src/React/Promise/Deferred.php | 6 +--- src/React/Promise/DeferredPromise.php | 2 +- src/React/Promise/FulfilledPromise.php | 6 ++-- src/React/Promise/LazyPromise.php | 4 +-- src/React/Promise/PromiseInterface.php | 2 +- src/React/Promise/RejectedPromise.php | 8 ++--- src/React/Promise/When.php | 10 +++--- tests/React/Promise/DeferredProgressTest.php | 34 ------------------ tests/React/Promise/DeferredPromiseTest.php | 6 ++-- tests/React/Promise/DeferredRejectTest.php | 32 ----------------- tests/React/Promise/DeferredResolveTest.php | 31 ---------------- tests/React/Promise/ErrorCollector.php | 38 -------------------- tests/React/Promise/TestCase.php | 12 ------- 13 files changed, 18 insertions(+), 173 deletions(-) delete mode 100644 tests/React/Promise/ErrorCollector.php diff --git a/src/React/Promise/Deferred.php b/src/React/Promise/Deferred.php index 7e58900f..482a54a6 100644 --- a/src/React/Promise/Deferred.php +++ b/src/React/Promise/Deferred.php @@ -10,7 +10,7 @@ class Deferred implements PromiseInterface, ResolverInterface, PromisorInterface private $handlers = array(); private $progressHandlers = array(); - public function then($onFulfilled = null, $onRejected = null, $onProgress = null) + public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) { if (null !== $this->completed) { return $this->completed->then($onFulfilled, $onRejected, $onProgress); @@ -27,10 +27,6 @@ public function then($onFulfilled = null, $onRejected = null, $onProgress = null } }; } else { - if (null !== $onProgress) { - trigger_error('Invalid $onProgress argument passed to then(), must be null or callable.', E_USER_NOTICE); - } - $progHandler = array($deferred, 'progress'); } diff --git a/src/React/Promise/DeferredPromise.php b/src/React/Promise/DeferredPromise.php index e09c0702..ca5b1bed 100644 --- a/src/React/Promise/DeferredPromise.php +++ b/src/React/Promise/DeferredPromise.php @@ -11,7 +11,7 @@ public function __construct(Deferred $deferred) $this->deferred = $deferred; } - public function then($onFulfilled = null, $onRejected = null, $onProgress = null) + public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) { return $this->deferred->then($onFulfilled, $onRejected, $onProgress); } diff --git a/src/React/Promise/FulfilledPromise.php b/src/React/Promise/FulfilledPromise.php index 20689e77..a8a80242 100644 --- a/src/React/Promise/FulfilledPromise.php +++ b/src/React/Promise/FulfilledPromise.php @@ -11,15 +11,13 @@ public function __construct($value = null) $this->value = $value; } - public function then($onFulfilled = null, $onRejected = null, $onProgress = null) + public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) { try { $value = $this->value; - if (is_callable($onFulfilled)) { + if (null !== $onFulfilled) { $value = call_user_func($onFulfilled, $value); - } elseif (null !== $onFulfilled) { - trigger_error('Invalid $onFulfilled argument passed to then(), must be null or callable.', E_USER_NOTICE); } return Util::promiseFor($value); diff --git a/src/React/Promise/LazyPromise.php b/src/React/Promise/LazyPromise.php index e9932e10..d78e576c 100644 --- a/src/React/Promise/LazyPromise.php +++ b/src/React/Promise/LazyPromise.php @@ -7,12 +7,12 @@ class LazyPromise implements PromiseInterface private $factory; private $promise; - public function __construct($factory) + public function __construct(callable $factory) { $this->factory = $factory; } - public function then($onFulfilled = null, $onRejected = null, $onProgress = null) + public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) { if (null === $this->promise) { try { diff --git a/src/React/Promise/PromiseInterface.php b/src/React/Promise/PromiseInterface.php index 8af5d15c..422f9f16 100644 --- a/src/React/Promise/PromiseInterface.php +++ b/src/React/Promise/PromiseInterface.php @@ -4,5 +4,5 @@ interface PromiseInterface { - public function then($onFulfilled = null, $onRejected = null, $onProgress = null); + public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); } diff --git a/src/React/Promise/RejectedPromise.php b/src/React/Promise/RejectedPromise.php index 0b943c7c..d24809ed 100644 --- a/src/React/Promise/RejectedPromise.php +++ b/src/React/Promise/RejectedPromise.php @@ -11,14 +11,10 @@ public function __construct($reason = null) $this->reason = $reason; } - public function then($onFulfilled = null, $onRejected = null, $onProgress = null) + public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) { try { - if (!is_callable($onRejected)) { - if (null !== $onRejected) { - trigger_error('Invalid $onRejected argument passed to then(), must be null or callable.', E_USER_NOTICE); - } - + if (null === $onRejected) { return new RejectedPromise($this->reason); } diff --git a/src/React/Promise/When.php b/src/React/Promise/When.php index b55b301f..bc9cffee 100644 --- a/src/React/Promise/When.php +++ b/src/React/Promise/When.php @@ -19,7 +19,7 @@ public static function lazy($factory) return new LazyPromise($factory); } - public static function all($promisesOrValues, $onFulfilled = null, $onRejected = null, $onProgress = null) + public static function all($promisesOrValues, callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) { $promise = static::map($promisesOrValues, function ($val) { return $val; @@ -28,7 +28,7 @@ public static function all($promisesOrValues, $onFulfilled = null, $onRejected = return $promise->then($onFulfilled, $onRejected, $onProgress); } - public static function any($promisesOrValues, $onFulfilled = null, $onRejected = null, $onProgress = null) + public static function any($promisesOrValues, callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) { $unwrapSingleResult = function ($val) use ($onFulfilled) { $val = array_shift($val); @@ -39,7 +39,7 @@ public static function any($promisesOrValues, $onFulfilled = null, $onRejected = return static::some($promisesOrValues, 1, $unwrapSingleResult, $onRejected, $onProgress); } - public static function some($promisesOrValues, $howMany, $onFulfilled = null, $onRejected = null, $onProgress = null) + public static function some($promisesOrValues, $howMany, callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) { return When::resolve($promisesOrValues)->then(function ($array) use ($howMany, $onFulfilled, $onRejected, $onProgress) { if (!is_array($array)) { @@ -104,7 +104,7 @@ public static function some($promisesOrValues, $howMany, $onFulfilled = null, $o }); } - public static function map($promisesOrValues, $mapFunc) + public static function map($promisesOrValues, callable $mapFunc) { return When::resolve($promisesOrValues)->then(function ($array) use ($mapFunc) { if (!is_array($array)) { @@ -142,7 +142,7 @@ function ($mapped) use (&$values, $i, &$toResolve, $deferred) { }); } - public static function reduce($promisesOrValues, $reduceFunc , $initialValue = null) + public static function reduce($promisesOrValues, callable $reduceFunc , $initialValue = null) { return When::resolve($promisesOrValues)->then(function ($array) use ($reduceFunc, $initialValue) { if (!is_array($array)) { diff --git a/tests/React/Promise/DeferredProgressTest.php b/tests/React/Promise/DeferredProgressTest.php index 6df0ee01..8020d069 100644 --- a/tests/React/Promise/DeferredProgressTest.php +++ b/tests/React/Promise/DeferredProgressTest.php @@ -312,38 +312,4 @@ public function shouldAllowRejectAfterProgress() ->resolver() ->reject(2); } - - /** - * @test - * @dataProvider invalidCallbackDataProvider - **/ - public function shouldIgnoreNonFunctionsAndTriggerPhpNotice($var) - { - $errorCollector = new ErrorCollector(); - $errorCollector->register(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $d = new Deferred(); - $d - ->then( - null, - null, - $var - ) - ->then( - $this->expectCallableNever(), - $this->expectCallableNever(), - $mock - ); - - $d->progress(1); - - $errorCollector->assertCollectedError('Invalid $onProgress argument passed to then(), must be null or callable.', E_USER_NOTICE); - $errorCollector->unregister(); - } } diff --git a/tests/React/Promise/DeferredPromiseTest.php b/tests/React/Promise/DeferredPromiseTest.php index 123f65c7..925fd858 100644 --- a/tests/React/Promise/DeferredPromiseTest.php +++ b/tests/React/Promise/DeferredPromiseTest.php @@ -11,13 +11,15 @@ class DeferredPromiseTest extends TestCase /** @test */ public function shouldForwardToDeferred() { + $callable = $this->createCallableMock(); + $mock = $this->getMock('React\\Promise\\Deferred'); $mock ->expects($this->once()) ->method('then') - ->with(1, 2, 3); + ->with($callable, $callable, $callable); $p = new DeferredPromise($mock); - $p->then(1, 2, 3); + $p->then($callable, $callable, $callable); } } diff --git a/tests/React/Promise/DeferredRejectTest.php b/tests/React/Promise/DeferredRejectTest.php index b3fef741..3486082e 100644 --- a/tests/React/Promise/DeferredRejectTest.php +++ b/tests/React/Promise/DeferredRejectTest.php @@ -125,36 +125,4 @@ public function shouldForwardReasonWhenCallbackIsNull() $d->reject(1); } - - /** - * @test - * @dataProvider invalidCallbackDataProvider - **/ - public function shouldIgnoreNonFunctionsAndTriggerPhpNotice($var) - { - $errorCollector = new ErrorCollector(); - $errorCollector->register(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $d = new Deferred(); - $d - ->then( - null, - $var - ) - ->then( - $this->expectCallableNever(), - $mock - ); - - $d->reject(1); - - $errorCollector->assertCollectedError('Invalid $onRejected argument passed to then(), must be null or callable.', E_USER_NOTICE); - $errorCollector->unregister(); - } } diff --git a/tests/React/Promise/DeferredResolveTest.php b/tests/React/Promise/DeferredResolveTest.php index 055e07a1..e26f61c3 100644 --- a/tests/React/Promise/DeferredResolveTest.php +++ b/tests/React/Promise/DeferredResolveTest.php @@ -162,35 +162,4 @@ public function shouldForwardValueWhenCallbackIsNull() $d->resolve(1); } - - /** - * @test - * @dataProvider invalidCallbackDataProvider - **/ - public function shouldIgnoreNonFunctionsAndTriggerPhpNotice($var) - { - $errorCollector = new ErrorCollector(); - $errorCollector->register(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $d = new Deferred(); - $d - ->then( - $var - ) - ->then( - $mock, - $this->expectCallableNever() - ); - - $d->resolve(1); - - $errorCollector->assertCollectedError('Invalid $onFulfilled argument passed to then(), must be null or callable.', E_USER_NOTICE); - $errorCollector->unregister(); - } } diff --git a/tests/React/Promise/ErrorCollector.php b/tests/React/Promise/ErrorCollector.php deleted file mode 100644 index 648ab68a..00000000 --- a/tests/React/Promise/ErrorCollector.php +++ /dev/null @@ -1,38 +0,0 @@ -errors = &$errors; - } - - public function unregister() - { - $this->errors = array(); - restore_error_handler(); - } - - public function assertCollectedError($errstr, $errno) - { - foreach ($this->errors as $error) { - if ($error['errstr'] === $errstr && $error['errno'] === $errno) { - return; - } - } - - $message = 'Error with level ' . $errno . ' and message "' . $errstr . '" not found in ' . var_export($this->errors, true); - - throw new \PHPUnit_Framework_AssertionFailedError($message); - } -} diff --git a/tests/React/Promise/TestCase.php b/tests/React/Promise/TestCase.php index 1d5e4f97..60ea46d7 100644 --- a/tests/React/Promise/TestCase.php +++ b/tests/React/Promise/TestCase.php @@ -38,16 +38,4 @@ public function createCallableMock() { return $this->getMock('React\\Promise\Stub\CallableStub'); } - - public function invalidCallbackDataProvider() - { - return array( - 'empty string' => array(''), - 'true' => array(true), - 'false' => array(false), - 'object' => array(new \stdClass), - 'truthy' => array(1), - 'falsey' => array(0) - ); - } } From fb0e424ea9a63423f00383ee65d7a026e229a68f Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 09:05:38 +0200 Subject: [PATCH 04/29] Get rid of When/Util classes and move its methods to functions --- README.md | 158 +++++++++--------- composer.json | 3 +- src/React/Promise/Deferred.php | 6 +- src/React/Promise/FulfilledPromise.php | 2 +- src/React/Promise/LazyPromise.php | 2 +- src/React/Promise/RejectedPromise.php | 2 +- src/React/Promise/Util.php | 26 --- src/React/Promise/{When.php => functions.php} | 95 ++++++----- tests/React/Promise/DeferredResolveTest.php | 4 +- .../{WhenAllTest.php => FunctionAllTest.php} | 46 ++--- .../{WhenAnyTest.php => FunctionAnyTest.php} | 52 ++---- .../{WhenMapTest.php => FunctionMapTest.php} | 32 ++-- ...nReduceTest.php => FunctionReduceTest.php} | 58 +++---- ...nRejectTest.php => FunctionRejectTest.php} | 22 +-- ...esolveTest.php => FunctionResolveTest.php} | 24 +-- ...{WhenSomeTest.php => FunctionSomeTest.php} | 62 +++---- tests/React/Promise/UtilPromiseForTest.php | 68 -------- .../Promise/UtilRejectedPromiseForTest.php | 68 -------- tests/React/Promise/WhenLazyTest.php | 28 ---- 19 files changed, 253 insertions(+), 505 deletions(-) delete mode 100644 src/React/Promise/Util.php rename src/React/Promise/{When.php => functions.php} (60%) rename tests/React/Promise/{WhenAllTest.php => FunctionAllTest.php} (72%) rename tests/React/Promise/{WhenAnyTest.php => FunctionAnyTest.php} (73%) rename tests/React/Promise/{WhenMapTest.php => FunctionMapTest.php} (83%) rename tests/React/Promise/{WhenReduceTest.php => FunctionReduceTest.php} (87%) rename tests/React/Promise/{WhenRejectTest.php => FunctionRejectTest.php} (76%) rename tests/React/Promise/{WhenResolveTest.php => FunctionResolveTest.php} (78%) rename tests/React/Promise/{WhenSomeTest.php => FunctionSomeTest.php} (72%) delete mode 100644 tests/React/Promise/UtilPromiseForTest.php delete mode 100644 tests/React/Promise/UtilRejectedPromiseForTest.php delete mode 100644 tests/React/Promise/WhenLazyTest.php diff --git a/README.md b/README.md index 80bd9ade..5c767417 100644 --- a/README.md +++ b/README.md @@ -17,16 +17,16 @@ Table of Contents 3. [API](#api) * [Deferred](#deferred-1) * [Promise](#promise-1) + * [LazyPromise](#lazypromise) * [Resolver](#resolver-1) - * [When](#when) - * [When::all()](#whenall) - * [When::any()](#whenany) - * [When::some()](#whensome) - * [When::map()](#whenmap) - * [When::reduce()](#whenreduce) - * [When::resolve()](#whenresolve) - * [When::reject()](#whenreject) - * [When::lazy()](#whenlazy) + * [Functions](#functions) + * [resolve()](#resolve) + * [reject()](#reject) + * [all()](#all) + * [any()](#any) + * [some()](#some) + * [map()](#map) + * [reduce()](#reduce) * [Promisor](#promisor) 4. [Examples](#examples) * [How to use Deferred](#how-to-use-deferred) @@ -142,8 +142,29 @@ the same call to `then()`: #### See also -* [When::resolve()](#whenresolve) - Creating a resolved Promise -* [When::reject()](#whenreject) - Creating a rejected Promise +* [resolve()](#resolve) - Creating a resolved Promise +* [reject()](#reject) - Creating a rejected Promise + +### LazyPromise + +Creates a Promise which will be lazily initialized by `$factory` once a consumer +calls the `then()` method. + +```php +$factory = function () { + $deferred = new React\Promise\Deferred(); + + // Do some heavy stuff here and resolve the Deferred once completed + + return $deferred->promise(); +}; + +$promise = React\Promise\LazyPromise($factory); + +// $factory will only be executed once we call then() +$promise->then(function ($value) { +}); +``` ### Resolver @@ -183,15 +204,46 @@ is making progress toward its result. All consumers are notified by having `$onProgress` (which they registered via `$promise->then()`) called with `$update`. -### When +### Functions + +Useful functions for creating, joining, mapping and reducing collections of +Promises. + +#### resolve() + +``` php +$promise = React\Promise\resolve(mixed $promiseOrValue); +``` + +Creates a resolved Promise for the supplied `$promiseOrValue`. + +If `$promiseOrValue` is a value, it will be the resolution value of the +returned Promise. + +If `$promiseOrValue` is a Promise, it will simply be returned. + +#### reject() + +``` php +$promise = React\Promise\reject(mixed $promiseOrValue); +``` + +Creates a rejected Promise for the supplied `$promiseOrValue`. + +If `$promiseOrValue` is a value, it will be the rejection value of the +returned Promise. + +If `$promiseOrValue` is a Promise, its completion value will be the rejected +value of the returned Promise. -The `React\Promise\When` class provides useful methods for creating, joining, -mapping and reducing collections of Promises. +This can be useful in situations where you need to reject a Promise without +throwing an exception. For example, it allows you to propagate a rejection with +the value of another Promise. -#### When::all() +#### all() ``` php -$promise = React\Promise\When::all(array|React\Promise\PromiseInterface $promisesOrValues, callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); +$promise = React\Promise\all(array|React\Promise\PromiseInterface $promisesOrValues); ``` Returns a Promise that will resolve only once all the items in @@ -199,10 +251,10 @@ Returns a Promise that will resolve only once all the items in will be an array containing the resolution values of each of the items in `$promisesOrValues`. -#### When::any() +#### any() ``` php -$promise = React\Promise\When::any(array|React\Promise\PromiseInterface $promisesOrValues, callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); +$promise = React\Promise\any(array|React\Promise\PromiseInterface $promisesOrValues); ``` Returns a Promise that will resolve when any one of the items in @@ -212,10 +264,10 @@ will be the resolution value of the triggering item. The returned Promise will only reject if *all* items in `$promisesOrValues` are rejected. The rejection value will be an array of all rejection reasons. -#### When::some() +#### some() ``` php -$promise = React\Promise\When::some(array|React\Promise\PromiseInterface $promisesOrValues, integer $howMany, callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); +$promise = React\Promise\some(array|React\Promise\PromiseInterface $promisesOrValues, integer $howMany); ``` Returns a Promise that will resolve when `$howMany` of the supplied items in @@ -228,10 +280,10 @@ to resolve (that is, when `(count($promisesOrValues) - $howMany) + 1` items reject). The rejection value will be an array of `(count($promisesOrValues) - $howMany) + 1` rejection reasons. -#### When::map() +#### map() ``` php -$promise = React\Promise\When::map(array|React\Promise\PromiseInterface $promisesOrValues, callable $mapFunc); +$promise = React\Promise\map(array|React\Promise\PromiseInterface $promisesOrValues, callable $mapFunc); ``` Traditional map function, similar to `array_map()`, but allows input to contain @@ -240,10 +292,10 @@ Promises and/or values, and `$mapFunc` may return either a value or a Promise. The map function receives each item as argument, where item is a fully resolved value of a Promise or value in `$promisesOrValues`. -#### When::reduce() +#### reduce() ``` php -$promise = React\Promise\When::reduce(array|React\Promise\PromiseInterface $promisesOrValues, callable $reduceFunc , $initialValue = null); +$promise = React\Promise\reduce(array|React\Promise\PromiseInterface $promisesOrValues, callable $reduceFunc , $initialValue = null); ``` Traditional reduce function, similar to `array_reduce()`, but input may contain @@ -251,62 +303,6 @@ Promises and/or values, and `$reduceFunc` may return either a value or a Promise, *and* `$initialValue` may be a Promise or a value for the starting value. -#### When::resolve() - -``` php -$promise = React\Promise\When::resolve(mixed $promiseOrValue); -``` - -Creates a resolved Promise for the supplied `$promiseOrValue`. - -If `$promiseOrValue` is a value, it will be the resolution value of the -returned Promise. - -If `$promiseOrValue` is a Promise, it will simply be returned. - -#### When::reject() - -``` php -$promise = React\Promise\When::reject(mixed $promiseOrValue); -``` - -Creates a rejected Promise for the supplied `$promiseOrValue`. - -If `$promiseOrValue` is a value, it will be the rejection value of the -returned Promise. - -If `$promiseOrValue` is a Promise, its completion value will be the rejected -value of the returned Promise. - -This can be useful in situations where you need to reject a Promise without -throwing an exception. For example, it allows you to propagate a rejection with -the value of another Promise. - -#### When::lazy() - -``` php -$promise = React\Promise\When::lazy(callable $factory); -``` - -Creates a Promise which will be lazily initialized by `$factory` once a consumer -calls the `then()` method. - -```php -$factory = function () { - $deferred = new React\Promise\Deferred(); - - // Do some heavy stuff here and resolve the Deferred once completed - - return $deferred->promise(); -}; - -$promise = React\Promise\When::lazy($factory); - -// $factory will only be executed once we call then() -$promise->then(function ($value) { -}); -``` - ### Promisor The `React\Promise\PromisorInterface` provides a common interface for objects @@ -414,7 +410,7 @@ $deferred->promise() }) ->then(null, function (\Exception $x) { // Can also propagate by returning another rejection - return React\Promise\When::reject((integer) $x->getMessage() + 1); + return React\Promise\reject((integer) $x->getMessage() + 1); }) ->then(null, function ($x) { echo 'Reject ' . $x; // 3 diff --git a/composer.json b/composer.json index 95bf9633..53fcf463 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,8 @@ "autoload": { "psr-0": { "React\\Promise": "src/" - } + }, + "files": ["src/React/Promise/functions.php"] }, "extra": { "branch-alias": { diff --git a/src/React/Promise/Deferred.php b/src/React/Promise/Deferred.php index 482a54a6..e9174846 100644 --- a/src/React/Promise/Deferred.php +++ b/src/React/Promise/Deferred.php @@ -48,10 +48,10 @@ public function then(callable $onFulfilled = null, callable $onRejected = null, public function resolve($value = null) { if (null !== $this->completed) { - return Util::promiseFor($value); + return resolve($value); } - $this->completed = Util::promiseFor($value); + $this->completed = resolve($value); $this->processQueue($this->handlers, $this->completed); @@ -62,7 +62,7 @@ public function resolve($value = null) public function reject($reason = null) { - return $this->resolve(Util::rejectedPromiseFor($reason)); + return $this->resolve(reject($reason)); } public function progress($update = null) diff --git a/src/React/Promise/FulfilledPromise.php b/src/React/Promise/FulfilledPromise.php index a8a80242..3e4d69f0 100644 --- a/src/React/Promise/FulfilledPromise.php +++ b/src/React/Promise/FulfilledPromise.php @@ -20,7 +20,7 @@ public function then(callable $onFulfilled = null, callable $onRejected = null, $value = call_user_func($onFulfilled, $value); } - return Util::promiseFor($value); + return resolve($value); } catch (\Exception $exception) { return new RejectedPromise($exception); } diff --git a/src/React/Promise/LazyPromise.php b/src/React/Promise/LazyPromise.php index d78e576c..82acce19 100644 --- a/src/React/Promise/LazyPromise.php +++ b/src/React/Promise/LazyPromise.php @@ -16,7 +16,7 @@ public function then(callable $onFulfilled = null, callable $onRejected = null, { if (null === $this->promise) { try { - $this->promise = Util::promiseFor(call_user_func($this->factory)); + $this->promise = resolve(call_user_func($this->factory)); } catch (\Exception $exception) { $this->promise = new RejectedPromise($exception); } diff --git a/src/React/Promise/RejectedPromise.php b/src/React/Promise/RejectedPromise.php index d24809ed..b9abd3cc 100644 --- a/src/React/Promise/RejectedPromise.php +++ b/src/React/Promise/RejectedPromise.php @@ -18,7 +18,7 @@ public function then(callable $onFulfilled = null, callable $onRejected = null, return new RejectedPromise($this->reason); } - return Util::promiseFor(call_user_func($onRejected, $this->reason)); + return resolve(call_user_func($onRejected, $this->reason)); } catch (\Exception $exception) { return new RejectedPromise($exception); } diff --git a/src/React/Promise/Util.php b/src/React/Promise/Util.php deleted file mode 100644 index c2d4b546..00000000 --- a/src/React/Promise/Util.php +++ /dev/null @@ -1,26 +0,0 @@ -then(function ($value) { - return new RejectedPromise($value); - }); - } - - return new RejectedPromise($promiseOrValue); - } -} diff --git a/src/React/Promise/When.php b/src/React/Promise/functions.php similarity index 60% rename from src/React/Promise/When.php rename to src/React/Promise/functions.php index bc9cffee..787a8ac8 100644 --- a/src/React/Promise/When.php +++ b/src/React/Promise/functions.php @@ -2,46 +2,45 @@ namespace React\Promise; -class When +function resolve($promiseOrValue = null) { - public static function resolve($promiseOrValue = null) - { - return Util::promiseFor($promiseOrValue); + if ($promiseOrValue instanceof PromiseInterface) { + return $promiseOrValue; } - public static function reject($promiseOrValue = null) - { - return Util::rejectedPromiseFor($promiseOrValue); - } - - public static function lazy($factory) - { - return new LazyPromise($factory); - } + return new FulfilledPromise($promiseOrValue); +} - public static function all($promisesOrValues, callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - $promise = static::map($promisesOrValues, function ($val) { - return $val; +function reject($promiseOrValue = null) +{ + if ($promiseOrValue instanceof PromiseInterface) { + return $promiseOrValue->then(function ($value) { + return new RejectedPromise($value); }); - - return $promise->then($onFulfilled, $onRejected, $onProgress); } - public static function any($promisesOrValues, callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - $unwrapSingleResult = function ($val) use ($onFulfilled) { - $val = array_shift($val); + return new RejectedPromise($promiseOrValue); +} - return $onFulfilled ? $onFulfilled($val) : $val; - }; +function all($promisesOrValues) +{ + return map($promisesOrValues, function ($val) { + return $val; + }); +} - return static::some($promisesOrValues, 1, $unwrapSingleResult, $onRejected, $onProgress); - } +function any($promisesOrValues) +{ + return some($promisesOrValues, 1) + ->then(function ($val) { + return array_shift($val); + }); +} - public static function some($promisesOrValues, $howMany, callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - return When::resolve($promisesOrValues)->then(function ($array) use ($howMany, $onFulfilled, $onRejected, $onProgress) { +function some($promisesOrValues, $howMany) +{ + return resolve($promisesOrValues) + ->then(function ($array) use ($howMany) { if (!is_array($array)) { $array = array(); } @@ -96,17 +95,19 @@ public static function some($promisesOrValues, $howMany, callable $onFulfilled = } }; - When::resolve($promiseOrValue)->then($fulfiller, $rejecter, $progress); + resolve($promiseOrValue) + ->then($fulfiller, $rejecter, $progress); } } - return $deferred->then($onFulfilled, $onRejected, $onProgress); + return $deferred->promise(); }); - } +} - public static function map($promisesOrValues, callable $mapFunc) - { - return When::resolve($promisesOrValues)->then(function ($array) use ($mapFunc) { +function map($promisesOrValues, callable $mapFunc) +{ + return resolve($promisesOrValues) + ->then(function ($array) use ($mapFunc) { if (!is_array($array)) { $array = array(); } @@ -119,7 +120,7 @@ public static function map($promisesOrValues, callable $mapFunc) $deferred->resolve($values); } else { $resolve = function ($item, $i) use ($mapFunc, &$values, &$toResolve, $deferred) { - When::resolve($item) + resolve($item) ->then($mapFunc) ->then( function ($mapped) use (&$values, $i, &$toResolve, $deferred) { @@ -140,11 +141,12 @@ function ($mapped) use (&$values, $i, &$toResolve, $deferred) { return $deferred->promise(); }); - } +} - public static function reduce($promisesOrValues, callable $reduceFunc , $initialValue = null) - { - return When::resolve($promisesOrValues)->then(function ($array) use ($reduceFunc, $initialValue) { +function reduce($promisesOrValues, callable $reduceFunc , $initialValue = null) +{ + return resolve($promisesOrValues) + ->then(function ($array) use ($reduceFunc, $initialValue) { if (!is_array($array)) { $array = array(); } @@ -155,14 +157,15 @@ public static function reduce($promisesOrValues, callable $reduceFunc , $initial // Wrap the supplied $reduceFunc with one that handles promises and then // delegates to the supplied. $wrappedReduceFunc = function ($current, $val) use ($reduceFunc, $total, &$i) { - return When::resolve($current)->then(function ($c) use ($reduceFunc, $total, &$i, $val) { - return When::resolve($val)->then(function ($value) use ($reduceFunc, $total, &$i, $c) { - return call_user_func($reduceFunc, $c, $value, $i++, $total); + return resolve($current) + ->then(function ($c) use ($reduceFunc, $total, &$i, $val) { + return resolve($val) + ->then(function ($value) use ($reduceFunc, $total, &$i, $c) { + return call_user_func($reduceFunc, $c, $value, $i++, $total); + }); }); - }); }; return array_reduce($array, $wrappedReduceFunc, $initialValue); }); - } } diff --git a/tests/React/Promise/DeferredResolveTest.php b/tests/React/Promise/DeferredResolveTest.php index e26f61c3..131b54cf 100644 --- a/tests/React/Promise/DeferredResolveTest.php +++ b/tests/React/Promise/DeferredResolveTest.php @@ -98,7 +98,7 @@ public function shouldReturnAPromiseForAPromisedResolutionValue() $d ->resolver() - ->resolve(When::resolve(1)) + ->resolve(resolve(1)) ->then($mock); } @@ -117,7 +117,7 @@ public function shouldReturnAPromiseForAPromisedRejectionValue() // be rejected with the same value $d ->resolver() - ->resolve(When::reject(1)) + ->resolve(reject(1)) ->then($this->expectCallableNever(), $mock); } diff --git a/tests/React/Promise/WhenAllTest.php b/tests/React/Promise/FunctionAllTest.php similarity index 72% rename from tests/React/Promise/WhenAllTest.php rename to tests/React/Promise/FunctionAllTest.php index 555eb382..991e0650 100644 --- a/tests/React/Promise/WhenAllTest.php +++ b/tests/React/Promise/FunctionAllTest.php @@ -2,11 +2,7 @@ namespace React\Promise; -/** - * @group When - * @group WhenAll - */ -class WhenAllTest extends TestCase +class FunctionAllTest extends TestCase { /** @test */ public function shouldResolveEmptyInput() @@ -17,7 +13,8 @@ public function shouldResolveEmptyInput() ->method('__invoke') ->with($this->identicalTo(array())); - When::all(array(), $mock); + all(array()) + ->then($mock); } /** @test */ @@ -29,10 +26,8 @@ public function shouldResolveValuesArray() ->method('__invoke') ->with($this->identicalTo(array(1, 2, 3))); - When::all( - array(1, 2, 3), - $mock - ); + all(array(1, 2, 3)) + ->then($mock); } /** @test */ @@ -44,10 +39,8 @@ public function shouldResolvePromisesArray() ->method('__invoke') ->with($this->identicalTo(array(1, 2, 3))); - When::all( - array(When::resolve(1), When::resolve(2), When::resolve(3)), - $mock - ); + all(array(resolve(1), resolve(2), resolve(3))) + ->then($mock); } /** @test */ @@ -59,10 +52,8 @@ public function shouldResolveSparseArrayInput() ->method('__invoke') ->with($this->identicalTo(array(null, 1, null, 1, 1))); - When::all( - array(null, 1, null, 1, 1), - $mock - ); + all(array(null, 1, null, 1, 1)) + ->then($mock); } /** @test */ @@ -74,11 +65,8 @@ public function shouldRejectIfAnyInputPromiseRejects() ->method('__invoke') ->with($this->identicalTo(2)); - When::all( - array(When::resolve(1), When::reject(2), When::resolve(3)), - $this->expectCallableNever(), - $mock - ); + all(array(resolve(1), reject(2), resolve(3))) + ->then($this->expectCallableNever(), $mock); } /** @test */ @@ -90,10 +78,8 @@ public function shouldAcceptAPromiseForAnArray() ->method('__invoke') ->with($this->identicalTo(array(1, 2, 3))); - When::all( - When::resolve(array(1, 2, 3)), - $mock - ); + all(resolve(array(1, 2, 3))) + ->then($mock); } /** @test */ @@ -105,9 +91,7 @@ public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray() ->method('__invoke') ->with($this->identicalTo(array())); - When::all( - When::resolve(1), - $mock - ); + all(resolve(1)) + ->then($mock); } } diff --git a/tests/React/Promise/WhenAnyTest.php b/tests/React/Promise/FunctionAnyTest.php similarity index 73% rename from tests/React/Promise/WhenAnyTest.php rename to tests/React/Promise/FunctionAnyTest.php index 467fec1a..1fa77f6d 100644 --- a/tests/React/Promise/WhenAnyTest.php +++ b/tests/React/Promise/FunctionAnyTest.php @@ -2,11 +2,7 @@ namespace React\Promise; -/** - * @group When - * @group WhenAny - */ -class WhenAnyTest extends TestCase +class FunctionAnyTest extends TestCase { /** @test */ public function shouldResolveToNullWithEmptyInputArray() @@ -17,7 +13,8 @@ public function shouldResolveToNullWithEmptyInputArray() ->method('__invoke') ->with($this->identicalTo(null)); - When::any(array(), $mock); + any(array()) + ->then($mock); } /** @test */ @@ -29,10 +26,8 @@ public function shouldResolveWithAnInputValue() ->method('__invoke') ->with($this->identicalTo(1)); - When::any( - array(1, 2, 3), - $mock - ); + any(array(1, 2, 3)) + ->then($mock); } /** @test */ @@ -44,10 +39,8 @@ public function shouldResolveWithAPromisedInputValue() ->method('__invoke') ->with($this->identicalTo(1)); - When::any( - array(When::resolve(1), When::resolve(2), When::resolve(3)), - $mock - ); + any(array(resolve(1), resolve(2), resolve(3))) + ->then($mock); } /** @test */ @@ -59,11 +52,8 @@ public function shouldRejectWithAllRejectedInputValuesIfAllInputsAreRejected() ->method('__invoke') ->with($this->identicalTo(array(0 => 1, 1 => 2, 2 => 3))); - When::any( - array(When::reject(1), When::reject(2), When::reject(3)), - $this->expectCallableNever(), - $mock - ); + any(array(reject(1), reject(2), reject(3))) + ->then($this->expectCallableNever(), $mock); } /** @test */ @@ -75,10 +65,8 @@ public function shouldResolveWhenFirstInputPromiseResolves() ->method('__invoke') ->with($this->identicalTo(1)); - When::any( - array(When::resolve(1), When::reject(2), When::reject(3)), - $mock - ); + any(array(resolve(1), reject(2), reject(3))) + ->then($mock); } /** @test */ @@ -90,10 +78,8 @@ public function shouldAcceptAPromiseForAnArray() ->method('__invoke') ->with($this->identicalTo(1)); - When::any( - When::resolve(array(1, 2, 3)), - $mock - ); + any(resolve(array(1, 2, 3))) + ->then($mock); } /** @test */ @@ -105,10 +91,8 @@ public function shouldResolveToNullArrayWhenInputPromiseDoesNotResolveToArray() ->method('__invoke') ->with($this->identicalTo(null)); - When::any( - When::resolve(1), - $mock - ); + any(resolve(1)) + ->then($mock); } /** @test */ @@ -123,10 +107,8 @@ public function shouldNotRelyOnArryIndexesWhenUnwrappingToASingleResolutionValue $d1 = new Deferred(); $d2 = new Deferred(); - When::any( - array('abc' => $d1->promise(), 1 => $d2->promise()), - $mock - ); + any(array('abc' => $d1->promise(), 1 => $d2->promise())) + ->then($mock); $d2->resolve(2); $d1->resolve(1); diff --git a/tests/React/Promise/WhenMapTest.php b/tests/React/Promise/FunctionMapTest.php similarity index 83% rename from tests/React/Promise/WhenMapTest.php rename to tests/React/Promise/FunctionMapTest.php index 9dfc88a9..c40a665f 100644 --- a/tests/React/Promise/WhenMapTest.php +++ b/tests/React/Promise/FunctionMapTest.php @@ -2,11 +2,7 @@ namespace React\Promise; -/** - * @group When - * @group WhenMap - */ -class WhenMapTest extends TestCase +class FunctionMapTest extends TestCase { protected function mapper() { @@ -18,7 +14,7 @@ protected function mapper() protected function promiseMapper() { return function ($val) { - return When::resolve($val * 2); + return resolve($val * 2); }; } @@ -31,7 +27,7 @@ public function shouldMapInputValuesArray() ->method('__invoke') ->with($this->identicalTo(array(2, 4, 6))); - When::map( + map( array(1, 2, 3), $this->mapper() )->then($mock); @@ -46,8 +42,8 @@ public function shouldMapInputPromisesArray() ->method('__invoke') ->with($this->identicalTo(array(2, 4, 6))); - When::map( - array(When::resolve(1), When::resolve(2), When::resolve(3)), + map( + array(resolve(1), resolve(2), resolve(3)), $this->mapper() )->then($mock); } @@ -61,8 +57,8 @@ public function shouldMapMixedInputArray() ->method('__invoke') ->with($this->identicalTo(array(2, 4, 6))); - When::map( - array(1, When::resolve(2), 3), + map( + array(1, resolve(2), 3), $this->mapper() )->then($mock); } @@ -76,7 +72,7 @@ public function shouldMapInputWhenMapperReturnsAPromise() ->method('__invoke') ->with($this->identicalTo(array(2, 4, 6))); - When::map( + map( array(1, 2, 3), $this->promiseMapper() )->then($mock); @@ -91,8 +87,8 @@ public function shouldAcceptAPromiseForAnArray() ->method('__invoke') ->with($this->identicalTo(array(2, 4, 6))); - When::map( - When::resolve(array(1, When::resolve(2), 3)), + map( + resolve(array(1, resolve(2), 3)), $this->mapper() )->then($mock); } @@ -106,8 +102,8 @@ public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray() ->method('__invoke') ->with($this->identicalTo(array())); - When::map( - When::resolve(1), + map( + resolve(1), $this->mapper() )->then($mock); } @@ -121,8 +117,8 @@ public function shouldRejectWhenInputContainsRejection() ->method('__invoke') ->with($this->identicalTo(2)); - When::map( - array(When::resolve(1), When::reject(2), When::resolve(3)), + map( + array(resolve(1), reject(2), resolve(3)), $this->mapper() )->then($this->expectCallableNever(), $mock); } diff --git a/tests/React/Promise/WhenReduceTest.php b/tests/React/Promise/FunctionReduceTest.php similarity index 87% rename from tests/React/Promise/WhenReduceTest.php rename to tests/React/Promise/FunctionReduceTest.php index 425019f9..bb0211d0 100644 --- a/tests/React/Promise/WhenReduceTest.php +++ b/tests/React/Promise/FunctionReduceTest.php @@ -2,11 +2,7 @@ namespace React\Promise; -/** - * @group When - * @group WhenReduce - */ -class WhenReduceTest extends TestCase +class FunctionReduceTest extends TestCase { protected function plus() { @@ -31,7 +27,7 @@ public function shouldReduceValuesWithoutInitialValue() ->method('__invoke') ->with($this->identicalTo(6)); - When::reduce( + reduce( array(1, 2, 3), $this->plus() )->then($mock); @@ -46,7 +42,7 @@ public function shouldReduceValuesWithInitialValue() ->method('__invoke') ->with($this->identicalTo(7)); - When::reduce( + reduce( array(1, 2, 3), $this->plus(), 1 @@ -62,10 +58,10 @@ public function shouldReduceValuesWithInitialPromise() ->method('__invoke') ->with($this->identicalTo(7)); - When::reduce( + reduce( array(1, 2, 3), $this->plus(), - When::resolve(1) + resolve(1) )->then($mock); } @@ -78,8 +74,8 @@ public function shouldReducePromisedValuesWithoutInitialValue() ->method('__invoke') ->with($this->identicalTo(6)); - When::reduce( - array(When::resolve(1), When::resolve(2), When::resolve(3)), + reduce( + array(resolve(1), resolve(2), resolve(3)), $this->plus() )->then($mock); } @@ -93,8 +89,8 @@ public function shouldReducePromisedValuesWithInitialValue() ->method('__invoke') ->with($this->identicalTo(7)); - When::reduce( - array(When::resolve(1), When::resolve(2), When::resolve(3)), + reduce( + array(resolve(1), resolve(2), resolve(3)), $this->plus(), 1 )->then($mock); @@ -109,10 +105,10 @@ public function shouldReducePromisedValuesWithInitialPromise() ->method('__invoke') ->with($this->identicalTo(7)); - When::reduce( - array(When::resolve(1), When::resolve(2), When::resolve(3)), + reduce( + array(resolve(1), resolve(2), resolve(3)), $this->plus(), - When::resolve(1) + resolve(1) )->then($mock); } @@ -125,7 +121,7 @@ public function shouldReduceEmptyInputWithInitialValue() ->method('__invoke') ->with($this->identicalTo(1)); - When::reduce( + reduce( array(), $this->plus(), 1 @@ -141,10 +137,10 @@ public function shouldReduceEmptyInputWithInitialPromise() ->method('__invoke') ->with($this->identicalTo(1)); - When::reduce( + reduce( array(), $this->plus(), - When::resolve(1) + resolve(1) )->then($mock); } @@ -157,10 +153,10 @@ public function shouldRejectWhenInputContainsRejection() ->method('__invoke') ->with($this->identicalTo(2)); - When::reduce( - array(When::resolve(1), When::reject(2), When::resolve(3)), + reduce( + array(resolve(1), reject(2), resolve(3)), $this->plus(), - When::resolve(1) + resolve(1) )->then($this->expectCallableNever(), $mock); } @@ -177,7 +173,7 @@ public function shouldResolveWithNullWhenInputIsEmptyAndNoInitialValueOrPromiseP ->method('__invoke') ->with($this->identicalTo(null)); - When::reduce( + reduce( array(), $this->plus() )->then($mock); @@ -192,7 +188,7 @@ public function shouldAllowSparseArrayInputWithoutInitialValue() ->method('__invoke') ->with($this->identicalTo(3)); - When::reduce( + reduce( array(null, null, 1, null, 1, 1), $this->plus() )->then($mock); @@ -207,7 +203,7 @@ public function shouldAllowSparseArrayInputWithInitialValue() ->method('__invoke') ->with($this->identicalTo(4)); - When::reduce( + reduce( array(null, null, 1, null, 1, 1), $this->plus(), 1 @@ -223,7 +219,7 @@ public function shouldReduceInInputOrder() ->method('__invoke') ->with($this->identicalTo('123')); - When::reduce( + reduce( array(1, 2, 3), $this->append(), '' @@ -239,8 +235,8 @@ public function shouldAcceptAPromiseForAnArray() ->method('__invoke') ->with($this->identicalTo('123')); - When::reduce( - When::resolve(array(1, 2, 3)), + reduce( + resolve(array(1, 2, 3)), $this->append(), '' )->then($mock); @@ -255,8 +251,8 @@ public function shouldResolveToInitialValueWhenInputPromiseDoesNotResolveToAnArr ->method('__invoke') ->with($this->identicalTo(1)); - When::reduce( - When::resolve(1), + reduce( + resolve(1), $this->plus(), 1 )->then($mock); @@ -281,7 +277,7 @@ public function shouldProvideCorrectBasisValue() ->method('__invoke') ->with($this->identicalTo(array(1, 2, 3))); - When::reduce( + reduce( array($d1->promise(), $d2->promise(), $d3->promise()), $insertIntoArray, array() diff --git a/tests/React/Promise/WhenRejectTest.php b/tests/React/Promise/FunctionRejectTest.php similarity index 76% rename from tests/React/Promise/WhenRejectTest.php rename to tests/React/Promise/FunctionRejectTest.php index 1116eabd..f09fc479 100644 --- a/tests/React/Promise/WhenRejectTest.php +++ b/tests/React/Promise/FunctionRejectTest.php @@ -2,11 +2,7 @@ namespace React\Promise; -/** - * @group When - * @group WhenReject - */ -class WhenRejectTest extends TestCase +class FunctionRejectTest extends TestCase { /** @test */ public function shouldRejectAnImmediateValue() @@ -19,20 +15,19 @@ public function shouldRejectAnImmediateValue() ->method('__invoke') ->with($this->identicalTo($expected)); - When::reject($expected) + reject($expected) ->then( $this->expectCallableNever(), $mock ); } - + /** @test */ - public function shouldRejectAResolvedPromise() + public function shouldRejectAFulfilledPromise() { $expected = 123; - $d = new Deferred(); - $d->resolve($expected); + $resolved = new FulfilledPromise($expected); $mock = $this->createCallableMock(); $mock @@ -40,7 +35,7 @@ public function shouldRejectAResolvedPromise() ->method('__invoke') ->with($this->identicalTo($expected)); - When::reject($d->promise()) + reject($resolved) ->then( $this->expectCallableNever(), $mock @@ -52,8 +47,7 @@ public function shouldRejectARejectedPromise() { $expected = 123; - $d = new Deferred(); - $d->reject($expected); + $resolved = new RejectedPromise($expected); $mock = $this->createCallableMock(); $mock @@ -61,7 +55,7 @@ public function shouldRejectARejectedPromise() ->method('__invoke') ->with($this->identicalTo($expected)); - When::reject($d->promise()) + reject($resolved) ->then( $this->expectCallableNever(), $mock diff --git a/tests/React/Promise/WhenResolveTest.php b/tests/React/Promise/FunctionResolveTest.php similarity index 78% rename from tests/React/Promise/WhenResolveTest.php rename to tests/React/Promise/FunctionResolveTest.php index ab591423..e6e707b0 100644 --- a/tests/React/Promise/WhenResolveTest.php +++ b/tests/React/Promise/FunctionResolveTest.php @@ -2,11 +2,7 @@ namespace React\Promise; -/** - * @group When - * @group WhenResolve - */ -class WhenResolveTest extends TestCase +class FunctionResolveTest extends TestCase { /** @test */ public function shouldResolveAnImmediateValue() @@ -19,7 +15,7 @@ public function shouldResolveAnImmediateValue() ->method('__invoke') ->with($this->identicalTo($expected)); - When::resolve($expected) + resolve($expected) ->then( $mock, $this->expectCallableNever() @@ -27,12 +23,11 @@ public function shouldResolveAnImmediateValue() } /** @test */ - public function shouldResolveAResolvedPromise() + public function shouldResolveAFulfilledPromise() { $expected = 123; - $d = new Deferred(); - $d->resolve($expected); + $resolved = new FulfilledPromise($expected); $mock = $this->createCallableMock(); $mock @@ -40,7 +35,7 @@ public function shouldResolveAResolvedPromise() ->method('__invoke') ->with($this->identicalTo($expected)); - When::resolve($d->promise()) + resolve($resolved) ->then( $mock, $this->expectCallableNever() @@ -52,8 +47,7 @@ public function shouldRejectARejectedPromise() { $expected = 123; - $d = new Deferred(); - $d->reject($expected); + $resolved = new RejectedPromise($expected); $mock = $this->createCallableMock(); $mock @@ -61,7 +55,7 @@ public function shouldRejectARejectedPromise() ->method('__invoke') ->with($this->identicalTo($expected)); - When::resolve($d->promise()) + resolve($resolved) ->then( $this->expectCallableNever(), $mock @@ -74,7 +68,7 @@ public function shouldSupportDeepNestingInPromiseChains() $d = new Deferred(); $d->resolve(false); - $result = When::resolve(When::resolve($d->then(function ($val) { + $result = resolve(resolve($d->then(function ($val) { $d = new Deferred(); $d->resolve($val); @@ -82,7 +76,7 @@ public function shouldSupportDeepNestingInPromiseChains() return $val; }; - return When::resolve($d->then($identity))->then( + return resolve($d->then($identity))->then( function ($val) { return !$val; } diff --git a/tests/React/Promise/WhenSomeTest.php b/tests/React/Promise/FunctionSomeTest.php similarity index 72% rename from tests/React/Promise/WhenSomeTest.php rename to tests/React/Promise/FunctionSomeTest.php index 9704b734..4230b18d 100644 --- a/tests/React/Promise/WhenSomeTest.php +++ b/tests/React/Promise/FunctionSomeTest.php @@ -2,11 +2,7 @@ namespace React\Promise; -/** - * @group When - * @group WhenSome - */ -class WhenSomeTest extends TestCase +class FunctionSomeTest extends TestCase { /** @test */ public function shouldResolveEmptyInput() @@ -17,7 +13,10 @@ public function shouldResolveEmptyInput() ->method('__invoke') ->with($this->identicalTo(array())); - When::some(array(), 1, $mock); + some( + array(), + 1 + )->then($mock); } /** @test */ @@ -29,11 +28,10 @@ public function shouldResolveValuesArray() ->method('__invoke') ->with($this->identicalTo(array(1, 2))); - When::some( + some( array(1, 2, 3), - 2, - $mock - ); + 2 + )->then($mock); } /** @test */ @@ -45,11 +43,10 @@ public function shouldResolvePromisesArray() ->method('__invoke') ->with($this->identicalTo(array(1, 2))); - When::some( - array(When::resolve(1), When::resolve(2), When::resolve(3)), - 2, - $mock - ); + some( + array(resolve(1), resolve(2), resolve(3)), + 2 + )->then($mock); } /** @test */ @@ -61,11 +58,10 @@ public function shouldResolveSparseArrayInput() ->method('__invoke') ->with($this->identicalTo(array(null, 1))); - When::some( + some( array(null, 1, null, 2, 3), - 2, - $mock - ); + 2 + )->then($mock); } /** @test */ @@ -77,12 +73,10 @@ public function shouldRejectIfAnyInputPromiseRejectsBeforeDesiredNumberOfInputsA ->method('__invoke') ->with($this->identicalTo(array(1 => 2, 2 => 3))); - When::some( - array(When::resolve(1), When::reject(2), When::reject(3)), - 2, - $this->expectCallableNever(), - $mock - ); + some( + array(resolve(1), reject(2), reject(3)), + 2 + )->then($this->expectCallableNever(), $mock); } /** @test */ @@ -94,11 +88,10 @@ public function shouldAcceptAPromiseForAnArray() ->method('__invoke') ->with($this->identicalTo(array(1, 2))); - When::some( - When::resolve(array(1, 2, 3)), - 2, - $mock - ); + some( + resolve(array(1, 2, 3)), + 2 + )->then($mock); } /** @test */ @@ -110,10 +103,9 @@ public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray() ->method('__invoke') ->with($this->identicalTo(array())); - When::some( - When::resolve(1), - 1, - $mock - ); + some( + resolve(1), + 1 + )->then($mock); } } diff --git a/tests/React/Promise/UtilPromiseForTest.php b/tests/React/Promise/UtilPromiseForTest.php deleted file mode 100644 index cdadf2e3..00000000 --- a/tests/React/Promise/UtilPromiseForTest.php +++ /dev/null @@ -1,68 +0,0 @@ -createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($expected)); - - Util::promiseFor($expected) - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function shouldResolveAFulfilledPromise() - { - $expected = 123; - - $resolved = new FulfilledPromise($expected); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($expected)); - - Util::promiseFor($resolved) - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function shouldRejectARejectedPromise() - { - $expected = 123; - - $resolved = new RejectedPromise($expected); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($expected)); - - Util::promiseFor($resolved) - ->then( - $this->expectCallableNever(), - $mock - ); - } -} diff --git a/tests/React/Promise/UtilRejectedPromiseForTest.php b/tests/React/Promise/UtilRejectedPromiseForTest.php deleted file mode 100644 index 3c2265a7..00000000 --- a/tests/React/Promise/UtilRejectedPromiseForTest.php +++ /dev/null @@ -1,68 +0,0 @@ -createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($expected)); - - Util::rejectedPromiseFor($expected) - ->then( - $this->expectCallableNever(), - $mock - ); - } - - /** @test */ - public function shouldRejectWithFulfilledPromise() - { - $expected = 123; - - $resolved = new FulfilledPromise($expected); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($expected)); - - Util::rejectedPromiseFor($resolved) - ->then( - $this->expectCallableNever(), - $mock - ); - } - - /** @test */ - public function shouldRejectWithRejectedPromise() - { - $expected = 123; - - $resolved = new RejectedPromise($expected); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($expected)); - - Util::rejectedPromiseFor($resolved) - ->then( - $this->expectCallableNever(), - $mock - ); - } -} diff --git a/tests/React/Promise/WhenLazyTest.php b/tests/React/Promise/WhenLazyTest.php deleted file mode 100644 index a6512388..00000000 --- a/tests/React/Promise/WhenLazyTest.php +++ /dev/null @@ -1,28 +0,0 @@ -assertInstanceOf('React\\Promise\\PromiseInterface', When::lazy(function () {})); - } - - /** @test */ - public function shouldCallFactoryIfThenIsInvoked() - { - $factory = $this->createCallableMock(); - $factory - ->expects($this->once()) - ->method('__invoke'); - - When::lazy($factory) - ->then(); - } -} From 1f7bf126d89c386374a9591cb69c25127f5e91e3 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 09:44:41 +0200 Subject: [PATCH 05/29] Remove ResolverInterface and PromiseInterface from Deferred --- README.md | 121 ++++++++++--------- src/React/Promise/Deferred.php | 78 ++++++------ src/React/Promise/DeferredPromise.php | 8 +- src/React/Promise/DeferredResolver.php | 28 ----- src/React/Promise/ResolverInterface.php | 10 -- tests/React/Promise/DeferredProgressTest.php | 14 --- tests/React/Promise/DeferredPromiseTest.php | 6 +- tests/React/Promise/DeferredRejectTest.php | 6 +- tests/React/Promise/DeferredResolveTest.php | 8 +- tests/React/Promise/DeferredResolverTest.php | 33 ----- tests/React/Promise/FunctionResolveTest.php | 4 +- 11 files changed, 107 insertions(+), 209 deletions(-) delete mode 100644 src/React/Promise/DeferredResolver.php delete mode 100644 src/React/Promise/ResolverInterface.php delete mode 100644 tests/React/Promise/DeferredResolverTest.php diff --git a/README.md b/README.md index 5c767417..49a8c2ab 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,14 @@ Table of Contents 2. [Concepts](#concepts) * [Deferred](#deferred) * [Promise](#promise) - * [Resolver](#resolver) 3. [API](#api) * [Deferred](#deferred-1) + * [Deferred::promise()](#deferredpromise) + * [Deferred::resolve()](#deferredresolve) + * [Deferred::reject()](#deferredreject) + * [Deferred::progress()](#deferredprogress) * [Promise](#promise-1) * [LazyPromise](#lazypromise) - * [Resolver](#resolver-1) * [Functions](#functions) * [resolve()](#resolve) * [reject()](#reject) @@ -65,43 +67,78 @@ While a Deferred represents the computation itself, a **Promise** represents the result of that computation. Thus, each Deferred has a Promise that acts as a placeholder for its actual result. -### Resolver - -A **Resolver** can resolve, reject or trigger progress notifications on behalf -of a Deferred without knowing any details about consumers. - -Sometimes it can be useful to hand out a resolver and allow another -(possibly untrusted) party to provide the resolution value for a Promise. - API --- ### Deferred A deferred represents an operation whose resolution is pending. It has separate -Promise and Resolver parts that can be safely given out to separate groups of -consumers and producers to allow safe, one-way communication. +promise and resolver parts. ``` php $deferred = new React\Promise\Deferred(); -$promise = $deferred->promise(); -$resolver = $deferred->resolver(); +$promise = $deferred->promise(); + +$deferred->resolve(mixed $value = null); +$deferred->reject(mixed $reason = null); +$deferred->progress(mixed $update = null); ``` -Although a Deferred has the full Promise + Resolver API, this should be used for -convenience only by the creator of the deferred. Only the Promise and Resolver -should be given to consumers and producers. +The `promise` method returns the promise of the deferred. + +The `resolve` and `reject` methods control the state of the deferred. + +The `progress` method is for progress notification. + +#### Deferred::promise() ``` php -$deferred = new React\Promise\Deferred(); +$promise = $deferred->promise(); +``` -$deferred->then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); -$deferred->resolve(mixed $promiseOrValue = null); -$deferred->reject(mixed $reason = null); -$deferred->progress(mixed $update = null); +Returns the promise of the deferred, which you can hand out to others while +keeping the authority to modify its state to yourself. + +#### Deferred::resolve() + +``` php +$deferred->resolve(mixed $value = null); ``` +Resolves the promise returned by `promise()`. All consumers are notified by +having `$onFulfilled` (which they registered via `$promise->then()`) called with +`$value`. + +If `$value` itself is a promise, the promise will transition to the state of +this promise once it is resolved. + +#### Deferred::reject() + +``` php +$resolver->reject(mixed $reason = null); +``` + +Rejects the promise returned by `promise()`, signalling that the Deferred's +computation failed. +All consumers are notified by having `$onRejected` (which they registered via +`$promise->then()`) called with `$reason`. + +If `$reason` itself is a promise, the promise will be rejected with the outcome +of this promise regardless whether it fulfills or rejects. + +#### Deferred::progress() + +``` php +$resolver->progress(mixed $update = null); +``` + +Triggers progress notifications, to indicate to consumers that the computation +is making progress toward its result. + +All consumers are notified by having `$onProgress` (which they registered via +`$promise->then()`) called with `$update`. + ### Promise The Promise represents the eventual outcome, which is either fulfillment @@ -112,6 +149,8 @@ value or reason, and produces a new Promise for the result. A Promise has a single method `then()` which registers new fulfilled, rejection and progress handlers with this Promise (all parameters are optional): +#### Promise::then() + ``` php $newPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); ``` @@ -166,44 +205,6 @@ $promise->then(function ($value) { }); ``` -### Resolver - -The Resolver represents the responsibility of fulfilling, rejecting and -progressing the associated Promise. - -A Resolver has 3 methods: `resolve()`, `reject()` and `progress()`: - -``` php -$resolver->resolve(mixed $value = null); -``` - -Resolves a Deferred. All consumers are notified by having `$onFulfilled` -(which they registered via `$promise->then()`) called with `$value`. - -If `$value` itself is a promise, the Deferred will transition to the state of -this promise once it is resolved. - -``` php -$resolver->reject(mixed $reason = null); -``` - -Rejects a Deferred, signalling that the Deferred's computation failed. -All consumers are notified by having `$onRejected` (which they registered via -`$promise->then()`) called with `$reason`. - -If `$reason` itself is a promise, the Deferred will be rejected with the outcome -of this promise regardless whether it fulfills or rejects. - -``` php -$resolver->progress(mixed $update = null); -``` - -Triggers progress notifications, to indicate to consumers that the computation -is making progress toward its result. - -All consumers are notified by having `$onProgress` (which they registered via -`$promise->then()`) called with `$update`. - ### Functions Useful functions for creating, joining, mapping and reducing collections of diff --git a/src/React/Promise/Deferred.php b/src/React/Promise/Deferred.php index e9174846..6a91f5c7 100644 --- a/src/React/Promise/Deferred.php +++ b/src/React/Promise/Deferred.php @@ -2,49 +2,13 @@ namespace React\Promise; -class Deferred implements PromiseInterface, ResolverInterface, PromisorInterface +class Deferred implements PromisorInterface { private $completed; private $promise; - private $resolver; private $handlers = array(); private $progressHandlers = array(); - public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - if (null !== $this->completed) { - return $this->completed->then($onFulfilled, $onRejected, $onProgress); - } - - $deferred = new static(); - - if (is_callable($onProgress)) { - $progHandler = function ($update) use ($deferred, $onProgress) { - try { - $deferred->progress(call_user_func($onProgress, $update)); - } catch (\Exception $e) { - $deferred->progress($e); - } - }; - } else { - $progHandler = array($deferred, 'progress'); - } - - $this->handlers[] = function ($promise) use ($onFulfilled, $onRejected, $deferred, $progHandler) { - $promise - ->then($onFulfilled, $onRejected) - ->then( - array($deferred, 'resolve'), - array($deferred, 'reject'), - $progHandler - ); - }; - - $this->progressHandlers[] = $progHandler; - - return $deferred->promise(); - } - public function resolve($value = null) { if (null !== $this->completed) { @@ -77,19 +41,47 @@ public function progress($update = null) public function promise() { if (null === $this->promise) { - $this->promise = new DeferredPromise($this); + $this->promise = new DeferredPromise($this->getThenCallback()); } return $this->promise; } - public function resolver() + protected function getThenCallback() { - if (null === $this->resolver) { - $this->resolver = new DeferredResolver($this); - } + return function (callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) { + if (null !== $this->completed) { + return $this->completed->then($onFulfilled, $onRejected, $onProgress); + } + + $deferred = new static(); + + if (is_callable($onProgress)) { + $progHandler = function ($update) use ($deferred, $onProgress) { + try { + $deferred->progress(call_user_func($onProgress, $update)); + } catch (\Exception $e) { + $deferred->progress($e); + } + }; + } else { + $progHandler = array($deferred, 'progress'); + } + + $this->handlers[] = function ($promise) use ($onFulfilled, $onRejected, $deferred, $progHandler) { + $promise + ->then($onFulfilled, $onRejected) + ->then( + array($deferred, 'resolve'), + array($deferred, 'reject'), + $progHandler + ); + }; - return $this->resolver; + $this->progressHandlers[] = $progHandler; + + return $deferred->promise(); + }; } protected function processQueue($queue, $value) diff --git a/src/React/Promise/DeferredPromise.php b/src/React/Promise/DeferredPromise.php index ca5b1bed..1c81c28d 100644 --- a/src/React/Promise/DeferredPromise.php +++ b/src/React/Promise/DeferredPromise.php @@ -4,15 +4,15 @@ class DeferredPromise implements PromiseInterface { - private $deferred; + private $thenCallback; - public function __construct(Deferred $deferred) + public function __construct(callable $thenCallback) { - $this->deferred = $deferred; + $this->thenCallback = $thenCallback; } public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) { - return $this->deferred->then($onFulfilled, $onRejected, $onProgress); + return call_user_func($this->thenCallback, $onFulfilled, $onRejected, $onProgress); } } diff --git a/src/React/Promise/DeferredResolver.php b/src/React/Promise/DeferredResolver.php deleted file mode 100644 index aa6b5b6a..00000000 --- a/src/React/Promise/DeferredResolver.php +++ /dev/null @@ -1,28 +0,0 @@ -deferred = $deferred; - } - - public function resolve($result = null) - { - return $this->deferred->resolve($result); - } - - public function reject($reason = null) - { - return $this->deferred->reject($reason); - } - - public function progress($update = null) - { - return $this->deferred->progress($update); - } -} diff --git a/src/React/Promise/ResolverInterface.php b/src/React/Promise/ResolverInterface.php deleted file mode 100644 index f4a93610..00000000 --- a/src/React/Promise/ResolverInterface.php +++ /dev/null @@ -1,10 +0,0 @@ -then($this->expectCallableNever(), $this->expectCallableNever(), $mock); $d - ->resolver() ->progress($sentinel); } @@ -63,7 +62,6 @@ public function shouldPropagateProgressToDownstreamPromises() ); $d - ->resolver() ->progress($sentinel); } @@ -100,7 +98,6 @@ public function shouldPropagateTransformedProgressToDownstreamPromises() ); $d - ->resolver() ->progress(1); } @@ -137,7 +134,6 @@ public function shouldPropagateCaughtExceptionValueAsProgress() ); $d - ->resolver() ->progress(1); } @@ -157,7 +153,6 @@ public function shouldForwardProgressEventsWhenIntermediaryCallbackTiedToAResolv // resolve $d BEFORE calling attaching progress handler $d - ->resolver() ->resolve(); $d @@ -172,7 +167,6 @@ public function shouldForwardProgressEventsWhenIntermediaryCallbackTiedToAResolv ); $d2 - ->resolver() ->progress($sentinel); } @@ -203,10 +197,8 @@ public function shouldForwardProgressEventsWhenIntermediaryCallbackTiedToAnUnres // resolve $d AFTER calling attaching progress handler $d - ->resolver() ->resolve(); $d2 - ->resolver() ->progress($sentinel); } @@ -244,10 +236,8 @@ public function shouldForwardProgressWhenResolvedWithAnotherPromise() ); $d - ->resolver() ->resolve($d2->promise()); $d2 - ->resolver() ->progress($sentinel); } @@ -275,10 +265,8 @@ public function shouldAllowResolveAfterProgress() ); $d - ->resolver() ->progress(1); $d - ->resolver() ->resolve(2); } @@ -306,10 +294,8 @@ public function shouldAllowRejectAfterProgress() ); $d - ->resolver() ->progress(1); $d - ->resolver() ->reject(2); } } diff --git a/tests/React/Promise/DeferredPromiseTest.php b/tests/React/Promise/DeferredPromiseTest.php index 925fd858..795a733d 100644 --- a/tests/React/Promise/DeferredPromiseTest.php +++ b/tests/React/Promise/DeferredPromiseTest.php @@ -9,14 +9,14 @@ class DeferredPromiseTest extends TestCase { /** @test */ - public function shouldForwardToDeferred() + public function shouldInvokeThenCallback() { $callable = $this->createCallableMock(); - $mock = $this->getMock('React\\Promise\\Deferred'); + $mock = $this->createCallableMock(); $mock ->expects($this->once()) - ->method('then') + ->method('__invoke') ->with($callable, $callable, $callable); $p = new DeferredPromise($mock); diff --git a/tests/React/Promise/DeferredRejectTest.php b/tests/React/Promise/DeferredRejectTest.php index 3486082e..a64c783a 100644 --- a/tests/React/Promise/DeferredRejectTest.php +++ b/tests/React/Promise/DeferredRejectTest.php @@ -24,7 +24,6 @@ public function shouldRejectWithAnImmediateValue() ->then($this->expectCallableNever(), $mock); $d - ->resolver() ->reject(1); } @@ -44,7 +43,6 @@ public function shouldRejectWithFulfilledPromise() ->then($this->expectCallableNever(), $mock); $d - ->resolver() ->reject(new FulfilledPromise(1)); } @@ -64,7 +62,6 @@ public function shouldRejectWithRejectedPromise() ->then($this->expectCallableNever(), $mock); $d - ->resolver() ->reject(new RejectedPromise(1)); } @@ -80,7 +77,6 @@ public function shouldReturnAPromiseForTheRejectionValue() ->with($this->identicalTo(1)); $d - ->resolver() ->reject(1) ->then($this->expectCallableNever(), $mock); } @@ -90,7 +86,6 @@ public function shouldInvokeNewlyAddedErrbackWhenAlreadyRejected() { $d = new Deferred(); $d - ->resolver() ->reject(1); $mock = $this->createCallableMock(); @@ -115,6 +110,7 @@ public function shouldForwardReasonWhenCallbackIsNull() $d = new Deferred(); $d + ->promise() ->then( $this->expectCallableNever() ) diff --git a/tests/React/Promise/DeferredResolveTest.php b/tests/React/Promise/DeferredResolveTest.php index 131b54cf..c83795ab 100644 --- a/tests/React/Promise/DeferredResolveTest.php +++ b/tests/React/Promise/DeferredResolveTest.php @@ -24,7 +24,6 @@ public function shouldResolve() ->then($mock); $d - ->resolver() ->resolve(1); } @@ -44,7 +43,6 @@ public function shouldResolveWithPromisedValue() ->then($mock); $d - ->resolver() ->resolve(new FulfilledPromise(1)); } @@ -64,7 +62,6 @@ public function shouldRejectWhenResolvedWithRejectedPromise() ->then($this->expectCallableNever(), $mock); $d - ->resolver() ->resolve(new RejectedPromise(1)); } @@ -80,7 +77,6 @@ public function shouldReturnAPromiseForTheResolutionValue() ->with($this->identicalTo(1)); $d - ->resolver() ->resolve(1) ->then($mock); } @@ -97,7 +93,6 @@ public function shouldReturnAPromiseForAPromisedResolutionValue() ->with($this->identicalTo(1)); $d - ->resolver() ->resolve(resolve(1)) ->then($mock); } @@ -116,7 +111,6 @@ public function shouldReturnAPromiseForAPromisedRejectionValue() // Both the returned promise, and the deferred's own promise should // be rejected with the same value $d - ->resolver() ->resolve(reject(1)) ->then($this->expectCallableNever(), $mock); } @@ -126,7 +120,6 @@ public function shouldInvokeNewlyAddedCallbackWhenAlreadyResolved() { $d = new Deferred(); $d - ->resolver() ->resolve(1); $mock = $this->createCallableMock(); @@ -151,6 +144,7 @@ public function shouldForwardValueWhenCallbackIsNull() $d = new Deferred(); $d + ->promise() ->then( null, $this->expectCallableNever() diff --git a/tests/React/Promise/DeferredResolverTest.php b/tests/React/Promise/DeferredResolverTest.php deleted file mode 100644 index 349bdc2c..00000000 --- a/tests/React/Promise/DeferredResolverTest.php +++ /dev/null @@ -1,33 +0,0 @@ -getMock('React\\Promise\\Deferred'); - $mock - ->expects($this->once()) - ->method('resolve') - ->with(1); - $mock - ->expects($this->once()) - ->method('reject') - ->with(1); - $mock - ->expects($this->once()) - ->method('progress') - ->with(1); - - $p = new DeferredResolver($mock); - $p->resolve(1); - $p->reject(1); - $p->progress(1); - } -} diff --git a/tests/React/Promise/FunctionResolveTest.php b/tests/React/Promise/FunctionResolveTest.php index e6e707b0..01307d5a 100644 --- a/tests/React/Promise/FunctionResolveTest.php +++ b/tests/React/Promise/FunctionResolveTest.php @@ -68,7 +68,7 @@ public function shouldSupportDeepNestingInPromiseChains() $d = new Deferred(); $d->resolve(false); - $result = resolve(resolve($d->then(function ($val) { + $result = resolve(resolve($d->promise()->then(function ($val) { $d = new Deferred(); $d->resolve($val); @@ -76,7 +76,7 @@ public function shouldSupportDeepNestingInPromiseChains() return $val; }; - return resolve($d->then($identity))->then( + return resolve($d->promise()->then($identity))->then( function ($val) { return !$val; } From ee0d837269f55409e105de34e88fb09bbe924efb Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 14:48:05 +0200 Subject: [PATCH 06/29] Refactor common promise tests into traits --- tests/React/Promise/DeferredPromiseTest.php | 25 --- tests/React/Promise/DeferredRejectTest.php | 124 ------------- tests/React/Promise/DeferredTest.php | 83 +-------- tests/React/Promise/FulfilledPromiseTest.php | 159 +++-------------- tests/React/Promise/LazyPromiseTest.php | 24 ++- .../Promise/PromiseTest/FullTestTrait.php | 13 ++ .../ProgressTestTrait.php} | 167 +++++++++--------- .../PromiseFullfilledTestTrait.php | 137 ++++++++++++++ .../PromiseTest/PromiseRejectedTestTrait.php | 142 +++++++++++++++ .../Promise/PromiseTest/PromiseTestTrait.php | 24 +++ .../Promise/PromiseTest/RejectTestTrait.php | 150 ++++++++++++++++ .../ResolveTestTrait.php} | 118 ++++++++----- tests/React/Promise/RejectedPromiseTest.php | 160 +++-------------- tests/React/Promise/TestCase.php | 11 ++ 14 files changed, 707 insertions(+), 630 deletions(-) delete mode 100644 tests/React/Promise/DeferredPromiseTest.php delete mode 100644 tests/React/Promise/DeferredRejectTest.php create mode 100644 tests/React/Promise/PromiseTest/FullTestTrait.php rename tests/React/Promise/{DeferredProgressTest.php => PromiseTest/ProgressTestTrait.php} (63%) create mode 100644 tests/React/Promise/PromiseTest/PromiseFullfilledTestTrait.php create mode 100644 tests/React/Promise/PromiseTest/PromiseRejectedTestTrait.php create mode 100644 tests/React/Promise/PromiseTest/PromiseTestTrait.php create mode 100644 tests/React/Promise/PromiseTest/RejectTestTrait.php rename tests/React/Promise/{DeferredResolveTest.php => PromiseTest/ResolveTestTrait.php} (50%) diff --git a/tests/React/Promise/DeferredPromiseTest.php b/tests/React/Promise/DeferredPromiseTest.php deleted file mode 100644 index 795a733d..00000000 --- a/tests/React/Promise/DeferredPromiseTest.php +++ /dev/null @@ -1,25 +0,0 @@ -createCallableMock(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($callable, $callable, $callable); - - $p = new DeferredPromise($mock); - $p->then($callable, $callable, $callable); - } -} diff --git a/tests/React/Promise/DeferredRejectTest.php b/tests/React/Promise/DeferredRejectTest.php deleted file mode 100644 index a64c783a..00000000 --- a/tests/React/Promise/DeferredRejectTest.php +++ /dev/null @@ -1,124 +0,0 @@ -createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $d - ->promise() - ->then($this->expectCallableNever(), $mock); - - $d - ->reject(1); - } - - /** @test */ - public function shouldRejectWithFulfilledPromise() - { - $d = new Deferred(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $d - ->promise() - ->then($this->expectCallableNever(), $mock); - - $d - ->reject(new FulfilledPromise(1)); - } - - /** @test */ - public function shouldRejectWithRejectedPromise() - { - $d = new Deferred(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $d - ->promise() - ->then($this->expectCallableNever(), $mock); - - $d - ->reject(new RejectedPromise(1)); - } - - /** @test */ - public function shouldReturnAPromiseForTheRejectionValue() - { - $d = new Deferred(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $d - ->reject(1) - ->then($this->expectCallableNever(), $mock); - } - - /** @test */ - public function shouldInvokeNewlyAddedErrbackWhenAlreadyRejected() - { - $d = new Deferred(); - $d - ->reject(1); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $d - ->promise() - ->then($this->expectCallableNever(), $mock); - } - - /** @test */ - public function shouldForwardReasonWhenCallbackIsNull() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $d = new Deferred(); - $d - ->promise() - ->then( - $this->expectCallableNever() - ) - ->then( - $this->expectCallableNever(), - $mock - ); - - $d->reject(1); - } -} diff --git a/tests/React/Promise/DeferredTest.php b/tests/React/Promise/DeferredTest.php index 51eb2520..a853b457 100644 --- a/tests/React/Promise/DeferredTest.php +++ b/tests/React/Promise/DeferredTest.php @@ -2,86 +2,19 @@ namespace React\Promise; -/** - * @group Deferred - */ class DeferredTest extends TestCase { - /** @test */ - public function shouldReturnAPromiseForPassedInResolutionValueWhenAlreadyResolved() - { - $d = new Deferred(); - $d->resolve(1); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $d->resolve(2)->then($mock); - } - - /** @test */ - public function shouldReturnAPromiseForPassedInRejectionValueWhenAlreadyResolved() - { - $d = new Deferred(); - $d->resolve(1); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $d->reject(2)->then($this->expectCallableNever(), $mock); - } - - /** @test */ - public function shouldReturnSilentlyOnProgressWhenAlreadyResolved() - { - $d = new Deferred(); - $d->resolve(1); - - $this->assertNull($d->progress()); - } - - /** @test */ - public function shouldReturnAPromiseForPassedInResolutionValueWhenAlreadyRejected() - { - $d = new Deferred(); - $d->reject(1); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $d->resolve(2)->then($mock); - } - - /** @test */ - public function shouldReturnAPromiseForPassedInRejectionValueWhenAlreadyRejected() - { - $d = new Deferred(); - $d->reject(1); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $d->reject(2)->then($this->expectCallableNever(), $mock); - } + use PromiseTest\FullTestTrait; - /** @test */ - public function shouldReturnSilentlyOnProgressWhenAlreadyRejected() + public function getPromiseTestAdapter() { $d = new Deferred(); - $d->reject(1); - $this->assertNull($d->progress()); + return [ + 'promise' => $this->toClosure(array($d, 'promise')), + 'resolve' => $this->toClosure(array($d, 'resolve')), + 'reject' => $this->toClosure(array($d, 'reject')), + 'progress' => $this->toClosure(array($d, 'progress')), + ]; } } diff --git a/tests/React/Promise/FulfilledPromiseTest.php b/tests/React/Promise/FulfilledPromiseTest.php index ef574a8e..23cb0dcd 100644 --- a/tests/React/Promise/FulfilledPromiseTest.php +++ b/tests/React/Promise/FulfilledPromiseTest.php @@ -2,142 +2,35 @@ namespace React\Promise; -/** - * @group Promise - * @group FulfilledPromise - */ class FulfilledPromiseTest extends TestCase { - /** @test */ - public function shouldReturnAPromise() - { - $p = new FulfilledPromise(); - $this->assertInstanceOf('React\\Promise\\PromiseInterface', $p->then()); - } - - /** @test */ - public function shouldReturnAllowNull() - { - $p = new FulfilledPromise(); - $this->assertInstanceOf('React\\Promise\\PromiseInterface', $p->then(null, null, null)); - } - - /** @test */ - public function shouldForwardResultWhenCallbackIsNull() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $p = new FulfilledPromise(1); - $p - ->then( - null, - $this->expectCallableNever() - ) - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function shouldForwardCallbackResultToNextCallback() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $p = new FulfilledPromise(1); - $p - ->then( - function ($val) { - return $val + 1; - }, - $this->expectCallableNever() - ) - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function shouldForwardPromisedCallbackResultValueToNextCallback() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $p = new FulfilledPromise(1); - $p - ->then( - function ($val) { - return new FulfilledPromise($val + 1); - }, - $this->expectCallableNever() - ) - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function shouldSwitchFromCallbacksToErrbacksWhenCallbackReturnsARejection() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $p = new FulfilledPromise(1); - $p - ->then( - function ($val) { - return new RejectedPromise($val + 1); - }, - $this->expectCallableNever() - ) - ->then( - $this->expectCallableNever(), - $mock - ); - } + use PromiseTest\PromiseTestTrait, + PromiseTest\PromiseFullfilledTestTrait; - /** @test */ - public function shouldSwitchFromCallbacksToErrbacksWhenCallbackThrows() + public function getPromiseTestAdapter() { - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->will($this->throwException($exception)); - - $mock2 = $this->createCallableMock(); - $mock2 - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $p = new FulfilledPromise(1); - $p - ->then( - $mock, - $this->expectCallableNever() - ) - ->then( - $this->expectCallableNever(), - $mock2 - ); + $val = null; + $promiseCalled = false; + + return [ + 'promise' => function () use (&$val, &$promiseCalled) { + $promiseCalled = true; + + return new FulfilledPromise($val); + }, + 'resolve' => function ($value) use (&$val, &$promiseCalled) { + if ($promiseCalled) { + throw new \LogicException('You must call resolve() before promise() for React\Promise\FulfilledPromise'); + } + + $val = $value; + }, + 'reject' => function () { + throw new \LogicException('You cannot call reject() for React\Promise\FulfilledPromise'); + }, + 'progress' => function () { + throw new \LogicException('You cannot call progress() for React\Promise\FulfilledPromise'); + }, + ]; } } diff --git a/tests/React/Promise/LazyPromiseTest.php b/tests/React/Promise/LazyPromiseTest.php index 34d87d9a..21b176f6 100644 --- a/tests/React/Promise/LazyPromiseTest.php +++ b/tests/React/Promise/LazyPromiseTest.php @@ -2,12 +2,28 @@ namespace React\Promise; -/** - * @group Promise - * @group LazyPromise - */ class LazyPromiseTest extends TestCase { + use PromiseTest\FullTestTrait; + + public function getPromiseTestAdapter() + { + $d = new Deferred(); + + $factory = function () use ($d) { + return $d->promise(); + }; + + return [ + 'promise' => function () use ($factory) { + return new LazyPromise($factory); + }, + 'resolve' => $this->toClosure(array($d, 'resolve')), + 'reject' => $this->toClosure(array($d, 'reject')), + 'progress' => $this->toClosure(array($d, 'progress')), + ]; + } + /** @test */ public function shouldNotCallFactoryIfThenIsNotInvoked() { diff --git a/tests/React/Promise/PromiseTest/FullTestTrait.php b/tests/React/Promise/PromiseTest/FullTestTrait.php new file mode 100644 index 00000000..98978346 --- /dev/null +++ b/tests/React/Promise/PromiseTest/FullTestTrait.php @@ -0,0 +1,13 @@ +getPromiseTestAdapter()); - $d = new Deferred(); + $sentinel = new \stdClass(); $mock = $this->createCallableMock(); $mock @@ -21,20 +19,18 @@ public function shouldProgress() ->method('__invoke') ->with($sentinel); - $d - ->promise() + $promise() ->then($this->expectCallableNever(), $this->expectCallableNever(), $mock); - $d - ->progress($sentinel); + $progress($sentinel); } /** @test */ - public function shouldPropagateProgressToDownstreamPromises() + public function progressShouldPropagateProgressToDownstreamPromises() { - $sentinel = new \stdClass(); + extract($this->getPromiseTestAdapter()); - $d = new Deferred(); + $sentinel = new \stdClass(); $mock = $this->createCallableMock(); $mock @@ -48,8 +44,7 @@ public function shouldPropagateProgressToDownstreamPromises() ->method('__invoke') ->with($sentinel); - $d - ->promise() + $promise() ->then( $this->expectCallableNever(), $this->expectCallableNever(), @@ -61,16 +56,15 @@ public function shouldPropagateProgressToDownstreamPromises() $mock2 ); - $d - ->progress($sentinel); + $progress($sentinel); } /** @test */ - public function shouldPropagateTransformedProgressToDownstreamPromises() + public function progressShouldPropagateTransformedProgressToDownstreamPromises() { - $sentinel = new \stdClass(); + extract($this->getPromiseTestAdapter()); - $d = new Deferred(); + $sentinel = new \stdClass(); $mock = $this->createCallableMock(); $mock @@ -84,8 +78,7 @@ public function shouldPropagateTransformedProgressToDownstreamPromises() ->method('__invoke') ->with($sentinel); - $d - ->promise() + $promise() ->then( $this->expectCallableNever(), $this->expectCallableNever(), @@ -97,16 +90,15 @@ public function shouldPropagateTransformedProgressToDownstreamPromises() $mock2 ); - $d - ->progress(1); + $progress(1); } /** @test */ - public function shouldPropagateCaughtExceptionValueAsProgress() + public function progressShouldPropagateCaughtExceptionValueAsProgress() { - $exception = new \Exception(); + extract($this->getPromiseTestAdapter()); - $d = new Deferred(); + $exception = new \Exception(); $mock = $this->createCallableMock(); $mock @@ -120,8 +112,7 @@ public function shouldPropagateCaughtExceptionValueAsProgress() ->method('__invoke') ->with($this->identicalTo($exception)); - $d - ->promise() + $promise() ->then( $this->expectCallableNever(), $this->expectCallableNever(), @@ -133,17 +124,16 @@ public function shouldPropagateCaughtExceptionValueAsProgress() $mock2 ); - $d - ->progress(1); + $progress(1); } /** @test */ - public function shouldForwardProgressEventsWhenIntermediaryCallbackTiedToAResolvedPromiseReturnsAPromise() + public function progressShouldForwardProgressEventsWhenIntermediaryCallbackTiedToAResolvedPromiseReturnsAPromise() { - $sentinel = new \stdClass(); + extract($this->getPromiseTestAdapter()); + extract($this->getPromiseTestAdapter(), EXTR_PREFIX_ALL, 'other'); - $d = new Deferred(); - $d2 = new Deferred(); + $sentinel = new \stdClass(); $mock = $this->createCallableMock(); $mock @@ -151,14 +141,12 @@ public function shouldForwardProgressEventsWhenIntermediaryCallbackTiedToAResolv ->method('__invoke') ->with($sentinel); - // resolve $d BEFORE calling attaching progress handler - $d - ->resolve(); + // resolve BEFORE attaching progress handler + $resolve(); - $d - ->promise() - ->then(function () use ($d2) { - return $d2->promise(); + $promise() + ->then(function () use ($other_promise) { + return $other_promise(); }) ->then( $this->expectCallableNever(), @@ -166,17 +154,16 @@ public function shouldForwardProgressEventsWhenIntermediaryCallbackTiedToAResolv $mock ); - $d2 - ->progress($sentinel); + $other_progress($sentinel); } /** @test */ - public function shouldForwardProgressEventsWhenIntermediaryCallbackTiedToAnUnresolvedPromiseReturnsAPromise() + public function progressShouldForwardProgressEventsWhenIntermediaryCallbackTiedToAnUnresolvedPromiseReturnsAPromise() { - $sentinel = new \stdClass(); + extract($this->getPromiseTestAdapter()); + extract($this->getPromiseTestAdapter(), EXTR_PREFIX_ALL, 'other'); - $d = new Deferred(); - $d2 = new Deferred(); + $sentinel = new \stdClass(); $mock = $this->createCallableMock(); $mock @@ -184,10 +171,9 @@ public function shouldForwardProgressEventsWhenIntermediaryCallbackTiedToAnUnres ->method('__invoke') ->with($sentinel); - $d - ->promise() - ->then(function () use ($d2) { - return $d2->promise(); + $promise() + ->then(function () use ($other_promise) { + return $other_promise(); }) ->then( $this->expectCallableNever(), @@ -195,20 +181,18 @@ public function shouldForwardProgressEventsWhenIntermediaryCallbackTiedToAnUnres $mock ); - // resolve $d AFTER calling attaching progress handler - $d - ->resolve(); - $d2 - ->progress($sentinel); + // resolve AFTER attaching progress handler + $resolve(); + $other_progress($sentinel); } /** @test */ - public function shouldForwardProgressWhenResolvedWithAnotherPromise() + public function progressShouldForwardProgressWhenResolvedWithAnotherPromise() { - $sentinel = new \stdClass(); + extract($this->getPromiseTestAdapter()); + extract($this->getPromiseTestAdapter(), EXTR_PREFIX_ALL, 'other'); - $d = new Deferred(); - $d2 = new Deferred(); + $sentinel = new \stdClass(); $mock = $this->createCallableMock(); $mock @@ -222,8 +206,7 @@ public function shouldForwardProgressWhenResolvedWithAnotherPromise() ->method('__invoke') ->with($sentinel); - $d - ->promise() + $promise() ->then( $this->expectCallableNever(), $this->expectCallableNever(), @@ -235,16 +218,14 @@ public function shouldForwardProgressWhenResolvedWithAnotherPromise() $mock2 ); - $d - ->resolve($d2->promise()); - $d2 - ->progress($sentinel); + $resolve($other_promise()); + $other_progress($sentinel); } /** @test */ - public function shouldAllowResolveAfterProgress() + public function progressShouldAllowResolveAfterProgress() { - $d = new Deferred(); + extract($this->getPromiseTestAdapter()); $mock = $this->createCallableMock(); $mock @@ -256,24 +237,21 @@ public function shouldAllowResolveAfterProgress() ->method('__invoke') ->with($this->identicalTo(2)); - $d - ->promise() + $promise() ->then( $mock, $this->expectCallableNever(), $mock ); - $d - ->progress(1); - $d - ->resolve(2); + $progress(1); + $resolve(2); } /** @test */ - public function shouldAllowRejectAfterProgress() + public function progressShouldAllowRejectAfterProgress() { - $d = new Deferred(); + extract($this->getPromiseTestAdapter()); $mock = $this->createCallableMock(); $mock @@ -285,17 +263,34 @@ public function shouldAllowRejectAfterProgress() ->method('__invoke') ->with($this->identicalTo(2)); - $d - ->promise() + $promise() ->then( $this->expectCallableNever(), $mock, $mock ); - $d - ->progress(1); - $d - ->reject(2); + $progress(1); + $reject(2); + } + + /** @test */ + public function progressShouldReturnSilentlyOnProgressWhenAlreadyResolved() + { + extract($this->getPromiseTestAdapter()); + + $resolve(1); + + $this->assertNull($progress()); + } + + /** @test */ + public function progressShouldReturnSilentlyOnProgressWhenAlreadyRejected() + { + extract($this->getPromiseTestAdapter()); + + $reject(1); + + $this->assertNull($progress()); } } diff --git a/tests/React/Promise/PromiseTest/PromiseFullfilledTestTrait.php b/tests/React/Promise/PromiseTest/PromiseFullfilledTestTrait.php new file mode 100644 index 00000000..5e5ba0c3 --- /dev/null +++ b/tests/React/Promise/PromiseTest/PromiseFullfilledTestTrait.php @@ -0,0 +1,137 @@ +getPromiseTestAdapter()); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $resolve(1); + $promise() + ->then( + null, + $this->expectCallableNever() + ) + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function thenShouldForwardCallbackResultToNextCallback() + { + extract($this->getPromiseTestAdapter()); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $resolve(1); + $promise() + ->then( + function ($val) { + return $val + 1; + }, + $this->expectCallableNever() + ) + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function thenShouldForwardPromisedCallbackResultValueToNextCallback() + { + extract($this->getPromiseTestAdapter()); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $resolve(1); + $promise() + ->then( + function ($val) { + return \React\Promise\resolve($val + 1); + }, + $this->expectCallableNever() + ) + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function thenShouldSwitchFromCallbacksToErrbacksWhenCallbackReturnsARejection() + { + extract($this->getPromiseTestAdapter()); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $resolve(1); + $promise() + ->then( + function ($val) { + return \React\Promise\reject($val + 1); + }, + $this->expectCallableNever() + ) + ->then( + $this->expectCallableNever(), + $mock + ); + } + + /** @test */ + public function thenShouldSwitchFromCallbacksToErrbacksWhenCallbackThrows() + { + extract($this->getPromiseTestAdapter()); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->will($this->throwException($exception)); + + $mock2 = $this->createCallableMock(); + $mock2 + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $resolve(1); + $promise() + ->then( + $mock, + $this->expectCallableNever() + ) + ->then( + $this->expectCallableNever(), + $mock2 + ); + } +} diff --git a/tests/React/Promise/PromiseTest/PromiseRejectedTestTrait.php b/tests/React/Promise/PromiseTest/PromiseRejectedTestTrait.php new file mode 100644 index 00000000..39bce79f --- /dev/null +++ b/tests/React/Promise/PromiseTest/PromiseRejectedTestTrait.php @@ -0,0 +1,142 @@ +getPromiseTestAdapter()); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with(null); + + $reject(1); + $promise() + ->then( + $this->expectCallableNever(), + function () { + // Presence of rejection handler is enough to switch back + // to resolve mode, even though it returns undefined. + // The ONLY way to propagate a rejection is to re-throw or + // return a rejected promise; + } + ) + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function shouldSwitchFromErrbacksToCallbacksWhenErrbackDoesNotExplicitlyPropagate() + { + extract($this->getPromiseTestAdapter()); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $reject(1); + $promise() + ->then( + $this->expectCallableNever(), + function ($val) { + return $val + 1; + } + ) + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function shouldSwitchFromErrbacksToCallbacksWhenErrbackReturnsAResolution() + { + extract($this->getPromiseTestAdapter()); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $reject(1); + $promise() + ->then( + $this->expectCallableNever(), + function ($val) { + return \React\Promise\resolve($val + 1); + } + ) + ->then( + $mock, + $this->expectCallableNever() + ); + } + + /** @test */ + public function shouldPropagateRejectionsWhenErrbackThrows() + { + extract($this->getPromiseTestAdapter()); + + $exception = new \Exception(); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->will($this->throwException($exception)); + + $mock2 = $this->createCallableMock(); + $mock2 + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $reject(1); + $promise() + ->then( + $this->expectCallableNever(), + $mock + ) + ->then( + $this->expectCallableNever(), + $mock2 + ); + } + + /** @test */ + public function shouldPropagateRejectionsWhenErrbackReturnsARejection() + { + extract($this->getPromiseTestAdapter()); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $reject(1); + $promise() + ->then( + $this->expectCallableNever(), + function ($val) { + return \React\Promise\reject($val + 1); + } + ) + ->then( + $this->expectCallableNever(), + $mock + ); + } +} diff --git a/tests/React/Promise/PromiseTest/PromiseTestTrait.php b/tests/React/Promise/PromiseTest/PromiseTestTrait.php new file mode 100644 index 00000000..9c83b937 --- /dev/null +++ b/tests/React/Promise/PromiseTest/PromiseTestTrait.php @@ -0,0 +1,24 @@ +getPromiseTestAdapter()); + + $this->assertInstanceOf('React\\Promise\\PromiseInterface', $promise()->then()); + } + + /** @test */ + public function thenShouldReturnAllowNull() + { + extract($this->getPromiseTestAdapter()); + + $this->assertInstanceOf('React\\Promise\\PromiseInterface', $promise()->then(null, null, null)); + } +} diff --git a/tests/React/Promise/PromiseTest/RejectTestTrait.php b/tests/React/Promise/PromiseTest/RejectTestTrait.php new file mode 100644 index 00000000..d8a9dcd4 --- /dev/null +++ b/tests/React/Promise/PromiseTest/RejectTestTrait.php @@ -0,0 +1,150 @@ +getPromiseTestAdapter()); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $promise() + ->then($this->expectCallableNever(), $mock); + + $reject(1); + } + + /** @test */ + public function rejectShouldRejectWithFulfilledPromise() + { + extract($this->getPromiseTestAdapter()); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $promise() + ->then($this->expectCallableNever(), $mock); + + $reject(Promise\resolve(1)); + } + + /** @test */ + public function rejectShouldRejectWithRejectedPromise() + { + extract($this->getPromiseTestAdapter()); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $promise() + ->then($this->expectCallableNever(), $mock); + + $reject(Promise\reject(1)); + } + + /** @test */ + public function rejectShouldReturnAPromiseForTheRejectionValue() + { + extract($this->getPromiseTestAdapter()); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $reject(1) + ->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function rejectShouldReturnAPromiseForPassedInRejectionValueWhenAlreadyResolved() + { + extract($this->getPromiseTestAdapter()); + + $resolve(1); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $reject(2) + ->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function rejectShouldReturnAPromiseForPassedInRejectionValueWhenAlreadyRejected() + { + extract($this->getPromiseTestAdapter()); + + $reject(1); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $reject(2) + ->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function rejectShouldInvokeNewlyAddedErrbackWhenAlreadyRejected() + { + extract($this->getPromiseTestAdapter()); + + $reject(1); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $promise() + ->then($this->expectCallableNever(), $mock); + } + + /** @test */ + public function rejectShouldForwardReasonWhenCallbackIsNull() + { + extract($this->getPromiseTestAdapter()); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + $promise() + ->then( + $this->expectCallableNever() + ) + ->then( + $this->expectCallableNever(), + $mock + ); + + $reject(1); + } +} diff --git a/tests/React/Promise/DeferredResolveTest.php b/tests/React/Promise/PromiseTest/ResolveTestTrait.php similarity index 50% rename from tests/React/Promise/DeferredResolveTest.php rename to tests/React/Promise/PromiseTest/ResolveTestTrait.php index c83795ab..b1182629 100644 --- a/tests/React/Promise/DeferredResolveTest.php +++ b/tests/React/Promise/PromiseTest/ResolveTestTrait.php @@ -1,17 +1,17 @@ getPromiseTestAdapter()); $mock = $this->createCallableMock(); $mock @@ -19,18 +19,16 @@ public function shouldResolve() ->method('__invoke') ->with($this->identicalTo(1)); - $d - ->promise() + $promise() ->then($mock); - $d - ->resolve(1); + $resolve(1); } /** @test */ - public function shouldResolveWithPromisedValue() + public function resolveShouldResolveWithPromisedValue() { - $d = new Deferred(); + extract($this->getPromiseTestAdapter()); $mock = $this->createCallableMock(); $mock @@ -38,18 +36,16 @@ public function shouldResolveWithPromisedValue() ->method('__invoke') ->with($this->identicalTo(1)); - $d - ->promise() + $promise() ->then($mock); - $d - ->resolve(new FulfilledPromise(1)); + $resolve(Promise\resolve(1)); } /** @test */ - public function shouldRejectWhenResolvedWithRejectedPromise() + public function resolveShouldRejectWhenResolvedWithRejectedPromise() { - $d = new Deferred(); + extract($this->getPromiseTestAdapter()); $mock = $this->createCallableMock(); $mock @@ -57,18 +53,16 @@ public function shouldRejectWhenResolvedWithRejectedPromise() ->method('__invoke') ->with($this->identicalTo(1)); - $d - ->promise() + $promise() ->then($this->expectCallableNever(), $mock); - $d - ->resolve(new RejectedPromise(1)); + $resolve(Promise\reject(1)); } /** @test */ - public function shouldReturnAPromiseForTheResolutionValue() + public function resolveShouldReturnAPromiseForTheResolutionValue() { - $d = new Deferred(); + extract($this->getPromiseTestAdapter()); $mock = $this->createCallableMock(); $mock @@ -76,15 +70,14 @@ public function shouldReturnAPromiseForTheResolutionValue() ->method('__invoke') ->with($this->identicalTo(1)); - $d - ->resolve(1) + $resolve(1) ->then($mock); } /** @test */ - public function shouldReturnAPromiseForAPromisedResolutionValue() + public function resolveShouldReturnAPromiseForAPromisedResolutionValue() { - $d = new Deferred(); + extract($this->getPromiseTestAdapter()); $mock = $this->createCallableMock(); $mock @@ -92,15 +85,14 @@ public function shouldReturnAPromiseForAPromisedResolutionValue() ->method('__invoke') ->with($this->identicalTo(1)); - $d - ->resolve(resolve(1)) + $resolve(Promise\resolve(1)) ->then($mock); } /** @test */ - public function shouldReturnAPromiseForAPromisedRejectionValue() + public function resolveShouldReturnAPromiseForAPromisedRejectionValue() { - $d = new Deferred(); + extract($this->getPromiseTestAdapter()); $mock = $this->createCallableMock(); $mock @@ -110,17 +102,50 @@ public function shouldReturnAPromiseForAPromisedRejectionValue() // Both the returned promise, and the deferred's own promise should // be rejected with the same value - $d - ->resolve(reject(1)) + $resolve(Promise\reject(1)) ->then($this->expectCallableNever(), $mock); } /** @test */ - public function shouldInvokeNewlyAddedCallbackWhenAlreadyResolved() + public function resolveShouldReturnAPromiseForPassedInResolutionValueWhenAlreadyResolved() + { + extract($this->getPromiseTestAdapter()); + + $resolve(1); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $resolve(2) + ->then($mock); + } + + /** @test */ + public function resolveShouldReturnAPromiseForPassedInResolutionValueWhenAlreadyRejected() { - $d = new Deferred(); - $d - ->resolve(1); + extract($this->getPromiseTestAdapter()); + + $reject(1); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $resolve(2) + ->then($mock); + } + + /** @test */ + public function resolveShouldInvokeNewlyAddedCallbackWhenAlreadyResolved() + { + extract($this->getPromiseTestAdapter()); + + $resolve(1); $mock = $this->createCallableMock(); $mock @@ -128,23 +153,22 @@ public function shouldInvokeNewlyAddedCallbackWhenAlreadyResolved() ->method('__invoke') ->with($this->identicalTo(1)); - $d - ->promise() + $promise() ->then($mock, $this->expectCallableNever()); } /** @test */ - public function shouldForwardValueWhenCallbackIsNull() + public function resolveShouldForwardValueWhenCallbackIsNull() { + extract($this->getPromiseTestAdapter()); + $mock = $this->createCallableMock(); $mock ->expects($this->once()) ->method('__invoke') ->with($this->identicalTo(1)); - $d = new Deferred(); - $d - ->promise() + $promise() ->then( null, $this->expectCallableNever() @@ -154,6 +178,6 @@ public function shouldForwardValueWhenCallbackIsNull() $this->expectCallableNever() ); - $d->resolve(1); + $resolve(1); } } diff --git a/tests/React/Promise/RejectedPromiseTest.php b/tests/React/Promise/RejectedPromiseTest.php index df953803..d358963e 100644 --- a/tests/React/Promise/RejectedPromiseTest.php +++ b/tests/React/Promise/RejectedPromiseTest.php @@ -2,147 +2,35 @@ namespace React\Promise; -/** - * @group Promise - * @group RejectedPromise - */ class RejectedPromiseTest extends TestCase { - /** @test */ - public function shouldReturnAPromise() - { - $p = new RejectedPromise(); - $this->assertInstanceOf('React\\Promise\\PromiseInterface', $p->then()); - } - - /** @test */ - public function shouldReturnAllowNull() - { - $p = new RejectedPromise(); - $this->assertInstanceOf('React\\Promise\\PromiseInterface', $p->then(null, null, null)); - } - - /** @test */ - public function shouldForwardUndefinedRejectionValue() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with(null); - - $p = new RejectedPromise(1); - $p - ->then( - $this->expectCallableNever(), - function () { - // Presence of rejection handler is enough to switch back - // to resolve mode, even though it returns undefined. - // The ONLY way to propagate a rejection is to re-throw or - // return a rejected promise; - } - ) - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function shouldSwitchFromErrbacksToCallbacksWhenErrbackDoesNotExplicitlyPropagate() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $p = new RejectedPromise(1); - $p - ->then( - $this->expectCallableNever(), - function ($val) { - return $val + 1; - } - ) - ->then( - $mock, - $this->expectCallableNever() - ); - } + use PromiseTest\PromiseTestTrait, + PromiseTest\PromiseRejectedTestTrait; - /** @test */ - public function shouldSwitchFromErrbacksToCallbacksWhenErrbackReturnsAResolution() + public function getPromiseTestAdapter() { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $p = new RejectedPromise(1); - $p - ->then( - $this->expectCallableNever(), - function ($val) { - return new FulfilledPromise($val + 1); + $val = null; + $promiseCalled = false; + + return [ + 'promise' => function () use (&$val, &$promiseCalled) { + $promiseCalled = true; + + return new RejectedPromise($val); + }, + 'resolve' => function ($value) { + throw new \LogicException('You cannot call resolve() for React\Promise\RejectedPromise'); + }, + 'reject' => function ($reason) use (&$val, &$promiseCalled) { + if ($promiseCalled) { + throw new \LogicException('You must call reject() before promise() for React\Promise\RejectedPromise'); } - ) - ->then( - $mock, - $this->expectCallableNever() - ); - } - - /** @test */ - public function shouldPropagateRejectionsWhenErrbackThrows() - { - $exception = new \Exception(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->will($this->throwException($exception)); - $mock2 = $this->createCallableMock(); - $mock2 - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo($exception)); - - $p = new RejectedPromise(1); - $p - ->then( - $this->expectCallableNever(), - $mock - ) - ->then( - $this->expectCallableNever(), - $mock2 - ); - } - - /** @test */ - public function shouldPropagateRejectionsWhenErrbackReturnsARejection() - { - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $p = new RejectedPromise(1); - $p - ->then( - $this->expectCallableNever(), - function ($val) { - return new RejectedPromise($val + 1); - } - ) - ->then( - $this->expectCallableNever(), - $mock - ); + $val = $reason; + }, + 'progress' => function () { + throw new \LogicException('You cannot call progress() for React\Promise\RejectedPromise'); + }, + ]; } } diff --git a/tests/React/Promise/TestCase.php b/tests/React/Promise/TestCase.php index 60ea46d7..08b9f4d1 100644 --- a/tests/React/Promise/TestCase.php +++ b/tests/React/Promise/TestCase.php @@ -4,6 +4,17 @@ class TestCase extends \PHPUnit_Framework_TestCase { + public function toClosure(callable $callable) + { + if ($callable instanceof \Closure) { + return $callable; + } + + return function () use ($callable) { + return call_user_func_array($callable, func_get_args()); + }; + } + public function expectCallableExactly($amount) { $mock = $this->createCallableMock(); From 8dc158ffce55c87b5643efa18b97b0aa8798c7fe Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 14:49:33 +0200 Subject: [PATCH 07/29] Remove PHP 5.3 versions from .travis.yml --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6b9b05b0..b0b5c89e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,6 @@ language: php php: - - 5.3.3 - - 5.3 - 5.4 - 5.5 From 411627305d6d0d189bc3166d70f17d6db9090ca5 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 15:01:24 +0200 Subject: [PATCH 08/29] Replace array() with short syntax --- src/React/Promise/Deferred.php | 12 ++++---- src/React/Promise/functions.php | 16 +++++----- tests/React/Promise/DeferredTest.php | 8 ++--- tests/React/Promise/FunctionAllTest.php | 24 +++++++-------- tests/React/Promise/FunctionAnyTest.php | 16 +++++----- tests/React/Promise/FunctionMapTest.php | 24 +++++++-------- tests/React/Promise/FunctionReduceTest.php | 34 +++++++++++----------- tests/React/Promise/FunctionSomeTest.php | 26 ++++++++--------- tests/React/Promise/LazyPromiseTest.php | 6 ++-- 9 files changed, 83 insertions(+), 83 deletions(-) diff --git a/src/React/Promise/Deferred.php b/src/React/Promise/Deferred.php index 6a91f5c7..2ccda454 100644 --- a/src/React/Promise/Deferred.php +++ b/src/React/Promise/Deferred.php @@ -6,8 +6,8 @@ class Deferred implements PromisorInterface { private $completed; private $promise; - private $handlers = array(); - private $progressHandlers = array(); + private $handlers = []; + private $progressHandlers = []; public function resolve($value = null) { @@ -19,7 +19,7 @@ public function resolve($value = null) $this->processQueue($this->handlers, $this->completed); - $this->progressHandlers = $this->handlers = array(); + $this->progressHandlers = $this->handlers = []; return $this->completed; } @@ -65,15 +65,15 @@ protected function getThenCallback() } }; } else { - $progHandler = array($deferred, 'progress'); + $progHandler = [$deferred, 'progress']; } $this->handlers[] = function ($promise) use ($onFulfilled, $onRejected, $deferred, $progHandler) { $promise ->then($onFulfilled, $onRejected) ->then( - array($deferred, 'resolve'), - array($deferred, 'reject'), + [$deferred, 'resolve'], + [$deferred, 'reject'], $progHandler ); }; diff --git a/src/React/Promise/functions.php b/src/React/Promise/functions.php index 787a8ac8..23ebb131 100644 --- a/src/React/Promise/functions.php +++ b/src/React/Promise/functions.php @@ -42,21 +42,21 @@ function some($promisesOrValues, $howMany) return resolve($promisesOrValues) ->then(function ($array) use ($howMany) { if (!is_array($array)) { - $array = array(); + $array = []; } $len = count($array); $toResolve = max(0, min($howMany, $len)); - $values = array(); + $values = []; $deferred = new Deferred(); if (!$toResolve) { $deferred->resolve($values); } else { $toReject = ($len - $toResolve) + 1; - $reasons = array(); + $reasons = []; - $progress = array($deferred, 'progress'); + $progress = [$deferred, 'progress']; $fulfillOne = function ($val, $i) use (&$values, &$toResolve, $deferred) { $values[$i] = $val; @@ -109,11 +109,11 @@ function map($promisesOrValues, callable $mapFunc) return resolve($promisesOrValues) ->then(function ($array) use ($mapFunc) { if (!is_array($array)) { - $array = array(); + $array = []; } $toResolve = count($array); - $values = array(); + $values = []; $deferred = new Deferred(); if (!$toResolve) { @@ -130,7 +130,7 @@ function ($mapped) use (&$values, $i, &$toResolve, $deferred) { $deferred->resolve($values); } }, - array($deferred, 'reject') + [$deferred, 'reject'] ); }; @@ -148,7 +148,7 @@ function reduce($promisesOrValues, callable $reduceFunc , $initialValue = null) return resolve($promisesOrValues) ->then(function ($array) use ($reduceFunc, $initialValue) { if (!is_array($array)) { - $array = array(); + $array = []; } $total = count($array); diff --git a/tests/React/Promise/DeferredTest.php b/tests/React/Promise/DeferredTest.php index a853b457..da32d83b 100644 --- a/tests/React/Promise/DeferredTest.php +++ b/tests/React/Promise/DeferredTest.php @@ -11,10 +11,10 @@ public function getPromiseTestAdapter() $d = new Deferred(); return [ - 'promise' => $this->toClosure(array($d, 'promise')), - 'resolve' => $this->toClosure(array($d, 'resolve')), - 'reject' => $this->toClosure(array($d, 'reject')), - 'progress' => $this->toClosure(array($d, 'progress')), + 'promise' => $this->toClosure([$d, 'promise']), + 'resolve' => $this->toClosure([$d, 'resolve']), + 'reject' => $this->toClosure([$d, 'reject']), + 'progress' => $this->toClosure([$d, 'progress']), ]; } } diff --git a/tests/React/Promise/FunctionAllTest.php b/tests/React/Promise/FunctionAllTest.php index 991e0650..fcd0a5df 100644 --- a/tests/React/Promise/FunctionAllTest.php +++ b/tests/React/Promise/FunctionAllTest.php @@ -11,9 +11,9 @@ public function shouldResolveEmptyInput() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array())); + ->with($this->identicalTo([])); - all(array()) + all([]) ->then($mock); } @@ -24,9 +24,9 @@ public function shouldResolveValuesArray() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(1, 2, 3))); + ->with($this->identicalTo([1, 2, 3])); - all(array(1, 2, 3)) + all([1, 2, 3]) ->then($mock); } @@ -37,9 +37,9 @@ public function shouldResolvePromisesArray() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(1, 2, 3))); + ->with($this->identicalTo([1, 2, 3])); - all(array(resolve(1), resolve(2), resolve(3))) + all([resolve(1), resolve(2), resolve(3)]) ->then($mock); } @@ -50,9 +50,9 @@ public function shouldResolveSparseArrayInput() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(null, 1, null, 1, 1))); + ->with($this->identicalTo([null, 1, null, 1, 1])); - all(array(null, 1, null, 1, 1)) + all([null, 1, null, 1, 1]) ->then($mock); } @@ -65,7 +65,7 @@ public function shouldRejectIfAnyInputPromiseRejects() ->method('__invoke') ->with($this->identicalTo(2)); - all(array(resolve(1), reject(2), resolve(3))) + all([resolve(1), reject(2), resolve(3)]) ->then($this->expectCallableNever(), $mock); } @@ -76,9 +76,9 @@ public function shouldAcceptAPromiseForAnArray() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(1, 2, 3))); + ->with($this->identicalTo([1, 2, 3])); - all(resolve(array(1, 2, 3))) + all(resolve([1, 2, 3])) ->then($mock); } @@ -89,7 +89,7 @@ public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array())); + ->with($this->identicalTo([])); all(resolve(1)) ->then($mock); diff --git a/tests/React/Promise/FunctionAnyTest.php b/tests/React/Promise/FunctionAnyTest.php index 1fa77f6d..bf8a0db4 100644 --- a/tests/React/Promise/FunctionAnyTest.php +++ b/tests/React/Promise/FunctionAnyTest.php @@ -13,7 +13,7 @@ public function shouldResolveToNullWithEmptyInputArray() ->method('__invoke') ->with($this->identicalTo(null)); - any(array()) + any([]) ->then($mock); } @@ -26,7 +26,7 @@ public function shouldResolveWithAnInputValue() ->method('__invoke') ->with($this->identicalTo(1)); - any(array(1, 2, 3)) + any([1, 2, 3]) ->then($mock); } @@ -39,7 +39,7 @@ public function shouldResolveWithAPromisedInputValue() ->method('__invoke') ->with($this->identicalTo(1)); - any(array(resolve(1), resolve(2), resolve(3))) + any([resolve(1), resolve(2), resolve(3)]) ->then($mock); } @@ -50,9 +50,9 @@ public function shouldRejectWithAllRejectedInputValuesIfAllInputsAreRejected() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(0 => 1, 1 => 2, 2 => 3))); + ->with($this->identicalTo([0 => 1, 1 => 2, 2 => 3])); - any(array(reject(1), reject(2), reject(3))) + any([reject(1), reject(2), reject(3)]) ->then($this->expectCallableNever(), $mock); } @@ -65,7 +65,7 @@ public function shouldResolveWhenFirstInputPromiseResolves() ->method('__invoke') ->with($this->identicalTo(1)); - any(array(resolve(1), reject(2), reject(3))) + any([resolve(1), reject(2), reject(3)]) ->then($mock); } @@ -78,7 +78,7 @@ public function shouldAcceptAPromiseForAnArray() ->method('__invoke') ->with($this->identicalTo(1)); - any(resolve(array(1, 2, 3))) + any(resolve([1, 2, 3])) ->then($mock); } @@ -107,7 +107,7 @@ public function shouldNotRelyOnArryIndexesWhenUnwrappingToASingleResolutionValue $d1 = new Deferred(); $d2 = new Deferred(); - any(array('abc' => $d1->promise(), 1 => $d2->promise())) + any(['abc' => $d1->promise(), 1 => $d2->promise()]) ->then($mock); $d2->resolve(2); diff --git a/tests/React/Promise/FunctionMapTest.php b/tests/React/Promise/FunctionMapTest.php index c40a665f..b8bf3a83 100644 --- a/tests/React/Promise/FunctionMapTest.php +++ b/tests/React/Promise/FunctionMapTest.php @@ -25,10 +25,10 @@ public function shouldMapInputValuesArray() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(2, 4, 6))); + ->with($this->identicalTo([2, 4, 6])); map( - array(1, 2, 3), + [1, 2, 3], $this->mapper() )->then($mock); } @@ -40,10 +40,10 @@ public function shouldMapInputPromisesArray() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(2, 4, 6))); + ->with($this->identicalTo([2, 4, 6])); map( - array(resolve(1), resolve(2), resolve(3)), + [resolve(1), resolve(2), resolve(3)], $this->mapper() )->then($mock); } @@ -55,10 +55,10 @@ public function shouldMapMixedInputArray() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(2, 4, 6))); + ->with($this->identicalTo([2, 4, 6])); map( - array(1, resolve(2), 3), + [1, resolve(2), 3], $this->mapper() )->then($mock); } @@ -70,10 +70,10 @@ public function shouldMapInputWhenMapperReturnsAPromise() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(2, 4, 6))); + ->with($this->identicalTo([2, 4, 6])); map( - array(1, 2, 3), + [1, 2, 3], $this->promiseMapper() )->then($mock); } @@ -85,10 +85,10 @@ public function shouldAcceptAPromiseForAnArray() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(2, 4, 6))); + ->with($this->identicalTo([2, 4, 6])); map( - resolve(array(1, resolve(2), 3)), + resolve([1, resolve(2), 3]), $this->mapper() )->then($mock); } @@ -100,7 +100,7 @@ public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array())); + ->with($this->identicalTo([])); map( resolve(1), @@ -118,7 +118,7 @@ public function shouldRejectWhenInputContainsRejection() ->with($this->identicalTo(2)); map( - array(resolve(1), reject(2), resolve(3)), + [resolve(1), reject(2), resolve(3)], $this->mapper() )->then($this->expectCallableNever(), $mock); } diff --git a/tests/React/Promise/FunctionReduceTest.php b/tests/React/Promise/FunctionReduceTest.php index bb0211d0..715e8477 100644 --- a/tests/React/Promise/FunctionReduceTest.php +++ b/tests/React/Promise/FunctionReduceTest.php @@ -28,7 +28,7 @@ public function shouldReduceValuesWithoutInitialValue() ->with($this->identicalTo(6)); reduce( - array(1, 2, 3), + [1, 2, 3], $this->plus() )->then($mock); } @@ -43,7 +43,7 @@ public function shouldReduceValuesWithInitialValue() ->with($this->identicalTo(7)); reduce( - array(1, 2, 3), + [1, 2, 3], $this->plus(), 1 )->then($mock); @@ -59,7 +59,7 @@ public function shouldReduceValuesWithInitialPromise() ->with($this->identicalTo(7)); reduce( - array(1, 2, 3), + [1, 2, 3], $this->plus(), resolve(1) )->then($mock); @@ -75,7 +75,7 @@ public function shouldReducePromisedValuesWithoutInitialValue() ->with($this->identicalTo(6)); reduce( - array(resolve(1), resolve(2), resolve(3)), + [resolve(1), resolve(2), resolve(3)], $this->plus() )->then($mock); } @@ -90,7 +90,7 @@ public function shouldReducePromisedValuesWithInitialValue() ->with($this->identicalTo(7)); reduce( - array(resolve(1), resolve(2), resolve(3)), + [resolve(1), resolve(2), resolve(3)], $this->plus(), 1 )->then($mock); @@ -106,7 +106,7 @@ public function shouldReducePromisedValuesWithInitialPromise() ->with($this->identicalTo(7)); reduce( - array(resolve(1), resolve(2), resolve(3)), + [resolve(1), resolve(2), resolve(3)], $this->plus(), resolve(1) )->then($mock); @@ -122,7 +122,7 @@ public function shouldReduceEmptyInputWithInitialValue() ->with($this->identicalTo(1)); reduce( - array(), + [], $this->plus(), 1 )->then($mock); @@ -138,7 +138,7 @@ public function shouldReduceEmptyInputWithInitialPromise() ->with($this->identicalTo(1)); reduce( - array(), + [], $this->plus(), resolve(1) )->then($mock); @@ -154,7 +154,7 @@ public function shouldRejectWhenInputContainsRejection() ->with($this->identicalTo(2)); reduce( - array(resolve(1), reject(2), resolve(3)), + [resolve(1), reject(2), resolve(3)], $this->plus(), resolve(1) )->then($this->expectCallableNever(), $mock); @@ -174,7 +174,7 @@ public function shouldResolveWithNullWhenInputIsEmptyAndNoInitialValueOrPromiseP ->with($this->identicalTo(null)); reduce( - array(), + [], $this->plus() )->then($mock); } @@ -189,7 +189,7 @@ public function shouldAllowSparseArrayInputWithoutInitialValue() ->with($this->identicalTo(3)); reduce( - array(null, null, 1, null, 1, 1), + [null, null, 1, null, 1, 1], $this->plus() )->then($mock); } @@ -204,7 +204,7 @@ public function shouldAllowSparseArrayInputWithInitialValue() ->with($this->identicalTo(4)); reduce( - array(null, null, 1, null, 1, 1), + [null, null, 1, null, 1, 1], $this->plus(), 1 )->then($mock); @@ -220,7 +220,7 @@ public function shouldReduceInInputOrder() ->with($this->identicalTo('123')); reduce( - array(1, 2, 3), + [1, 2, 3], $this->append(), '' )->then($mock); @@ -236,7 +236,7 @@ public function shouldAcceptAPromiseForAnArray() ->with($this->identicalTo('123')); reduce( - resolve(array(1, 2, 3)), + resolve([1, 2, 3]), $this->append(), '' )->then($mock); @@ -275,12 +275,12 @@ public function shouldProvideCorrectBasisValue() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(1, 2, 3))); + ->with($this->identicalTo([1, 2, 3])); reduce( - array($d1->promise(), $d2->promise(), $d3->promise()), + [$d1->promise(), $d2->promise(), $d3->promise()], $insertIntoArray, - array() + [] )->then($mock); $d3->resolve(3); diff --git a/tests/React/Promise/FunctionSomeTest.php b/tests/React/Promise/FunctionSomeTest.php index 4230b18d..b1ed95d6 100644 --- a/tests/React/Promise/FunctionSomeTest.php +++ b/tests/React/Promise/FunctionSomeTest.php @@ -11,10 +11,10 @@ public function shouldResolveEmptyInput() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array())); + ->with($this->identicalTo([])); some( - array(), + [], 1 )->then($mock); } @@ -26,10 +26,10 @@ public function shouldResolveValuesArray() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(1, 2))); + ->with($this->identicalTo([1, 2])); some( - array(1, 2, 3), + [1, 2, 3], 2 )->then($mock); } @@ -41,10 +41,10 @@ public function shouldResolvePromisesArray() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(1, 2))); + ->with($this->identicalTo([1, 2])); some( - array(resolve(1), resolve(2), resolve(3)), + [resolve(1), resolve(2), resolve(3)], 2 )->then($mock); } @@ -56,10 +56,10 @@ public function shouldResolveSparseArrayInput() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(null, 1))); + ->with($this->identicalTo([null, 1])); some( - array(null, 1, null, 2, 3), + [null, 1, null, 2, 3], 2 )->then($mock); } @@ -71,10 +71,10 @@ public function shouldRejectIfAnyInputPromiseRejectsBeforeDesiredNumberOfInputsA $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(1 => 2, 2 => 3))); + ->with($this->identicalTo([1 => 2, 2 => 3])); some( - array(resolve(1), reject(2), reject(3)), + [resolve(1), reject(2), reject(3)], 2 )->then($this->expectCallableNever(), $mock); } @@ -86,10 +86,10 @@ public function shouldAcceptAPromiseForAnArray() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array(1, 2))); + ->with($this->identicalTo([1, 2])); some( - resolve(array(1, 2, 3)), + resolve([1, 2, 3]), 2 )->then($mock); } @@ -101,7 +101,7 @@ public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray() $mock ->expects($this->once()) ->method('__invoke') - ->with($this->identicalTo(array())); + ->with($this->identicalTo([])); some( resolve(1), diff --git a/tests/React/Promise/LazyPromiseTest.php b/tests/React/Promise/LazyPromiseTest.php index 21b176f6..d27971f4 100644 --- a/tests/React/Promise/LazyPromiseTest.php +++ b/tests/React/Promise/LazyPromiseTest.php @@ -18,9 +18,9 @@ public function getPromiseTestAdapter() 'promise' => function () use ($factory) { return new LazyPromise($factory); }, - 'resolve' => $this->toClosure(array($d, 'resolve')), - 'reject' => $this->toClosure(array($d, 'reject')), - 'progress' => $this->toClosure(array($d, 'progress')), + 'resolve' => $this->toClosure([$d, 'resolve']), + 'reject' => $this->toClosure([$d, 'reject']), + 'progress' => $this->toClosure([$d, 'progress']), ]; } From e33467cca24a02f80427551657d12696a2cd0084 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 16:26:38 +0200 Subject: [PATCH 09/29] Add standalone Promise class --- src/React/Promise/Promise.php | 101 ++++++++++++++++++++++++++++ tests/React/Promise/PromiseTest.php | 47 +++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 src/React/Promise/Promise.php create mode 100644 tests/React/Promise/PromiseTest.php diff --git a/src/React/Promise/Promise.php b/src/React/Promise/Promise.php new file mode 100644 index 00000000..cedd6307 --- /dev/null +++ b/src/React/Promise/Promise.php @@ -0,0 +1,101 @@ +resolve($value); + }, + function ($reason = null) { + return $this->reject($reason); + }, + function ($update = null) { + $this->progress($update); + } + ); + } catch (\Exception $e) { + $this->reject($e); + } + } + + public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + if (null !== $this->result) { + return $this->result->then($onFulfilled, $onRejected, $onProgress); + } + + return new static($this->resolver($onFulfilled, $onRejected, $onProgress)); + } + + private function resolver(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) + { + return function ($resolve, $reject, $progress) use ($onFulfilled, $onRejected, $onProgress) { + if ($onProgress) { + $progressHandler = function ($update) use ($progress, $onProgress) { + try { + $progress(call_user_func($onProgress, $update)); + } catch (\Exception $e) { + $progress($e); + } + }; + } else { + $progressHandler = $progress; + } + + $this->handlers[] = function (PromiseInterface $promise) use ($onFulfilled, $onRejected, $resolve, $reject, $progressHandler) { + $promise + ->then($onFulfilled, $onRejected) + ->then($resolve, $reject, $progressHandler); + }; + + $this->progressHandlers[] = $progressHandler; + }; + } + + private function resolve($value = null) + { + return $this->settle(resolve($value)); + } + + private function reject($reason = null) + { + return $this->settle(reject($reason)); + } + + private function progress($update = null) + { + if (null !== $this->result) { + return; + } + + foreach ($this->progressHandlers as $handler) { + call_user_func($handler, $update); + } + } + + private function settle(PromiseInterface $result) + { + if (null !== $this->result) { + return $result; + } + + foreach ($this->handlers as $handler) { + call_user_func($handler, $result); + } + + $this->progressHandlers = $this->handlers = []; + + return $this->result = $result; + } +} diff --git a/tests/React/Promise/PromiseTest.php b/tests/React/Promise/PromiseTest.php new file mode 100644 index 00000000..46f0abe3 --- /dev/null +++ b/tests/React/Promise/PromiseTest.php @@ -0,0 +1,47 @@ + function () use ($promise) { + return $promise; + }, + 'resolve' => $resolveCallback, + 'reject' => $rejectCallback, + 'progress' => $progressCallback, + ]; + } + + /** @test */ + public function shouldRejectIfResolverThrowsException() + { + $exception = new \Exception('foo'); + + $promise = new Promise(function () use ($exception) { + throw $exception; + }); + + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo($exception)); + + $promise + ->then($this->expectCallableNever(), $mock); + } +} From 46048ecca6ea5e8b4adcad2f185bbf208dcdb724 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 16:39:20 +0200 Subject: [PATCH 10/29] Let Deferred use Promise class --- src/React/Promise/Deferred.php | 88 ++++++--------------------- src/React/Promise/DeferredPromise.php | 18 ------ 2 files changed, 20 insertions(+), 86 deletions(-) delete mode 100644 src/React/Promise/DeferredPromise.php diff --git a/src/React/Promise/Deferred.php b/src/React/Promise/Deferred.php index 2ccda454..f00dd895 100644 --- a/src/React/Promise/Deferred.php +++ b/src/React/Promise/Deferred.php @@ -4,90 +4,42 @@ class Deferred implements PromisorInterface { - private $completed; private $promise; - private $handlers = []; - private $progressHandlers = []; - - public function resolve($value = null) - { - if (null !== $this->completed) { - return resolve($value); - } - - $this->completed = resolve($value); - - $this->processQueue($this->handlers, $this->completed); - - $this->progressHandlers = $this->handlers = []; - - return $this->completed; - } - - public function reject($reason = null) - { - return $this->resolve(reject($reason)); - } - - public function progress($update = null) - { - if (null !== $this->completed) { - return; - } - - $this->processQueue($this->progressHandlers, $update); - } + private $resolveCallback; + private $rejectCallback; + private $progressCallback; public function promise() { if (null === $this->promise) { - $this->promise = new DeferredPromise($this->getThenCallback()); + $this->promise = new Promise(function ($resolve, $reject, $progress) { + $this->resolveCallback = $resolve; + $this->rejectCallback = $reject; + $this->progressCallback = $progress; + }); } return $this->promise; } - protected function getThenCallback() + public function resolve($value = null) { - return function (callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) { - if (null !== $this->completed) { - return $this->completed->then($onFulfilled, $onRejected, $onProgress); - } - - $deferred = new static(); + $this->promise(); - if (is_callable($onProgress)) { - $progHandler = function ($update) use ($deferred, $onProgress) { - try { - $deferred->progress(call_user_func($onProgress, $update)); - } catch (\Exception $e) { - $deferred->progress($e); - } - }; - } else { - $progHandler = [$deferred, 'progress']; - } - - $this->handlers[] = function ($promise) use ($onFulfilled, $onRejected, $deferred, $progHandler) { - $promise - ->then($onFulfilled, $onRejected) - ->then( - [$deferred, 'resolve'], - [$deferred, 'reject'], - $progHandler - ); - }; + return call_user_func($this->resolveCallback, $value); + } - $this->progressHandlers[] = $progHandler; + public function reject($reason = null) + { + $this->promise(); - return $deferred->promise(); - }; + return call_user_func($this->rejectCallback, $reason); } - protected function processQueue($queue, $value) + public function progress($update = null) { - foreach ($queue as $handler) { - call_user_func($handler, $value); - } + $this->promise(); + + return call_user_func($this->progressCallback, $update); } } diff --git a/src/React/Promise/DeferredPromise.php b/src/React/Promise/DeferredPromise.php deleted file mode 100644 index 1c81c28d..00000000 --- a/src/React/Promise/DeferredPromise.php +++ /dev/null @@ -1,18 +0,0 @@ -thenCallback = $thenCallback; - } - - public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null) - { - return call_user_func($this->thenCallback, $onFulfilled, $onRejected, $onProgress); - } -} From 466e1be1f3bb8d4284f5e3421a5ab6250649d79c Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 16:40:53 +0200 Subject: [PATCH 11/29] CS fixes --- tests/React/Promise/FulfilledPromiseTest.php | 2 +- tests/React/Promise/FunctionRejectTest.php | 2 +- tests/React/Promise/LazyPromiseTest.php | 4 ++-- .../Promise/PromiseTest/PromiseFullfilledTestTrait.php | 10 +++++----- .../Promise/PromiseTest/PromiseRejectedTestTrait.php | 10 +++++----- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/React/Promise/FulfilledPromiseTest.php b/tests/React/Promise/FulfilledPromiseTest.php index 23cb0dcd..102cffd8 100644 --- a/tests/React/Promise/FulfilledPromiseTest.php +++ b/tests/React/Promise/FulfilledPromiseTest.php @@ -18,7 +18,7 @@ public function getPromiseTestAdapter() return new FulfilledPromise($val); }, - 'resolve' => function ($value) use (&$val, &$promiseCalled) { + 'resolve' => function ($value) use (&$val, &$promiseCalled) { if ($promiseCalled) { throw new \LogicException('You must call resolve() before promise() for React\Promise\FulfilledPromise'); } diff --git a/tests/React/Promise/FunctionRejectTest.php b/tests/React/Promise/FunctionRejectTest.php index f09fc479..84b8ec6a 100644 --- a/tests/React/Promise/FunctionRejectTest.php +++ b/tests/React/Promise/FunctionRejectTest.php @@ -21,7 +21,7 @@ public function shouldRejectAnImmediateValue() $mock ); } - + /** @test */ public function shouldRejectAFulfilledPromise() { diff --git a/tests/React/Promise/LazyPromiseTest.php b/tests/React/Promise/LazyPromiseTest.php index d27971f4..a8d43aa8 100644 --- a/tests/React/Promise/LazyPromiseTest.php +++ b/tests/React/Promise/LazyPromiseTest.php @@ -79,12 +79,12 @@ public function shouldReturnPromiseIfFactoryReturnsNull() $p = new LazyPromise($factory); $this->assertInstanceOf('React\\Promise\\PromiseInterface', $p->then()); } - + /** @test */ public function shouldReturnRejectedPromiseIfFactoryThrowsException() { $exception = new \Exception(); - + $factory = $this->createCallableMock(); $factory ->expects($this->once()) diff --git a/tests/React/Promise/PromiseTest/PromiseFullfilledTestTrait.php b/tests/React/Promise/PromiseTest/PromiseFullfilledTestTrait.php index 5e5ba0c3..fbefe58e 100644 --- a/tests/React/Promise/PromiseTest/PromiseFullfilledTestTrait.php +++ b/tests/React/Promise/PromiseTest/PromiseFullfilledTestTrait.php @@ -10,7 +10,7 @@ abstract public function getPromiseTestAdapter(); public function thenShouldForwardResultWhenCallbackIsNull() { extract($this->getPromiseTestAdapter()); - + $mock = $this->createCallableMock(); $mock ->expects($this->once()) @@ -33,7 +33,7 @@ public function thenShouldForwardResultWhenCallbackIsNull() public function thenShouldForwardCallbackResultToNextCallback() { extract($this->getPromiseTestAdapter()); - + $mock = $this->createCallableMock(); $mock ->expects($this->once()) @@ -58,7 +58,7 @@ function ($val) { public function thenShouldForwardPromisedCallbackResultValueToNextCallback() { extract($this->getPromiseTestAdapter()); - + $mock = $this->createCallableMock(); $mock ->expects($this->once()) @@ -83,7 +83,7 @@ function ($val) { public function thenShouldSwitchFromCallbacksToErrbacksWhenCallbackReturnsARejection() { extract($this->getPromiseTestAdapter()); - + $mock = $this->createCallableMock(); $mock ->expects($this->once()) @@ -108,7 +108,7 @@ function ($val) { public function thenShouldSwitchFromCallbacksToErrbacksWhenCallbackThrows() { extract($this->getPromiseTestAdapter()); - + $exception = new \Exception(); $mock = $this->createCallableMock(); diff --git a/tests/React/Promise/PromiseTest/PromiseRejectedTestTrait.php b/tests/React/Promise/PromiseTest/PromiseRejectedTestTrait.php index 39bce79f..8c97186e 100644 --- a/tests/React/Promise/PromiseTest/PromiseRejectedTestTrait.php +++ b/tests/React/Promise/PromiseTest/PromiseRejectedTestTrait.php @@ -10,7 +10,7 @@ abstract public function getPromiseTestAdapter(); public function shouldForwardUndefinedRejectionValue() { extract($this->getPromiseTestAdapter()); - + $mock = $this->createCallableMock(); $mock ->expects($this->once()) @@ -38,7 +38,7 @@ function () { public function shouldSwitchFromErrbacksToCallbacksWhenErrbackDoesNotExplicitlyPropagate() { extract($this->getPromiseTestAdapter()); - + $mock = $this->createCallableMock(); $mock ->expects($this->once()) @@ -63,7 +63,7 @@ function ($val) { public function shouldSwitchFromErrbacksToCallbacksWhenErrbackReturnsAResolution() { extract($this->getPromiseTestAdapter()); - + $mock = $this->createCallableMock(); $mock ->expects($this->once()) @@ -88,7 +88,7 @@ function ($val) { public function shouldPropagateRejectionsWhenErrbackThrows() { extract($this->getPromiseTestAdapter()); - + $exception = new \Exception(); $mock = $this->createCallableMock(); @@ -119,7 +119,7 @@ public function shouldPropagateRejectionsWhenErrbackThrows() public function shouldPropagateRejectionsWhenErrbackReturnsARejection() { extract($this->getPromiseTestAdapter()); - + $mock = $this->createCallableMock(); $mock ->expects($this->once()) From d80f0b0cc302532e67b58cc91ab924abfc8992e9 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 17:06:24 +0200 Subject: [PATCH 12/29] Update readme for new Promise class --- README.md | 155 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 97 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 49a8c2ab..acb5fc37 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Table of Contents * [Deferred::reject()](#deferredreject) * [Deferred::progress()](#deferredprogress) * [Promise](#promise-1) + * [Promise::then()](#promisethen) * [LazyPromise](#lazypromise) * [Functions](#functions) * [resolve()](#resolve) @@ -32,7 +33,7 @@ Table of Contents * [Promisor](#promisor) 4. [Examples](#examples) * [How to use Deferred](#how-to-use-deferred) - * [How Promise forwarding works](#how-promise-forwarding-works) + * [How promise forwarding works](#how-promise-forwarding-works) * [Resolution forwarding](#resolution-forwarding) * [Rejection forwarding](#rejection-forwarding) * [Mixed resolution and rejection forwarding](#mixed-resolution-and-rejection-forwarding) @@ -46,10 +47,10 @@ Introduction React/Promise is a library implementing [CommonJS Promises/A](http://wiki.commonjs.org/wiki/Promises/A) for PHP. -It also provides several other useful Promise-related concepts, such as joining -multiple Promises and mapping and reducing collections of Promises. +It also provides several other useful promise-related concepts, such as joining +multiple promises and mapping and reducing collections of promises. -If you've never heard about Promises before, +If you've never heard about promises before, [read this first](https://gist.github.com/3889970). Concepts @@ -63,8 +64,8 @@ that executes asynchronously and completes at some point in the future. ### Promise -While a Deferred represents the computation itself, a **Promise** represents -the result of that computation. Thus, each Deferred has a Promise that acts as +While a deferred represents the computation itself, a **Promise** represents +the result of that computation. Thus, each deferred has a promise that acts as a placeholder for its actual result. API @@ -119,7 +120,7 @@ this promise once it is resolved. $resolver->reject(mixed $reason = null); ``` -Rejects the promise returned by `promise()`, signalling that the Deferred's +Rejects the promise returned by `promise()`, signalling that the deferred's computation failed. All consumers are notified by having `$onRejected` (which they registered via `$promise->then()`) called with `$reason`. @@ -141,13 +142,48 @@ All consumers are notified by having `$onProgress` (which they registered via ### Promise -The Promise represents the eventual outcome, which is either fulfillment +The promise represents an eventual outcome, which is either fulfillment (success) and an associated value, or rejection (failure) and an associated -reason. The Promise provides mechanisms for arranging to call a function on its -value or reason, and produces a new Promise for the result. +reason. -A Promise has a single method `then()` which registers new fulfilled, rejection -and progress handlers with this Promise (all parameters are optional): +``` php +$promise = new React\Promise\Promise(function (callable $resolve, callable $reject, callable $progress) { + // Do some work, possibly asynchronously, and then + // resolve or reject. You can notify of progress events + // along the way if you want/need. + + $resolve($awesomeResult); + // or $resolve($anotherPromise); + // or $reject($nastyError); + // or $progress($progressNotification); +}); + +$promise->then( + function ($value) { + // Promise resolved, do something with $value + }, + function ($reason) { + // Promise rejected, do something with $reason + }, + function ($update) { + // Progress notification triggered, do something with $update + } +); +``` + +The promise constructor receives a resolver function which will be called +with 3 arguments: + + * `$resolve($value)` - Primary function that seals the fate of the + returned promise. Accepts either a non-promise value, or another promise. + When called with a non-promise value, fulfills promise with that value. + When called with another promise, e.g. `$resolve($otherPromise)`, promise's + fate will be equivalent to that of `$otherPromise`. + * `$reject($reason)` - Function that rejects the promise. + * `$progress($update)` - Function that issues progress events for the promise. + +If the resolver throws an exception, the promise will be rejected with that +thrown exception as the rejection reason. #### Promise::then() @@ -155,22 +191,25 @@ and progress handlers with this Promise (all parameters are optional): $newPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); ``` - * `$onFulfilled` will be invoked once the Promise is fulfilled and passed +The `then()` method registers new fulfilled, rejection and progress handlers +with this promise (all parameters are optional): + + * `$onFulfilled` will be invoked once the promise is fulfilled and passed the result as the first argument. - * `$onRejected` will be invoked once the Promise is rejected and passed the + * `$onRejected` will be invoked once the promise is rejected and passed the reason as the first argument. - * `$onProgress` will be invoked whenever the producer of the Promise + * `$onProgress` will be invoked whenever the producer of the promise triggers progress notifications and passed a single argument (whatever it wants) to indicate progress. -Returns a new Promise that will fulfill with the return value of either +Returns a new promise that will fulfill with the return value of either `$onFulfilled` or `$onRejected`, whichever is called, or will reject with the thrown exception if either throws. -Once in the fulfilled or rejected state, a Promise becomes immutable. +Once in the fulfilled or rejected state, a promise becomes immutable. Neither its state nor its result (or error) can be modified. -A Promise makes the following guarantees about handlers registered in +A promise makes the following guarantees about handlers registered in the same call to `then()`: 1. Only one of `$onFulfilled` or `$onRejected` will be called, @@ -181,19 +220,19 @@ the same call to `then()`: #### See also -* [resolve()](#resolve) - Creating a resolved Promise -* [reject()](#reject) - Creating a rejected Promise +* [resolve()](#resolve) - Creating a resolved promise +* [reject()](#reject) - Creating a rejected promise ### LazyPromise -Creates a Promise which will be lazily initialized by `$factory` once a consumer +Creates a promise which will be lazily initialized by `$factory` once a consumer calls the `then()` method. ```php $factory = function () { $deferred = new React\Promise\Deferred(); - // Do some heavy stuff here and resolve the Deferred once completed + // Do some heavy stuff here and resolve the deferred once completed return $deferred->promise(); }; @@ -208,7 +247,7 @@ $promise->then(function ($value) { ### Functions Useful functions for creating, joining, mapping and reducing collections of -Promises. +promises. #### resolve() @@ -216,12 +255,12 @@ Promises. $promise = React\Promise\resolve(mixed $promiseOrValue); ``` -Creates a resolved Promise for the supplied `$promiseOrValue`. +Creates a resolved promise for the supplied `$promiseOrValue`. If `$promiseOrValue` is a value, it will be the resolution value of the -returned Promise. +returned promise. -If `$promiseOrValue` is a Promise, it will simply be returned. +If `$promiseOrValue` is a promise, it will simply be returned. #### reject() @@ -229,17 +268,17 @@ If `$promiseOrValue` is a Promise, it will simply be returned. $promise = React\Promise\reject(mixed $promiseOrValue); ``` -Creates a rejected Promise for the supplied `$promiseOrValue`. +Creates a rejected promise for the supplied `$promiseOrValue`. If `$promiseOrValue` is a value, it will be the rejection value of the -returned Promise. +returned promise. -If `$promiseOrValue` is a Promise, its completion value will be the rejected -value of the returned Promise. +If `$promiseOrValue` is a promise, its completion value will be the rejected +value of the returned promise. -This can be useful in situations where you need to reject a Promise without +This can be useful in situations where you need to reject a promise without throwing an exception. For example, it allows you to propagate a rejection with -the value of another Promise. +the value of another promise. #### all() @@ -247,8 +286,8 @@ the value of another Promise. $promise = React\Promise\all(array|React\Promise\PromiseInterface $promisesOrValues); ``` -Returns a Promise that will resolve only once all the items in -`$promisesOrValues` have resolved. The resolution value of the returned Promise +Returns a promise that will resolve only once all the items in +`$promisesOrValues` have resolved. The resolution value of the returned promise will be an array containing the resolution values of each of the items in `$promisesOrValues`. @@ -258,11 +297,11 @@ will be an array containing the resolution values of each of the items in $promise = React\Promise\any(array|React\Promise\PromiseInterface $promisesOrValues); ``` -Returns a Promise that will resolve when any one of the items in -`$promisesOrValues` resolves. The resolution value of the returned Promise +Returns a promise that will resolve when any one of the items in +`$promisesOrValues` resolves. The resolution value of the returned promise will be the resolution value of the triggering item. -The returned Promise will only reject if *all* items in `$promisesOrValues` are +The returned promise will only reject if *all* items in `$promisesOrValues` are rejected. The rejection value will be an array of all rejection reasons. #### some() @@ -271,12 +310,12 @@ rejected. The rejection value will be an array of all rejection reasons. $promise = React\Promise\some(array|React\Promise\PromiseInterface $promisesOrValues, integer $howMany); ``` -Returns a Promise that will resolve when `$howMany` of the supplied items in -`$promisesOrValues` resolve. The resolution value of the returned Promise +Returns a promise that will resolve when `$howMany` of the supplied items in +`$promisesOrValues` resolve. The resolution value of the returned promise will be an array of length `$howMany` containing the resolution values of the triggering items. -The returned Promise will reject if it becomes impossible for `$howMany` items +The returned promise will reject if it becomes impossible for `$howMany` items to resolve (that is, when `(count($promisesOrValues) - $howMany) + 1` items reject). The rejection value will be an array of `(count($promisesOrValues) - $howMany) + 1` rejection reasons. @@ -288,10 +327,10 @@ $promise = React\Promise\map(array|React\Promise\PromiseInterface $promisesOrVal ``` Traditional map function, similar to `array_map()`, but allows input to contain -Promises and/or values, and `$mapFunc` may return either a value or a Promise. +promises and/or values, and `$mapFunc` may return either a value or a promise. The map function receives each item as argument, where item is a fully resolved -value of a Promise or value in `$promisesOrValues`. +value of a promise or value in `$promisesOrValues`. #### reduce() @@ -300,8 +339,8 @@ $promise = React\Promise\reduce(array|React\Promise\PromiseInterface $promisesOr ``` Traditional reduce function, similar to `array_reduce()`, but input may contain -Promises and/or values, and `$reduceFunc` may return either a value or a -Promise, *and* `$initialValue` may be a Promise or a value for the starting +promises and/or values, and `$reduceFunc` may return either a value or a +promise, *and* `$initialValue` may be a promise or a value for the starting value. ### Promisor @@ -320,11 +359,11 @@ function getAwesomeResultPromise() { $deferred = new React\Promise\Deferred(); - // Pass only the Resolver, to provide the resolution value for the Promise + // Pass only the Resolver, to provide the resolution value for the promise computeAwesomeResultAsynchronously($deferred->resolver()); - // Return only the Promise, so that the caller cannot - // resolve, reject, or otherwise muck with the original Deferred. + // Return only the promise, so that the caller cannot + // resolve, reject, or otherwise muck with the original deferred. return $deferred->promise(); } @@ -342,21 +381,21 @@ getAwesomeResultPromise() ); ``` -### How Promise forwarding works +### How promise forwarding works A few simple examples to show how the mechanics of Promises/A forwarding works. -These examples are contrived, of course, and in real usage, Promise chains will +These examples are contrived, of course, and in real usage, promise chains will typically be spread across several function calls, or even several levels of your application architecture. #### Resolution forwarding -Resolved Promises forward resolution values to the next Promise. -The first Promise, `$deferred->promise()`, will resolve with the value passed +Resolved promises forward resolution values to the next promise. +The first promise, `$deferred->promise()`, will resolve with the value passed to `$deferred->resolve()` below. -Each call to `then()` returns a new Promise that will resolve with the return -value of the previous handler. This creates a Promise "pipeline". +Each call to `then()` returns a new promise that will resolve with the return +value of the previous handler. This creates a promise "pipeline". ``` php $deferred = new React\Promise\Deferred(); @@ -364,7 +403,7 @@ $deferred = new React\Promise\Deferred(); $deferred->promise() ->then(function ($x) { // $x will be the value passed to $deferred->resolve() below - // and returns a *new Promise* for $x + 1 + // and returns a *new promise* for $x + 1 return $x + 1; }) ->then(function ($x) { @@ -391,12 +430,12 @@ $deferred->resolve(1); // Prints "Resolve 4" #### Rejection forwarding -Rejected Promises behave similarly, and also work similarly to try/catch: +Rejected promises behave similarly, and also work similarly to try/catch: When you catch an exception, you must rethrow for it to propagate. -Similarly, when you handle a rejected Promise, to propagate the rejection, -"rethrow" it by either returning a rejected Promise, or actually throwing -(since Promise translates thrown exceptions into rejections) +Similarly, when you handle a rejected promise, to propagate the rejection, +"rethrow" it by either returning a rejected promise, or actually throwing +(since promise translates thrown exceptions into rejections) ``` php $deferred = new React\Promise\Deferred(); From 17c2baabf783c8e0f0266edfeaa8294b53ff2ad2 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 17:21:49 +0200 Subject: [PATCH 13/29] Ensure FullfilledPromise/RejectedPromise are not created with a promise --- src/React/Promise/FulfilledPromise.php | 4 ++++ src/React/Promise/RejectedPromise.php | 4 ++++ tests/React/Promise/FulfilledPromiseTest.php | 8 ++++++++ tests/React/Promise/RejectedPromiseTest.php | 8 ++++++++ 4 files changed, 24 insertions(+) diff --git a/src/React/Promise/FulfilledPromise.php b/src/React/Promise/FulfilledPromise.php index 3e4d69f0..ba98182c 100644 --- a/src/React/Promise/FulfilledPromise.php +++ b/src/React/Promise/FulfilledPromise.php @@ -8,6 +8,10 @@ class FulfilledPromise implements PromiseInterface public function __construct($value = null) { + if ($value instanceof PromiseInterface) { + throw new \InvalidArgumentException('You cannot create React\Promise\FulfilledPromise with a promise. Use React\Promise\resolve($promiseOrValue) instead.'); + } + $this->value = $value; } diff --git a/src/React/Promise/RejectedPromise.php b/src/React/Promise/RejectedPromise.php index b9abd3cc..2da3c4ee 100644 --- a/src/React/Promise/RejectedPromise.php +++ b/src/React/Promise/RejectedPromise.php @@ -8,6 +8,10 @@ class RejectedPromise implements PromiseInterface public function __construct($reason = null) { + if ($reason instanceof PromiseInterface) { + throw new \InvalidArgumentException('You cannot create React\Promise\RejectedPromise with a promise. Use React\Promise\reject($promiseOrValue) instead.'); + } + $this->reason = $reason; } diff --git a/tests/React/Promise/FulfilledPromiseTest.php b/tests/React/Promise/FulfilledPromiseTest.php index 102cffd8..18631e62 100644 --- a/tests/React/Promise/FulfilledPromiseTest.php +++ b/tests/React/Promise/FulfilledPromiseTest.php @@ -33,4 +33,12 @@ public function getPromiseTestAdapter() }, ]; } + + /** @test */ + public function shouldThrowExceptionIfConstructedWithAPromise() + { + $this->setExpectedException('\InvalidArgumentException'); + + return new FulfilledPromise(new FulfilledPromise()); + } } diff --git a/tests/React/Promise/RejectedPromiseTest.php b/tests/React/Promise/RejectedPromiseTest.php index d358963e..97cd6da1 100644 --- a/tests/React/Promise/RejectedPromiseTest.php +++ b/tests/React/Promise/RejectedPromiseTest.php @@ -33,4 +33,12 @@ public function getPromiseTestAdapter() }, ]; } + + /** @test */ + public function shouldThrowExceptionIfConstructedWithAPromise() + { + $this->setExpectedException('\InvalidArgumentException'); + + return new RejectedPromise(new RejectedPromise()); + } } From 1922d4549da3a7e9797bac4207b068d6a3d60413 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 19:27:47 +0200 Subject: [PATCH 14/29] Fix branch-alias --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 53fcf463..ee83717a 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ }, "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "2.0-dev" } } } From 1052c2736b83a071084a1e9cbd67c4ae4dcd6e17 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 19:51:50 +0200 Subject: [PATCH 15/29] Fix typo in filename/classname --- tests/React/Promise/FulfilledPromiseTest.php | 2 +- tests/React/Promise/PromiseTest/FullTestTrait.php | 2 +- ...iseFullfilledTestTrait.php => PromiseFulfilledTestTrait.php} | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename tests/React/Promise/PromiseTest/{PromiseFullfilledTestTrait.php => PromiseFulfilledTestTrait.php} (99%) diff --git a/tests/React/Promise/FulfilledPromiseTest.php b/tests/React/Promise/FulfilledPromiseTest.php index 18631e62..d34065b7 100644 --- a/tests/React/Promise/FulfilledPromiseTest.php +++ b/tests/React/Promise/FulfilledPromiseTest.php @@ -5,7 +5,7 @@ class FulfilledPromiseTest extends TestCase { use PromiseTest\PromiseTestTrait, - PromiseTest\PromiseFullfilledTestTrait; + PromiseTest\PromiseFulfilledTestTrait; public function getPromiseTestAdapter() { diff --git a/tests/React/Promise/PromiseTest/FullTestTrait.php b/tests/React/Promise/PromiseTest/FullTestTrait.php index 98978346..ca3cda78 100644 --- a/tests/React/Promise/PromiseTest/FullTestTrait.php +++ b/tests/React/Promise/PromiseTest/FullTestTrait.php @@ -5,7 +5,7 @@ trait FullTestTrait { use PromiseTestTrait, - PromiseFullfilledTestTrait, + PromiseFulfilledTestTrait, PromiseRejectedTestTrait, ResolveTestTrait, RejectTestTrait, diff --git a/tests/React/Promise/PromiseTest/PromiseFullfilledTestTrait.php b/tests/React/Promise/PromiseTest/PromiseFulfilledTestTrait.php similarity index 99% rename from tests/React/Promise/PromiseTest/PromiseFullfilledTestTrait.php rename to tests/React/Promise/PromiseTest/PromiseFulfilledTestTrait.php index fbefe58e..0a1967af 100644 --- a/tests/React/Promise/PromiseTest/PromiseFullfilledTestTrait.php +++ b/tests/React/Promise/PromiseTest/PromiseFulfilledTestTrait.php @@ -2,7 +2,7 @@ namespace React\Promise\PromiseTest; -trait PromiseFullfilledTestTrait +trait PromiseFulfilledTestTrait { abstract public function getPromiseTestAdapter(); From f26c258bcb84b0b0fea5a980236ebc13bb0c83c7 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 19:52:09 +0200 Subject: [PATCH 16/29] Update changelog --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a1dec3b..8ec0d072 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,20 @@ CHANGELOG ========= +* 2.0.0 (xxxx-xx-xx) + + New major release. The goal was to streamline the API and to make it more + compliant with other promise libraries and especially with the new upcoming + [ES6 promises specification](https://github.com/domenic/promises-unwrapping/). + + * Add standalone Promise class. + * BC break: Bump minimum PHP version to PHP 5.4. + * BC break: Remove ResolverInterface and PromiseInterface from Deferred. + * BC break: Change signature of PromiseInterface. + * BC break: Remove When and Util classes and move static methods to functions. + * BC break: FulfilledPromise and RejectedPromise now throw an exception when + initialized with a promise instead of a value/reason. + * 1.0.4 (2013-04-03) * Trigger PHP errors when invalid callback is passed. From b6d2d6f22d674e2e5caf8b7e9772a315eb5fa5f6 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 19:58:57 +0200 Subject: [PATCH 17/29] Add FulfilledPromise and RejectedPromise to API docs --- README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/README.md b/README.md index acb5fc37..cdb043bd 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,8 @@ Table of Contents * [Deferred::progress()](#deferredprogress) * [Promise](#promise-1) * [Promise::then()](#promisethen) + * [FulfilledPromise](#fulfilledpromise) + * [RejectedPromise](#rejectedpromise) * [LazyPromise](#lazypromise) * [Functions](#functions) * [resolve()](#resolve) @@ -223,6 +225,28 @@ the same call to `then()`: * [resolve()](#resolve) - Creating a resolved promise * [reject()](#reject) - Creating a rejected promise +### FulfilledPromise + +Creates a already fulfilled promise. + +```php +$promise = React\Promise\FulfilledPromise($value); +``` + +Note, that `$value` **cannot** be a promise. It's recommended to use +[resolve()](#resolve) for creating resolved promises. + +### RejectedPromise + +Creates a already rejected promise. + +```php +$promise = React\Promise\RejectedPromise($reason); +``` + +Note, that `$reason` **cannot** be a promise. It's recommended to use +[reject()](#reject) for creating rejected promises. + ### LazyPromise Creates a promise which will be lazily initialized by `$factory` once a consumer From 03da574e0efc59e33bb2ced44be8e4f866abd90f Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 20:21:31 +0200 Subject: [PATCH 18/29] Extend and restructure readme --- README.md | 104 +++++++++++++++++++++++++++++------------------------- 1 file changed, 55 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index cdb043bd..0cb83af3 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,9 @@ Table of Contents * [Deferred::resolve()](#deferredresolve) * [Deferred::reject()](#deferredreject) * [Deferred::progress()](#deferredprogress) + * [PromiseInterface](#promiseinterface) + * [PromiseInterface::then()](#promiseinterfacethen) * [Promise](#promise-1) - * [Promise::then()](#promisethen) * [FulfilledPromise](#fulfilledpromise) * [RejectedPromise](#rejectedpromise) * [LazyPromise](#lazypromise) @@ -32,7 +33,7 @@ Table of Contents * [some()](#some) * [map()](#map) * [reduce()](#reduce) - * [Promisor](#promisor) + * [PromisorInterface](#promisorinterface) 4. [Examples](#examples) * [How to use Deferred](#how-to-use-deferred) * [How promise forwarding works](#how-promise-forwarding-works) @@ -142,52 +143,18 @@ is making progress toward its result. All consumers are notified by having `$onProgress` (which they registered via `$promise->then()`) called with `$update`. -### Promise - -The promise represents an eventual outcome, which is either fulfillment -(success) and an associated value, or rejection (failure) and an associated -reason. - -``` php -$promise = new React\Promise\Promise(function (callable $resolve, callable $reject, callable $progress) { - // Do some work, possibly asynchronously, and then - // resolve or reject. You can notify of progress events - // along the way if you want/need. - - $resolve($awesomeResult); - // or $resolve($anotherPromise); - // or $reject($nastyError); - // or $progress($progressNotification); -}); - -$promise->then( - function ($value) { - // Promise resolved, do something with $value - }, - function ($reason) { - // Promise rejected, do something with $reason - }, - function ($update) { - // Progress notification triggered, do something with $update - } -); -``` +### PromiseInterface -The promise constructor receives a resolver function which will be called -with 3 arguments: +The promise interface provides the common interface for all promise +implementations. - * `$resolve($value)` - Primary function that seals the fate of the - returned promise. Accepts either a non-promise value, or another promise. - When called with a non-promise value, fulfills promise with that value. - When called with another promise, e.g. `$resolve($otherPromise)`, promise's - fate will be equivalent to that of `$otherPromise`. - * `$reject($reason)` - Function that rejects the promise. - * `$progress($update)` - Function that issues progress events for the promise. +A promise represents an eventual outcome, which is either fulfillment (success) +and an associated value, or rejection (failure) and an associated reason. -If the resolver throws an exception, the promise will be rejected with that -thrown exception as the rejection reason. +Once in the fulfilled or rejected state, a promise becomes immutable. +Neither its state nor its result (or error) can be modified. -#### Promise::then() +#### PromiseInterface::then() ``` php $newPromise = $promise->then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null); @@ -204,13 +171,10 @@ with this promise (all parameters are optional): triggers progress notifications and passed a single argument (whatever it wants) to indicate progress. -Returns a new promise that will fulfill with the return value of either +It returns a new promise that will fulfill with the return value of either `$onFulfilled` or `$onRejected`, whichever is called, or will reject with the thrown exception if either throws. -Once in the fulfilled or rejected state, a promise becomes immutable. -Neither its state nor its result (or error) can be modified. - A promise makes the following guarantees about handlers registered in the same call to `then()`: @@ -220,11 +184,53 @@ the same call to `then()`: than once. 3. `$onProgress` may be called multiple times. +#### Implementations + +* [Promise](#promise-1) +* [FulfilledPromise](#fulfilledpromise) +* [RejectedPromise](#rejectedpromise) +* [LazyPromise](#lazypromise) + #### See also * [resolve()](#resolve) - Creating a resolved promise * [reject()](#reject) - Creating a rejected promise +### Promise + +Creates a promise whose state is controlled by the functions passed to +`$resolver`. + +``` php +$resolver = function (callable $resolve, callable $reject, callable $progress) { + // Do some work, possibly asynchronously, and then + // resolve or reject. You can notify of progress events + // along the way if you want/need. + + $resolve($awesomeResult); + // or $resolve($anotherPromise); + // or $reject($nastyError); + // or $progress($progressNotification); +}; + +$promise = new React\Promise\Promise($resolver); +``` + +The promise constructor receives a resolver function which will be called +with 3 arguments: + + * `$resolve($value)` - Primary function that seals the fate of the + returned promise. Accepts either a non-promise value, or another promise. + When called with a non-promise value, fulfills promise with that value. + When called with another promise, e.g. `$resolve($otherPromise)`, promise's + fate will be equivalent to that of `$otherPromise`. + * `$reject($reason)` - Function that rejects the promise. + * `$progress($update)` - Function that issues progress events for the promise. + +If the resolver throws an exception, the promise will be rejected with that +thrown exception as the rejection reason. + + ### FulfilledPromise Creates a already fulfilled promise. @@ -367,7 +373,7 @@ promises and/or values, and `$reduceFunc` may return either a value or a promise, *and* `$initialValue` may be a promise or a value for the starting value. -### Promisor +### PromisorInterface The `React\Promise\PromisorInterface` provides a common interface for objects that provide a promise. `React\Promise\Deferred` implements it, but since it From bbc97688242c479300620a37616a485c45c5227e Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Tue, 22 Oct 2013 20:32:43 +0200 Subject: [PATCH 19/29] Revert phpunit php notice setting --- phpunit.xml.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 0f98aea7..0eebb58b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -4,7 +4,7 @@ backupStaticAttributes="false" colors="true" convertErrorsToExceptions="true" - convertNoticesToExceptions="false" + convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" From 4807d4373a99bd4ca2055495291b41e0de267342 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Thu, 24 Oct 2013 19:45:41 +0200 Subject: [PATCH 20/29] Remove unneeded toClosue() --- tests/React/Promise/DeferredTest.php | 8 ++++---- tests/React/Promise/LazyPromiseTest.php | 6 +++--- tests/React/Promise/TestCase.php | 11 ----------- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/tests/React/Promise/DeferredTest.php b/tests/React/Promise/DeferredTest.php index da32d83b..e66aceb9 100644 --- a/tests/React/Promise/DeferredTest.php +++ b/tests/React/Promise/DeferredTest.php @@ -11,10 +11,10 @@ public function getPromiseTestAdapter() $d = new Deferred(); return [ - 'promise' => $this->toClosure([$d, 'promise']), - 'resolve' => $this->toClosure([$d, 'resolve']), - 'reject' => $this->toClosure([$d, 'reject']), - 'progress' => $this->toClosure([$d, 'progress']), + 'promise' => [$d, 'promise'], + 'resolve' => [$d, 'resolve'], + 'reject' => [$d, 'reject'], + 'progress' => [$d, 'progress'], ]; } } diff --git a/tests/React/Promise/LazyPromiseTest.php b/tests/React/Promise/LazyPromiseTest.php index a8d43aa8..f10609cc 100644 --- a/tests/React/Promise/LazyPromiseTest.php +++ b/tests/React/Promise/LazyPromiseTest.php @@ -18,9 +18,9 @@ public function getPromiseTestAdapter() 'promise' => function () use ($factory) { return new LazyPromise($factory); }, - 'resolve' => $this->toClosure([$d, 'resolve']), - 'reject' => $this->toClosure([$d, 'reject']), - 'progress' => $this->toClosure([$d, 'progress']), + 'resolve' => [$d, 'resolve'], + 'reject' => [$d, 'reject'], + 'progress' => [$d, 'progress'], ]; } diff --git a/tests/React/Promise/TestCase.php b/tests/React/Promise/TestCase.php index 08b9f4d1..60ea46d7 100644 --- a/tests/React/Promise/TestCase.php +++ b/tests/React/Promise/TestCase.php @@ -4,17 +4,6 @@ class TestCase extends \PHPUnit_Framework_TestCase { - public function toClosure(callable $callable) - { - if ($callable instanceof \Closure) { - return $callable; - } - - return function () use ($callable) { - return call_user_func_array($callable, func_get_args()); - }; - } - public function expectCallableExactly($amount) { $mock = $this->createCallableMock(); From e0257036d1bc908c9a67fdb6193eb7c09cc0f9ed Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Thu, 28 Nov 2013 10:50:44 +0100 Subject: [PATCH 21/29] Simplify code --- src/React/Promise/functions.php | 111 +++++++++++++++----------------- 1 file changed, 51 insertions(+), 60 deletions(-) diff --git a/src/React/Promise/functions.php b/src/React/Promise/functions.php index 23ebb131..0c394543 100644 --- a/src/React/Promise/functions.php +++ b/src/React/Promise/functions.php @@ -41,63 +41,58 @@ function some($promisesOrValues, $howMany) { return resolve($promisesOrValues) ->then(function ($array) use ($howMany) { - if (!is_array($array)) { - $array = []; + if (!is_array($array) || !$array) { + return resolve([]); } $len = count($array); $toResolve = max(0, min($howMany, $len)); + $toReject = ($len - $toResolve) + 1; $values = []; + $reasons = []; $deferred = new Deferred(); - if (!$toResolve) { - $deferred->resolve($values); - } else { - $toReject = ($len - $toResolve) + 1; - $reasons = []; + $progress = [$deferred, 'progress']; - $progress = [$deferred, 'progress']; + $fulfillOne = function ($val, $i) use (&$values, &$toResolve, $deferred) { + $values[$i] = $val; - $fulfillOne = function ($val, $i) use (&$values, &$toResolve, $deferred) { - $values[$i] = $val; + if (0 === --$toResolve) { + $deferred->resolve($values); - if (0 === --$toResolve) { - $deferred->resolve($values); + return true; + } + }; - return true; - } - }; + $rejectOne = function ($reason, $i) use (&$reasons, &$toReject, $deferred) { + $reasons[$i] = $reason; - $rejectOne = function ($reason, $i) use (&$reasons, &$toReject, $deferred) { - $reasons[$i] = $reason; + if (0 === --$toReject) { + $deferred->reject($reasons); - if (0 === --$toReject) { - $deferred->reject($reasons); + return true; + } + }; - return true; + foreach ($array as $i => $promiseOrValue) { + $fulfiller = function ($val) use ($i, &$fulfillOne, &$rejectOne) { + $reset = $fulfillOne($val, $i); + + if (true === $reset) { + $fulfillOne = $rejectOne = function () {}; } }; - foreach ($array as $i => $promiseOrValue) { - $fulfiller = function ($val) use ($i, &$fulfillOne, &$rejectOne) { - $reset = $fulfillOne($val, $i); - - if (true === $reset) { - $fulfillOne = $rejectOne = function () {}; - } - }; + $rejecter = function ($val) use ($i, &$fulfillOne, &$rejectOne) { + $reset = $rejectOne($val, $i); - $rejecter = function ($val) use ($i, &$fulfillOne, &$rejectOne) { - $reset = $rejectOne($val, $i); - - if (true === $reset) { - $fulfillOne = $rejectOne = function () {}; - } - }; + if (true === $reset) { + $fulfillOne = $rejectOne = function () {}; + } + }; - resolve($promiseOrValue) - ->then($fulfiller, $rejecter, $progress); - } + resolve($promiseOrValue) + ->then($fulfiller, $rejecter, $progress); } return $deferred->promise(); @@ -108,35 +103,31 @@ function map($promisesOrValues, callable $mapFunc) { return resolve($promisesOrValues) ->then(function ($array) use ($mapFunc) { - if (!is_array($array)) { - $array = []; + if (!is_array($array) || !$array) { + return resolve([]); } $toResolve = count($array); $values = []; $deferred = new Deferred(); - if (!$toResolve) { - $deferred->resolve($values); - } else { - $resolve = function ($item, $i) use ($mapFunc, &$values, &$toResolve, $deferred) { - resolve($item) - ->then($mapFunc) - ->then( - function ($mapped) use (&$values, $i, &$toResolve, $deferred) { - $values[$i] = $mapped; - - if (0 === --$toResolve) { - $deferred->resolve($values); - } - }, - [$deferred, 'reject'] - ); - }; + $resolve = function ($item, $i) use ($mapFunc, &$values, &$toResolve, $deferred) { + resolve($item) + ->then($mapFunc) + ->then( + function ($mapped) use (&$values, $i, &$toResolve, $deferred) { + $values[$i] = $mapped; + + if (0 === --$toResolve) { + $deferred->resolve($values); + } + }, + [$deferred, 'reject'] + ); + }; - foreach ($array as $i => $item) { - $resolve($item, $i); - } + foreach ($array as $i => $item) { + $resolve($item, $i); } return $deferred->promise(); From 1193dae3d5dc2986275356fb301cf70a2cdcba40 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Thu, 28 Nov 2013 10:54:36 +0100 Subject: [PATCH 22/29] Remove unneeded call_user_func()'s --- src/React/Promise/FulfilledPromise.php | 2 +- src/React/Promise/Promise.php | 9 ++++----- src/React/Promise/RejectedPromise.php | 2 +- src/React/Promise/functions.php | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/React/Promise/FulfilledPromise.php b/src/React/Promise/FulfilledPromise.php index ba98182c..44e1a798 100644 --- a/src/React/Promise/FulfilledPromise.php +++ b/src/React/Promise/FulfilledPromise.php @@ -21,7 +21,7 @@ public function then(callable $onFulfilled = null, callable $onRejected = null, $value = $this->value; if (null !== $onFulfilled) { - $value = call_user_func($onFulfilled, $value); + $value = $onFulfilled($value); } return resolve($value); diff --git a/src/React/Promise/Promise.php b/src/React/Promise/Promise.php index cedd6307..d99c8d7c 100644 --- a/src/React/Promise/Promise.php +++ b/src/React/Promise/Promise.php @@ -12,8 +12,7 @@ class Promise implements PromiseInterface public function __construct(callable $resolver) { try { - call_user_func( - $resolver, + $resolver( function ($value = null) { return $this->resolve($value); }, @@ -44,7 +43,7 @@ private function resolver(callable $onFulfilled = null, callable $onRejected = n if ($onProgress) { $progressHandler = function ($update) use ($progress, $onProgress) { try { - $progress(call_user_func($onProgress, $update)); + $progress($onProgress($update)); } catch (\Exception $e) { $progress($e); } @@ -80,7 +79,7 @@ private function progress($update = null) } foreach ($this->progressHandlers as $handler) { - call_user_func($handler, $update); + $handler($update); } } @@ -91,7 +90,7 @@ private function settle(PromiseInterface $result) } foreach ($this->handlers as $handler) { - call_user_func($handler, $result); + $handler($result); } $this->progressHandlers = $this->handlers = []; diff --git a/src/React/Promise/RejectedPromise.php b/src/React/Promise/RejectedPromise.php index 2da3c4ee..994d7d8c 100644 --- a/src/React/Promise/RejectedPromise.php +++ b/src/React/Promise/RejectedPromise.php @@ -22,7 +22,7 @@ public function then(callable $onFulfilled = null, callable $onRejected = null, return new RejectedPromise($this->reason); } - return resolve(call_user_func($onRejected, $this->reason)); + return resolve($onRejected($this->reason)); } catch (\Exception $exception) { return new RejectedPromise($exception); } diff --git a/src/React/Promise/functions.php b/src/React/Promise/functions.php index 0c394543..1850ae24 100644 --- a/src/React/Promise/functions.php +++ b/src/React/Promise/functions.php @@ -152,7 +152,7 @@ function reduce($promisesOrValues, callable $reduceFunc , $initialValue = null) ->then(function ($c) use ($reduceFunc, $total, &$i, $val) { return resolve($val) ->then(function ($value) use ($reduceFunc, $total, &$i, $c) { - return call_user_func($reduceFunc, $c, $value, $i++, $total); + return $reduceFunc($c, $value, $i++, $total); }); }); }; From c09facccd40fc964c9e1504d869cc4aa2f08d161 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Thu, 28 Nov 2013 11:28:57 +0100 Subject: [PATCH 23/29] Simplify some() --- src/React/Promise/functions.php | 77 ++++++++++++--------------------- 1 file changed, 27 insertions(+), 50 deletions(-) diff --git a/src/React/Promise/functions.php b/src/React/Promise/functions.php index 1850ae24..625c09e2 100644 --- a/src/React/Promise/functions.php +++ b/src/React/Promise/functions.php @@ -45,57 +45,34 @@ function some($promisesOrValues, $howMany) return resolve([]); } - $len = count($array); - $toResolve = max(0, min($howMany, $len)); - $toReject = ($len - $toResolve) + 1; - $values = []; - $reasons = []; - $deferred = new Deferred(); - - $progress = [$deferred, 'progress']; - - $fulfillOne = function ($val, $i) use (&$values, &$toResolve, $deferred) { - $values[$i] = $val; - - if (0 === --$toResolve) { - $deferred->resolve($values); - - return true; - } - }; - - $rejectOne = function ($reason, $i) use (&$reasons, &$toReject, $deferred) { - $reasons[$i] = $reason; - - if (0 === --$toReject) { - $deferred->reject($reasons); - - return true; + return new Promise(function ($resolve, $reject, $progress) use ($array, $howMany) { + $len = count($array); + $toResolve = max(0, min($howMany, $len)); + $toReject = ($len - $toResolve) + 1; + $values = []; + $reasons = []; + + foreach ($array as $i => $promiseOrValue) { + $fulfiller = function ($val) use ($i, &$values, &$toResolve, $resolve) { + $values[$i] = $val; + + if (0 === --$toResolve) { + $resolve($values); + } + }; + + $rejecter = function ($reason) use ($i, &$reasons, &$toReject, $reject) { + $reasons[$i] = $reason; + + if (0 === --$toReject) { + $reject($reasons); + } + }; + + resolve($promiseOrValue) + ->then($fulfiller, $rejecter, $progress); } - }; - - foreach ($array as $i => $promiseOrValue) { - $fulfiller = function ($val) use ($i, &$fulfillOne, &$rejectOne) { - $reset = $fulfillOne($val, $i); - - if (true === $reset) { - $fulfillOne = $rejectOne = function () {}; - } - }; - - $rejecter = function ($val) use ($i, &$fulfillOne, &$rejectOne) { - $reset = $rejectOne($val, $i); - - if (true === $reset) { - $fulfillOne = $rejectOne = function () {}; - } - }; - - resolve($promiseOrValue) - ->then($fulfiller, $rejecter, $progress); - } - - return $deferred->promise(); + }); }); } From 219e7dbcaaacfd2a1b2ad196710c8818377f92fe Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Thu, 28 Nov 2013 11:39:30 +0100 Subject: [PATCH 24/29] Refactor map() to use Promise instead Deferred --- src/React/Promise/functions.php | 42 +++++++++++++++------------------ 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/React/Promise/functions.php b/src/React/Promise/functions.php index 625c09e2..f2451355 100644 --- a/src/React/Promise/functions.php +++ b/src/React/Promise/functions.php @@ -84,30 +84,26 @@ function map($promisesOrValues, callable $mapFunc) return resolve([]); } - $toResolve = count($array); - $values = []; - $deferred = new Deferred(); - - $resolve = function ($item, $i) use ($mapFunc, &$values, &$toResolve, $deferred) { - resolve($item) - ->then($mapFunc) - ->then( - function ($mapped) use (&$values, $i, &$toResolve, $deferred) { - $values[$i] = $mapped; - - if (0 === --$toResolve) { - $deferred->resolve($values); - } - }, - [$deferred, 'reject'] - ); - }; - - foreach ($array as $i => $item) { - $resolve($item, $i); - } + return new Promise(function ($resolve, $reject, $progress) use ($array, $mapFunc) { + $toResolve = count($array); + $values = []; - return $deferred->promise(); + foreach ($array as $i => $promiseOrValue) { + resolve($promiseOrValue) + ->then($mapFunc) + ->then( + function ($mapped) use ($i, &$values, &$toResolve, $resolve) { + $values[$i] = $mapped; + + if (0 === --$toResolve) { + $resolve($values); + } + }, + $reject, + $progress + ); + } + }); }); } From 0f21360cb2ccc690928caff98a579bd95fee24d0 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Thu, 28 Nov 2013 11:58:49 +0100 Subject: [PATCH 25/29] Ensure to resolve early when some() is called with < 1 --- src/React/Promise/functions.php | 2 +- tests/React/Promise/FunctionSomeTest.php | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/React/Promise/functions.php b/src/React/Promise/functions.php index f2451355..b48873ec 100644 --- a/src/React/Promise/functions.php +++ b/src/React/Promise/functions.php @@ -41,7 +41,7 @@ function some($promisesOrValues, $howMany) { return resolve($promisesOrValues) ->then(function ($array) use ($howMany) { - if (!is_array($array) || !$array) { + if (!is_array($array) || !$array || $howMany < 1) { return resolve([]); } diff --git a/tests/React/Promise/FunctionSomeTest.php b/tests/React/Promise/FunctionSomeTest.php index b1ed95d6..09e53504 100644 --- a/tests/React/Promise/FunctionSomeTest.php +++ b/tests/React/Promise/FunctionSomeTest.php @@ -94,6 +94,21 @@ public function shouldAcceptAPromiseForAnArray() )->then($mock); } + /** @test */ + public function shouldResolveWithEmptyArrayIfHowManyIsLessThanOne() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo([])); + + some( + [1], + 0 + )->then($mock); + } + /** @test */ public function shouldResolveToEmptyArrayWhenInputPromiseDoesNotResolveToArray() { From bdda7eb7853e3a7c09d20acfb83508f66750cb0f Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Thu, 28 Nov 2013 12:09:06 +0100 Subject: [PATCH 26/29] Ensure value/reason arrays get not stacked up if already resolved --- src/React/Promise/functions.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/React/Promise/functions.php b/src/React/Promise/functions.php index b48873ec..69e5691e 100644 --- a/src/React/Promise/functions.php +++ b/src/React/Promise/functions.php @@ -53,7 +53,11 @@ function some($promisesOrValues, $howMany) $reasons = []; foreach ($array as $i => $promiseOrValue) { - $fulfiller = function ($val) use ($i, &$values, &$toResolve, $resolve) { + $fulfiller = function ($val) use ($i, &$values, &$toResolve, $toReject, $resolve) { + if ($toResolve < 1 || $toReject < 1) { + return; + } + $values[$i] = $val; if (0 === --$toResolve) { @@ -61,7 +65,11 @@ function some($promisesOrValues, $howMany) } }; - $rejecter = function ($reason) use ($i, &$reasons, &$toReject, $reject) { + $rejecter = function ($reason) use ($i, &$reasons, &$toReject, $toResolve, $reject) { + if ($toResolve < 1 || $toReject < 1) { + return; + } + $reasons[$i] = $reason; if (0 === --$toReject) { @@ -93,6 +101,10 @@ function map($promisesOrValues, callable $mapFunc) ->then($mapFunc) ->then( function ($mapped) use ($i, &$values, &$toResolve, $resolve) { + if ($toResolve < 1) { + return; + } + $values[$i] = $mapped; if (0 === --$toResolve) { From 894deb22172b061a069fb20678eb996da51a1efa Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Thu, 28 Nov 2013 14:03:07 +0100 Subject: [PATCH 27/29] Remove unneeded max() check --- src/React/Promise/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/React/Promise/functions.php b/src/React/Promise/functions.php index 69e5691e..66ef2ff6 100644 --- a/src/React/Promise/functions.php +++ b/src/React/Promise/functions.php @@ -47,7 +47,7 @@ function some($promisesOrValues, $howMany) return new Promise(function ($resolve, $reject, $progress) use ($array, $howMany) { $len = count($array); - $toResolve = max(0, min($howMany, $len)); + $toResolve = min($howMany, $len); $toReject = ($len - $toResolve) + 1; $values = []; $reasons = []; From a13300b77fec58e989f3ddc77e3177a1063c9d85 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Thu, 28 Nov 2013 14:17:19 +0100 Subject: [PATCH 28/29] Add new React\Promise\race() function --- CHANGELOG.md | 1 + README.md | 10 ++ src/React/Promise/functions.php | 17 ++++ tests/React/Promise/FunctionRaceTest.php | 122 +++++++++++++++++++++++ 4 files changed, 150 insertions(+) create mode 100644 tests/React/Promise/FunctionRaceTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ec0d072..d74e6cee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ CHANGELOG [ES6 promises specification](https://github.com/domenic/promises-unwrapping/). * Add standalone Promise class. + * Add new React\Promise\race() function. * BC break: Bump minimum PHP version to PHP 5.4. * BC break: Remove ResolverInterface and PromiseInterface from Deferred. * BC break: Change signature of PromiseInterface. diff --git a/README.md b/README.md index 0cb83af3..820e6a83 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Table of Contents * [resolve()](#resolve) * [reject()](#reject) * [all()](#all) + * [race()](#race) * [any()](#any) * [some()](#some) * [map()](#map) @@ -321,6 +322,15 @@ Returns a promise that will resolve only once all the items in will be an array containing the resolution values of each of the items in `$promisesOrValues`. +#### race() + +``` php +$promise = React\Promise\race(array|React\Promise\PromiseInterface $promisesOrValues); +``` + +Initiates a competitive race that allows one winner. Returns a promise which is +resolved in the same way the first settled promise resolves. + #### any() ``` php diff --git a/src/React/Promise/functions.php b/src/React/Promise/functions.php index 66ef2ff6..e4b8aa57 100644 --- a/src/React/Promise/functions.php +++ b/src/React/Promise/functions.php @@ -29,6 +29,23 @@ function all($promisesOrValues) }); } +function race($promisesOrValues) +{ + return resolve($promisesOrValues) + ->then(function ($array) { + if (!is_array($array) || !$array) { + return resolve(); + } + + return new Promise(function ($resolve, $reject, $progress) use ($array) { + foreach ($array as $promiseOrValue) { + resolve($promiseOrValue) + ->then($resolve, $reject, $progress); + } + }); + }); +} + function any($promisesOrValues) { return some($promisesOrValues, 1) diff --git a/tests/React/Promise/FunctionRaceTest.php b/tests/React/Promise/FunctionRaceTest.php new file mode 100644 index 00000000..553220c5 --- /dev/null +++ b/tests/React/Promise/FunctionRaceTest.php @@ -0,0 +1,122 @@ +createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(null)); + + race( + [] + )->then($mock); + } + + /** @test */ + public function shouldResolveValuesArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + race( + [1, 2, 3] + )->then($mock); + } + + /** @test */ + public function shouldResolvePromisesArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $d1 = new Deferred(); + $d2 = new Deferred(); + $d3 = new Deferred(); + + race( + [$d1->promise(), $d2->promise(), $d3->promise()] + )->then($mock); + + $d2->resolve(2); + + $d1->resolve(1); + $d3->resolve(3); + } + + /** @test */ + public function shouldResolveSparseArrayInput() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(null)); + + race( + [null, 1, null, 2, 3] + )->then($mock); + } + + /** @test */ + public function shouldRejectIfFirstSettledPromiseRejects() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(2)); + + $d1 = new Deferred(); + $d2 = new Deferred(); + $d3 = new Deferred(); + + race( + [$d1->promise(), $d2->promise(), $d3->promise()] + )->then($this->expectCallableNever(), $mock); + + $d2->reject(2); + + $d1->resolve(1); + $d3->resolve(3); + } + + /** @test */ + public function shouldAcceptAPromiseForAnArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(1)); + + race( + resolve([1, 2, 3]) + )->then($mock); + } + + /** @test */ + public function shouldResolveToNullWhenInputPromiseDoesNotResolveToArray() + { + $mock = $this->createCallableMock(); + $mock + ->expects($this->once()) + ->method('__invoke') + ->with($this->identicalTo(null)); + + race( + resolve(1) + )->then($mock); + } +} From e78de7dd0d135c9288c60a3271f00dd54f598fd7 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Thu, 28 Nov 2013 20:16:21 +0100 Subject: [PATCH 29/29] Remove return value from Deferred::resolve and Deferred::reject --- CHANGELOG.md | 2 + src/React/Promise/Deferred.php | 6 +- src/React/Promise/Promise.php | 22 ++--- .../Promise/PromiseTest/RejectTestTrait.php | 49 ----------- .../Promise/PromiseTest/ResolveTestTrait.php | 81 ------------------- 5 files changed, 18 insertions(+), 142 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d74e6cee..35c0aa2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ CHANGELOG * BC break: Remove When and Util classes and move static methods to functions. * BC break: FulfilledPromise and RejectedPromise now throw an exception when initialized with a promise instead of a value/reason. + * BC break: React\Promise\Deferred::resolve() and React\Promise\Deferred::reject() + no longer return a promise. * 1.0.4 (2013-04-03) diff --git a/src/React/Promise/Deferred.php b/src/React/Promise/Deferred.php index f00dd895..1a7e4895 100644 --- a/src/React/Promise/Deferred.php +++ b/src/React/Promise/Deferred.php @@ -26,20 +26,20 @@ public function resolve($value = null) { $this->promise(); - return call_user_func($this->resolveCallback, $value); + call_user_func($this->resolveCallback, $value); } public function reject($reason = null) { $this->promise(); - return call_user_func($this->rejectCallback, $reason); + call_user_func($this->rejectCallback, $reason); } public function progress($update = null) { $this->promise(); - return call_user_func($this->progressCallback, $update); + call_user_func($this->progressCallback, $update); } } diff --git a/src/React/Promise/Promise.php b/src/React/Promise/Promise.php index d99c8d7c..061d960c 100644 --- a/src/React/Promise/Promise.php +++ b/src/React/Promise/Promise.php @@ -14,10 +14,10 @@ public function __construct(callable $resolver) try { $resolver( function ($value = null) { - return $this->resolve($value); + $this->resolve($value); }, function ($reason = null) { - return $this->reject($reason); + $this->reject($reason); }, function ($update = null) { $this->progress($update); @@ -64,12 +64,20 @@ private function resolver(callable $onFulfilled = null, callable $onRejected = n private function resolve($value = null) { - return $this->settle(resolve($value)); + if (null !== $this->result) { + return; + } + + $this->settle(resolve($value)); } private function reject($reason = null) { - return $this->settle(reject($reason)); + if (null !== $this->result) { + return; + } + + $this->settle(reject($reason)); } private function progress($update = null) @@ -85,16 +93,12 @@ private function progress($update = null) private function settle(PromiseInterface $result) { - if (null !== $this->result) { - return $result; - } - foreach ($this->handlers as $handler) { $handler($result); } $this->progressHandlers = $this->handlers = []; - return $this->result = $result; + $this->result = $result; } } diff --git a/tests/React/Promise/PromiseTest/RejectTestTrait.php b/tests/React/Promise/PromiseTest/RejectTestTrait.php index d8a9dcd4..09119ead 100644 --- a/tests/React/Promise/PromiseTest/RejectTestTrait.php +++ b/tests/React/Promise/PromiseTest/RejectTestTrait.php @@ -59,55 +59,6 @@ public function rejectShouldRejectWithRejectedPromise() $reject(Promise\reject(1)); } - /** @test */ - public function rejectShouldReturnAPromiseForTheRejectionValue() - { - extract($this->getPromiseTestAdapter()); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $reject(1) - ->then($this->expectCallableNever(), $mock); - } - - /** @test */ - public function rejectShouldReturnAPromiseForPassedInRejectionValueWhenAlreadyResolved() - { - extract($this->getPromiseTestAdapter()); - - $resolve(1); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $reject(2) - ->then($this->expectCallableNever(), $mock); - } - - /** @test */ - public function rejectShouldReturnAPromiseForPassedInRejectionValueWhenAlreadyRejected() - { - extract($this->getPromiseTestAdapter()); - - $reject(1); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $reject(2) - ->then($this->expectCallableNever(), $mock); - } - /** @test */ public function rejectShouldInvokeNewlyAddedErrbackWhenAlreadyRejected() { diff --git a/tests/React/Promise/PromiseTest/ResolveTestTrait.php b/tests/React/Promise/PromiseTest/ResolveTestTrait.php index b1182629..d6bdd9d0 100644 --- a/tests/React/Promise/PromiseTest/ResolveTestTrait.php +++ b/tests/React/Promise/PromiseTest/ResolveTestTrait.php @@ -59,87 +59,6 @@ public function resolveShouldRejectWhenResolvedWithRejectedPromise() $resolve(Promise\reject(1)); } - /** @test */ - public function resolveShouldReturnAPromiseForTheResolutionValue() - { - extract($this->getPromiseTestAdapter()); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $resolve(1) - ->then($mock); - } - - /** @test */ - public function resolveShouldReturnAPromiseForAPromisedResolutionValue() - { - extract($this->getPromiseTestAdapter()); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - $resolve(Promise\resolve(1)) - ->then($mock); - } - - /** @test */ - public function resolveShouldReturnAPromiseForAPromisedRejectionValue() - { - extract($this->getPromiseTestAdapter()); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(1)); - - // Both the returned promise, and the deferred's own promise should - // be rejected with the same value - $resolve(Promise\reject(1)) - ->then($this->expectCallableNever(), $mock); - } - - /** @test */ - public function resolveShouldReturnAPromiseForPassedInResolutionValueWhenAlreadyResolved() - { - extract($this->getPromiseTestAdapter()); - - $resolve(1); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $resolve(2) - ->then($mock); - } - - /** @test */ - public function resolveShouldReturnAPromiseForPassedInResolutionValueWhenAlreadyRejected() - { - extract($this->getPromiseTestAdapter()); - - $reject(1); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo(2)); - - $resolve(2) - ->then($mock); - } - /** @test */ public function resolveShouldInvokeNewlyAddedCallbackWhenAlreadyResolved() {