diff --git a/CHANGELOG.md b/CHANGELOG.md index 04a2fdf1..b02ac528 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,3 +12,32 @@ 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` (#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(); + } + ``` 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 deleted file mode 100644 index 896db2d3..00000000 --- a/src/CancellablePromiseInterface.php +++ /dev/null @@ -1,11 +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..7a32e27c 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 { private $factory; private $promise; diff --git a/src/Promise.php b/src/Promise.php index 0466a571..d24ecaa5 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 { 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..1e7f6284 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 { 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 004747c7..40685213 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 5b205d25..80eaff02 100644 --- a/tests/FunctionMapTest.php +++ b/tests/FunctionMapTest.php @@ -116,15 +116,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 @@ -