PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 27.5
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v27.5
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / vendor_prefixed / guzzlehttp / guzzle / src / Client.php

Client.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 27.5, at vendor_prefixed/guzzlehttp/guzzle/src/Client.php

403 lines 19.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace YoastSEO_Vendor\GuzzleHttp;
4
5 use YoastSEO_Vendor\GuzzleHttp\Cookie\CookieJar;
6 use YoastSEO_Vendor\GuzzleHttp\Exception\GuzzleException;
7 use YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException;
8 use YoastSEO_Vendor\GuzzleHttp\Promise as P;
9 use YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface;
10 use YoastSEO_Vendor\Psr\Http\Message\RequestInterface;
11 use YoastSEO_Vendor\Psr\Http\Message\ResponseInterface;
12 use YoastSEO_Vendor\Psr\Http\Message\UriInterface;
13 /**
14 * @final
15 */
16 class Client implements \YoastSEO_Vendor\GuzzleHttp\ClientInterface, \YoastSEO_Vendor\Psr\Http\Client\ClientInterface
17 {
18 use ClientTrait;
19 /**
20 * @var array Default request options
21 */
22 private $config;
23 /**
24 * Clients accept an array of constructor parameters.
25 *
26 * Here's an example of creating a client using a base_uri and an array of
27 * default request options to apply to each request:
28 *
29 * $client = new Client([
30 * 'base_uri' => 'http://www.foo.com/1.0/',
31 * 'timeout' => 0,
32 * 'allow_redirects' => false,
33 * 'proxy' => '192.168.16.1:10'
34 * ]);
35 *
36 * Client configuration settings include the following options:
37 *
38 * - handler: (callable) Function that transfers HTTP requests over the
39 * wire. The function is called with a Psr7\Http\Message\RequestInterface
40 * and array of transfer options, and must return a
41 * GuzzleHttp\Promise\PromiseInterface that is fulfilled with a
42 * Psr7\Http\Message\ResponseInterface on success.
43 * If no handler is provided, a default handler will be created
44 * that enables all of the request options below by attaching all of the
45 * default middleware to the handler.
46 * - base_uri: (string|UriInterface) Base URI of the client that is merged
47 * into relative URIs. Can be a string or instance of UriInterface.
48 * - **: any request option
49 *
50 * @param array $config Client configuration settings.
51 *
52 * @see RequestOptions for a list of available request options.
53 */
54 public function __construct(array $config = [])
55 {
56 if (!isset($config['handler'])) {
57 $config['handler'] = \YoastSEO_Vendor\GuzzleHttp\HandlerStack::create();
58 } elseif (!\is_callable($config['handler'])) {
59 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('handler must be a callable');
60 }
61 // Convert the base_uri to a UriInterface
62 if (isset($config['base_uri'])) {
63 $config['base_uri'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::uriFor($config['base_uri']);
64 }
65 $this->configureDefaults($config);
66 }
67 /**
68 * @param string $method
69 * @param array $args
70 *
71 * @return PromiseInterface|ResponseInterface
72 *
73 * @deprecated Client::__call will be removed in guzzlehttp/guzzle:8.0.
74 */
75 public function __call($method, $args)
76 {
77 if (\count($args) < 1) {
78 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('Magic request methods require a URI and optional options array');
79 }
80 $uri = $args[0];
81 $opts = $args[1] ?? [];
82 return \substr($method, -5) === 'Async' ? $this->requestAsync(\substr($method, 0, -5), $uri, $opts) : $this->request($method, $uri, $opts);
83 }
84 /**
85 * Asynchronously send an HTTP request.
86 *
87 * @param array $options Request options to apply to the given
88 * request and to the transfer. See \GuzzleHttp\RequestOptions.
89 */
90 public function sendAsync(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options = []) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface
91 {
92 // Merge the base URI into the request URI if needed.
93 $options = $this->prepareDefaults($options);
94 return $this->transfer($request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')), $options);
95 }
96 /**
97 * Send an HTTP request.
98 *
99 * @param array $options Request options to apply to the given
100 * request and to the transfer. See \GuzzleHttp\RequestOptions.
101 *
102 * @throws GuzzleException
103 */
104 public function send(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options = []) : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface
105 {
106 $options[\YoastSEO_Vendor\GuzzleHttp\RequestOptions::SYNCHRONOUS] = \true;
107 return $this->sendAsync($request, $options)->wait();
108 }
109 /**
110 * The HttpClient PSR (PSR-18) specify this method.
111 *
112 * {@inheritDoc}
113 */
114 public function sendRequest(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request) : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface
115 {
116 $options[\YoastSEO_Vendor\GuzzleHttp\RequestOptions::SYNCHRONOUS] = \true;
117 $options[\YoastSEO_Vendor\GuzzleHttp\RequestOptions::ALLOW_REDIRECTS] = \false;
118 $options[\YoastSEO_Vendor\GuzzleHttp\RequestOptions::HTTP_ERRORS] = \false;
119 return $this->sendAsync($request, $options)->wait();
120 }
121 /**
122 * Create and send an asynchronous HTTP request.
123 *
124 * Use an absolute path to override the base path of the client, or a
125 * relative path to append to the base path of the client. The URL can
126 * contain the query string as well. Use an array to provide a URL
127 * template and additional variables to use in the URL template expansion.
128 *
129 * @param string $method HTTP method
130 * @param string|UriInterface $uri URI object or string.
131 * @param array $options Request options to apply. See \GuzzleHttp\RequestOptions.
132 */
133 public function requestAsync(string $method, $uri = '', array $options = []) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface
134 {
135 $options = $this->prepareDefaults($options);
136 // Remove request modifying parameter because it can be done up-front.
137 $headers = $options['headers'] ?? [];
138 $body = $options['body'] ?? null;
139 $version = $options['version'] ?? '1.1';
140 // Merge the URI into the base URI.
141 $uri = $this->buildUri(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::uriFor($uri), $options);
142 if (\is_array($body)) {
143 throw $this->invalidBody();
144 }
145 $request = new \YoastSEO_Vendor\GuzzleHttp\Psr7\Request($method, $uri, $headers, $body, $version);
146 // Remove the option so that they are not doubly-applied.
147 unset($options['headers'], $options['body'], $options['version']);
148 return $this->transfer($request, $options);
149 }
150 /**
151 * Create and send an HTTP request.
152 *
153 * Use an absolute path to override the base path of the client, or a
154 * relative path to append to the base path of the client. The URL can
155 * contain the query string as well.
156 *
157 * @param string $method HTTP method.
158 * @param string|UriInterface $uri URI object or string.
159 * @param array $options Request options to apply. See \GuzzleHttp\RequestOptions.
160 *
161 * @throws GuzzleException
162 */
163 public function request(string $method, $uri = '', array $options = []) : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface
164 {
165 $options[\YoastSEO_Vendor\GuzzleHttp\RequestOptions::SYNCHRONOUS] = \true;
166 return $this->requestAsync($method, $uri, $options)->wait();
167 }
168 /**
169 * Get a client configuration option.
170 *
171 * These options include default request options of the client, a "handler"
172 * (if utilized by the concrete client), and a "base_uri" if utilized by
173 * the concrete client.
174 *
175 * @param string|null $option The config option to retrieve.
176 *
177 * @return mixed
178 *
179 * @deprecated Client::getConfig will be removed in guzzlehttp/guzzle:8.0.
180 */
181 public function getConfig(?string $option = null)
182 {
183 return $option === null ? $this->config : $this->config[$option] ?? null;
184 }
185 private function buildUri(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri, array $config) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface
186 {
187 if (isset($config['base_uri'])) {
188 $uri = \YoastSEO_Vendor\GuzzleHttp\Psr7\UriResolver::resolve(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::uriFor($config['base_uri']), $uri);
189 }
190 if (isset($config['idn_conversion']) && $config['idn_conversion'] !== \false) {
191 $idnOptions = $config['idn_conversion'] === \true ? \IDNA_DEFAULT : $config['idn_conversion'];
192 $uri = \YoastSEO_Vendor\GuzzleHttp\Utils::idnUriConvert($uri, $idnOptions);
193 }
194 return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri;
195 }
196 /**
197 * Configures the default options for a client.
198 */
199 private function configureDefaults(array $config) : void
200 {
201 $defaults = ['allow_redirects' => \YoastSEO_Vendor\GuzzleHttp\RedirectMiddleware::$defaultSettings, 'http_errors' => \true, 'decode_content' => \true, 'verify' => \true, 'cookies' => \false, 'idn_conversion' => \false];
202 // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.
203 // We can only trust the HTTP_PROXY environment variable in a CLI
204 // process due to the fact that PHP has no reliable mechanism to
205 // get environment variables that start with "HTTP_".
206 if (\PHP_SAPI === 'cli' && ($proxy = \YoastSEO_Vendor\GuzzleHttp\Utils::getenv('HTTP_PROXY'))) {
207 $defaults['proxy']['http'] = $proxy;
208 }
209 if ($proxy = \YoastSEO_Vendor\GuzzleHttp\Utils::getenv('HTTPS_PROXY')) {
210 $defaults['proxy']['https'] = $proxy;
211 }
212 if ($noProxy = \YoastSEO_Vendor\GuzzleHttp\Utils::getenv('NO_PROXY')) {
213 $cleanedNoProxy = \str_replace(' ', '', $noProxy);
214 $defaults['proxy']['no'] = \explode(',', $cleanedNoProxy);
215 }
216 $this->config = $config + $defaults;
217 if (!empty($config['cookies']) && $config['cookies'] === \true) {
218 $this->config['cookies'] = new \YoastSEO_Vendor\GuzzleHttp\Cookie\CookieJar();
219 }
220 // Add the default user-agent header.
221 if (!isset($this->config['headers'])) {
222 $this->config['headers'] = ['User-Agent' => \YoastSEO_Vendor\GuzzleHttp\Utils::defaultUserAgent()];
223 } else {
224 // Add the User-Agent header if one was not already set.
225 foreach (\array_keys($this->config['headers']) as $name) {
226 if (\strtolower($name) === 'user-agent') {
227 return;
228 }
229 }
230 $this->config['headers']['User-Agent'] = \YoastSEO_Vendor\GuzzleHttp\Utils::defaultUserAgent();
231 }
232 }
233 /**
234 * Merges default options into the array.
235 *
236 * @param array $options Options to modify by reference
237 */
238 private function prepareDefaults(array $options) : array
239 {
240 $defaults = $this->config;
241 if (!empty($defaults['headers'])) {
242 // Default headers are only added if they are not present.
243 $defaults['_conditional'] = $defaults['headers'];
244 unset($defaults['headers']);
245 }
246 // Special handling for headers is required as they are added as
247 // conditional headers and as headers passed to a request ctor.
248 if (\array_key_exists('headers', $options)) {
249 // Allows default headers to be unset.
250 if ($options['headers'] === null) {
251 $defaults['_conditional'] = [];
252 unset($options['headers']);
253 } elseif (!\is_array($options['headers'])) {
254 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('headers must be an array');
255 }
256 }
257 // Shallow merge defaults underneath options.
258 $result = $options + $defaults;
259 // Remove null values.
260 foreach ($result as $k => $v) {
261 if ($v === null) {
262 unset($result[$k]);
263 }
264 }
265 return $result;
266 }
267 /**
268 * Transfers the given request and applies request options.
269 *
270 * The URI of the request is not modified and the request options are used
271 * as-is without merging in default options.
272 *
273 * @param array $options See \GuzzleHttp\RequestOptions.
274 */
275 private function transfer(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface
276 {
277 $request = $this->applyOptions($request, $options);
278 /** @var HandlerStack $handler */
279 $handler = $options['handler'];
280 try {
281 return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::promiseFor($handler($request, $options));
282 } catch (\Exception $e) {
283 return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor($e);
284 }
285 }
286 /**
287 * Applies the array of request options to a request.
288 */
289 private function applyOptions(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options) : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface
290 {
291 $modify = ['set_headers' => []];
292 if (isset($options['headers'])) {
293 if (\array_keys($options['headers']) === \range(0, \count($options['headers']) - 1)) {
294 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('The headers array must have header name as keys.');
295 }
296 $modify['set_headers'] = $options['headers'];
297 unset($options['headers']);
298 }
299 if (isset($options['form_params'])) {
300 if (isset($options['multipart'])) {
301 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('You cannot use ' . 'form_params and multipart at the same time. Use the ' . 'form_params option if you want to send application/' . 'x-www-form-urlencoded requests, and the multipart ' . 'option to send multipart/form-data requests.');
302 }
303 $options['body'] = \http_build_query($options['form_params'], '', '&');
304 unset($options['form_params']);
305 // Ensure that we don't have the header in different case and set the new value.
306 $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
307 $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded';
308 }
309 if (isset($options['multipart'])) {
310 $options['body'] = new \YoastSEO_Vendor\GuzzleHttp\Psr7\MultipartStream($options['multipart']);
311 unset($options['multipart']);
312 }
313 if (isset($options['json'])) {
314 $options['body'] = \YoastSEO_Vendor\GuzzleHttp\Utils::jsonEncode($options['json']);
315 unset($options['json']);
316 // Ensure that we don't have the header in different case and set the new value.
317 $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
318 $options['_conditional']['Content-Type'] = 'application/json';
319 }
320 if (!empty($options['decode_content']) && $options['decode_content'] !== \true) {
321 // Ensure that we don't have the header in different case and set the new value.
322 $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Accept-Encoding'], $options['_conditional']);
323 $modify['set_headers']['Accept-Encoding'] = $options['decode_content'];
324 }
325 if (isset($options['body'])) {
326 if (\is_array($options['body'])) {
327 throw $this->invalidBody();
328 }
329 $modify['body'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($options['body']);
330 unset($options['body']);
331 }
332 if (!empty($options['auth']) && \is_array($options['auth'])) {
333 $value = $options['auth'];
334 $type = isset($value[2]) ? \strtolower($value[2]) : 'basic';
335 switch ($type) {
336 case 'basic':
337 // Ensure that we don't have the header in different case and set the new value.
338 $modify['set_headers'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Authorization'], $modify['set_headers']);
339 $modify['set_headers']['Authorization'] = 'Basic ' . \base64_encode("{$value[0]}:{$value[1]}");
340 break;
341 case 'digest':
342 // @todo: Do not rely on curl
343 $options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_DIGEST;
344 $options['curl'][\CURLOPT_USERPWD] = "{$value[0]}:{$value[1]}";
345 break;
346 case 'ntlm':
347 $options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_NTLM;
348 $options['curl'][\CURLOPT_USERPWD] = "{$value[0]}:{$value[1]}";
349 break;
350 }
351 }
352 if (isset($options['query'])) {
353 $value = $options['query'];
354 if (\is_array($value)) {
355 $value = \http_build_query($value, '', '&', \PHP_QUERY_RFC3986);
356 }
357 if (!\is_string($value)) {
358 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('query must be a string or array');
359 }
360 $modify['query'] = $value;
361 unset($options['query']);
362 }
363 // Ensure that sink is not an invalid value.
364 if (isset($options['sink'])) {
365 // TODO: Add more sink validation?
366 if (\is_bool($options['sink'])) {
367 throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('sink must not be a boolean');
368 }
369 }
370 if (isset($options['version'])) {
371 $modify['version'] = $options['version'];
372 }
373 $request = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::modifyRequest($request, $modify);
374 if ($request->getBody() instanceof \YoastSEO_Vendor\GuzzleHttp\Psr7\MultipartStream) {
375 // Use a multipart/form-data POST if a Content-Type is not set.
376 // Ensure that we don't have the header in different case and set the new value.
377 $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']);
378 $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary=' . $request->getBody()->getBoundary();
379 }
380 // Merge in conditional headers if they are not present.
381 if (isset($options['_conditional'])) {
382 // Build up the changes so it's in a single clone of the message.
383 $modify = [];
384 foreach ($options['_conditional'] as $k => $v) {
385 if (!$request->hasHeader($k)) {
386 $modify['set_headers'][$k] = $v;
387 }
388 }
389 $request = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::modifyRequest($request, $modify);
390 // Don't pass this internal value along to middleware/handlers.
391 unset($options['_conditional']);
392 }
393 return $request;
394 }
395 /**
396 * Return an InvalidArgumentException with pre-set message.
397 */
398 private function invalidBody() : \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException
399 {
400 return new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('Passing in the "body" request ' . 'option as an array to send a request is not supported. ' . 'Please use the "form_params" request option to send a ' . 'application/x-www-form-urlencoded request, or the "multipart" ' . 'request option to send a multipart/form-data request.');
401 }
402 }
403