X-Git-Url: https://git.m6w6.name/?a=blobdiff_plain;f=lib%2FAPI.php;h=6dc12b4a1c32422cd897f69fe82618d9174b4623;hb=d38b3ae03472ba2f9af5009778574b23472bb3f7;hp=15b38302c23015c78170667099873d89bce4b44f;hpb=2451d97f1cb7b97e445b4dd839835b8673a4d0fc;p=m6w6%2Fseekat diff --git a/lib/API.php b/lib/API.php index 15b3830..6dc12b4 100644 --- a/lib/API.php +++ b/lib/API.php @@ -11,12 +11,10 @@ use IteratorAggregate; use Psr\Log\{ LoggerInterface, NullLogger }; -use React\Promise\{ - ExtendedPromiseInterface, function resolve -}; -use seekat\{ - API\Call, API\Consumer, API\ContentType, API\Iterator, API\Links, Exception\InvalidArgumentException +use seekat\API\{ + Call, Consumer, ContentType, Future, Iterator, Links }; +use seekat\Exception\InvalidArgumentException; class API implements IteratorAggregate, Countable { /** @@ -25,30 +23,18 @@ class API implements IteratorAggregate, Countable { */ private $url; - /** - * Logger - * @var LoggerInterface - */ - private $logger; - - /** - * Cache - * @var Call\Cache\Service - */ - private $cache; - - /** - * The HTTP client - * @var Client - */ - private $client; - /** * Default headers to send out to the API endpoint * @var array */ private $headers; + /** + * Current endpoints links + * @var Links + */ + private $links; + /** * Current endpoint data's Content-Type * @var API\ContentType @@ -62,20 +48,41 @@ class API implements IteratorAggregate, Countable { private $data; /** - * Current endpoints links - * @var Links + * Logger + * @var LoggerInterface */ - private $links; + private $logger; + + /** + * Cache + * @var Call\Cache\Service + */ + private $cache; + + /** + * Promisor + * @var Future + */ + private $future; + + /** + * The HTTP client + * @var Client + */ + private $client; /** * Create a new API endpoint root * + * @param Future $future pretending to fulfill promises * @param array $headers Standard request headers, defaults to ["Accept" => "application/vnd.github.v3+json"] * @param Url $url The API's endpoint, defaults to https://api.github.com * @param Client $client The HTTP client to use for executing requests * @param LoggerInterface $log A logger + * @param Call\Cache\Service $cache A cache */ - function __construct(array $headers = null, Url $url = null, Client $client = null, LoggerInterface $log = null, Call\Cache\Service $cache = null) { + function __construct(Future $future, array $headers = null, Url $url = null, Client $client = null, LoggerInterface $log = null, Call\Cache\Service $cache = null) { + $this->future = $future; $this->cache = $cache; $this->logger = $log ?? new NullLogger; $this->url = $url ?? new Url("https://api.github.com"); @@ -102,7 +109,7 @@ class API implements IteratorAggregate, Countable { $this->exists($seg, $that->data); } - $this->logger->debug(__FUNCTION__."($seg)", [ + $this->logger->debug("get($seg)", [ "url" => [ (string) $this->url, (string) $that->url @@ -117,26 +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 ExtendedPromiseInterface + * @return mixed promise */ - function __call(string $method, array $args) : ExtendedPromiseInterface { + 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. */ - if ($method === "then") { - return $this->get()->then(...$args); - } - + if ($method === "then" /* * very short-hand version: * ->users->m6w6->gists->get()->then(...) * vs: * ->users->m6w6->gists(...) */ - if (is_callable(current($args))) { - return $this->api->get()->then(current($args)); + || is_callable(current($args))) { + return $this->future->handlePromise($this->get(), ...$args); } return (new Call($this, $method))($args); @@ -146,13 +150,15 @@ 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 ExtendedPromiseInterface The promise of the generator's return value + * @return mixed The promise of the generator's return value * @throws InvalidArgumentException */ - function __invoke($cbg) : ExtendedPromiseInterface { + function __invoke($cbg) { $this->logger->debug(__FUNCTION__); - $consumer = new Consumer($this->client); + $consumer = new Consumer($this->getFuture(), function() { + $this->client->send(); + }); invoke: if ($cbg instanceof Generator) { @@ -164,12 +170,8 @@ class API implements IteratorAggregate, Countable { goto invoke; } - throw InvalidArgumentException( - "Expected callable or Generator, got ".( - is_object($cbg) - ? "instance of ".get_class($cbg) - : gettype($cbg).": ".var_export($cbg, true) - ) + throw new InvalidArgumentException( + "Expected callable or Generator, got ".typeof($cbg, true) ); } @@ -226,6 +228,13 @@ class API implements IteratorAggregate, Countable { return $this->logger; } + /** + * @return Future + */ + function getFuture() { + return $this->future; + } + /** * @return Client */ @@ -257,7 +266,7 @@ class API implements IteratorAggregate, Countable { function export() : array { $data = $this->data; $url = clone $this->url; - $type = clone $this->type; + $type = $this->type ? clone $this->type : null; $links = $this->links ? clone $this->links : null; $headers = $this->headers; return compact("url", "data", "type", "links", "headers"); @@ -339,14 +348,25 @@ class API implements IteratorAggregate, Countable { return $that; } + /** + * Perform a HEAD request against the endpoint's underlying URL + * + * @param mixed $args The HTTP query string parameters + * @param array $headers The request's additional HTTP headers + * @return mixed promise + */ + function head($args = null, array $headers = null, $cache = null) { + return $this->request("HEAD", $args, null, $headers, $cache); + } + /** * Perform a GET request against the endpoint's underlying URL * * @param mixed $args The HTTP query string parameters * @param array $headers The request's additional HTTP headers - * @return ExtendedPromiseInterface + * @return mixed promise */ - function get($args = null, array $headers = null, $cache = null) : ExtendedPromiseInterface { + function get($args = null, array $headers = null, $cache = null) { return $this->request("GET", $args, null, $headers, $cache); } @@ -355,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 ExtendedPromiseInterface + * @return mixed promise */ - function delete($args = null, array $headers = null) : ExtendedPromiseInterface { + function delete($args = null, array $headers = null) { return $this->request("DELETE", $args, null, $headers); } @@ -367,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 ExtendedPromiseInterface + * @return mixed promise */ - function post($body = null, $args = null, array $headers = null) : ExtendedPromiseInterface { + function post($body = null, $args = null, array $headers = null) { return $this->request("POST", $args, $body, $headers); } @@ -379,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 ExtendedPromiseInterface + * @return mixed promise */ - function put($body = null, $args = null, array $headers = null) : ExtendedPromiseInterface { + function put($body = null, $args = null, array $headers = null) { return $this->request("PUT", $args, $body, $headers); } @@ -391,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 ExtendedPromiseInterface + * @return mixed promise */ - function patch($body = null, $args = null, array $headers = null) : ExtendedPromiseInterface { + function patch($body = null, $args = null, array $headers = null) { return $this->request("PATCH", $args, $body, $headers); } @@ -403,11 +423,11 @@ class API implements IteratorAggregate, Countable { * @return API self */ function send() : API { - $this->logger->debug(__FUNCTION__.": start loop"); + $this->logger->debug("send: start loop"); while (count($this->client)) { $this->client->send(); } - $this->logger->debug(__FUNCTION__.": end loop"); + $this->logger->debug("send: end loop"); return $this; } @@ -430,14 +450,8 @@ class API implements IteratorAggregate, Countable { $exists = false; } - $this->logger->debug(__FUNCTION__."($seg) in ".( - is_object($this->data) - ? get_class($this->data) - : gettype($this->data) - )." -> ".( - $exists - ? "true" - : "false" + $this->logger->debug(sprintf("exists(%s) in %s -> %s", + $seg, typeof($this->data, false), $exists ? "true" : "false" ), [ "url" => (string) $this->url, "val" => $val, @@ -454,19 +468,19 @@ 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 ExtendedPromiseInterface + * @return mixed promise */ - private function request(string $method, $args = null, $body = null, array $headers = null, Call\Cache\Service $cache = null) : ExtendedPromiseInterface { + 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, - "url" => (string)$this->url, + "url" => (string) $this->url, "args" => $args, "body" => $body, "headers" => $headers, ]); - return resolve($this); + return Future\resolve($this->future, $this); } $url = $this->url->mod(["query" => new QueryString($args)]); @@ -484,6 +498,6 @@ class API implements IteratorAggregate, Countable { "headers" => $headers, ]); - return (new Call\Deferred($this, $request, $cache ?: $this->cache))->promise(); + return (new Call\Deferred($this, $request, $cache ?: $this->cache))(); } }