diff --git a/lib/functions.php b/lib/functions.php index a937b61..02d4f4e 100644 --- a/lib/functions.php +++ b/lib/functions.php @@ -91,9 +91,19 @@ function call(callable $functor, ...$args): Promise { /** * Registers a callback that will forward the failure reason to the Loop error handler if the promise fails. * - * @param \Amp\Promise $promise + * @param \Amp\Promise|\React\Promise\PromiseInterface $promise + * + * @throws \TypeError If $promise is not an instance of \Amp\Promise or \React\Promise\PromiseInterface. */ -function rethrow(Promise $promise) { +function rethrow($promise) { + if (!$promise instanceof Promise) { + if ($promise instanceof ReactPromise) { + $promise = adapt($promise); + } else { + throw new \TypeError("Must provide an instance of %s or %s", Promise::class, ReactPromise::class); + } + } + $promise->when(function ($exception) { if ($exception) { throw $exception; @@ -104,13 +114,22 @@ function rethrow(Promise $promise) { /** * Runs the event loop until the promise is resolved. Should not be called within a running event loop. * - * @param \Amp\Promise $promise + * @param \Amp\Promise|\React\Promise\PromiseInterface $promise * * @return mixed Promise success value. * + * @throws \TypeError If $promise is not an instance of \Amp\Promise or \React\Promise\PromiseInterface. * @throws \Throwable Promise failure reason. */ -function wait(Promise $promise) { +function wait($promise) { + if (!$promise instanceof Promise) { + if ($promise instanceof ReactPromise) { + $promise = adapt($promise); + } else { + throw new \TypeError("Must provide an instance of %s or %s", Promise::class, ReactPromise::class); + } + } + $resolved = false; Loop::run(function () use (&$resolved, &$value, &$exception, $promise) { $promise->when(function ($e, $v) use (&$resolved, &$value, &$exception) { @@ -135,12 +154,22 @@ function wait(Promise $promise) { /** * Pipe the promised value through the specified functor once it resolves. * - * @param \Amp\Promise $promise + * @param \Amp\Promise|\React\Promise\PromiseInterface $promise * @param callable(mixed $value): mixed $functor * * @return \Amp\Promise + * + * @throws \TypeError If $promise is not an instance of \Amp\Promise or \React\Promise\PromiseInterface. */ -function pipe(Promise $promise, callable $functor): Promise { +function pipe($promise, callable $functor): Promise { + if (!$promise instanceof Promise) { + if ($promise instanceof ReactPromise) { + $promise = adapt($promise); + } else { + throw new \TypeError("Must provide an instance of %s or %s", Promise::class, ReactPromise::class); + } + } + $deferred = new Deferred; $promise->when(function ($exception, $value) use ($deferred, $functor) { @@ -160,14 +189,24 @@ function pipe(Promise $promise, callable $functor): Promise { } /** - * @param \Amp\Promise $promise + * @param \Amp\Promise|\React\Promise\PromiseInterface $promise * @param string $className Exception class name to capture. Given callback will only be invoked if the failure reason * is an instance of the given exception class name. * @param callable(\Throwable $exception): mixed $functor * * @return \Amp\Promise + * + * @throws \TypeError If $promise is not an instance of \Amp\Promise or \React\Promise\PromiseInterface. */ -function capture(Promise $promise, string $className, callable $functor): Promise { +function capture($promise, string $className, callable $functor): Promise { + if (!$promise instanceof Promise) { + if ($promise instanceof ReactPromise) { + $promise = adapt($promise); + } else { + throw new \TypeError("Must provide an instance of %s or %s", Promise::class, ReactPromise::class); + } + } + $deferred = new Deferred; $promise->when(function ($exception, $value) use ($deferred, $className, $functor) { @@ -197,12 +236,22 @@ function capture(Promise $promise, string $className, callable $functor): Promis * If the timeout expires before the promise is resolved, the returned promise fails with an instance of * \Amp\TimeoutException. * - * @param \Amp\Promise $promise + * @param \Amp\Promise|\React\Promise\PromiseInterface $promise * @param int $timeout Timeout in milliseconds. * * @return \Amp\Promise + * + * @throws \TypeError If $promise is not an instance of \Amp\Promise or \React\Promise\PromiseInterface. */ -function timeout(Promise $promise, int $timeout): Promise { +function timeout($promise, int $timeout): Promise { + if (!$promise instanceof Promise) { + if ($promise instanceof ReactPromise) { + $promise = adapt($promise); + } else { + throw new \TypeError("Must provide an instance of %s or %s", Promise::class, ReactPromise::class); + } + } + $deferred = new Deferred; $resolved = false; diff --git a/test/CaptureTest.php b/test/CaptureTest.php index 25044ec..c06d7f5 100644 --- a/test/CaptureTest.php +++ b/test/CaptureTest.php @@ -6,6 +6,7 @@ use Amp; use Amp\Failure; use Amp\Success; use Amp\Promise; +use function React\Promise\reject; class CaptureTest extends \PHPUnit\Framework\TestCase { public function testSuccessfulPromise() { @@ -110,4 +111,33 @@ class CaptureTest extends \PHPUnit\Framework\TestCase { $this->assertFalse($invoked); $this->assertSame($exception, $reason); } + + /** + * @depends testFailedPromise + */ + public function testReactPromise() { + $invoked = false; + $callback = function ($exception) use (&$invoked, &$reason) { + $invoked = true; + $reason = $exception; + return -1; + }; + + $exception = new \Exception; + + $promise = reject($exception); + + $promise = Amp\capture($promise, \Exception::class, $callback); + $this->assertInstanceOf(Promise::class, $promise); + + $callback = function ($exception, $value) use (&$result) { + $result = $value; + }; + + $promise->when($callback); + + $this->assertTrue($invoked); + $this->assertSame($exception, $reason); + $this->assertSame(-1, $result); + } } diff --git a/test/PipeTest.php b/test/PipeTest.php index 83a9918..e6ffded 100644 --- a/test/PipeTest.php +++ b/test/PipeTest.php @@ -6,6 +6,7 @@ use Amp; use Amp\Failure; use Amp\Success; use Amp\Promise; +use function React\Promise\resolve; class PipeTest extends \PHPUnit\Framework\TestCase { public function testSuccessfulPromise() { @@ -81,4 +82,31 @@ class PipeTest extends \PHPUnit\Framework\TestCase { $this->assertTrue($invoked); $this->assertSame($exception, $reason); } + + /** + * @depends testSuccessfulPromise + */ + public function testReactPromise() { + $invoked = false; + $callback = function ($value) use (&$invoked) { + $invoked = true; + return $value + 1; + }; + + $value = 1; + + $promise = resolve($value); + + $promise = Amp\pipe($promise, $callback); + $this->assertInstanceOf(Promise::class, $promise); + + $callback = function ($exception, $value) use (&$result) { + $result = $value; + }; + + $promise->when($callback); + + $this->assertTrue($invoked); + $this->assertSame($value + 1, $result); + } } diff --git a/test/RethrowTest.php b/test/RethrowTest.php index 6302765..6812bc6 100644 --- a/test/RethrowTest.php +++ b/test/RethrowTest.php @@ -6,9 +6,10 @@ use Amp; use Amp\Failure; use Amp\Loop; use PHPUnit\Framework\TestCase; +use function React\Promise\reject; class RethrowTest extends TestCase { - public function testWaitOnPendingPromise() { + public function testRethrow() { $exception = new \Exception; try { @@ -24,4 +25,24 @@ class RethrowTest extends TestCase { $this->fail('Failed promise reason should be thrown from loop'); } + + /** + * @depends testRethrow + */ + public function testReactPromise() { + $exception = new \Exception; + + try { + Loop::run(function () use ($exception) { + $promise = reject($exception); + + Amp\rethrow($promise); + }); + } catch (\Exception $reason) { + $this->assertSame($exception, $reason); + return; + } + + $this->fail('Failed promise reason should be thrown from loop'); + } } diff --git a/test/TimeoutTest.php b/test/TimeoutTest.php index e34c9c6..dfe32cf 100644 --- a/test/TimeoutTest.php +++ b/test/TimeoutTest.php @@ -8,6 +8,7 @@ use Amp\Loop; use Amp\Pause; use Amp\Success; use Amp\Promise; +use function React\Promise\resolve; class TimeoutTest extends \PHPUnit\Framework\TestCase { public function testSuccessfulPromise() { @@ -89,4 +90,26 @@ class TimeoutTest extends \PHPUnit\Framework\TestCase { $this->assertInstanceOf(Amp\TimeoutException::class, $reason); } + + /** + * @depends testSuccessfulPromise + */ + public function testReactPromise() { + Loop::run(function () { + $value = 1; + + $promise = resolve($value); + + $promise = Amp\timeout($promise, 100); + $this->assertInstanceOf(Promise::class, $promise); + + $callback = function ($exception, $value) use (&$result) { + $result = $value; + }; + + $promise->when($callback); + + $this->assertSame($value, $result); + }); + } } diff --git a/test/WaitTest.php b/test/WaitTest.php index 62f7788..cf82e53 100644 --- a/test/WaitTest.php +++ b/test/WaitTest.php @@ -8,6 +8,7 @@ use Amp\Failure; use Amp\Pause; use Amp\Success; use Amp\Loop; +use function React\Promise\resolve; class WaitTest extends \PHPUnit\Framework\TestCase { public function testWaitOnSuccessfulPromise() { @@ -59,4 +60,17 @@ class WaitTest extends \PHPUnit\Framework\TestCase { $result = Amp\wait($promise->promise()); } + + /** + * @depends testWaitOnSuccessfulPromise + */ + public function testReactPromise() { + $value = 1; + + $promise = resolve($value); + + $result = Amp\wait($promise); + + $this->assertSame($value, $result); + } }