drop async-interop
authorMichael Wallner <mike@php.net>
Tue, 19 Sep 2017 15:57:37 +0000 (17:57 +0200)
committerMichael Wallner <mike@php.net>
Tue, 19 Sep 2017 16:29:42 +0000 (18:29 +0200)
14 files changed:
composer.json
examples/cli.php
examples/generator.php
examples/hooks.php
examples/promise.php
lib/API.php
lib/API/Call.php
lib/API/Call/Deferred.php
lib/API/Consumer.php
lib/API/Future.php
lib/API/Future/functions.php
lib/API/Links/functions.php
tests/APITest.php
tests/bootstrap.php

index 49a40af8bb650a10ef5bf75ec50d543336ec9dba..ad06cbf463ea00bf92e492430a40617ad3233aec 100644 (file)
         "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"
     }
 }
index fc72130200e41c390d9342d50e84057ca98bba01..caae47c17d0c802b468a60bf5cb15f4b00364556 100755 (executable)
@@ -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();
index 1fd81e60e1edf0296e241bf9556a7101ca26c28a..88b69cd004c9eddc6bd2e1b84520f0cfec7def8f 100755 (executable)
@@ -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);
 });
 
index dfcb6359d51d19bf154570cb8e761ab90cae90a8..612e6de75dbfc014f7ac97daaf11a7152896e50a 100755 (executable)
@@ -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;
                        }
index cee5c74a9652bb2d66392a8f3d6f6e3afc3845fe..e1f5b9d4fbd4a3311e322fe2706d6ffa11423928 100755 (executable)
@@ -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) {
index a0474122cd78e67b0d24df24c72ced3032e5e7ce..6dc12b4a1c32422cd897f69fe82618d9174b4623 100644 (file)
@@ -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,
index 7eba2a80b7fbfb76f6b7748c728c06797410c357..8b0f62fd726d67ca30f4c6dcbacfc57469d55d41 100644 (file)
@@ -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);
index 562978c79ba44ad012f9a4c795576983d9039e5e..304ec363bf1d7acdbf59ecf80b2e418c12b217a8 100644 (file)
@@ -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(),
index d593588aee7883c47302b9c4b03c953793e7a929..926eb736e756f066d61d9b7db1b497dfa0b18a4e 100644 (file)
@@ -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)();
        }
index 2b53ea1cf847c827be0838eb5320f54557f1694f..6032e028d42b55117661b3c6c42410965812231c 100644 (file)
@@ -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);
 }
index b6e890886aa511a8b8152fd9f308dbc7c1d03087..b9559128da8d7df09be7df6a9a12cc4757cbedec 100644 (file)
@@ -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);
                }
        };
index f740851e9de6bc00131750539a63751142a89763..f2b87d78123796f487dcc08746abbf453631aa3e 100644 (file)
@@ -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);
index 9f17af6c5dbd25e5a53a9582bc4752c0544e400f..8b1067ff1d8ddb296d0469979730072775ca8728 100644 (file)
@@ -1,6 +1,5 @@
 <?php
 
-use AsyncInterop\Promise;
 use seekat\API;
 
 class APITest extends BaseTest
@@ -44,7 +43,7 @@ class APITest extends BaseTest
         * @dataProvider provideAPI
         */
        function testReturnsPromiseOnMethodCall($api) {
-               $this->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()));
        }
 
        /**
index 3d502bee969efba1bc8d3d59b32cd21d6a66891d..f25f4ade6f78aa9768691a1fef2300d0ad761950 100644 (file)
@@ -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]));
        }
 }