From d91c9303473ede9ba4d7ff3489d499b2438764b7 Mon Sep 17 00:00:00 2001 From: Jan Sorgalla Date: Wed, 21 Dec 2016 20:10:12 +0100 Subject: [PATCH 1/3] Merge promise interface Merge PromiseInterface, ExtendedPromiseInterface and CancellablePromiseInterface into a single PromiseInterface. The CancellablePromiseInterface is kept for backward compatibility but is marked as deprecated and must not be used anymore. Closes #44 --- CHANGELOG.md | 5 ++ README.md | 50 ++++--------------- src/CancellablePromiseInterface.php | 13 ++++- src/ExtendedPromiseInterface.php | 21 -------- src/FulfilledPromise.php | 4 +- src/LazyPromise.php | 2 +- src/Promise.php | 8 +-- src/PromiseInterface.php | 20 ++++++++ src/RejectedPromise.php | 4 +- src/functions.php | 2 +- tests/CancellationQueueTest.php | 2 +- tests/FunctionAnyTest.php | 6 +-- tests/FunctionMapTest.php | 10 +++- tests/FunctionRaceTest.php | 8 +-- tests/FunctionReduceTest.php | 10 +++- tests/FunctionResolveTest.php | 10 ---- tests/FunctionSomeTest.php | 8 +-- tests/PromiseTest.php | 34 ------------- tests/fixtures/SimpleFulfilledTestPromise.php | 21 -------- tests/fixtures/SimpleRejectedTestPromise.php | 21 -------- 20 files changed, 84 insertions(+), 175 deletions(-) delete mode 100644 src/ExtendedPromiseInterface.php delete mode 100644 tests/fixtures/SimpleFulfilledTestPromise.php delete mode 100644 tests/fixtures/SimpleRejectedTestPromise.php diff --git a/CHANGELOG.md b/CHANGELOG.md index d73e1bc3..0f8be8b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,3 +12,8 @@ CHANGELOG for 3.x `some()`, `map()`, `reduce()`) now require an array of promises or values as input. Before, arrays and promises which resolve to an array were supported, other input types resolved to empty arrays or `null`. (#35). + * BC break: The interfaces `PromiseInterface`, `ExtendedPromiseInterface` + and `CancellablePromiseInterface` have been merged into a single + `PromiseInterface`. The `CancellablePromiseInterface` has been kept for + backward compatibility but is marked as deprecated and must not be used + anymore. diff --git a/README.md b/README.md index 8019f464..e2823b4a 100644 --- a/README.md +++ b/README.md @@ -25,12 +25,10 @@ Table of Contents * [Deferred::reject()](#deferredreject) * [PromiseInterface](#promiseinterface) * [PromiseInterface::then()](#promiseinterfacethen) - * [ExtendedPromiseInterface](#extendedpromiseinterface) - * [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone) - * [ExtendedPromiseInterface::otherwise()](#extendedpromiseinterfaceotherwise) - * [ExtendedPromiseInterface::always()](#extendedpromiseinterfacealways) - * [CancellablePromiseInterface](#cancellablepromiseinterface) - * [CancellablePromiseInterface::cancel()](#cancellablepromiseinterfacecancel) + * [PromiseInterface::done()](#promiseinterfacedone) + * [PromiseInterface::otherwise()](#promiseinterfaceotherwise) + * [PromiseInterface::always()](#promiseinterfacealways) + * [PromiseInterface::cancel()](#promiseinterfacecancel) * [Promise](#promise-1) * [FulfilledPromise](#fulfilledpromise) * [RejectedPromise](#rejectedpromise) @@ -193,22 +191,10 @@ the same call to `then()`: * [resolve()](#resolve) - Creating a resolved promise * [reject()](#reject) - Creating a rejected promise -* [ExtendedPromiseInterface::done()](#extendedpromiseinterfacedone) +* [PromiseInterface::done()](#promiseinterfacedone) * [done() vs. then()](#done-vs-then) -### ExtendedPromiseInterface - -The ExtendedPromiseInterface extends the PromiseInterface with useful shortcut -and utility methods which are not part of the Promises/A specification. - -#### Implementations - -* [Promise](#promise-1) -* [FulfilledPromise](#fulfilledpromise) -* [RejectedPromise](#rejectedpromise) -* [LazyPromise](#lazypromise) - -#### ExtendedPromiseInterface::done() +#### PromiseInterface::done() ```php $promise->done(callable $onFulfilled = null, callable $onRejected = null); @@ -228,7 +214,7 @@ Since the purpose of `done()` is consumption rather than transformation, * [PromiseInterface::then()](#promiseinterfacethen) * [done() vs. then()](#done-vs-then) -#### ExtendedPromiseInterface::otherwise() +#### PromiseInterface::otherwise() ```php $promise->otherwise(callable $onRejected); @@ -254,7 +240,7 @@ $promise )}; ``` -#### ExtendedPromiseInterface::always() +#### PromiseInterface::always() ```php $newPromise = $promise->always(callable $onFulfilledOrRejected); @@ -301,13 +287,7 @@ return doSomething() ->always('cleanup'); ``` -### CancellablePromiseInterface - -A cancellable promise provides a mechanism for consumers to notify the creator -of the promise that they are not longer interested in the result of an -operation. - -#### CancellablePromiseInterface::cancel() +#### PromiseInterface::cancel() ``` php $promise->cancel(); @@ -319,13 +299,6 @@ further interest in the results of the operation. Once a promise is settled (either fulfilled or rejected), calling `cancel()` on a promise has no effect. -#### Implementations - -* [Promise](#promise-1) -* [FulfilledPromise](#fulfilledpromise) -* [RejectedPromise](#rejectedpromise) -* [LazyPromise](#lazypromise) - ### Promise Creates a promise whose state is controlled by the functions passed to @@ -435,11 +408,6 @@ a trusted promise that follows the state of the thenable is returned. If `$promiseOrValue` is a promise, it will be returned as is. -Note: The promise returned is always a promise implementing -[ExtendedPromiseInterface](#extendedpromiseinterface). If you pass in a custom -promise which only implements [PromiseInterface](#promiseinterface), this -promise will be assimilated to a extended promise following `$promiseOrValue`. - #### reject() ```php diff --git a/src/CancellablePromiseInterface.php b/src/CancellablePromiseInterface.php index 896db2d3..34096451 100644 --- a/src/CancellablePromiseInterface.php +++ b/src/CancellablePromiseInterface.php @@ -2,8 +2,19 @@ namespace React\Promise; -interface CancellablePromiseInterface extends PromiseInterface +/** + * This interface is only kept for backward compatibility and must not be used + * anymore. + * + * @deprecated + */ +interface CancellablePromiseInterface { + /** + * @return PromiseInterface + */ + public function then(callable $onFulfilled = null, callable $onRejected = null); + /** * @return void */ diff --git a/src/ExtendedPromiseInterface.php b/src/ExtendedPromiseInterface.php deleted file mode 100644 index b5372712..00000000 --- a/src/ExtendedPromiseInterface.php +++ /dev/null @@ -1,21 +0,0 @@ -enqueue(function () use ($onFulfilled) { $result = $onFulfilled($this->value); - if ($result instanceof ExtendedPromiseInterface) { + if ($result instanceof PromiseInterface) { $result->done(); } }); diff --git a/src/LazyPromise.php b/src/LazyPromise.php index a2aecd2d..d46ce563 100644 --- a/src/LazyPromise.php +++ b/src/LazyPromise.php @@ -2,7 +2,7 @@ namespace React\Promise; -class LazyPromise implements ExtendedPromiseInterface, CancellablePromiseInterface +class LazyPromise implements PromiseInterface, CancellablePromiseInterface { private $factory; private $promise; diff --git a/src/Promise.php b/src/Promise.php index 0466a571..4fd54737 100644 --- a/src/Promise.php +++ b/src/Promise.php @@ -2,7 +2,7 @@ namespace React\Promise; -class Promise implements ExtendedPromiseInterface, CancellablePromiseInterface +class Promise implements PromiseInterface, CancellablePromiseInterface { private $canceller; private $result; @@ -45,7 +45,7 @@ public function done(callable $onFulfilled = null, callable $onRejected = null) return $this->result()->done($onFulfilled, $onRejected); } - $this->handlers[] = function (ExtendedPromiseInterface $promise) use ($onFulfilled, $onRejected) { + $this->handlers[] = function (PromiseInterface $promise) use ($onFulfilled, $onRejected) { $promise ->done($onFulfilled, $onRejected); }; @@ -90,7 +90,7 @@ public function cancel() private function resolver(callable $onFulfilled = null, callable $onRejected = null) { return function ($resolve, $reject) use ($onFulfilled, $onRejected) { - $this->handlers[] = function (ExtendedPromiseInterface $promise) use ($onFulfilled, $onRejected, $resolve, $reject) { + $this->handlers[] = function (PromiseInterface $promise) use ($onFulfilled, $onRejected, $resolve, $reject) { $promise ->then($onFulfilled, $onRejected) ->done($resolve, $reject); @@ -116,7 +116,7 @@ private function reject($reason = null) $this->settle(reject($reason)); } - private function settle(ExtendedPromiseInterface $result) + private function settle(PromiseInterface $result) { if ($result instanceof LazyPromise) { $result = $result->promise(); diff --git a/src/PromiseInterface.php b/src/PromiseInterface.php index da138e8f..f926cda5 100644 --- a/src/PromiseInterface.php +++ b/src/PromiseInterface.php @@ -8,4 +8,24 @@ interface PromiseInterface * @return PromiseInterface */ public function then(callable $onFulfilled = null, callable $onRejected = null); + + /** + * @return void + */ + public function done(callable $onFulfilled = null, callable $onRejected = null); + + /** + * @return PromiseInterface + */ + public function otherwise(callable $onRejected); + + /** + * @return PromiseInterface + */ + public function always(callable $onFulfilledOrRejected); + + /** + * @return void + */ + public function cancel(); } diff --git a/src/RejectedPromise.php b/src/RejectedPromise.php index 6ea75fc9..c5d8eec7 100644 --- a/src/RejectedPromise.php +++ b/src/RejectedPromise.php @@ -2,7 +2,7 @@ namespace React\Promise; -class RejectedPromise implements ExtendedPromiseInterface, CancellablePromiseInterface +class RejectedPromise implements PromiseInterface, CancellablePromiseInterface { private $reason; @@ -47,7 +47,7 @@ public function done(callable $onFulfilled = null, callable $onRejected = null) throw UnhandledRejectionException::resolve($result->reason); } - if ($result instanceof ExtendedPromiseInterface) { + if ($result instanceof PromiseInterface) { $result->done(); } }); diff --git a/src/functions.php b/src/functions.php index 006e5205..c670cd45 100644 --- a/src/functions.php +++ b/src/functions.php @@ -4,7 +4,7 @@ function resolve($promiseOrValue = null) { - if ($promiseOrValue instanceof ExtendedPromiseInterface) { + if ($promiseOrValue instanceof PromiseInterface) { return $promiseOrValue; } diff --git a/tests/CancellationQueueTest.php b/tests/CancellationQueueTest.php index 32cedf47..f74a74e7 100644 --- a/tests/CancellationQueueTest.php +++ b/tests/CancellationQueueTest.php @@ -75,7 +75,7 @@ public function rethrowsExceptionsThrownFromCancel() $this->setExpectedException('\Exception', 'test'); $mock = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); $mock ->expects($this->once()) diff --git a/tests/FunctionAnyTest.php b/tests/FunctionAnyTest.php index 90d1a8c8..ae6ce5cd 100644 --- a/tests/FunctionAnyTest.php +++ b/tests/FunctionAnyTest.php @@ -99,14 +99,14 @@ public function shouldNotRelyOnArryIndexesWhenUnwrappingToASingleResolutionValue public function shouldCancelInputArrayPromises() { $mock1 = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); $mock1 ->expects($this->once()) ->method('cancel'); $mock2 = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); $mock2 ->expects($this->once()) @@ -128,7 +128,7 @@ public function shouldNotCancelOtherPendingInputArrayPromisesIfOnePromiseFulfill $deferred->resolve(); $mock2 = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); $mock2 ->expects($this->never()) diff --git a/tests/FunctionMapTest.php b/tests/FunctionMapTest.php index 40e33961..abc7789b 100644 --- a/tests/FunctionMapTest.php +++ b/tests/FunctionMapTest.php @@ -97,15 +97,21 @@ public function shouldRejectWhenInputContainsRejection() public function shouldCancelInputArrayPromises() { $mock1 = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); + $mock1 + ->method('then') + ->will($this->returnSelf()); $mock1 ->expects($this->once()) ->method('cancel'); $mock2 = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); + $mock2 + ->method('then') + ->will($this->returnSelf()); $mock2 ->expects($this->once()) ->method('cancel'); diff --git a/tests/FunctionRaceTest.php b/tests/FunctionRaceTest.php index 04d4b7a8..d8b531c6 100644 --- a/tests/FunctionRaceTest.php +++ b/tests/FunctionRaceTest.php @@ -96,14 +96,14 @@ public function shouldRejectIfFirstSettledPromiseRejects() public function shouldCancelInputArrayPromises() { $mock1 = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); $mock1 ->expects($this->once()) ->method('cancel'); $mock2 = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); $mock2 ->expects($this->once()) @@ -124,7 +124,7 @@ public function shouldNotCancelOtherPendingInputArrayPromisesIfOnePromiseFulfill $deferred->resolve(); $mock2 = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); $mock2 ->expects($this->never()) @@ -145,7 +145,7 @@ public function shouldNotCancelOtherPendingInputArrayPromisesIfOnePromiseRejects $deferred->reject(); $mock2 = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); $mock2 ->expects($this->never()) diff --git a/tests/FunctionReduceTest.php b/tests/FunctionReduceTest.php index 0247cff6..a81feba9 100644 --- a/tests/FunctionReduceTest.php +++ b/tests/FunctionReduceTest.php @@ -260,15 +260,21 @@ public function shouldProvideCorrectBasisValue() public function shouldCancelInputArrayPromises() { $mock1 = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); + $mock1 + ->method('then') + ->will($this->returnSelf()); $mock1 ->expects($this->once()) ->method('cancel'); $mock2 = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); + $mock2 + ->method('then') + ->will($this->returnSelf()); $mock2 ->expects($this->once()) ->method('cancel'); diff --git a/tests/FunctionResolveTest.php b/tests/FunctionResolveTest.php index 29dd5d8e..5f1f12e9 100644 --- a/tests/FunctionResolveTest.php +++ b/tests/FunctionResolveTest.php @@ -157,14 +157,4 @@ public function shouldSupportVeryDeepNestedPromises() $deferreds[0]->promise()->then($mock); } - - /** @test */ - public function returnsExtendePromiseForSimplePromise() - { - $promise = $this - ->getMockBuilder('React\Promise\PromiseInterface') - ->getMock(); - - $this->assertInstanceOf('React\Promise\ExtendedPromiseInterface', resolve($promise)); - } } diff --git a/tests/FunctionSomeTest.php b/tests/FunctionSomeTest.php index e43ecf36..23ce63ae 100644 --- a/tests/FunctionSomeTest.php +++ b/tests/FunctionSomeTest.php @@ -126,14 +126,14 @@ public function shouldResolveWithEmptyArrayIfHowManyIsLessThanOne() public function shouldCancelInputArrayPromises() { $mock1 = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); $mock1 ->expects($this->once()) ->method('cancel'); $mock2 = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); $mock2 ->expects($this->once()) @@ -154,7 +154,7 @@ public function shouldCancelOtherPendingInputArrayPromisesIfEnoughPromisesFulfil $deferred->resolve(); $mock2 = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); $mock2 ->expects($this->never()) @@ -175,7 +175,7 @@ public function shouldNotCancelOtherPendingInputArrayPromisesIfEnoughPromisesRej $deferred->reject(); $mock2 = $this - ->getMockBuilder('React\Promise\CancellablePromiseInterface') + ->getMockBuilder('React\Promise\PromiseInterface') ->getMock(); $mock2 ->expects($this->never()) diff --git a/tests/PromiseTest.php b/tests/PromiseTest.php index b6ee45a7..52a0cb1c 100644 --- a/tests/PromiseTest.php +++ b/tests/PromiseTest.php @@ -45,38 +45,4 @@ public function shouldRejectIfResolverThrowsException() $promise ->then($this->expectCallableNever(), $mock); } - - /** @test */ - public function shouldFulfillIfFullfilledWithSimplePromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo('foo')); - - $adapter->promise() - ->then($mock); - - $adapter->resolve(new SimpleFulfilledTestPromise()); - } - - /** @test */ - public function shouldRejectIfRejectedWithSimplePromise() - { - $adapter = $this->getPromiseTestAdapter(); - - $mock = $this->createCallableMock(); - $mock - ->expects($this->once()) - ->method('__invoke') - ->with($this->identicalTo('foo')); - - $adapter->promise() - ->then($this->expectCallableNever(), $mock); - - $adapter->resolve(new SimpleRejectedTestPromise()); - } } diff --git a/tests/fixtures/SimpleFulfilledTestPromise.php b/tests/fixtures/SimpleFulfilledTestPromise.php deleted file mode 100644 index 92995225..00000000 --- a/tests/fixtures/SimpleFulfilledTestPromise.php +++ /dev/null @@ -1,21 +0,0 @@ - Date: Wed, 4 Jan 2017 14:30:30 +0100 Subject: [PATCH 2/3] Remove CancellablePromiseInterface retained for BC only --- CHANGELOG.md | 4 +--- src/CancellablePromiseInterface.php | 22 ---------------------- src/FulfilledPromise.php | 2 +- src/LazyPromise.php | 2 +- src/Promise.php | 2 +- src/RejectedPromise.php | 2 +- 6 files changed, 5 insertions(+), 29 deletions(-) delete mode 100644 src/CancellablePromiseInterface.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f8be8b6..c17f8d7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,4 @@ CHANGELOG for 3.x supported, other input types resolved to empty arrays or `null`. (#35). * BC break: The interfaces `PromiseInterface`, `ExtendedPromiseInterface` and `CancellablePromiseInterface` have been merged into a single - `PromiseInterface`. The `CancellablePromiseInterface` has been kept for - backward compatibility but is marked as deprecated and must not be used - anymore. + `PromiseInterface`. diff --git a/src/CancellablePromiseInterface.php b/src/CancellablePromiseInterface.php deleted file mode 100644 index 34096451..00000000 --- a/src/CancellablePromiseInterface.php +++ /dev/null @@ -1,22 +0,0 @@ - Date: Wed, 4 Jan 2017 15:28:11 +0100 Subject: [PATCH 3/3] Add issue number and migration guide to interface merge paragraph --- CHANGELOG.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c17f8d7e..ca5d7fe1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,4 +14,30 @@ CHANGELOG for 3.x supported, other input types resolved to empty arrays or `null`. (#35). * BC break: The interfaces `PromiseInterface`, `ExtendedPromiseInterface` and `CancellablePromiseInterface` have been merged into a single - `PromiseInterface`. + `PromiseInterface` (#75). + + Please note, that the following code (which has been commonly used to + conditionally cancel a promise) is not longer possible: + + ```php + if ($promise instanceof CancellablePromiseInterface) { + $promise->cancel(); + } + ``` + + If only supporting react/promise >= 3.0, it can be simply changed to: + + ```php + if ($promise instanceof PromiseInterface) { + $promise->cancel(); + } + ``` + + If also react/promise < 3.0 must be supported, the following code can be + used: + + ```php + if ($promise instanceof PromiseInterface) { + \React\Promise\resolve($promise)->cancel(); + } + ```