namespace seekat;
+use AsyncInterop\Promise;
use Countable;
use Generator;
use http\{
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 {
/**
*/
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
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");
$this->exists($seg, $that->data);
}
- $this->logger->debug(__FUNCTION__."($seg)", [
+ $this->logger->debug("get($seg)", [
"url" => [
(string) $this->url,
(string) $that->url
*
* @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 Promise
*/
- function __call(string $method, array $args) : ExtendedPromiseInterface {
+ function __call(string $method, array $args) : Promise {
/* 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 === "then") {
- return $this->get()->then(...$args);
+ if ($method === "when") {
+ $promise = $this->get();
+ $promise->when(...$args);
+ return $promise;
}
/*
* very short-hand version:
- * ->users->m6w6->gists->get()->then(...)
+ * ->users->m6w6->gists->get()->when(...)
* vs:
* ->users->m6w6->gists(...)
*/
if (is_callable(current($args))) {
- return $this->api->get()->then(current($args));
+ $promise = $this->get();
+ $promise->when(current($args));
+ return $promise;
}
return (new Call($this, $method))($args);
* 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 Promise The promise of the generator's return value
* @throws InvalidArgumentException
*/
- function __invoke($cbg) : ExtendedPromiseInterface {
+ function __invoke($cbg) : Promise {
$this->logger->debug(__FUNCTION__);
- $consumer = new Consumer($this->client);
+ $consumer = new Consumer($this->getFuture(), function() {
+ $this->client->send();
+ });
invoke:
if ($cbg instanceof Generator) {
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)
);
}
return $this->logger;
}
+ /**
+ * @return Future
+ */
+ function getFuture() {
+ return $this->future;
+ }
+
/**
* @return Client
*/
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");
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 Promise
+ */
+ function head($args = null, array $headers = null, $cache = null) : Promise {
+ 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 Promise
*/
- function get($args = null, array $headers = null, $cache = null) : ExtendedPromiseInterface {
+ function get($args = null, array $headers = null, $cache = null) : Promise {
return $this->request("GET", $args, null, $headers, $cache);
}
*
* @param mixed $args The HTTP query string parameters
* @param array $headers The request's additional HTTP headers
- * @return ExtendedPromiseInterface
+ * @return Promise
*/
- function delete($args = null, array $headers = null) : ExtendedPromiseInterface {
+ function delete($args = null, array $headers = null) : Promise {
return $this->request("DELETE", $args, null, $headers);
}
* @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 Promise
*/
- function post($body = null, $args = null, array $headers = null) : ExtendedPromiseInterface {
+ function post($body = null, $args = null, array $headers = null) : Promise {
return $this->request("POST", $args, $body, $headers);
}
* @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 Promise
*/
- function put($body = null, $args = null, array $headers = null) : ExtendedPromiseInterface {
+ function put($body = null, $args = null, array $headers = null) : Promise {
return $this->request("PUT", $args, $body, $headers);
}
* @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 Promise
*/
- function patch($body = null, $args = null, array $headers = null) : ExtendedPromiseInterface {
+ function patch($body = null, $args = null, array $headers = null) : Promise {
return $this->request("PATCH", $args, $body, $headers);
}
* @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;
}
$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,
* @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 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) : Promise {
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)]);
"headers" => $headers,
]);
- return (new Call\Deferred($this, $request, $cache ?: $this->cache))->promise();
+ return (new Call\Deferred($this, $request, $cache ?: $this->cache))();
}
}