Codebase list php-react-promise / 7e66e77
Imported Upstream version 2.4.0 David Prévot 8 years ago
15 changed file(s) with 296 addition(s) and 99 deletion(s). Raw diff Collapse all Expand all
44 - 5.5
55 - 5.6
66 - 7.0
7 - nightly
78 - hhvm
89
910 before_install:
1011 - composer self-update
1112
1213 install:
13 - composer install --prefer-source
14 - composer install
1415
1516 script:
1617 - phpunit -v --coverage-text
00 CHANGELOG
11 =========
22
3 * 2.4.0 (2016-03-31)
4
5 * Support foreign thenables in `resolve()`.
6 Any object that provides a `then()` method is now assimilated to a trusted
7 promise that follows the state of this thenable (#52).
8 * Fix `some()` and `any()` for input arrays containing not enough items
9 (#34).
10
311 * 2.3.0 (2016-03-24)
412
5 * Allow cancellation of promises returned by functions working on promise
6 collections (#36).
7 * Handle \Throwable in the same way as \Exception (#51 by @joshdifabio).
13 * Allow cancellation of promises returned by functions working on promise
14 collections (#36).
15 * Handle `\Throwable` in the same way as `\Exception` (#51 by @joshdifabio).
816
917 * 2.2.2 (2016-02-26)
1018
11 * Fix cancellation handlers called multiple times (#47 by @clue).
12
13 * 2.2.2 (2016-02-26)
14
15 * Fix cancellation handlers called multiple times (#47 by @clue).
19 * Fix cancellation handlers called multiple times (#47 by @clue).
1620
1721 * 2.2.1 (2015-07-03)
1822
19 * Fix stack error when resolving a promise in its own fulfillment or rejection
20 handlers.
23 * Fix stack error when resolving a promise in its own fulfillment or
24 rejection handlers.
2125
2226 * 2.2.0 (2014-12-30)
2327
24 * Introduce new ExtendedPromiseInterface implemented by all promises
25 * Add new .done() method (part of the ExtendedPromiseInterface)
26 * Add new .otherwise() method (part of the ExtendedPromiseInterface)
27 * Add new .always() method (part of the ExtendedPromiseInterface)
28 * Add new .progress() method (part of the ExtendedPromiseInterface)
29 * Rename Deferred::progress to Deferred::notify to avoid confusion with
30 ExtendedPromiseInterface::progress (a Deferred::progress alias is still
31 available for backward compatibility)
32 * resolve() now always returns a ExtendedPromiseInterface
28 * Introduce new `ExtendedPromiseInterface` implemented by all promises.
29 * Add new `done()` method (part of the `ExtendedPromiseInterface`).
30 * Add new `otherwise()` method (part of the `ExtendedPromiseInterface`).
31 * Add new `always()` method (part of the `ExtendedPromiseInterface`).
32 * Add new `progress()` method (part of the `ExtendedPromiseInterface`).
33 * Rename `Deferred::progress` to `Deferred::notify` to avoid confusion with
34 `ExtendedPromiseInterface::progress` (a `Deferred::progress` alias is
35 still available for backward compatibility)
36 * `resolve()` now always returns a `ExtendedPromiseInterface`.
3337
3438 * 2.1.0 (2014-10-15)
3539
36 * Introduce new CancellablePromiseInterface implemented by all promises
37 * Add new .cancel() method (part of the CancellablePromiseInterface)
40 * Introduce new `CancellablePromiseInterface` implemented by all promises.
41 * Add new `cancel()` method (part of the `CancellablePromiseInterface`).
3842
3943 * 2.0.0 (2013-12-10)
4044
41 New major release. The goal was to streamline the API and to make it more
42 compliant with other promise libraries and especially with the new upcoming
43 [ES6 promises specification](https://github.com/domenic/promises-unwrapping/).
45 New major release. The goal is to streamline the API and to make it more
46 compliant with other promise libraries and especially with the new upcoming
47 [ES6 promises specification](https://github.com/domenic/promises-unwrapping/).
4448
45 * Add standalone Promise class.
46 * Add new React\Promise\race() function.
47 * BC break: Bump minimum PHP version to PHP 5.4.
48 * BC break: Remove ResolverInterface and PromiseInterface from Deferred.
49 * BC break: Change signature of PromiseInterface.
50 * BC break: Remove When and Util classes and move static methods to functions.
51 * BC break: FulfilledPromise and RejectedPromise now throw an exception when
52 initialized with a promise instead of a value/reason.
53 * BC break: React\Promise\Deferred::resolve() and React\Promise\Deferred::reject()
54 no longer return a promise.
49 * Add standalone Promise class.
50 * Add new `race()` function.
51 * BC break: Bump minimum PHP version to PHP 5.4.
52 * BC break: Remove `ResolverInterface` and `PromiseInterface` from
53 `Deferred`.
54 * BC break: Change signature of `PromiseInterface`.
55 * BC break: Remove `When` and `Util` classes and move static methods to
56 functions.
57 * BC break: `FulfilledPromise` and `RejectedPromise` now throw an exception
58 when initialized with a promise instead of a value/reason.
59 * BC break: `Deferred::resolve()` and `Deferred::reject()` no longer return
60 a promise.
5561
5662 * 1.0.4 (2013-04-03)
5763
58 * Trigger PHP errors when invalid callback is passed.
59 * Fully resolve rejection value before calling rejection handler.
60 * Add When::lazy() to create lazy promises which will be initialized once a
61 consumer calls the then() method.
64 * Trigger PHP errors when invalid callback is passed.
65 * Fully resolve rejection value before calling rejection handler.
66 * Add `When::lazy()` to create lazy promises which will be initialized once
67 a consumer calls the `then()` method.
6268
6369 * 1.0.3 (2012-11-17)
6470
65 * Add `PromisorInterface` for objects that have a `promise()` method.
71 * Add `PromisorInterface` for objects that have a `promise()` method.
6672
6773 * 1.0.2 (2012-11-14)
6874
69 * Fix bug in When::any() not correctly unwrapping to a single result value
70 * $promiseOrValue argument of When::resolve() and When::reject() is now optional
75 * Fix bug in `When::any()` not correctly unwrapping to a single result
76 value.
77 * `$promiseOrValue` argument of `When::resolve()` and When::reject() is now
78 optional.
7179
7280 * 1.0.1 (2012-11-13)
7381
74 * Prevent deep recursion which was reaching `xdebug.max_nesting_level` default of 100
82 * Prevent deep recursion which was reaching `xdebug.max_nesting_level`
83 default of 100.
7584
7685 * 1.0.0 (2012-11-07)
7786
78 * First tagged release
87 * First tagged release.
461461 If `$promiseOrValue` is a value, it will be the resolution value of the
462462 returned promise.
463463
464 If `$promiseOrValue` is a promise, it will simply be returned.
464 If `$promiseOrValue` is a thenable (any object that provides a `then()` method),
465 a trusted promise that follows the state of the thenable is returned.
466
467 If `$promiseOrValue` is a promise, it will be returned as is.
465468
466469 Note: The promise returned is always a promise implementing
467470 [ExtendedPromiseInterface](#extendedpromiseinterface). If you pass in a custom
519522 The returned promise will only reject if *all* items in `$promisesOrValues` are
520523 rejected. The rejection value will be an array of all rejection reasons.
521524
525 The returned promise will also reject with a `React\Promise\Exception\LengthException`
526 if `$promisesOrValues` contains 0 items.
527
522528 #### some()
523529
524530 ```php
534540 to resolve (that is, when `(count($promisesOrValues) - $howMany) + 1` items
535541 reject). The rejection value will be an array of
536542 `(count($promisesOrValues) - $howMany) + 1` rejection reasons.
543
544 The returned promise will also reject with a `React\Promise\Exception\LengthException`
545 if `$promisesOrValues` contains less items than `$howMany`.
537546
538547 #### map()
539548
664673 ->then(function ($x) {
665674 throw new \Exception($x + 1);
666675 })
667 ->then(null, function (\Exception $x) {
676 ->otherwise(function (\Exception $x) {
668677 // Propagate the rejection
669678 throw $x;
670679 })
671 ->then(null, function (\Exception $x) {
680 ->otherwise(function (\Exception $x) {
672681 // Can also propagate by returning another rejection
673 return React\Promise\reject((integer) $x->getMessage() + 1);
674 })
675 ->then(null, function ($x) {
676 echo 'Reject ' . $x; // 3
682 return React\Promise\reject(
683 new \Exception($x->getMessage() + 1)
684 );
685 })
686 ->otherwise(function ($x) {
687 echo 'Reject ' . $x->getMessage(); // 3
677688 });
678689
679690 $deferred->resolve(1); // Prints "Reject 3"
692703 return $x + 1;
693704 })
694705 ->then(function ($x) {
695 throw \Exception($x + 1);
696 })
697 ->then(null, function (\Exception $x) {
706 throw new \Exception($x + 1);
707 })
708 ->otherwise(function (\Exception $x) {
698709 // Handle the rejection, and don't propagate.
699710 // This is like catch without a rethrow
700 return (integer) $x->getMessage() + 1;
711 return $x->getMessage() + 1;
701712 })
702713 ->then(function ($x) {
703714 echo 'Mixed ' . $x; // 4
44 class CancellationQueue
55 {
66 private $started = false;
7
8 /**
9 * @var CancellablePromiseInterface[]
10 */
117 private $queue = [];
128
139 public function __invoke()
2016 $this->drain();
2117 }
2218
23 public function enqueue($promise)
19 public function enqueue($cancellable)
2420 {
25 if (!$promise instanceof CancellablePromiseInterface) {
21 if (!method_exists($cancellable, 'then') || !method_exists($cancellable, 'cancel')) {
2622 return;
2723 }
2824
29 $length = array_push($this->queue, $promise);
25 $length = array_push($this->queue, $cancellable);
3026
3127 if ($this->started && 1 === $length) {
3228 $this->drain();
3632 private function drain()
3733 {
3834 for ($i = key($this->queue); isset($this->queue[$i]); $i++) {
39 $promise = $this->queue[$i];
35 $cancellable = $this->queue[$i];
4036
4137 $exception = null;
4238
4339 try {
44 $promise->cancel();
40 $cancellable->cancel();
41 } catch (\Throwable $exception) {
4542 } catch (\Exception $exception) {
4643 }
4744
0 <?php
1
2 namespace React\Promise\Exception;
3
4 class LengthException extends \LengthException
5 {
6 }
33
44 function resolve($promiseOrValue = null)
55 {
6 if (!$promiseOrValue instanceof PromiseInterface) {
7 return new FulfilledPromise($promiseOrValue);
8 }
9
106 if ($promiseOrValue instanceof ExtendedPromiseInterface) {
117 return $promiseOrValue;
128 }
139
14 return new Promise(function ($resolve, $reject, $notify) use ($promiseOrValue) {
15 $promiseOrValue->then($resolve, $reject, $notify);
16 });
10 if (method_exists($promiseOrValue, 'then')) {
11 $canceller = null;
12
13 if (method_exists($promiseOrValue, 'cancel')) {
14 $canceller = [$promiseOrValue, 'cancel'];
15 }
16
17 return new Promise(function ($resolve, $reject, $notify) use ($promiseOrValue) {
18 $promiseOrValue->then($resolve, $reject, $notify);
19 }, $canceller);
20 }
21
22 return new FulfilledPromise($promiseOrValue);
1723 }
1824
1925 function reject($promiseOrValue = null)
4753 return;
4854 }
4955
56 $fulfiller = function ($value) use ($cancellationQueue, $resolve) {
57 $cancellationQueue();
58 $resolve($value);
59 };
60
61 $rejecter = function ($reason) use ($cancellationQueue, $reject) {
62 $cancellationQueue();
63 $reject($reason);
64 };
65
5066 foreach ($array as $promiseOrValue) {
5167 $cancellationQueue->enqueue($promiseOrValue);
52
53 $fulfiller = function ($value) use ($cancellationQueue, $resolve) {
54 $cancellationQueue();
55 $resolve($value);
56 };
57
58 $rejecter = function ($reason) use ($cancellationQueue, $reject) {
59 $cancellationQueue();
60 $reject($reason);
61 };
6268
6369 resolve($promiseOrValue)
6470 ->done($fulfiller, $rejecter, $notify);
8389 return new Promise(function ($resolve, $reject, $notify) use ($promisesOrValues, $howMany, $cancellationQueue) {
8490 resolve($promisesOrValues)
8591 ->done(function ($array) use ($howMany, $cancellationQueue, $resolve, $reject, $notify) {
86 if (!is_array($array) || !$array || $howMany < 1) {
92 if (!is_array($array) || $howMany < 1) {
8793 $resolve([]);
8894 return;
8995 }
9096
91 $len = count($array);
92 $toResolve = min($howMany, $len);
97 $len = count($array);
98
99 if ($len < $howMany) {
100 throw new Exception\LengthException(
101 sprintf(
102 'Input array must contain at least %d item%s but contains only %s item%s.',
103 $howMany,
104 1 === $howMany ? '' : 's',
105 $len,
106 1 === $len ? '' : 's'
107 )
108 );
109 }
110
111 $toResolve = $howMany;
93112 $toReject = ($len - $toResolve) + 1;
94113 $values = [];
95114 $reasons = [];
44 class CancellationQueueTest extends TestCase
55 {
66 /** @test */
7 public function ignoresNonCancellablePromises()
7 public function acceptsSimpleCancellableThenable()
88 {
9 $p = new SimpleFulfilledTestPromise();
9 $p = new SimpleTestCancellableThenable();
10
11 $cancellationQueue = new CancellationQueue();
12 $cancellationQueue->enqueue($p);
13
14 $cancellationQueue();
15
16 $this->assertTrue($p->cancelCalled);
17 }
18
19 /** @test */
20 public function ignoresSimpleCancellable()
21 {
22 $p = new SimpleTestCancellable();
1023
1124 $cancellationQueue = new CancellationQueue();
1225 $cancellationQueue->enqueue($p);
11
22 namespace React\Promise;
33
4 use React\Promise\Exception\LengthException;
5
46 class FunctionAnyTest extends TestCase
57 {
68 /** @test */
7 public function shouldResolveToNullWithEmptyInputArray()
9 public function shouldRejectWithLengthExceptionWithEmptyInputArray()
10 {
11 $mock = $this->createCallableMock();
12 $mock
13 ->expects($this->once())
14 ->method('__invoke')
15 ->with(
16 $this->callback(function($exception){
17 return $exception instanceof LengthException &&
18 'Input array must contain at least 1 item but contains only 0 items.' === $exception->getMessage();
19 })
20 );
21
22 any([])
23 ->then($this->expectCallableNever(), $mock);
24 }
25
26 /** @test */
27 public function shouldResolveToNullWithNonArrayInput()
828 {
929 $mock = $this->createCallableMock();
1030 $mock
1232 ->method('__invoke')
1333 ->with($this->identicalTo(null));
1434
15 any([])
35 any(null)
1636 ->then($mock);
1737 }
1838
3939 $mock,
4040 $this->expectCallableNever()
4141 );
42 }
43
44 /** @test */
45 public function shouldResolveAThenable()
46 {
47 $thenable = new SimpleFulfilledTestThenable();
48
49 $mock = $this->createCallableMock();
50 $mock
51 ->expects($this->once())
52 ->method('__invoke')
53 ->with($this->identicalTo('foo'));
54
55 resolve($thenable)
56 ->then(
57 $mock,
58 $this->expectCallableNever()
59 );
60 }
61
62 /** @test */
63 public function shouldResolveACancellableThenable()
64 {
65 $thenable = new SimpleTestCancellableThenable();
66
67 $promise = resolve($thenable);
68 $promise->cancel();
69
70 $this->assertTrue($thenable->cancelCalled);
4271 }
4372
4473 /** @test */
00 <?php
11
22 namespace React\Promise;
3
4 use React\Promise\Exception\LengthException;
35
46 class FunctionSomeTest extends TestCase
57 {
68 /** @test */
7 public function shouldResolveEmptyInput()
9 public function shouldRejectWithLengthExceptionWithEmptyInputArray()
10 {
11 $mock = $this->createCallableMock();
12 $mock
13 ->expects($this->once())
14 ->method('__invoke')
15 ->with(
16 $this->callback(function($exception){
17 return $exception instanceof LengthException &&
18 'Input array must contain at least 1 item but contains only 0 items.' === $exception->getMessage();
19 })
20 );
21
22 some(
23 [],
24 1
25 )->then($this->expectCallableNever(), $mock);
26 }
27
28 /** @test */
29 public function shouldRejectWithLengthExceptionWithInputArrayContainingNotEnoughItems()
30 {
31 $mock = $this->createCallableMock();
32 $mock
33 ->expects($this->once())
34 ->method('__invoke')
35 ->with(
36 $this->callback(function($exception){
37 return $exception instanceof LengthException &&
38 'Input array must contain at least 4 items but contains only 3 items.' === $exception->getMessage();
39 })
40 );
41
42 some(
43 [1, 2, 3],
44 4
45 )->then($this->expectCallableNever(), $mock);
46 }
47
48 /** @test */
49 public function shouldResolveToEmptyArrayWithNonArrayInput()
850 {
951 $mock = $this->createCallableMock();
1052 $mock
1355 ->with($this->identicalTo([]));
1456
1557 some(
16 [],
58 null,
1759 1
1860 )->then($mock);
1961 }
33
44 class SimpleFulfilledTestPromise implements PromiseInterface
55 {
6 public $cancelCalled = false;
7
86 public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
97 {
108 try {
1210 $onFulfilled('foo');
1311 }
1412
15 return new self('foo');
13 return new self();
14 } catch (\Throwable $exception) {
15 return new RejectedPromise($exception);
1616 } catch (\Exception $exception) {
1717 return new RejectedPromise($exception);
1818 }
1919 }
20
21 public function cancel()
22 {
23 $this->cancelCalled = true;
24 }
2520 }
0 <?php
1
2 namespace React\Promise;
3
4 class SimpleFulfilledTestThenable
5 {
6 public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
7 {
8 try {
9 if ($onFulfilled) {
10 $onFulfilled('foo');
11 }
12
13 return new self();
14 } catch (\Throwable $exception) {
15 return new RejectedPromise($exception);
16 } catch (\Exception $exception) {
17 return new RejectedPromise($exception);
18 }
19 }
20 }
1010 $onRejected('foo');
1111 }
1212
13 return new self('foo');
13 return new self();
14 } catch (\Throwable $exception) {
15 return new RejectedPromise($exception);
1416 } catch (\Exception $exception) {
1517 return new RejectedPromise($exception);
1618 }
0 <?php
1
2 namespace React\Promise;
3
4 class SimpleTestCancellable
5 {
6 public $cancelCalled = false;
7
8 public function cancel()
9 {
10 $this->cancelCalled = true;
11 }
12 }
0 <?php
1
2 namespace React\Promise;
3
4 class SimpleTestCancellableThenable
5 {
6 public $cancelCalled = false;
7
8 public function then(callable $onFulfilled = null, callable $onRejected = null, callable $onProgress = null)
9 {
10 return new self();
11 }
12
13 public function cancel()
14 {
15 $this->cancelCalled = true;
16 }
17 }