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 / Middleware.php

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

269 lines 11.2 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\Cookie\CookieJarInterface;
6 use Plausible\Analytics\WP\Client\Lib\GuzzleHttp\Exception\RequestException;
7 use Plausible\Analytics\WP\Client\Lib\GuzzleHttp\Promise as P;
8 use Plausible\Analytics\WP\Client\Lib\GuzzleHttp\Promise\PromiseInterface;
9 use Plausible\Analytics\WP\Client\Lib\Psr\Http\Message\RequestInterface;
10 use Plausible\Analytics\WP\Client\Lib\Psr\Http\Message\ResponseInterface;
11 use Psr\Log\LoggerInterface;
12
13 /**
14 * Functions used to create and wrap handlers with handler middleware.
15 */
16 final class Middleware
17 {
18 /**
19 * Middleware that adds cookies to requests.
20 *
21 * The options array must be set to a CookieJarInterface in order to use
22 * cookies. This is typically handled for you by a client.
23 *
24 * @return callable Returns a function that accepts the next handler.
25 */
26 public static function cookies(): callable
27 {
28 return static function (callable $handler): callable {
29 return static function ($request, array $options) use ($handler) {
30 if (empty($options['cookies'])) {
31 return $handler($request, $options);
32 } elseif (!($options['cookies'] instanceof CookieJarInterface)) {
33 throw new \InvalidArgumentException('cookies must be an instance of Plausible\Analytics\WP\Client\Lib\GuzzleHttp\Cookie\CookieJarInterface');
34 }
35 $cookieJar = $options['cookies'];
36 $request = $cookieJar->withCookieHeader($request);
37
38 return $handler($request, $options)
39 ->then(
40 static function (ResponseInterface $response) use ($cookieJar, $request): ResponseInterface {
41 $cookieJar->extractCookies($request, $response);
42
43 return $response;
44 }
45 );
46 };
47 };
48 }
49
50 /**
51 * Middleware that throws exceptions for 4xx or 5xx responses when the
52 * "http_errors" request option is set to true.
53 *
54 * @param BodySummarizerInterface|null $bodySummarizer The body summarizer to use in exception messages.
55 *
56 * @return callable(callable): callable Returns a function that accepts the next handler.
57 */
58 public static function httpErrors( ?BodySummarizerInterface $bodySummarizer = null ): callable
59 {
60 return static function (callable $handler) use ($bodySummarizer): callable {
61 return static function ($request, array $options) use ($handler, $bodySummarizer) {
62 if (empty($options['http_errors'])) {
63 return $handler($request, $options);
64 }
65
66 return $handler($request, $options)->then(
67 static function (ResponseInterface $response) use ($request, $bodySummarizer) {
68 $code = $response->getStatusCode();
69 if ($code < 400) {
70 return $response;
71 }
72 throw RequestException::create($request, $response, null, [], $bodySummarizer);
73 }
74 );
75 };
76 };
77 }
78
79 /**
80 * Middleware that pushes history data to an ArrayAccess container.
81 *
82 * @param array|\ArrayAccess<int, array> $container Container to hold the history (by reference).
83 *
84 * @return callable(callable): callable Returns a function that accepts the next handler.
85 *
86 * @throws \InvalidArgumentException if container is not an array or ArrayAccess.
87 */
88 public static function history(&$container): callable
89 {
90 if (!\is_array($container) && !$container instanceof \ArrayAccess) {
91 throw new \InvalidArgumentException('history container must be an array or object implementing ArrayAccess');
92 }
93
94 return static function (callable $handler) use (&$container): callable {
95 return static function (RequestInterface $request, array $options) use ($handler, &$container) {
96 return $handler($request, $options)->then(
97 static function ($value) use ($request, &$container, $options) {
98 $container[] = [
99 'request' => $request,
100 'response' => $value,
101 'error' => null,
102 'options' => $options,
103 ];
104
105 return $value;
106 },
107 static function ($reason) use ($request, &$container, $options) {
108 $container[] = [
109 'request' => $request,
110 'response' => null,
111 'error' => $reason,
112 'options' => $options,
113 ];
114
115 return P\Create::rejectionFor($reason);
116 }
117 );
118 };
119 };
120 }
121
122 /**
123 * Middleware that invokes a callback before and after sending a request.
124 *
125 * The provided listener cannot modify or alter the response. It simply
126 * "taps" into the chain to be notified before returning the promise. The
127 * before listener accepts a request and options array, and the after
128 * listener accepts a request, options array, and response promise.
129 *
130 * @param callable $before Function to invoke before forwarding the request.
131 * @param callable $after Function invoked after forwarding.
132 *
133 * @return callable Returns a function that accepts the next handler.
134 */
135 public static function tap( ?callable $before = null, ?callable $after = null ): callable
136 {
137 return static function (callable $handler) use ($before, $after): callable {
138 return static function (RequestInterface $request, array $options) use ($handler, $before, $after) {
139 if ($before) {
140 $before($request, $options);
141 }
142 $response = $handler($request, $options);
143 if ($after) {
144 $after($request, $options, $response);
145 }
146
147 return $response;
148 };
149 };
150 }
151
152 /**
153 * Middleware that handles request redirects.
154 *
155 * @return callable Returns a function that accepts the next handler.
156 */
157 public static function redirect(): callable
158 {
159 return static function (callable $handler): RedirectMiddleware {
160 return new RedirectMiddleware($handler);
161 };
162 }
163
164 /**
165 * Middleware that retries requests based on the boolean result of
166 * invoking the provided "decider" function.
167 *
168 * If no delay function is provided, a simple implementation of exponential
169 * backoff will be utilized.
170 *
171 * @param callable $decider Function that accepts the number of retries,
172 * a request, [response], and [exception] and
173 * returns true if the request is to be retried.
174 * @param callable $delay Function that accepts the number of retries and
175 * returns the number of milliseconds to delay.
176 *
177 * @return callable Returns a function that accepts the next handler.
178 */
179 public static function retry( callable $decider, ?callable $delay = null ): callable
180 {
181 return static function (callable $handler) use ($decider, $delay): RetryMiddleware {
182 return new RetryMiddleware($decider, $handler, $delay);
183 };
184 }
185
186 /**
187 * Middleware that logs requests, responses, and errors using a message
188 * formatter.
189 *
190 * @phpstan-param \Psr\Log\LogLevel::* $logLevel Level at which to log requests.
191 *
192 * @param LoggerInterface $logger Logs messages.
193 * @param MessageFormatterInterface|MessageFormatter $formatter Formatter used to create message strings.
194 * @param string $logLevel Level at which to log requests.
195 *
196 * @return callable Returns a function that accepts the next handler.
197 */
198 public static function log(LoggerInterface $logger, $formatter, string $logLevel = 'info'): callable
199 {
200 // To be compatible with Guzzle 7.1.x we need to allow users to pass a MessageFormatter
201 if (!$formatter instanceof MessageFormatter && !$formatter instanceof MessageFormatterInterface) {
202 throw new \LogicException(sprintf('Argument 2 to %s::log() must be of type %s', self::class, MessageFormatterInterface::class));
203 }
204
205 return static function (callable $handler) use ($logger, $formatter, $logLevel): callable {
206 return static function (RequestInterface $request, array $options = []) use ($handler, $logger, $formatter, $logLevel) {
207 return $handler($request, $options)->then(
208 static function ($response) use ($logger, $request, $formatter, $logLevel): ResponseInterface {
209 $message = $formatter->format($request, $response);
210 $logger->log($logLevel, $message);
211
212 return $response;
213 },
214 static function ($reason) use ($logger, $request, $formatter): PromiseInterface {
215 $response = $reason instanceof RequestException ? $reason->getResponse() : null;
216 $message = $formatter->format($request, $response, P\Create::exceptionFor($reason));
217 $logger->error($message);
218
219 return P\Create::rejectionFor($reason);
220 }
221 );
222 };
223 };
224 }
225
226 /**
227 * This middleware adds a default content-type if possible, a default
228 * content-length or transfer-encoding header, and the expect header.
229 */
230 public static function prepareBody(): callable
231 {
232 return static function (callable $handler): PrepareBodyMiddleware {
233 return new PrepareBodyMiddleware($handler);
234 };
235 }
236
237 /**
238 * Middleware that applies a map function to the request before passing to
239 * the next handler.
240 *
241 * @param callable $fn Function that accepts a RequestInterface and returns
242 * a RequestInterface.
243 */
244 public static function mapRequest(callable $fn): callable
245 {
246 return static function (callable $handler) use ($fn): callable {
247 return static function (RequestInterface $request, array $options) use ($handler, $fn) {
248 return $handler($fn($request), $options);
249 };
250 };
251 }
252
253 /**
254 * Middleware that applies a map function to the resolved promise's
255 * response.
256 *
257 * @param callable $fn Function that accepts a ResponseInterface and
258 * returns a ResponseInterface.
259 */
260 public static function mapResponse(callable $fn): callable
261 {
262 return static function (callable $handler) use ($fn): callable {
263 return static function (RequestInterface $request, array $options) use ($handler, $fn) {
264 return $handler($request, $options)->then($fn);
265 };
266 };
267 }
268 }
269