| 
<?phpnamespace GuzzleHttp;
 
 use GuzzleHttp\Promise\PromiseInterface;
 use GuzzleHttp\Promise\RejectedPromise;
 use GuzzleHttp\Psr7;
 use Psr\Http\Message\RequestInterface;
 
 /**
 * Middleware that retries requests based on the boolean result of
 * invoking the provided "decider" function.
 */
 class RetryMiddleware
 {
 /** @var callable  */
 private $nextHandler;
 
 /** @var callable */
 private $decider;
 
 /**
 * @param callable $decider     Function that accepts the number of retries,
 *                              a request, [response], and [exception] and
 *                              returns true if the request is to be
 *                              retried.
 * @param callable $nextHandler Next handler to invoke.
 * @param callable $delay       Function that accepts the number of retries
 *                              and returns the number of milliseconds to
 *                              delay.
 */
 public function __construct(
 callable $decider,
 callable $nextHandler,
 callable $delay = null
 ) {
 $this->decider = $decider;
 $this->nextHandler = $nextHandler;
 $this->delay = $delay ?: __CLASS__ . '::exponentialDelay';
 }
 
 /**
 * Default exponential backoff delay function.
 *
 * @param $retries
 *
 * @return int
 */
 public static function exponentialDelay($retries)
 {
 return (int) pow(2, $retries - 1);
 }
 
 /**
 * @param RequestInterface $request
 * @param array            $options
 *
 * @return PromiseInterface
 */
 public function __invoke(RequestInterface $request, array $options)
 {
 if (!isset($options['retries'])) {
 $options['retries'] = 0;
 }
 
 $fn = $this->nextHandler;
 return $fn($request, $options)
 ->then(
 $this->onFulfilled($request, $options),
 $this->onRejected($request, $options)
 );
 }
 
 private function onFulfilled(RequestInterface $req, array $options)
 {
 return function ($value) use ($req, $options) {
 if (!call_user_func(
 $this->decider,
 $options['retries'],
 $req,
 $value,
 null
 )) {
 return $value;
 }
 return $this->doRetry($req, $options);
 };
 }
 
 private function onRejected(RequestInterface $req, array $options)
 {
 return function ($reason) use ($req, $options) {
 if (!call_user_func(
 $this->decider,
 $options['retries'],
 $req,
 null,
 $reason
 )) {
 return new RejectedPromise($reason);
 }
 return $this->doRetry($req, $options);
 };
 }
 
 private function doRetry(RequestInterface $request, array $options)
 {
 $options['delay'] = call_user_func($this->delay, ++$options['retries']);
 
 return $this($request, $options);
 }
 }
 
 |