From d38b3ae03472ba2f9af5009778574b23472bb3f7 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Tue, 19 Sep 2017 17:57:37 +0200 Subject: [PATCH] drop async-interop --- composer.json | 10 +++--- examples/cli.php | 6 ++-- examples/generator.php | 4 +-- examples/hooks.php | 3 +- examples/promise.php | 4 +-- lib/API.php | 53 ++++++++++++----------------- lib/API/Call.php | 4 +-- lib/API/Call/Deferred.php | 5 ++- lib/API/Consumer.php | 50 +++++++++++++-------------- lib/API/Future.php | 34 +++++++++++++------ lib/API/Future/functions.php | 66 +++++++++++++++++++++++++----------- lib/API/Links/functions.php | 16 ++++----- tests/APITest.php | 5 ++- tests/bootstrap.php | 30 ++++++++++++---- 14 files changed, 166 insertions(+), 124 deletions(-) diff --git a/composer.json b/composer.json index 49a40af..ad06cbf 100644 --- a/composer.json +++ b/composer.json @@ -29,14 +29,12 @@ "seebz/uri-template": "dev-master", "psr/log": "^1.0", "psr/cache": "^1.0", - "psr/simple-cache": "^1.0", - "async-interop/promise": "^0.4.0" + "psr/simple-cache": "^1.0" }, "require-dev": { - "react/promise": "dev-async-interop", - "amphp/amp": "dev-master", - "amphp/loop": "dev-master", - "monolog/monolog": "^1.19", + "amphp/amp": "^2", + "react/promise": "^2", + "monolog/monolog": "^1", "phpunit/phpunit": "^5" } } diff --git a/examples/cli.php b/examples/cli.php index fc72130..caae47c 100755 --- a/examples/cli.php +++ b/examples/cli.php @@ -8,18 +8,18 @@ $api = new seekat\API(seekat\API\Future\react(), [ ]); array_shift($argv); -($self = function($error, $api) use(&$self) { +($self = function($api) use(&$self) { global $argv; while (null !== ($arg = array_shift($argv))) { if ("." === $arg) { - $api->when($self); + $api->then($self); return; } $api = $api->$arg; } echo $api, "\n"; -})(null, $api); +})($api); $api->send(); diff --git a/examples/generator.php b/examples/generator.php index 1fd81e6..88b69cd 100755 --- a/examples/generator.php +++ b/examples/generator.php @@ -11,7 +11,7 @@ $log->pushHandler((new Monolog\Handler\StreamHandler(STDERR))->setLevel(Monolog\ $cli = new http\Client("curl", "seekat"); -$api = new API(API\Future\react(), [ +$api = new API(API\Future\amp(), [ "Authorization" => "token ".getenv("GITHUB_TOKEN") ], null, $cli, $log); @@ -38,7 +38,7 @@ $api(function($api) { $events = yield $next; } return $count; -})->when(function($error, $count) { +})->onResolve(function($error, $count) { printf("Listed %d events\n", $count); }); diff --git a/examples/hooks.php b/examples/hooks.php index dfcb635..612e6de 100755 --- a/examples/hooks.php +++ b/examples/hooks.php @@ -30,7 +30,8 @@ $api(function() use($api) { foreach ($repos as $repo) { $batch[] = $repo->hooks(); } - foreach (yield $batch as $key => $hooks) { + $result = yield $batch; + foreach ($result as $key => $hooks) { if (!count($hooks)) { continue; } diff --git a/examples/promise.php b/examples/promise.php index cee5c74..e1f5b9d 100755 --- a/examples/promise.php +++ b/examples/promise.php @@ -7,10 +7,10 @@ use seekat\API; $api = new API(API\Future\amp(), API\auth("token", getenv("GITHUB_TOKEN"))); -$api->users->m6w6->gists()->when(function($error, $gists) { +$api->users->m6w6->gists()->onResolve(function($error, $gists) { $error and die($error); foreach ($gists as $gist) { - $gist->commits()->when(function($error, $commits) use($gist) { + $gist->commits()->onResolve(function($error, $commits) use($gist) { $error and die($error); foreach ($commits as $i => $commit) { if (!$i) { diff --git a/lib/API.php b/lib/API.php index a047412..6dc12b4 100644 --- a/lib/API.php +++ b/lib/API.php @@ -2,7 +2,6 @@ namespace seekat; -use AsyncInterop\Promise; use Countable; use Generator; use http\{ @@ -125,31 +124,23 @@ class API implements IteratorAggregate, Countable { * * @param string $method The API's "path" element to ascend into * @param array $args Array of arguments forwarded to \seekat\API::get() - * @return Promise + * @return mixed promise */ - function __call(string $method, array $args) : Promise { + function __call(string $method, array $args) { /* We cannot implement an explicit then() method, * because the Promise implementation might think * we're actually implementing Thenable, * which might cause an infinite loop. - * FIXXME: then/when */ - if ($method === "when") { - $promise = $this->get(); - $promise->when(...$args); - return $promise; - } - + if ($method === "then" /* * very short-hand version: - * ->users->m6w6->gists->get()->when(...) + * ->users->m6w6->gists->get()->then(...) * vs: * ->users->m6w6->gists(...) */ - if (is_callable(current($args))) { - $promise = $this->get(); - $promise->when(current($args)); - return $promise; + || is_callable(current($args))) { + return $this->future->handlePromise($this->get(), ...$args); } return (new Call($this, $method))($args); @@ -159,10 +150,10 @@ class API implements IteratorAggregate, Countable { * Run the send loop through a generator * * @param callable|Generator $cbg A \Generator or a factory of a \Generator yielding promises - * @return Promise The promise of the generator's return value + * @return mixed The promise of the generator's return value * @throws InvalidArgumentException */ - function __invoke($cbg) : Promise { + function __invoke($cbg) { $this->logger->debug(__FUNCTION__); $consumer = new Consumer($this->getFuture(), function() { @@ -362,9 +353,9 @@ class API implements IteratorAggregate, Countable { * * @param mixed $args The HTTP query string parameters * @param array $headers The request's additional HTTP headers - * @return Promise + * @return mixed promise */ - function head($args = null, array $headers = null, $cache = null) : Promise { + function head($args = null, array $headers = null, $cache = null) { return $this->request("HEAD", $args, null, $headers, $cache); } @@ -373,9 +364,9 @@ class API implements IteratorAggregate, Countable { * * @param mixed $args The HTTP query string parameters * @param array $headers The request's additional HTTP headers - * @return Promise + * @return mixed promise */ - function get($args = null, array $headers = null, $cache = null) : Promise { + function get($args = null, array $headers = null, $cache = null) { return $this->request("GET", $args, null, $headers, $cache); } @@ -384,9 +375,9 @@ class API implements IteratorAggregate, Countable { * * @param mixed $args The HTTP query string parameters * @param array $headers The request's additional HTTP headers - * @return Promise + * @return mixed promise */ - function delete($args = null, array $headers = null) : Promise { + function delete($args = null, array $headers = null) { return $this->request("DELETE", $args, null, $headers); } @@ -396,9 +387,9 @@ class API implements IteratorAggregate, Countable { * @param mixed $body The HTTP message's body * @param mixed $args The HTTP query string parameters * @param array $headers The request's additional HTTP headers - * @return Promise + * @return mixed promise */ - function post($body = null, $args = null, array $headers = null) : Promise { + function post($body = null, $args = null, array $headers = null) { return $this->request("POST", $args, $body, $headers); } @@ -408,9 +399,9 @@ class API implements IteratorAggregate, Countable { * @param mixed $body The HTTP message's body * @param mixed $args The HTTP query string parameters * @param array $headers The request's additional HTTP headers - * @return Promise + * @return mixed promise */ - function put($body = null, $args = null, array $headers = null) : Promise { + function put($body = null, $args = null, array $headers = null) { return $this->request("PUT", $args, $body, $headers); } @@ -420,9 +411,9 @@ class API implements IteratorAggregate, Countable { * @param mixed $body The HTTP message's body * @param mixed $args The HTTP query string parameters * @param array $headers The request's additional HTTP headers - * @return Promise + * @return mixed promise */ - function patch($body = null, $args = null, array $headers = null) : Promise { + function patch($body = null, $args = null, array $headers = null) { return $this->request("PATCH", $args, $body, $headers); } @@ -477,9 +468,9 @@ class API implements IteratorAggregate, Countable { * @param mixed $body Thee HTTP message's body * @param array $headers The request's additional HTTP headers * @param Call\Cache\Service $cache - * @return Promise + * @return mixed promise */ - private function request(string $method, $args = null, $body = null, array $headers = null, Call\Cache\Service $cache = null) : Promise { + private function request(string $method, $args = null, $body = null, array $headers = null, Call\Cache\Service $cache = null) { if (isset($this->data)) { $this->logger->debug("request -> resolve", [ "method" => $method, diff --git a/lib/API/Call.php b/lib/API/Call.php index 7eba2a8..8b0f62f 100644 --- a/lib/API/Call.php +++ b/lib/API/Call.php @@ -2,10 +2,8 @@ namespace seekat\API; -use AsyncInterop\Promise; use http\Url; use seekat\API; -use seekat\Exception; final class Call { @@ -24,7 +22,7 @@ final class Call $this->call = $call; } - function __invoke(array $args) : Promise { + function __invoke(array $args) { if ($this->api->exists($this->call."_url", $url)) { $url = new Url(uri_template($url, (array)current($args))); $promise = $this->api->withUrl($url)->get(...$args); diff --git a/lib/API/Call/Deferred.php b/lib/API/Call/Deferred.php index 562978c..304ec36 100644 --- a/lib/API/Call/Deferred.php +++ b/lib/API/Call/Deferred.php @@ -2,7 +2,6 @@ namespace seekat\API\Call; -use AsyncInterop\Promise; use http\{ Client, Client\Request, Client\Response }; @@ -52,7 +51,7 @@ final class Deferred private $response; /** - * @var Promise + * @var mixed */ private $promise; @@ -100,7 +99,7 @@ final class Deferred $this->reject = API\Future\rejecter($future, $context); } - function __invoke() : Promise { + function __invoke() { if ($this->cache->load($this->request, $cached)) { $this->logger->info("deferred -> cached", [ "method" => $this->request->getRequestMethod(), diff --git a/lib/API/Consumer.php b/lib/API/Consumer.php index d593588..926eb73 100644 --- a/lib/API/Consumer.php +++ b/lib/API/Consumer.php @@ -2,7 +2,6 @@ namespace seekat\API; -use AsyncInterop\Promise; use Generator; use http\Client; use seekat\API; @@ -31,9 +30,14 @@ final class Consumer private $cancelled = false; /** - * @var Promise + * @var Future */ - private $promise; + private $future; + + /** + * @var mixed + */ + private $context; /** * @var \Closure @@ -58,22 +62,22 @@ final class Consumer function __construct(Future $future, callable $loop) { $this->loop = $loop; - $context = $future->createContext(function() { + $this->future = $future; + $this->context = $future->createContext(function() { $this->cancelled = true; }); - $this->promise = $future->getPromise($context); - $this->resolve = API\Future\resolver($future, $context); - $this->reject = API\Future\rejecter($future, $context); - $this->reduce = API\Future\reducer($future, $context); + $this->resolve = API\Future\resolver($future, $this->context); + $this->reject = API\Future\rejecter($future, $this->context); + $this->reduce = API\Future\reducer($future, $this->context); } /** * Iterate over $gen, a \Generator yielding promises * * @param Generator $gen - * @return Promise + * @return mixed promise */ - function __invoke(Generator $gen) : Promise { + function __invoke(Generator $gen) { $this->cancelled = false; foreach ($gen as $promise) { @@ -94,13 +98,13 @@ final class Consumer ($this->reject)("Cancelled"); } - return $this->promise; + return $this->context->promise(); } /** * Promise handler * - * @param array|Promise $promise + * @param mixed $promise * @param Generator $gen */ private function give($promise, Generator $gen) { @@ -110,19 +114,15 @@ final class Consumer if (is_array($promise)) { $promise = ($this->reduce)($promise); } - if ($promise instanceof Promise) { - $promise->when(function($error, $result) use($gen) { - if ($error) { - $gen->throw(exception($error)); - } - if (($promise = $gen->send($result))) { - $this->give($promise, $gen); - } - }); - } else { - $gen->throw(new UnexpectedValueException( - "Expected Promise or array of Promises; got ".\seekat\typeof($promise))); - } + + $this->future->handlePromise($promise, function($result) use($gen) { + if (($promise = $gen->send($result))) { + $this->give($promise, $gen); + } + }, function($error) use($gen) { + $gen->throw(exception($error)); + }); + /* FIXME: external loop */ ($this->loop)(); } diff --git a/lib/API/Future.php b/lib/API/Future.php index 2b53ea1..6032e02 100644 --- a/lib/API/Future.php +++ b/lib/API/Future.php @@ -2,8 +2,6 @@ namespace seekat\API; -use AsyncInterop\Promise; - interface Future { /** @@ -14,34 +12,48 @@ interface Future /** * @param object $context Promisor - * @return Promise + * @return mixed promise + */ + function getPromise($context); + + /** + * @param mixed $promise + * @return bool */ - function getPromise($context) : Promise; + function isPromise($promise) : bool; /** - * @param Promise $promise + * @param mixed $promise * @return bool */ - function cancelPromise(Promise $promise) : bool; + function cancelPromise($promise) : bool; + + /** + * @param mixed $promise + * @param callable|null $onResult + * @param callable|null $onError + * @return mixed promise + */ + function handlePromise($promise, callable $onResult = null, callable $onError = null); /** * @param object $context Promisor returned by createContext * @param mixed $value * @return void */ - function onSuccess($context, $value); + function resolve($context, $value); /** - * @param object $context Proisor returned by createContext + * @param object $context Promisor returned by createContext * @param mixed $reason * @return void */ - function onFailure($context, $reason); + function reject($context, $reason); /** * @param object $context Promisor returned by createContext * @param array $promises - * @return Promise + * @return mixed promise */ - function onMultiple($context, array $promises) : Promise; + function all($context, array $promises); } diff --git a/lib/API/Future/functions.php b/lib/API/Future/functions.php index b6e8908..b955912 100644 --- a/lib/API/Future/functions.php +++ b/lib/API/Future/functions.php @@ -3,29 +3,30 @@ namespace seekat\API\Future; use Amp\Deferred as AmpDeferred; -use AsyncInterop\Promise; +use Amp\Promise as AmpPromise; use React\Promise\Deferred as ReactDeferred; +use React\Promise\PromiseInterface as ReactPromise; use seekat\API\Future; /** * @param Future $future * @param mixed $value - * @return Promise + * @return mixed promise */ function resolve(Future $future, $value) { $promisor = $future->createContext(); - $future->onSuccess($promisor, $value); + $future->resolve($promisor, $value); return $future->getPromise($promisor); } /** * @param Future $future * @param mixed $reason - * @return Promise + * @return mixed promise */ function reject(Future $future, $reason) { $promisor = $future->createContext(); - $future->onFailure($promisor, $reason); + $future->reject($promisor, $reason); return $future->getPromise($promisor); } @@ -36,7 +37,7 @@ function reject(Future $future, $reason) { */ function resolver(Future $future, $context) { return function($value) use($future, $context) { - return $future->onSuccess($context, $value); + return $future->resolve($context, $value); }; } @@ -47,7 +48,7 @@ function resolver(Future $future, $context) { */ function rejecter(Future $future, $context) { return function($reason) use($future, $context) { - return $future->onFailure($context, $reason); + return $future->reject($context, $reason); }; } @@ -57,8 +58,8 @@ function rejecter(Future $future, $context) { * @return \Closure */ function reducer(Future $future, $context) { - return function(array $promises) use($future, $context) : Promise { - return $future->onMultiple($context, $promises); + return function(array $promises) use($future, $context) { + return $future->all($context, $promises); }; } @@ -75,28 +76,36 @@ function react() { return new ReactDeferred($onCancel); } - function getPromise($context) : Promise { + function getPromise($context) { /* @var $context ReactDeferred */ return $context->promise(); } - function cancelPromise(Promise $promise) : bool { + function isPromise($promise) : bool { + return $promise instanceof ReactPromise; + } + + function handlePromise($promise, callable $onResult = null, callable $onError = null) { + return $promise->then($onResult, $onError); + } + + function cancelPromise($promise) : bool { /* @var $promise \React\Promise\Promise */ $promise->cancel(); return true; } - function onSuccess($context, $value) { + function resolve($context, $value) { /* @var $context ReactDeferred */ $context->resolve($value); } - function onFailure($context, $reason) { + function reject($context, $reason) { /* @var $context ReactDeferred */ $context->reject($reason); } - function onMultiple($context, array $promises) : Promise { + function all($context, array $promises) { return \React\Promise\all($promises); } }; @@ -114,26 +123,45 @@ function amp() { return new AmpDeferred(); } - function getPromise($context) : Promise { + function getPromise($context) { /* @var $context AmpDeferred */ return $context->promise(); } - function cancelPromise(Promise $promise) : bool { + function isPromise($promise) : bool { + return $promise instanceof AmpPromise; + } + + function handlePromise($promise, callable $onResult = null, callable $onError = null) { + $promise->onResolve(function($error = null, $result = null) use($onResult, $onError) { + if ($error) { + if ($onError) { + $onError($error); + } + } else { + if ($onResult) { + $onResult($result); + } + } + }); + return $promise; + } + + function cancelPromise($promise) : bool { return false; } - function onSuccess($context, $value) { + function resolve($context, $value) { /* @var $context AmpDeferred */ $context->resolve($value); } - function onFailure($context, $reason) { + function reject($context, $reason) { /* @var $context AmpDeferred */ $context->fail(\seekat\Exception\exception($reason)); } - function onMultiple($context, array $promises) : Promise { + function all($context, array $promises) { return \Amp\all($promises); } }; diff --git a/lib/API/Links/functions.php b/lib/API/Links/functions.php index f740851..f2b87d7 100644 --- a/lib/API/Links/functions.php +++ b/lib/API/Links/functions.php @@ -10,9 +10,9 @@ use seekat\API\Future; /** * Perform a GET request against the link's "first" relation * - * @return Promise + * @return mixed promise */ -function first(API $api, Cache\Service $cache = null) : Promise { +function first(API $api, Cache\Service $cache = null) { $links = $api->getLinks(); if ($links && ($first = $links->getFirst())) { return $api->withUrl($first)->get(null, null, $cache); @@ -23,9 +23,9 @@ function first(API $api, Cache\Service $cache = null) : Promise { /** * Perform a GET request against the link's "prev" relation * - * @return Promise + * @return mixed promise */ -function prev(API $api, Cache\Service $cache = null) : Promise { +function prev(API $api, Cache\Service $cache = null) { $links = $api->getLinks(); if ($links && ($prev = $links->getPrev())) { return $api->withUrl($prev)->get(null, null, $cache); @@ -36,9 +36,9 @@ function prev(API $api, Cache\Service $cache = null) : Promise { /** * Perform a GET request against the link's "next" relation * - * @return Promise + * @return mixed promise */ -function next(API $api, Cache\Service $cache = null) : Promise { +function next(API $api, Cache\Service $cache = null) { $links = $api->getLinks(); if ($links && ($next = $links->getNext())) { return $api->withUrl($next)->get(null, null, $cache); @@ -49,9 +49,9 @@ function next(API $api, Cache\Service $cache = null) : Promise { /** * Perform a GET request against the link's "last" relation * - * @return Promise + * @return mixed promise */ -function last(API $api, Cache\Service $cache = null) : Promise { +function last(API $api, Cache\Service $cache = null) { $links = $api->getLinks(); if ($links && ($last = $links->getLast())) { return $api->withUrl($last)->get(null, null, $cache); diff --git a/tests/APITest.php b/tests/APITest.php index 9f17af6..8b1067f 100644 --- a/tests/APITest.php +++ b/tests/APITest.php @@ -1,6 +1,5 @@ assertInstanceOf(Promise::class, $api->baz()); + $this->assertTrue($api->getFuture()->isPromise($api->baz())); } /** @@ -58,7 +57,7 @@ class APITest extends BaseTest * @dataProvider provideHttpMethodAndAPI */ function testReturnsPromiseOnMethodCallForStandardHttpMethod($api, $method) { - $this->assertInstanceOf(Promise::class, $api->$method()); + $this->assertTrue($api->getFuture()->isPromise($api->$method())); } /** diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 3d502be..f25f4ad 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -62,11 +62,27 @@ class BaseTest extends \PHPUnit\Framework\TestCase trait ConsumePromise { - function consumePromise(\AsyncInterop\Promise $p, &$errors, &$results) { - $p->when(function($error, $result) use(&$errors, &$results) { - if ($error) $errors[] = $error; - if ($result) $results[] = $result; - }); + function consumePromise($p, &$errors, &$results) { + if (method_exists($p, "done")) { + $p->then(function($result) use(&$results) { + if (isset($result)) { + $results[] = $result; + } + }, function($error) use (&$errors) { + if (isset($error)) { + $errors[] = $error; + } + }); + } else { + $p->onResolve(function($error, $result) use(&$errors, &$results) { + if (isset($error)) { + $errors[] = $error; + } + if (isset($result)) { + $results[] = $result; + } + }); + } } } @@ -93,11 +109,11 @@ trait AssertSuccess trait AssertCancelled { - function assertCancelled(\AsyncInterop\Promise $promise) { + function assertCancelled($promise) { $this->consumePromise($promise, $errors, $results); $this->assertEmpty($results); - $this->assertStringMatchesFormat("%SCancelled%S", $errors[0]->getMessage()); + $this->assertStringMatchesFormat("%SCancelled%S", \seekat\Exception\message($errors[0])); } } -- 2.30.2