| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- <?php
- declare(strict_types=1);
- namespace AsyncAws\Core;
- use AsyncAws\Core\Exception\Http\HttpException;
- use AsyncAws\Core\Exception\Http\NetworkException;
- use AsyncAws\Core\Exception\LogicException;
- /**
- * The waiter promise is always returned from every API call to a waiter.
- */
- class Waiter
- {
- public const STATE_SUCCESS = 'success';
- public const STATE_FAILURE = 'failure';
- public const STATE_PENDING = 'pending';
- protected const WAIT_TIMEOUT = 30.0;
- protected const WAIT_DELAY = 5.0;
- /**
- * @var AbstractApi|null
- */
- protected $awsClient;
- /**
- * Input used to build the API request that generate this Waiter.
- *
- * @var object|null
- */
- protected $input;
- /**
- * @var Response
- */
- private $response;
- /**
- * Whether or not a new response should be fetched.
- *
- * @var bool
- */
- private $needRefresh = false;
- /**
- * @var string|null
- */
- private $finalState;
- /**
- * @var bool
- */
- private $resolved = false;
- public function __construct(Response $response, AbstractApi $awsClient, $request)
- {
- $this->response = $response;
- $this->awsClient = $awsClient;
- $this->input = $request;
- }
- public function __destruct()
- {
- if (!$this->resolved) {
- $this->resolve();
- }
- }
- final public function isSuccess(): bool
- {
- return self::STATE_SUCCESS === $this->getState();
- }
- final public function isFailure(): bool
- {
- return self::STATE_FAILURE === $this->getState();
- }
- final public function isPending(): bool
- {
- return self::STATE_PENDING === $this->getState();
- }
- final public function getState(): string
- {
- if (null !== $this->finalState) {
- return $this->finalState;
- }
- if ($this->needRefresh) {
- $this->stealResponse($this->refreshState());
- }
- try {
- $this->response->resolve();
- $exception = null;
- } catch (HttpException $exception) {
- // use $exception later
- } finally {
- $this->resolved = true;
- $this->needRefresh = true;
- }
- $state = $this->extractState($this->response, $exception);
- switch ($state) {
- case self::STATE_SUCCESS:
- case self::STATE_FAILURE:
- $this->finalState = $state;
- break;
- case self::STATE_PENDING:
- break;
- default:
- throw new LogicException(sprintf('Unexpected state "%s" from Waiter "%s".', $state, __CLASS__));
- }
- return $state;
- }
- /**
- * Make sure the actual request is executed.
- *
- * @param float|null $timeout Duration in seconds before aborting. When null wait until the end of execution.
- *
- * @return bool false on timeout. True if the response has returned with as status code.
- *
- * @throws NetworkException
- */
- final public function resolve(?float $timeout = null): bool
- {
- try {
- return $this->response->resolve($timeout);
- } catch (HttpException $exception) {
- return true;
- } finally {
- $this->resolved = true;
- }
- }
- /**
- * Returns info on the current request.
- *
- * @return array{
- * resolved: bool,
- * body_downloaded: bool,
- * response: \Symfony\Contracts\HttpClient\ResponseInterface,
- * status: int,
- * }
- */
- final public function info(): array
- {
- return $this->response->info();
- }
- final public function cancel(): void
- {
- $this->response->cancel();
- $this->needRefresh = true;
- $this->resolved = true;
- }
- /**
- * Wait until the state is success.
- * Stopped when the state become Failure or the defined timeout is reached.
- *
- * @param float $timeout Duration in seconds before aborting
- * @param float $delay Duration in seconds between each check
- *
- * @return bool true if a final state was reached
- */
- final public function wait(?float $timeout = null, ?float $delay = null): bool
- {
- if (null !== $this->finalState) {
- return true;
- }
- $timeout = $timeout ?? static::WAIT_TIMEOUT;
- $delay = $delay ?? static::WAIT_DELAY;
- $start = microtime(true);
- while (true) {
- if ($this->needRefresh) {
- $this->stealResponse($this->refreshState());
- }
- // If request times out
- if (!$this->resolve($timeout - (microtime(true) - $start))) {
- break;
- }
- $this->getState();
- // If we reached a final state
- if ($this->finalState) {
- return true;
- }
- // If the timeout will expire during our sleep, then exit early.
- if ($delay > $timeout - (microtime(true) - $start)) {
- break;
- }
- usleep((int) ceil($delay * 1000000));
- }
- return false;
- }
- protected function extractState(Response $response, ?HttpException $exception): string
- {
- return self::STATE_PENDING;
- }
- protected function refreshState(): Waiter
- {
- return $this;
- }
- private function stealResponse(self $waiter): void
- {
- $this->response = $waiter->response;
- $this->resolved = $waiter->resolved;
- $waiter->resolved = true;
- $this->needRefresh = false;
- }
- }
|