X-Git-Url: https://git.m6w6.name/?a=blobdiff_plain;ds=sidebyside;f=lib%2FAPI%2FCall%2FDeferred.php;h=331cc2f65526aec5943c6f542c0100cc06703593;hb=654d736df2c46ec2520f73e9089d06a44f2b9c50;hp=7fc44de1ba607c50c36e22c4ee9c39fae130b887;hpb=626d8937c75f6d8fca463fa2b374f645068b2d6d;p=m6w6%2Fseekat diff --git a/lib/API/Call/Deferred.php b/lib/API/Call/Deferred.php index 7fc44de..331cc2f 100644 --- a/lib/API/Call/Deferred.php +++ b/lib/API/Call/Deferred.php @@ -2,155 +2,142 @@ namespace seekat\API\Call; -use Exception; -use http\{ - Client, Client\Request, Client\Response -}; +use http\{Client, Client\Request, Client\Response}; +use Psr\Log\LoggerInterface; use seekat\API; -use SplObserver; -use SplSubject; -final class Deferred extends \React\Promise\Deferred implements SplObserver -{ - /** - * The response importer - * - * @var Result - */ - private $result; - - /** - * The HTTP client - * - * @var Client - */ - private $client; +final class Deferred { + private Result $result; + private Client $client; + private LoggerInterface $logger; + private Cache $cache; /** - * Request cache - * - * @var callable + * The promised response */ - private $cache; + private ?Response $response = null; /** - * The executed request - * - * @var Request + * @var mixed */ - private $request; - - /** - * The promised response - * - * @var Response - */ - private $response; + private object $promise; + private \Closure $resolve; + private \Closure $reject; /** * Create a deferred promise for the response of $request * * @param API $api The endpoint of the request * @param Request $request The request to execute - * @param Cache\Service $cache */ - function __construct(API $api, Request $request, Cache\Service $cache = null) { - parent::__construct(function ($resolve, $reject) { - return $this->cancel($resolve, $reject); - }); - - $this->request = $request; - $this->client = $api->getClient(); + function __construct(API $api, private readonly Request $request) { $this->result = new Result($api); - $this->cache = new Cache($cache); - - if ($this->cache->load($this->request, $cached)) { - $api->getLogger()->info("deferred -> cached", [ - "method" => $request->getRequestMethod(), - "url" => $request->getRequestUrl(), - ]); + $this->client = $api->getClient(); + $this->logger = $api->getLogger(); + $this->cache = new Cache($this->logger, $api->getCache()); + + $future = $api->getFuture(); + $context = $future->createContext(function() { + if ($this->response) { + /* we did finish in the meantime */ + $this->complete(); + } else { + $this->client->dequeue($this->request); + ($this->reject)("Cancelled"); + } + }); + $this->promise = $future->getPromise($context); + $this->resolve = $future->resolver($context); + $this->reject = $future->rejecter($context); + } - $this->response = $cached; - $this->complete( - [$this, "resolve"], - [$this, "reject"] - ); - } else { - $this->client->attach($this); - $this->client->enqueue($this->request, function(Response $response) use($cached) { - if ($response->getResponseCode() == 304) { - $this->response = $cached; - } else { - $this->response = $response; - } - $this->complete( - [$this, "resolve"], - [$this, "reject"] - ); - return true; - }); - $api->getLogger()->info("deferred -> enqueued", [ - "method" => $request->getRequestMethod(), - "url" => $request->getRequestUrl(), - ]); - /* start off */ - $this->client->once(); + function __invoke() { + if (!$this->cached($cached)) { + $this->refresh($cached); } + + return $this->promise; } /** - * Progress observer + * Peek into cache * - * Import the response's data on success and resolve the promise. - * - * @param SplSubject $client The observed HTTP client - * @param Request $request The request which generated the update - * @param object $progress The progress information + * @param Response $cached + * @return bool */ - function update(SplSubject $client, Request $request = null, $progress = null) { - if ($request !== $this->request) { - return; + private function cached(Response &$cached = null) : bool { + $fresh = $this->cache->load($this->request, $cachedResponse); + + if (!$cachedResponse) { + return false; + } else { + $cached = $cachedResponse; + + $this->logger->info("deferred -> cached", [ + "method" => $this->request->getRequestMethod(), + "url" => $this->request->getRequestUrl(), + ]); + + + if (!$fresh) { + $this->logger->info("cached -> stale", [ + "method" => $this->request->getRequestMethod(), + "url" => $this->request->getRequestUrl(), + ]); + return false; + } } - $this->notify((object) compact("client", "request", "progress")); + $this->response = $cached; + $this->complete("cached"); + return true; + } + + private function refresh(Response $cached = null) : void { + $this->client->enqueue($this->request, function(Response $response) use($cached) { + $this->response = $response; + $this->complete(); + return true; + }); + + $this->logger->info(($cached ? "stale" : "deferred") . " -> enqueued", [ + "method" => $this->request->getRequestMethod(), + "url" => $this->request->getRequestUrl(), + ]); + + /* start off */ + $this->client->once(); } /** * Completion callback - * @param callable $resolve - * @param callable $reject */ - private function complete(callable $resolve, callable $reject) { - $this->client->detach($this); + private function complete(string $by = "enqueued") : void { + $this->logger->info("complete -> $by"); if ($this->response) { - try { - $api = ($this->result)($this->response); - - $this->cache->save($this->request, $this->response); + $this->logger->info("$by -> response", [ + "url" => $this->request->getRequestUrl(), + "info" => $this->response->getInfo(), + ]); - $resolve($api); - } catch (Exception $e) { - $reject($e); + try { + $this->cache->update($this->request, $this->response); + ($this->resolve)(($this->result)($this->response)); + } catch (\Throwable $e) { + $this->logger->warning("$by -> cache", ["exception" => $e]); + ($this->reject)($e); } } else { - $reject($this->client->getTransferInfo($this->request)->error); - } - } + $info = $this->client->getTransferInfo($this->request); - /** - * Cancellation callback - * @param callable $resolve - * @param callable $reject - */ - private function cancel(callable $resolve, callable $reject) { - /* did we finish in the meantime? */ - if ($this->response) { - $this->complete($resolve, $reject); - } else { - $this->client->detach($this); - $this->client->dequeue($this->request); - $reject("Cancelled"); + $this->logger->warning("$by -> no response", [ + "url" => $this->request->getRequestUrl(), + "info" => $info + ]); + + ($this->reject)($info->error); } } + }