run:R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
860 By
2026-03-11 16:18:52
R W Run
9.12 KB
2026-03-11 16:18:52
R W Run
652 By
2026-03-11 16:18:52
R W Run
15.03 KB
2026-03-11 16:18:52
R W Run
1.09 KB
2026-03-11 16:18:52
R W Run
709 By
2026-03-11 16:18:52
R W Run
2.96 KB
2026-03-11 16:18:52
R W Run
12.14 KB
2026-03-11 16:18:52
R W Run
5.51 KB
2026-03-11 16:18:52
R W Run
28.93 KB
2026-03-11 16:18:52
R W Run
1.47 KB
2026-03-11 16:18:52
R W Run
867 By
2026-03-11 16:18:52
R W Run
33.2 KB
2026-03-11 16:18:52
R W Run
4.18 KB
2026-03-11 16:18:52
R W Run
8.89 KB
2026-03-11 16:18:52
R W Run
5.3 KB
2026-03-11 16:18:52
R W Run
1.51 KB
2026-03-11 16:18:52
R W Run
error_log
📄Requests.php
1<?php
2/**
3 * Requests for PHP
4 *
5 * Inspired by Requests for Python.
6 *
7 * Based on concepts from SimplePie_File, RequestCore and WP_Http.
8 *
9 * @package Requests
10 */
11
12namespace WpOrg\Requests;
13
14use WpOrg\Requests\Auth\Basic;
15use WpOrg\Requests\Capability;
16use WpOrg\Requests\Cookie\Jar;
17use WpOrg\Requests\Exception;
18use WpOrg\Requests\Exception\InvalidArgument;
19use WpOrg\Requests\Hooks;
20use WpOrg\Requests\IdnaEncoder;
21use WpOrg\Requests\Iri;
22use WpOrg\Requests\Proxy\Http;
23use WpOrg\Requests\Response;
24use WpOrg\Requests\Transport\Curl;
25use WpOrg\Requests\Transport\Fsockopen;
26use WpOrg\Requests\Utility\InputValidator;
27
28/**
29 * Requests for PHP
30 *
31 * Inspired by Requests for Python.
32 *
33 * Based on concepts from SimplePie_File, RequestCore and WP_Http.
34 *
35 * @package Requests
36 */
37class Requests {
38 /**
39 * POST method
40 *
41 * @var string
42 */
43 const POST = 'POST';
44
45 /**
46 * PUT method
47 *
48 * @var string
49 */
50 const PUT = 'PUT';
51
52 /**
53 * GET method
54 *
55 * @var string
56 */
57 const GET = 'GET';
58
59 /**
60 * HEAD method
61 *
62 * @var string
63 */
64 const HEAD = 'HEAD';
65
66 /**
67 * DELETE method
68 *
69 * @var string
70 */
71 const DELETE = 'DELETE';
72
73 /**
74 * OPTIONS method
75 *
76 * @var string
77 */
78 const OPTIONS = 'OPTIONS';
79
80 /**
81 * TRACE method
82 *
83 * @var string
84 */
85 const TRACE = 'TRACE';
86
87 /**
88 * PATCH method
89 *
90 * @link https://tools.ietf.org/html/rfc5789
91 * @var string
92 */
93 const PATCH = 'PATCH';
94
95 /**
96 * Default size of buffer size to read streams
97 *
98 * @var integer
99 */
100 const BUFFER_SIZE = 1160;
101
102 /**
103 * Option defaults.
104 *
105 * @see \WpOrg\Requests\Requests::get_default_options()
106 * @see \WpOrg\Requests\Requests::request() for values returned by this method
107 *
108 * @since 2.0.0
109 *
110 * @var array
111 */
112 const OPTION_DEFAULTS = [
113 'timeout' => 10,
114 'connect_timeout' => 10,
115 'useragent' => 'php-requests/' . self::VERSION,
116 'protocol_version' => 1.1,
117 'redirected' => 0,
118 'redirects' => 10,
119 'follow_redirects' => true,
120 'blocking' => true,
121 'type' => self::GET,
122 'filename' => false,
123 'auth' => false,
124 'proxy' => false,
125 'cookies' => false,
126 'max_bytes' => false,
127 'idn' => true,
128 'hooks' => null,
129 'transport' => null,
130 'verify' => null,
131 'verifyname' => true,
132 ];
133
134 /**
135 * Default supported Transport classes.
136 *
137 * @since 2.0.0
138 *
139 * @var array
140 */
141 const DEFAULT_TRANSPORTS = [
142 Curl::class => Curl::class,
143 Fsockopen::class => Fsockopen::class,
144 ];
145
146 /**
147 * Current version of Requests
148 *
149 * @var string
150 */
151 const VERSION = '2.0.11';
152
153 /**
154 * Selected transport name
155 *
156 * Use {@see \WpOrg\Requests\Requests::get_transport()} instead
157 *
158 * @var array
159 */
160 public static $transport = [];
161
162 /**
163 * Registered transport classes
164 *
165 * @var array
166 */
167 protected static $transports = [];
168
169 /**
170 * Default certificate path.
171 *
172 * @see \WpOrg\Requests\Requests::get_certificate_path()
173 * @see \WpOrg\Requests\Requests::set_certificate_path()
174 *
175 * @var string
176 */
177 protected static $certificate_path = __DIR__ . '/../certificates/cacert.pem';
178
179 /**
180 * All (known) valid deflate, gzip header magic markers.
181 *
182 * These markers relate to different compression levels.
183 *
184 * @link https://stackoverflow.com/a/43170354/482864 Marker source.
185 *
186 * @since 2.0.0
187 *
188 * @var array
189 */
190 private static $magic_compression_headers = [
191 "\x1f\x8b" => true, // Gzip marker.
192 "\x78\x01" => true, // Zlib marker - level 1.
193 "\x78\x5e" => true, // Zlib marker - level 2 to 5.
194 "\x78\x9c" => true, // Zlib marker - level 6.
195 "\x78\xda" => true, // Zlib marker - level 7 to 9.
196 ];
197
198 /**
199 * This is a static class, do not instantiate it
200 *
201 * @codeCoverageIgnore
202 */
203 private function __construct() {}
204
205 /**
206 * Register a transport
207 *
208 * @param string $transport Transport class to add, must support the \WpOrg\Requests\Transport interface
209 */
210 public static function add_transport($transport) {
211 if (empty(self::$transports)) {
212 self::$transports = self::DEFAULT_TRANSPORTS;
213 }
214
215 self::$transports[$transport] = $transport;
216 }
217
218 /**
219 * Get the fully qualified class name (FQCN) for a working transport.
220 *
221 * @param array<string, bool> $capabilities Optional. Associative array of capabilities to test against, i.e. `['<capability>' => true]`.
222 * @return string FQCN of the transport to use, or an empty string if no transport was
223 * found which provided the requested capabilities.
224 */
225 protected static function get_transport_class(array $capabilities = []) {
226 // Caching code, don't bother testing coverage.
227 // @codeCoverageIgnoreStart
228 // Array of capabilities as a string to be used as an array key.
229 ksort($capabilities);
230 $cap_string = serialize($capabilities);
231
232 // Don't search for a transport if it's already been done for these $capabilities.
233 if (isset(self::$transport[$cap_string])) {
234 return self::$transport[$cap_string];
235 }
236
237 // Ensure we will not run this same check again later on.
238 self::$transport[$cap_string] = '';
239 // @codeCoverageIgnoreEnd
240
241 if (empty(self::$transports)) {
242 self::$transports = self::DEFAULT_TRANSPORTS;
243 }
244
245 // Find us a working transport.
246 foreach (self::$transports as $class) {
247 if (!class_exists($class)) {
248 continue;
249 }
250
251 $result = $class::test($capabilities);
252 if ($result === true) {
253 self::$transport[$cap_string] = $class;
254 break;
255 }
256 }
257
258 return self::$transport[$cap_string];
259 }
260
261 /**
262 * Get a working transport.
263 *
264 * @param array<string, bool> $capabilities Optional. Associative array of capabilities to test against, i.e. `['<capability>' => true]`.
265 * @return \WpOrg\Requests\Transport
266 * @throws \WpOrg\Requests\Exception If no valid transport is found (`notransport`).
267 */
268 protected static function get_transport(array $capabilities = []) {
269 $class = self::get_transport_class($capabilities);
270
271 if ($class === '') {
272 throw new Exception('No working transports found', 'notransport', self::$transports);
273 }
274
275 return new $class();
276 }
277
278 /**
279 * Checks to see if we have a transport for the capabilities requested.
280 *
281 * Supported capabilities can be found in the {@see \WpOrg\Requests\Capability}
282 * interface as constants.
283 *
284 * Example usage:
285 * `Requests::has_capabilities([Capability::SSL => true])`.
286 *
287 * @param array<string, bool> $capabilities Optional. Associative array of capabilities to test against, i.e. `['<capability>' => true]`.
288 * @return bool Whether the transport has the requested capabilities.
289 */
290 public static function has_capabilities(array $capabilities = []) {
291 return self::get_transport_class($capabilities) !== '';
292 }
293
294 /**#@+
295 * @see \WpOrg\Requests\Requests::request()
296 * @param string $url
297 * @param array $headers
298 * @param array $options
299 * @return \WpOrg\Requests\Response
300 */
301 /**
302 * Send a GET request
303 */
304 public static function get($url, $headers = [], $options = []) {
305 return self::request($url, $headers, null, self::GET, $options);
306 }
307
308 /**
309 * Send a HEAD request
310 */
311 public static function head($url, $headers = [], $options = []) {
312 return self::request($url, $headers, null, self::HEAD, $options);
313 }
314
315 /**
316 * Send a DELETE request
317 */
318 public static function delete($url, $headers = [], $options = []) {
319 return self::request($url, $headers, null, self::DELETE, $options);
320 }
321
322 /**
323 * Send a TRACE request
324 */
325 public static function trace($url, $headers = [], $options = []) {
326 return self::request($url, $headers, null, self::TRACE, $options);
327 }
328 /**#@-*/
329
330 /**#@+
331 * @see \WpOrg\Requests\Requests::request()
332 * @param string $url
333 * @param array $headers
334 * @param array $data
335 * @param array $options
336 * @return \WpOrg\Requests\Response
337 */
338 /**
339 * Send a POST request
340 */
341 public static function post($url, $headers = [], $data = [], $options = []) {
342 return self::request($url, $headers, $data, self::POST, $options);
343 }
344 /**
345 * Send a PUT request
346 */
347 public static function put($url, $headers = [], $data = [], $options = []) {
348 return self::request($url, $headers, $data, self::PUT, $options);
349 }
350
351 /**
352 * Send an OPTIONS request
353 */
354 public static function options($url, $headers = [], $data = [], $options = []) {
355 return self::request($url, $headers, $data, self::OPTIONS, $options);
356 }
357
358 /**
359 * Send a PATCH request
360 *
361 * Note: Unlike {@see \WpOrg\Requests\Requests::post()} and {@see \WpOrg\Requests\Requests::put()},
362 * `$headers` is required, as the specification recommends that should send an ETag
363 *
364 * @link https://tools.ietf.org/html/rfc5789
365 */
366 public static function patch($url, $headers, $data = [], $options = []) {
367 return self::request($url, $headers, $data, self::PATCH, $options);
368 }
369 /**#@-*/
370
371 /**
372 * Main interface for HTTP requests
373 *
374 * This method initiates a request and sends it via a transport before
375 * parsing.
376 *
377 * The `$options` parameter takes an associative array with the following
378 * options:
379 *
380 * - `timeout`: How long should we wait for a response?
381 * Note: for cURL, a minimum of 1 second applies, as DNS resolution
382 * operates at second-resolution only.
383 * (float, seconds with a millisecond precision, default: 10, example: 0.01)
384 * - `connect_timeout`: How long should we wait while trying to connect?
385 * (float, seconds with a millisecond precision, default: 10, example: 0.01)
386 * - `useragent`: Useragent to send to the server
387 * (string, default: php-requests/$version)
388 * - `follow_redirects`: Should we follow 3xx redirects?
389 * (boolean, default: true)
390 * - `redirects`: How many times should we redirect before erroring?
391 * (integer, default: 10)
392 * - `blocking`: Should we block processing on this request?
393 * (boolean, default: true)
394 * - `filename`: File to stream the body to instead.
395 * (string|boolean, default: false)
396 * - `auth`: Authentication handler or array of user/password details to use
397 * for Basic authentication
398 * (\WpOrg\Requests\Auth|array|boolean, default: false)
399 * - `proxy`: Proxy details to use for proxy by-passing and authentication
400 * (\WpOrg\Requests\Proxy|array|string|boolean, default: false)
401 * - `max_bytes`: Limit for the response body size.
402 * (integer|boolean, default: false)
403 * - `idn`: Enable IDN parsing
404 * (boolean, default: true)
405 * - `transport`: Custom transport. Either a class name, or a
406 * transport object. Defaults to the first working transport from
407 * {@see \WpOrg\Requests\Requests::getTransport()}
408 * (string|\WpOrg\Requests\Transport, default: {@see \WpOrg\Requests\Requests::getTransport()})
409 * - `hooks`: Hooks handler.
410 * (\WpOrg\Requests\HookManager, default: new WpOrg\Requests\Hooks())
411 * - `verify`: Should we verify SSL certificates? Allows passing in a custom
412 * certificate file as a string. (Using true uses the system-wide root
413 * certificate store instead, but this may have different behaviour
414 * across transports.)
415 * (string|boolean, default: certificates/cacert.pem)
416 * - `verifyname`: Should we verify the common name in the SSL certificate?
417 * (boolean, default: true)
418 * - `data_format`: How should we send the `$data` parameter?
419 * (string, one of 'query' or 'body', default: 'query' for
420 * HEAD/GET/DELETE, 'body' for POST/PUT/OPTIONS/PATCH)
421 *
422 * @param string|Stringable $url URL to request
423 * @param array $headers Extra headers to send with the request
424 * @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
425 * @param string $type HTTP request type (use Requests constants)
426 * @param array $options Options for the request (see description for more information)
427 * @return \WpOrg\Requests\Response
428 *
429 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string or Stringable.
430 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $type argument is not a string.
431 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
432 * @throws \WpOrg\Requests\Exception On invalid URLs (`nonhttp`)
433 */
434 public static function request($url, $headers = [], $data = [], $type = self::GET, $options = []) {
435 if (InputValidator::is_string_or_stringable($url) === false) {
436 throw InvalidArgument::create(1, '$url', 'string|Stringable', gettype($url));
437 }
438
439 if (is_string($type) === false) {
440 throw InvalidArgument::create(4, '$type', 'string', gettype($type));
441 }
442
443 if (is_array($options) === false) {
444 throw InvalidArgument::create(5, '$options', 'array', gettype($options));
445 }
446
447 if (empty($options['type'])) {
448 $options['type'] = $type;
449 }
450
451 $options = array_merge(self::get_default_options(), $options);
452
453 self::set_defaults($url, $headers, $data, $type, $options);
454
455 $options['hooks']->dispatch('requests.before_request', [&$url, &$headers, &$data, &$type, &$options]);
456
457 if (!empty($options['transport'])) {
458 $transport = $options['transport'];
459
460 if (is_string($options['transport'])) {
461 $transport = new $transport();
462 }
463 } else {
464 $need_ssl = (stripos($url, 'https://') === 0);
465 $capabilities = [Capability::SSL => $need_ssl];
466 $transport = self::get_transport($capabilities);
467 }
468
469 $response = $transport->request($url, $headers, $data, $options);
470
471 $options['hooks']->dispatch('requests.before_parse', [&$response, $url, $headers, $data, $type, $options]);
472
473 return self::parse_response($response, $url, $headers, $data, $options);
474 }
475
476 /**
477 * Send multiple HTTP requests simultaneously
478 *
479 * The `$requests` parameter takes an associative or indexed array of
480 * request fields. The key of each request can be used to match up the
481 * request with the returned data, or with the request passed into your
482 * `multiple.request.complete` callback.
483 *
484 * The request fields value is an associative array with the following keys:
485 *
486 * - `url`: Request URL Same as the `$url` parameter to
487 * {@see \WpOrg\Requests\Requests::request()}
488 * (string, required)
489 * - `headers`: Associative array of header fields. Same as the `$headers`
490 * parameter to {@see \WpOrg\Requests\Requests::request()}
491 * (array, default: `array()`)
492 * - `data`: Associative array of data fields or a string. Same as the
493 * `$data` parameter to {@see \WpOrg\Requests\Requests::request()}
494 * (array|string, default: `array()`)
495 * - `type`: HTTP request type (use \WpOrg\Requests\Requests constants). Same as the `$type`
496 * parameter to {@see \WpOrg\Requests\Requests::request()}
497 * (string, default: `\WpOrg\Requests\Requests::GET`)
498 * - `cookies`: Associative array of cookie name to value, or cookie jar.
499 * (array|\WpOrg\Requests\Cookie\Jar)
500 *
501 * If the `$options` parameter is specified, individual requests will
502 * inherit options from it. This can be used to use a single hooking system,
503 * or set all the types to `\WpOrg\Requests\Requests::POST`, for example.
504 *
505 * In addition, the `$options` parameter takes the following global options:
506 *
507 * - `complete`: A callback for when a request is complete. Takes two
508 * parameters, a \WpOrg\Requests\Response/\WpOrg\Requests\Exception reference, and the
509 * ID from the request array (Note: this can also be overridden on a
510 * per-request basis, although that's a little silly)
511 * (callback)
512 *
513 * @param array $requests Requests data (see description for more information)
514 * @param array $options Global and default options (see {@see \WpOrg\Requests\Requests::request()})
515 * @return array Responses (either \WpOrg\Requests\Response or a \WpOrg\Requests\Exception object)
516 *
517 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $requests argument is not an array or iterable object with array access.
518 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $options argument is not an array.
519 */
520 public static function request_multiple($requests, $options = []) {
521 if (InputValidator::has_array_access($requests) === false || InputValidator::is_iterable($requests) === false) {
522 throw InvalidArgument::create(1, '$requests', 'array|ArrayAccess&Traversable', gettype($requests));
523 }
524
525 if (is_array($options) === false) {
526 throw InvalidArgument::create(2, '$options', 'array', gettype($options));
527 }
528
529 $options = array_merge(self::get_default_options(true), $options);
530
531 if (!empty($options['hooks'])) {
532 $options['hooks']->register('transport.internal.parse_response', [static::class, 'parse_multiple']);
533 if (!empty($options['complete'])) {
534 $options['hooks']->register('multiple.request.complete', $options['complete']);
535 }
536 }
537
538 foreach ($requests as $id => &$request) {
539 if (!isset($request['headers'])) {
540 $request['headers'] = [];
541 }
542
543 if (!isset($request['data'])) {
544 $request['data'] = [];
545 }
546
547 if (!isset($request['type'])) {
548 $request['type'] = self::GET;
549 }
550
551 if (!isset($request['options'])) {
552 $request['options'] = $options;
553 $request['options']['type'] = $request['type'];
554 } else {
555 if (empty($request['options']['type'])) {
556 $request['options']['type'] = $request['type'];
557 }
558
559 $request['options'] = array_merge($options, $request['options']);
560 }
561
562 self::set_defaults($request['url'], $request['headers'], $request['data'], $request['type'], $request['options']);
563
564 // Ensure we only hook in once
565 if ($request['options']['hooks'] !== $options['hooks']) {
566 $request['options']['hooks']->register('transport.internal.parse_response', [static::class, 'parse_multiple']);
567 if (!empty($request['options']['complete'])) {
568 $request['options']['hooks']->register('multiple.request.complete', $request['options']['complete']);
569 }
570 }
571 }
572
573 unset($request);
574
575 if (!empty($options['transport'])) {
576 $transport = $options['transport'];
577
578 if (is_string($options['transport'])) {
579 $transport = new $transport();
580 }
581 } else {
582 $transport = self::get_transport();
583 }
584
585 $responses = $transport->request_multiple($requests, $options);
586
587 foreach ($responses as $id => &$response) {
588 // If our hook got messed with somehow, ensure we end up with the
589 // correct response
590 if (is_string($response)) {
591 $request = $requests[$id];
592 self::parse_multiple($response, $request);
593 $request['options']['hooks']->dispatch('multiple.request.complete', [&$response, $id]);
594 }
595 }
596
597 return $responses;
598 }
599
600 /**
601 * Get the default options
602 *
603 * @see \WpOrg\Requests\Requests::request() for values returned by this method
604 * @param boolean $multirequest Is this a multirequest?
605 * @return array Default option values
606 */
607 protected static function get_default_options($multirequest = false) {
608 $defaults = static::OPTION_DEFAULTS;
609 $defaults['verify'] = self::$certificate_path;
610
611 if ($multirequest !== false) {
612 $defaults['complete'] = null;
613 }
614
615 return $defaults;
616 }
617
618 /**
619 * Get default certificate path.
620 *
621 * @return string Default certificate path.
622 */
623 public static function get_certificate_path() {
624 return self::$certificate_path;
625 }
626
627 /**
628 * Set default certificate path.
629 *
630 * @param string|Stringable|bool $path Certificate path, pointing to a PEM file.
631 *
632 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed $url argument is not a string, Stringable or boolean.
633 */
634 public static function set_certificate_path($path) {
635 if (InputValidator::is_string_or_stringable($path) === false && is_bool($path) === false) {
636 throw InvalidArgument::create(1, '$path', 'string|Stringable|bool', gettype($path));
637 }
638
639 self::$certificate_path = $path;
640 }
641
642 /**
643 * Set the default values
644 *
645 * The $options parameter is updated with the results.
646 *
647 * @param string $url URL to request
648 * @param array $headers Extra headers to send with the request
649 * @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests
650 * @param string $type HTTP request type
651 * @param array $options Options for the request
652 * @return void
653 *
654 * @throws \WpOrg\Requests\Exception When the $url is not an http(s) URL.
655 */
656 protected static function set_defaults(&$url, &$headers, &$data, &$type, &$options) {
657 if (!preg_match('/^http(s)?:\/\//i', $url, $matches)) {
658 throw new Exception('Only HTTP(S) requests are handled.', 'nonhttp', $url);
659 }
660
661 if (empty($options['hooks'])) {
662 $options['hooks'] = new Hooks();
663 }
664
665 if (is_array($options['auth'])) {
666 $options['auth'] = new Basic($options['auth']);
667 }
668
669 if ($options['auth'] !== false) {
670 $options['auth']->register($options['hooks']);
671 }
672
673 if (is_string($options['proxy']) || is_array($options['proxy'])) {
674 $options['proxy'] = new Http($options['proxy']);
675 }
676
677 if ($options['proxy'] !== false) {
678 $options['proxy']->register($options['hooks']);
679 }
680
681 if (is_array($options['cookies'])) {
682 $options['cookies'] = new Jar($options['cookies']);
683 } elseif (empty($options['cookies'])) {
684 $options['cookies'] = new Jar();
685 }
686
687 if ($options['cookies'] !== false) {
688 $options['cookies']->register($options['hooks']);
689 }
690
691 if ($options['idn'] !== false) {
692 $iri = new Iri($url);
693 $iri->host = IdnaEncoder::encode($iri->ihost);
694 $url = $iri->uri;
695 }
696
697 // Massage the type to ensure we support it.
698 $type = strtoupper($type);
699
700 if (!isset($options['data_format'])) {
701 if (in_array($type, [self::HEAD, self::GET, self::DELETE], true)) {
702 $options['data_format'] = 'query';
703 } else {
704 $options['data_format'] = 'body';
705 }
706 }
707 }
708
709 /**
710 * HTTP response parser
711 *
712 * @param string $headers Full response text including headers and body
713 * @param string $url Original request URL
714 * @param array $req_headers Original $headers array passed to {@link request()}, in case we need to follow redirects
715 * @param array $req_data Original $data array passed to {@link request()}, in case we need to follow redirects
716 * @param array $options Original $options array passed to {@link request()}, in case we need to follow redirects
717 * @return \WpOrg\Requests\Response
718 *
719 * @throws \WpOrg\Requests\Exception On missing head/body separator (`requests.no_crlf_separator`)
720 * @throws \WpOrg\Requests\Exception On missing head/body separator (`noversion`)
721 * @throws \WpOrg\Requests\Exception On missing head/body separator (`toomanyredirects`)
722 */
723 protected static function parse_response($headers, $url, $req_headers, $req_data, $options) {
724 $return = new Response();
725 if (!$options['blocking']) {
726 return $return;
727 }
728
729 $return->raw = $headers;
730 $return->url = (string) $url;
731 $return->body = '';
732
733 if (!$options['filename']) {
734 $pos = strpos($headers, "\r\n\r\n");
735 if ($pos === false) {
736 // Crap!
737 throw new Exception('Missing header/body separator', 'requests.no_crlf_separator');
738 }
739
740 $headers = substr($return->raw, 0, $pos);
741 // Headers will always be separated from the body by two new lines - `\n\r\n\r`.
742 $body = substr($return->raw, $pos + 4);
743 if (!empty($body)) {
744 $return->body = $body;
745 }
746 }
747
748 // Pretend CRLF = LF for compatibility (RFC 2616, section 19.3)
749 $headers = str_replace("\r\n", "\n", $headers);
750 // Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2)
751 $headers = preg_replace('/\n[ \t]/', ' ', $headers);
752 $headers = explode("\n", $headers);
753 preg_match('#^HTTP/(1\.\d)[ \t]+(\d+)#i', array_shift($headers), $matches);
754 if (empty($matches)) {
755 throw new Exception('Response could not be parsed', 'noversion', $headers);
756 }
757
758 $return->protocol_version = (float) $matches[1];
759 $return->status_code = (int) $matches[2];
760 if ($return->status_code >= 200 && $return->status_code < 300) {
761 $return->success = true;
762 }
763
764 foreach ($headers as $header) {
765 list($key, $value) = explode(':', $header, 2);
766 $value = trim($value);
767 preg_replace('#(\s+)#i', ' ', $value);
768 $return->headers[$key] = $value;
769 }
770
771 if (isset($return->headers['transfer-encoding'])) {
772 $return->body = self::decode_chunked($return->body);
773 unset($return->headers['transfer-encoding']);
774 }
775
776 if (isset($return->headers['content-encoding'])) {
777 $return->body = self::decompress($return->body);
778 }
779
780 //fsockopen and cURL compatibility
781 if (isset($return->headers['connection'])) {
782 unset($return->headers['connection']);
783 }
784
785 $options['hooks']->dispatch('requests.before_redirect_check', [&$return, $req_headers, $req_data, $options]);
786
787 if ($return->is_redirect() && $options['follow_redirects'] === true) {
788 if (isset($return->headers['location']) && $options['redirected'] < $options['redirects']) {
789 if ($return->status_code === 303) {
790 $options['type'] = self::GET;
791 }
792
793 $options['redirected']++;
794 $location = $return->headers['location'];
795 if (strpos($location, 'http://') !== 0 && strpos($location, 'https://') !== 0) {
796 // relative redirect, for compatibility make it absolute
797 $location = Iri::absolutize($url, $location);
798 $location = $location->uri;
799 }
800
801 $hook_args = [
802 &$location,
803 &$req_headers,
804 &$req_data,
805 &$options,
806 $return,
807 ];
808 $options['hooks']->dispatch('requests.before_redirect', $hook_args);
809 $redirected = self::request($location, $req_headers, $req_data, $options['type'], $options);
810 $redirected->history[] = $return;
811 return $redirected;
812 } elseif ($options['redirected'] >= $options['redirects']) {
813 throw new Exception('Too many redirects', 'toomanyredirects', $return);
814 }
815 }
816
817 $return->redirects = $options['redirected'];
818
819 $options['hooks']->dispatch('requests.after_request', [&$return, $req_headers, $req_data, $options]);
820 return $return;
821 }
822
823 /**
824 * Callback for `transport.internal.parse_response`
825 *
826 * Internal use only. Converts a raw HTTP response to a \WpOrg\Requests\Response
827 * while still executing a multiple request.
828 *
829 * `$response` is either set to a \WpOrg\Requests\Response instance, or a \WpOrg\Requests\Exception object
830 *
831 * @param string $response Full response text including headers and body (will be overwritten with Response instance)
832 * @param array $request Request data as passed into {@see \WpOrg\Requests\Requests::request_multiple()}
833 * @return void
834 */
835 public static function parse_multiple(&$response, $request) {
836 try {
837 $url = $request['url'];
838 $headers = $request['headers'];
839 $data = $request['data'];
840 $options = $request['options'];
841 $response = self::parse_response($response, $url, $headers, $data, $options);
842 } catch (Exception $e) {
843 $response = $e;
844 }
845 }
846
847 /**
848 * Decoded a chunked body as per RFC 2616
849 *
850 * @link https://tools.ietf.org/html/rfc2616#section-3.6.1
851 * @param string $data Chunked body
852 * @return string Decoded body
853 */
854 protected static function decode_chunked($data) {
855 if (!preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', trim($data))) {
856 return $data;
857 }
858
859 $decoded = '';
860 $encoded = $data;
861
862 while (true) {
863 $is_chunked = (bool) preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', $encoded, $matches);
864 if (!$is_chunked) {
865 // Looks like it's not chunked after all
866 return $data;
867 }
868
869 $length = hexdec(trim($matches[1]));
870 if ($length === 0) {
871 // Ignore trailer headers
872 return $decoded;
873 }
874
875 $chunk_length = strlen($matches[0]);
876 $decoded .= substr($encoded, $chunk_length, $length);
877 $encoded = substr($encoded, $chunk_length + $length + 2);
878
879 if (trim($encoded) === '0' || empty($encoded)) {
880 return $decoded;
881 }
882 }
883
884 // We'll never actually get down here
885 // @codeCoverageIgnoreStart
886 }
887 // @codeCoverageIgnoreEnd
888
889 /**
890 * Convert a key => value array to a 'key: value' array for headers
891 *
892 * @param iterable $dictionary Dictionary of header values
893 * @return array List of headers
894 *
895 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not iterable.
896 */
897 public static function flatten($dictionary) {
898 if (InputValidator::is_iterable($dictionary) === false) {
899 throw InvalidArgument::create(1, '$dictionary', 'iterable', gettype($dictionary));
900 }
901
902 $return = [];
903 foreach ($dictionary as $key => $value) {
904 $return[] = sprintf('%s: %s', $key, $value);
905 }
906
907 return $return;
908 }
909
910 /**
911 * Decompress an encoded body
912 *
913 * Implements gzip, compress and deflate. Guesses which it is by attempting
914 * to decode.
915 *
916 * @param string $data Compressed data in one of the above formats
917 * @return string Decompressed string
918 *
919 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string.
920 */
921 public static function decompress($data) {
922 if (is_string($data) === false) {
923 throw InvalidArgument::create(1, '$data', 'string', gettype($data));
924 }
925
926 if (trim($data) === '') {
927 // Empty body does not need further processing.
928 return $data;
929 }
930
931 $marker = substr($data, 0, 2);
932 if (!isset(self::$magic_compression_headers[$marker])) {
933 // Not actually compressed. Probably cURL ruining this for us.
934 return $data;
935 }
936
937 if (function_exists('gzdecode')) {
938 $decoded = @gzdecode($data);
939 if ($decoded !== false) {
940 return $decoded;
941 }
942 }
943
944 if (function_exists('gzinflate')) {
945 $decoded = @gzinflate($data);
946 if ($decoded !== false) {
947 return $decoded;
948 }
949 }
950
951 $decoded = self::compatible_gzinflate($data);
952 if ($decoded !== false) {
953 return $decoded;
954 }
955
956 if (function_exists('gzuncompress')) {
957 $decoded = @gzuncompress($data);
958 if ($decoded !== false) {
959 return $decoded;
960 }
961 }
962
963 return $data;
964 }
965
966 /**
967 * Decompression of deflated string while staying compatible with the majority of servers.
968 *
969 * Certain Servers will return deflated data with headers which PHP's gzinflate()
970 * function cannot handle out of the box. The following function has been created from
971 * various snippets on the gzinflate() PHP documentation.
972 *
973 * Warning: Magic numbers within. Due to the potential different formats that the compressed
974 * data may be returned in, some "magic offsets" are needed to ensure proper decompression
975 * takes place. For a simple progmatic way to determine the magic offset in use, see:
976 * https://core.trac.wordpress.org/ticket/18273
977 *
978 * @since 1.6.0
979 * @link https://core.trac.wordpress.org/ticket/18273
980 * @link https://www.php.net/gzinflate#70875
981 * @link https://www.php.net/gzinflate#77336
982 *
983 * @param string $gz_data String to decompress.
984 * @return string|bool False on failure.
985 *
986 * @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string.
987 */
988 public static function compatible_gzinflate($gz_data) {
989 if (is_string($gz_data) === false) {
990 throw InvalidArgument::create(1, '$gz_data', 'string', gettype($gz_data));
991 }
992
993 if (trim($gz_data) === '') {
994 return false;
995 }
996
997 // Compressed data might contain a full zlib header, if so strip it for
998 // gzinflate()
999 if (substr($gz_data, 0, 3) === "\x1f\x8b\x08") {
1000 $i = 10;
1001 $flg = ord(substr($gz_data, 3, 1));
1002 if ($flg > 0) {
1003 if ($flg & 4) {
1004 list($xlen) = unpack('v', substr($gz_data, $i, 2));
1005 $i += 2 + $xlen;
1006 }
1007
1008 if ($flg & 8) {
1009 $i = strpos($gz_data, "\0", $i) + 1;
1010 }
1011
1012 if ($flg & 16) {
1013 $i = strpos($gz_data, "\0", $i) + 1;
1014 }
1015
1016 if ($flg & 2) {
1017 $i += 2;
1018 }
1019 }
1020
1021 $decompressed = self::compatible_gzinflate(substr($gz_data, $i));
1022 if ($decompressed !== false) {
1023 return $decompressed;
1024 }
1025 }
1026
1027 // If the data is Huffman Encoded, we must first strip the leading 2
1028 // byte Huffman marker for gzinflate()
1029 // The response is Huffman coded by many compressors such as
1030 // java.util.zip.Deflater, Ruby's Zlib::Deflate, and .NET's
1031 // System.IO.Compression.DeflateStream.
1032 //
1033 // See https://decompres.blogspot.com/ for a quick explanation of this
1034 // data type
1035 $huffman_encoded = false;
1036
1037 // low nibble of first byte should be 0x08
1038 list(, $first_nibble) = unpack('h', $gz_data);
1039
1040 // First 2 bytes should be divisible by 0x1F
1041 list(, $first_two_bytes) = unpack('n', $gz_data);
1042
1043 if ($first_nibble === 0x08 && ($first_two_bytes % 0x1F) === 0) {
1044 $huffman_encoded = true;
1045 }
1046
1047 if ($huffman_encoded) {
1048 $decompressed = @gzinflate(substr($gz_data, 2));
1049 if ($decompressed !== false) {
1050 return $decompressed;
1051 }
1052 }
1053
1054 if (substr($gz_data, 0, 4) === "\x50\x4b\x03\x04") {
1055 // ZIP file format header
1056 // Offset 6: 2 bytes, General-purpose field
1057 // Offset 26: 2 bytes, filename length
1058 // Offset 28: 2 bytes, optional field length
1059 // Offset 30: Filename field, followed by optional field, followed
1060 // immediately by data
1061 list(, $general_purpose_flag) = unpack('v', substr($gz_data, 6, 2));
1062
1063 // If the file has been compressed on the fly, 0x08 bit is set of
1064 // the general purpose field. We can use this to differentiate
1065 // between a compressed document, and a ZIP file
1066 $zip_compressed_on_the_fly = ((0x08 & $general_purpose_flag) === 0x08);
1067
1068 if (!$zip_compressed_on_the_fly) {
1069 // Don't attempt to decode a compressed zip file
1070 return $gz_data;
1071 }
1072
1073 // Determine the first byte of data, based on the above ZIP header
1074 // offsets:
1075 $first_file_start = array_sum(unpack('v2', substr($gz_data, 26, 4)));
1076 $decompressed = @gzinflate(substr($gz_data, 30 + $first_file_start));
1077 if ($decompressed !== false) {
1078 return $decompressed;
1079 }
1080
1081 return false;
1082 }
1083
1084 // Finally fall back to straight gzinflate
1085 $decompressed = @gzinflate($gz_data);
1086 if ($decompressed !== false) {
1087 return $decompressed;
1088 }
1089
1090 // Fallback for all above failing, not expected, but included for
1091 // debugging and preventing regressions and to track stats
1092 $decompressed = @gzinflate(substr($gz_data, 2));
1093 if ($decompressed !== false) {
1094 return $decompressed;
1095 }
1096
1097 return false;
1098 }
1099}
1100
Ui Ux Design – Teachers Night Out https://cardgames4educators.com Wed, 16 Oct 2024 22:24:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://cardgames4educators.com/wp-content/uploads/2024/06/cropped-Card-4-Educators-logo-32x32.png Ui Ux Design – Teachers Night Out https://cardgames4educators.com 32 32 Masters In English How English Speaker https://cardgames4educators.com/masters-in-english-how-english-speaker/ https://cardgames4educators.com/masters-in-english-how-english-speaker/#comments Mon, 27 May 2024 08:54:45 +0000 https://themexriver.com/wp/kadu/?p=1

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1