PluginProbe
Plausible Analytics / trunk
Plausible Analytics vtrunk
2.6.1 2.6.0 trunk 1.0.0 1.0.1 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 All 52 releases
plausible-analytics / src / Client / lib / Lib / GuzzleHttp / RetryMiddleware.php

RetryMiddleware.php in Plausible Analytics trunk, at src/Client/lib/Lib/GuzzleHttp/RetryMiddleware.php

120 lines 3.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Plausible\Analytics\WP\Client\Lib\GuzzleHttp;
4
5 use Plausible\Analytics\WP\Client\Lib\GuzzleHttp\Promise as P;
6 use Plausible\Analytics\WP\Client\Lib\GuzzleHttp\Promise\PromiseInterface;
7 use Plausible\Analytics\WP\Client\Lib\Psr\Http\Message\RequestInterface;
8 use Plausible\Analytics\WP\Client\Lib\Psr\Http\Message\ResponseInterface;
9
10 /**
11 * Middleware that retries requests based on the boolean result of
12 * invoking the provided "decider" function.
13 *
14 * @final
15 */
16 class RetryMiddleware
17 {
18 /**
19 * @var callable(RequestInterface, array): PromiseInterface
20 */
21 private $nextHandler;
22
23 /**
24 * @var callable
25 */
26 private $decider;
27
28 /**
29 * @var callable(int)
30 */
31 private $delay;
32
33 /**
34 * @param callable $decider Function that accepts the number of retries,
35 * a request, [response], and [exception] and
36 * returns true if the request is to be
37 * retried.
38 * @param callable(RequestInterface, array): PromiseInterface $nextHandler Next handler to invoke.
39 * @param (callable(int): int)|null $delay Function that accepts the number of retries
40 * and returns the number of
41 * milliseconds to delay.
42 */
43 public function __construct( callable $decider, callable $nextHandler, ?callable $delay = null )
44 {
45 $this->decider = $decider;
46 $this->nextHandler = $nextHandler;
47 $this->delay = $delay ?: __CLASS__.'::exponentialDelay';
48 }
49
50 /**
51 * Default exponential backoff delay function.
52 *
53 * @return int milliseconds.
54 */
55 public static function exponentialDelay(int $retries): int
56 {
57 return (int) 2 ** ($retries - 1) * 1000;
58 }
59
60 public function __invoke(RequestInterface $request, array $options): PromiseInterface
61 {
62 if (!isset($options['retries'])) {
63 $options['retries'] = 0;
64 }
65
66 $fn = $this->nextHandler;
67
68 return $fn($request, $options)
69 ->then(
70 $this->onFulfilled($request, $options),
71 $this->onRejected($request, $options)
72 );
73 }
74
75 /**
76 * Execute fulfilled closure
77 */
78 private function onFulfilled(RequestInterface $request, array $options): callable
79 {
80 return function ($value) use ($request, $options) {
81 if (!($this->decider)(
82 $options['retries'],
83 $request,
84 $value,
85 null
86 )) {
87 return $value;
88 }
89
90 return $this->doRetry($request, $options, $value);
91 };
92 }
93
94 /**
95 * Execute rejected closure
96 */
97 private function onRejected(RequestInterface $req, array $options): callable
98 {
99 return function ($reason) use ($req, $options) {
100 if (!($this->decider)(
101 $options['retries'],
102 $req,
103 null,
104 $reason
105 )) {
106 return P\Create::rejectionFor($reason);
107 }
108
109 return $this->doRetry($req, $options);
110 };
111 }
112
113 private function doRetry( RequestInterface $request, array $options, ?ResponseInterface $response = null ): PromiseInterface
114 {
115 $options['delay'] = ($this->delay)(++$options['retries'], $response, $request);
116
117 return $this($request, $options);
118 }
119 }
120