diff --git a/composer.lock b/composer.lock index aadb40f..c61b9a5 100644 --- a/composer.lock +++ b/composer.lock @@ -703,16 +703,16 @@ }, { "name": "phpstan/phpstan", - "version": "0.12.64", + "version": "0.12.67", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "23eb1cb7ae125f45f1d0e48051bcf67a9a9b08aa" + "reference": "ced7a5d9c5dba908c1c00cea3f1d4ad74da71b86" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/23eb1cb7ae125f45f1d0e48051bcf67a9a9b08aa", - "reference": "23eb1cb7ae125f45f1d0e48051bcf67a9a9b08aa", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ced7a5d9c5dba908c1c00cea3f1d4ad74da71b86", + "reference": "ced7a5d9c5dba908c1c00cea3f1d4ad74da71b86", "shasum": "" }, "require": { @@ -755,7 +755,7 @@ "type": "tidelift" } ], - "time": "2020-12-21T11:59:02+00:00" + "time": "2021-01-14T14:16:19+00:00" }, { "name": "phpunit/php-code-coverage", @@ -2056,16 +2056,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.20.0", + "version": "v1.22.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41" + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f4ba089a5b6366e453971d3aad5fe8e897b37f41", - "reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e", + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e", "shasum": "" }, "require": { @@ -2077,7 +2077,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.20-dev" + "dev-main": "1.22-dev" }, "thanks": { "name": "symfony/polyfill", @@ -2128,7 +2128,7 @@ "type": "tidelift" } ], - "time": "2020-10-23T14:02:19+00:00" + "time": "2021-01-07T16:49:33+00:00" }, { "name": "theseer/tokenizer", diff --git a/src/DeferredInterface.php b/src/DeferredInterface.php index c47c72c..a42ce14 100644 --- a/src/DeferredInterface.php +++ b/src/DeferredInterface.php @@ -27,7 +27,7 @@ public function resolve($value = null):void; * registered with the Promise's catch() function will be rejected * with this reason. */ - public function reject(Throwable $reason); + public function reject(Throwable $reason):void; /** * Assigns a callback as a task to perform to complete the deferred diff --git a/src/FulfilledPromise.php b/src/FulfilledPromise.php index 2b37c77..10fef2c 100644 --- a/src/FulfilledPromise.php +++ b/src/FulfilledPromise.php @@ -15,7 +15,7 @@ class FulfilledPromise implements PromiseInterface { /** @param ?mixed $promiseOrValue */ public function __construct($promiseOrValue = null) { if($promiseOrValue instanceof PromiseInterface) { - throw new PromiseException("A FulfilledPromise must be resolved with a concrete value, not a Promise."); + throw new FulfilledValueNotConcreteException(get_class($promiseOrValue)); } $this->value = $promiseOrValue; diff --git a/src/FulfilledValueNotConcreteException.php b/src/FulfilledValueNotConcreteException.php new file mode 100644 index 0000000..71fd785 --- /dev/null +++ b/src/FulfilledValueNotConcreteException.php @@ -0,0 +1,4 @@ +waitTask = $task; } - public function wait($unwrap = true) { + /** @param bool $unwrap */ + public function wait($unwrap = true):mixed { if(!isset($this->waitTask)) { - throw new PromiseException("Promise::wait() is only possible when a wait task is set"); + throw new PromiseWaitTaskNotSetException(); } - /** @var PromiseInterface $promise */ + /** @var Promise $promise */ $promise = $this; if($unwrap && $this instanceof Promise) { $promise = $this->unwrap($promise); @@ -29,5 +30,15 @@ public function wait($unwrap = true) { while($promise->getState() === HttpPromiseInterface::PENDING) { call_user_func($this->waitTask); } + + if($unwrap) { + $resolvedValue = null; + $promise->then(function(mixed $value) use(&$resolvedValue):void { + $resolvedValue = $value; + }); + return $resolvedValue; + } + + return null; } } \ No newline at end of file diff --git a/test/phpunit/FulfilledPromiseTest.php b/test/phpunit/FulfilledPromiseTest.php new file mode 100644 index 0000000..04bb13d --- /dev/null +++ b/test/phpunit/FulfilledPromiseTest.php @@ -0,0 +1,87 @@ +true; + $promise = new Promise($callback); + self::expectException(FulfilledValueNotConcreteException::class); + new FulfilledPromise($promise); + } + + public function testDoNothingWithNullComplete() { + $message = "Test message"; + $sut = new FulfilledPromise($message); + $exception = null; + try { + $sut->complete(); + } + catch(Exception $exception) {} + + self::assertNull($exception); + } + + public function testCompleteWithPromise() { + $message = "Test message"; + + $promise = self::createMock(Promise::class); + $promise->expects(self::once()) + ->method("complete"); + $callback = fn() => $promise; + + $sut = new FulfilledPromise($message); + $sut->complete($callback); + } + + public function testCatch() { + // Catch does nothing because a FulfilledPromise is already resolved. + $callCount = 0; + $callback = function() use(&$callCount) { + $callCount++; + }; + $sut = new FulfilledPromise(true); + self::assertSame($sut, $sut->catch($callback)); + self::assertEquals(0, $callCount); + } + + public function testFinally() { + $callCount = 0; + $callback = function() use(&$callCount) { + $callCount++; + }; + + $message = "Test message"; + $sut = new FulfilledPromise($message); + $sut->finally($callback); + self::assertEquals(1, $callCount); + } + + public function testCompleteWithInvalidCallback() { + $callback = function(string $requiredStringParameter) {}; + + $sut = new FulfilledPromise("Callback should not have a required string parameter!"); + $reasonArray = []; + $sut->finally($callback)->catch(function(\Throwable $reason) use (&$reasonArray) { + array_push($reasonArray, $reason); + }); + self::assertCount(1, $reasonArray); + self::assertInstanceOf(TypeError::class, $reasonArray[0]); + } + + public function testGetState() { + $sut = new FulfilledPromise(); + self::assertEquals( + HttpPromiseInterface::FULFILLED, + $sut->getState() + ); + } +} \ No newline at end of file diff --git a/test/phpunit/WaitableTest.php b/test/phpunit/WaitableTest.php new file mode 100644 index 0000000..f3f2873 --- /dev/null +++ b/test/phpunit/WaitableTest.php @@ -0,0 +1,63 @@ += 10) { + call_user_func($resolveCallback, $resolvedValue); + } + else { + $callCount++; + } + }; + + $sut->setWaitTask($waitTask); + self::assertEquals($resolvedValue, $sut->wait()); + self::assertEquals(10, $callCount); + } + + public function testWaitNotUnwrapped() { + $callCount = 0; + $resolveCallback = null; + $executor = function(callable $resolve, callable $reject) use(&$resolveCallback):void { + $resolveCallback = $resolve; + }; + $resolvedValue = "Done!"; + $sut = new Promise($executor); + + $waitTask = function() use(&$callCount, $resolveCallback, $resolvedValue) { + if($callCount >= 10) { + call_user_func($resolveCallback, $resolvedValue); + } + else { + $callCount++; + } + }; + + $sut->setWaitTask($waitTask); + self::assertNull($sut->wait(false)); + self::assertEquals(10, $callCount); + } + + public function testWaitWithNoWaitTask() { + $executor = function(callable $resolve, callable $reject) use(&$resolveCallback):void { + $resolveCallback = $resolve; + }; + $sut = new Promise($executor);; + self::expectException(PromiseWaitTaskNotSetException::class); + $sut->wait(); + } +} \ No newline at end of file