[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

/wp-includes/ -> class-requests.php (source)

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


Generated: Mon Jun 17 08:20:02 2019 Cross-referenced by PHPXref 0.7