123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- <?php
- namespace Aws;
- use Aws\Exception\AwsException;
- use GuzzleHttp\Promise\Coroutine;
- use GuzzleHttp\Promise\PromiseInterface;
- use GuzzleHttp\Promise\PromisorInterface;
- use GuzzleHttp\Promise\RejectedPromise;
- class Waiter implements PromisorInterface
- {
-
- private $client;
-
- private $name;
-
- private $args;
-
- private $config;
-
- private static $defaults = ['initDelay' => 0, 'before' => null];
-
- private static $required = [
- 'acceptors',
- 'delay',
- 'maxAttempts',
- 'operation',
- ];
-
- public function __construct(
- AwsClientInterface $client,
- $name,
- array $args = [],
- array $config = []
- ) {
- $this->client = $client;
- $this->name = $name;
- $this->args = $args;
-
- $this->config = $config + self::$defaults;
- foreach (self::$required as $key) {
- if (!isset($this->config[$key])) {
- throw new \InvalidArgumentException(
- 'The provided waiter configuration was incomplete.'
- );
- }
- }
- if ($this->config['before'] && !is_callable($this->config['before'])) {
- throw new \InvalidArgumentException(
- 'The provided "before" callback is not callable.'
- );
- }
- }
-
- public function promise(): PromiseInterface
- {
- return Coroutine::of(function () {
- $name = $this->config['operation'];
- for ($state = 'retry', $attempt = 1; $state === 'retry'; $attempt++) {
-
- $args = $this->getArgsForAttempt($attempt);
- $command = $this->client->getCommand($name, $args);
- try {
- if ($this->config['before']) {
- $this->config['before']($command, $attempt);
- }
- $result = (yield $this->client->executeAsync($command));
- } catch (AwsException $e) {
- $result = $e;
- }
-
- $state = $this->determineState($result);
- if ($state === 'success') {
- yield $command;
- } elseif ($state === 'failed') {
- $msg = "The {$this->name} waiter entered a failure state.";
- if ($result instanceof \Exception) {
- $msg .= ' Reason: ' . $result->getMessage();
- }
- yield new RejectedPromise(new \RuntimeException($msg));
- } elseif ($state === 'retry'
- && $attempt >= $this->config['maxAttempts']
- ) {
- $state = 'failed';
- yield new RejectedPromise(new \RuntimeException(
- "The {$this->name} waiter failed after attempt #{$attempt}."
- ));
- }
- }
- });
- }
-
- private function getArgsForAttempt($attempt)
- {
- $args = $this->args;
-
- $delay = ($attempt === 1)
- ? $this->config['initDelay']
- : $this->config['delay'];
- if (is_callable($delay)) {
- $delay = $delay($attempt);
- }
-
- if (!isset($args['@http'])) {
- $args['@http'] = [];
- }
- $args['@http']['delay'] = $delay * 1000;
- return $args;
- }
-
- private function determineState($result)
- {
- foreach ($this->config['acceptors'] as $acceptor) {
- $matcher = 'matches' . ucfirst($acceptor['matcher']);
- if ($this->{$matcher}($result, $acceptor)) {
- return $acceptor['state'];
- }
- }
- return $result instanceof \Exception ? 'failed' : 'retry';
- }
-
- private function matchesPath($result, array $acceptor)
- {
- return !($result instanceof ResultInterface)
- ? false
- : $acceptor['expected'] == $result->search($acceptor['argument']);
- }
-
- private function matchesPathAll($result, array $acceptor)
- {
- if (!($result instanceof ResultInterface)) {
- return false;
- }
- $actuals = $result->search($acceptor['argument']) ?: [];
- foreach ($actuals as $actual) {
- if ($actual != $acceptor['expected']) {
- return false;
- }
- }
- return true;
- }
-
- private function matchesPathAny($result, array $acceptor)
- {
- if (!($result instanceof ResultInterface)) {
- return false;
- }
- $actuals = $result->search($acceptor['argument']) ?: [];
- return in_array($acceptor['expected'], $actuals);
- }
-
- private function matchesStatus($result, array $acceptor)
- {
- if ($result instanceof ResultInterface) {
- return $acceptor['expected'] == $result['@metadata']['statusCode'];
- }
- if ($result instanceof AwsException && $response = $result->getResponse()) {
- return $acceptor['expected'] == $response->getStatusCode();
- }
- return false;
- }
-
- private function matchesError($result, array $acceptor)
- {
- if ($result instanceof AwsException) {
- return $result->isConnectionError()
- || $result->getAwsErrorCode() == $acceptor['expected'];
- }
- return false;
- }
- }
|