PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 19.0
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v19.0
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 19.0, at vendor_prefixed/guzzlehttp/guzzle/src/Client.php

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