wp/wp-includes/class-requests.php
changeset 18 be944660c56a
parent 16 a86126ab1dd4
child 21 48c4eec2b7e6
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
    86 	/**
    86 	/**
    87 	 * Current version of Requests
    87 	 * Current version of Requests
    88 	 *
    88 	 *
    89 	 * @var string
    89 	 * @var string
    90 	 */
    90 	 */
    91 	const VERSION = '1.7-3470169';
    91 	const VERSION = '1.8.1';
    92 
    92 
    93 	/**
    93 	/**
    94 	 * Registered transport classes
    94 	 * Registered transport classes
    95 	 *
    95 	 *
    96 	 * @var array
    96 	 * @var array
   141 			return;
   141 			return;
   142 		}
   142 		}
   143 
   143 
   144 		$file = str_replace('_', '/', $class);
   144 		$file = str_replace('_', '/', $class);
   145 		if (file_exists(dirname(__FILE__) . '/' . $file . '.php')) {
   145 		if (file_exists(dirname(__FILE__) . '/' . $file . '.php')) {
   146 			require_once(dirname(__FILE__) . '/' . $file . '.php');
   146 			require_once dirname(__FILE__) . '/' . $file . '.php';
   147 		}
   147 		}
   148 	}
   148 	}
   149 
   149 
   150 	/**
   150 	/**
   151 	 * Register the built-in autoloader
   151 	 * Register the built-in autoloader
   340 	 *    certificate file as a string. (Using true uses the system-wide root
   340 	 *    certificate file as a string. (Using true uses the system-wide root
   341 	 *    certificate store instead, but this may have different behaviour
   341 	 *    certificate store instead, but this may have different behaviour
   342 	 *    across transports.)
   342 	 *    across transports.)
   343 	 *    (string|boolean, default: library/Requests/Transport/cacert.pem)
   343 	 *    (string|boolean, default: library/Requests/Transport/cacert.pem)
   344 	 * - `verifyname`: Should we verify the common name in the SSL certificate?
   344 	 * - `verifyname`: Should we verify the common name in the SSL certificate?
   345 	 *    (boolean: default, true)
   345 	 *    (boolean, default: true)
   346 	 * - `data_format`: How should we send the `$data` parameter?
   346 	 * - `data_format`: How should we send the `$data` parameter?
   347 	 *    (string, one of 'query' or 'body', default: 'query' for
   347 	 *    (string, one of 'query' or 'body', default: 'query' for
   348 	 *    HEAD/GET/DELETE, 'body' for POST/PUT/OPTIONS/PATCH)
   348 	 *    HEAD/GET/DELETE, 'body' for POST/PUT/OPTIONS/PATCH)
   349 	 *
   349 	 *
   350 	 * @throws Requests_Exception On invalid URLs (`nonhttp`)
   350 	 * @throws Requests_Exception On invalid URLs (`nonhttp`)
   372 			if (is_string($options['transport'])) {
   372 			if (is_string($options['transport'])) {
   373 				$transport = new $transport();
   373 				$transport = new $transport();
   374 			}
   374 			}
   375 		}
   375 		}
   376 		else {
   376 		else {
   377 			$need_ssl = (0 === stripos($url, 'https://'));
   377 			$need_ssl     = (stripos($url, 'https://') === 0);
   378 			$capabilities = array('ssl' => $need_ssl);
   378 			$capabilities = array('ssl' => $need_ssl);
   379 			$transport = self::get_transport($capabilities);
   379 			$transport    = self::get_transport($capabilities);
   380 		}
   380 		}
   381 		$response = $transport->request($url, $headers, $data, $options);
   381 		$response = $transport->request($url, $headers, $data, $options);
   382 
   382 
   383 		$options['hooks']->dispatch('requests.before_parse', array(&$response, $url, $headers, $data, $type, $options));
   383 		$options['hooks']->dispatch('requests.before_parse', array(&$response, $url, $headers, $data, $type, $options));
   384 
   384 
   445 			}
   445 			}
   446 			if (!isset($request['type'])) {
   446 			if (!isset($request['type'])) {
   447 				$request['type'] = self::GET;
   447 				$request['type'] = self::GET;
   448 			}
   448 			}
   449 			if (!isset($request['options'])) {
   449 			if (!isset($request['options'])) {
   450 				$request['options'] = $options;
   450 				$request['options']         = $options;
   451 				$request['options']['type'] = $request['type'];
   451 				$request['options']['type'] = $request['type'];
   452 			}
   452 			}
   453 			else {
   453 			else {
   454 				if (empty($request['options']['type'])) {
   454 				if (empty($request['options']['type'])) {
   455 					$request['options']['type'] = $request['type'];
   455 					$request['options']['type'] = $request['type'];
   501 	 * @param boolean $multirequest Is this a multirequest?
   501 	 * @param boolean $multirequest Is this a multirequest?
   502 	 * @return array Default option values
   502 	 * @return array Default option values
   503 	 */
   503 	 */
   504 	protected static function get_default_options($multirequest = false) {
   504 	protected static function get_default_options($multirequest = false) {
   505 		$defaults = array(
   505 		$defaults = array(
   506 			'timeout' => 10,
   506 			'timeout'          => 10,
   507 			'connect_timeout' => 10,
   507 			'connect_timeout'  => 10,
   508 			'useragent' => 'php-requests/' . self::VERSION,
   508 			'useragent'        => 'php-requests/' . self::VERSION,
   509 			'protocol_version' => 1.1,
   509 			'protocol_version' => 1.1,
   510 			'redirected' => 0,
   510 			'redirected'       => 0,
   511 			'redirects' => 10,
   511 			'redirects'        => 10,
   512 			'follow_redirects' => true,
   512 			'follow_redirects' => true,
   513 			'blocking' => true,
   513 			'blocking'         => true,
   514 			'type' => self::GET,
   514 			'type'             => self::GET,
   515 			'filename' => false,
   515 			'filename'         => false,
   516 			'auth' => false,
   516 			'auth'             => false,
   517 			'proxy' => false,
   517 			'proxy'            => false,
   518 			'cookies' => false,
   518 			'cookies'          => false,
   519 			'max_bytes' => false,
   519 			'max_bytes'        => false,
   520 			'idn' => true,
   520 			'idn'              => true,
   521 			'hooks' => null,
   521 			'hooks'            => null,
   522 			'transport' => null,
   522 			'transport'        => null,
   523 			'verify' => Requests::get_certificate_path(),
   523 			'verify'           => self::get_certificate_path(),
   524 			'verifyname' => true,
   524 			'verifyname'       => true,
   525 		);
   525 		);
   526 		if ($multirequest !== false) {
   526 		if ($multirequest !== false) {
   527 			$defaults['complete'] = null;
   527 			$defaults['complete'] = null;
   528 		}
   528 		}
   529 		return $defaults;
   529 		return $defaults;
   533 	 * Get default certificate path.
   533 	 * Get default certificate path.
   534 	 *
   534 	 *
   535 	 * @return string Default certificate path.
   535 	 * @return string Default certificate path.
   536 	 */
   536 	 */
   537 	public static function get_certificate_path() {
   537 	public static function get_certificate_path() {
   538 		if ( ! empty( Requests::$certificate_path ) ) {
   538 		if (!empty(self::$certificate_path)) {
   539 			return Requests::$certificate_path;
   539 			return self::$certificate_path;
   540 		}
   540 		}
   541 
   541 
   542 		return dirname(__FILE__) . '/Requests/Transport/cacert.pem';
   542 		return dirname(__FILE__) . '/Requests/Transport/cacert.pem';
   543 	}
   543 	}
   544 
   544 
   545 	/**
   545 	/**
   546 	 * Set default certificate path.
   546 	 * Set default certificate path.
   547 	 *
   547 	 *
   548 	 * @param string $path Certificate path, pointing to a PEM file.
   548 	 * @param string $path Certificate path, pointing to a PEM file.
   549 	 */
   549 	 */
   550 	public static function set_certificate_path( $path ) {
   550 	public static function set_certificate_path($path) {
   551 		Requests::$certificate_path = $path;
   551 		self::$certificate_path = $path;
   552 	}
   552 	}
   553 
   553 
   554 	/**
   554 	/**
   555 	 * Set the default values
   555 	 * Set the default values
   556 	 *
   556 	 *
   593 		if ($options['cookies'] !== false) {
   593 		if ($options['cookies'] !== false) {
   594 			$options['cookies']->register($options['hooks']);
   594 			$options['cookies']->register($options['hooks']);
   595 		}
   595 		}
   596 
   596 
   597 		if ($options['idn'] !== false) {
   597 		if ($options['idn'] !== false) {
   598 			$iri = new Requests_IRI($url);
   598 			$iri       = new Requests_IRI($url);
   599 			$iri->host = Requests_IDNAEncoder::encode($iri->ihost);
   599 			$iri->host = Requests_IDNAEncoder::encode($iri->ihost);
   600 			$url = $iri->uri;
   600 			$url       = $iri->uri;
   601 		}
   601 		}
   602 
   602 
   603 		// Massage the type to ensure we support it.
   603 		// Massage the type to ensure we support it.
   604 		$type = strtoupper($type);
   604 		$type = strtoupper($type);
   605 
   605 
   606 		if (!isset($options['data_format'])) {
   606 		if (!isset($options['data_format'])) {
   607 			if (in_array($type, array(self::HEAD, self::GET, self::DELETE))) {
   607 			if (in_array($type, array(self::HEAD, self::GET, self::DELETE), true)) {
   608 				$options['data_format'] = 'query';
   608 				$options['data_format'] = 'query';
   609 			}
   609 			}
   610 			else {
   610 			else {
   611 				$options['data_format'] = 'body';
   611 				$options['data_format'] = 'body';
   612 			}
   612 			}
   631 		$return = new Requests_Response();
   631 		$return = new Requests_Response();
   632 		if (!$options['blocking']) {
   632 		if (!$options['blocking']) {
   633 			return $return;
   633 			return $return;
   634 		}
   634 		}
   635 
   635 
   636 		$return->raw = $headers;
   636 		$return->raw  = $headers;
   637 		$return->url = $url;
   637 		$return->url  = (string) $url;
       
   638 		$return->body = '';
   638 
   639 
   639 		if (!$options['filename']) {
   640 		if (!$options['filename']) {
   640 			if (($pos = strpos($headers, "\r\n\r\n")) === false) {
   641 			$pos = strpos($headers, "\r\n\r\n");
       
   642 			if ($pos === false) {
   641 				// Crap!
   643 				// Crap!
   642 				throw new Requests_Exception('Missing header/body separator', 'requests.no_crlf_separator');
   644 				throw new Requests_Exception('Missing header/body separator', 'requests.no_crlf_separator');
   643 			}
   645 			}
   644 
   646 
   645 			$headers = substr($return->raw, 0, $pos);
   647 			$headers = substr($return->raw, 0, $pos);
   646 			$return->body = substr($return->raw, $pos + strlen("\n\r\n\r"));
   648 			// Headers will always be separated from the body by two new lines - `\n\r\n\r`.
   647 		}
   649 			$body = substr($return->raw, $pos + 4);
   648 		else {
   650 			if (!empty($body)) {
   649 			$return->body = '';
   651 				$return->body = $body;
       
   652 			}
   650 		}
   653 		}
   651 		// Pretend CRLF = LF for compatibility (RFC 2616, section 19.3)
   654 		// Pretend CRLF = LF for compatibility (RFC 2616, section 19.3)
   652 		$headers = str_replace("\r\n", "\n", $headers);
   655 		$headers = str_replace("\r\n", "\n", $headers);
   653 		// Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2)
   656 		// Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2)
   654 		$headers = preg_replace('/\n[ \t]/', ' ', $headers);
   657 		$headers = preg_replace('/\n[ \t]/', ' ', $headers);
   656 		preg_match('#^HTTP/(1\.\d)[ \t]+(\d+)#i', array_shift($headers), $matches);
   659 		preg_match('#^HTTP/(1\.\d)[ \t]+(\d+)#i', array_shift($headers), $matches);
   657 		if (empty($matches)) {
   660 		if (empty($matches)) {
   658 			throw new Requests_Exception('Response could not be parsed', 'noversion', $headers);
   661 			throw new Requests_Exception('Response could not be parsed', 'noversion', $headers);
   659 		}
   662 		}
   660 		$return->protocol_version = (float) $matches[1];
   663 		$return->protocol_version = (float) $matches[1];
   661 		$return->status_code = (int) $matches[2];
   664 		$return->status_code      = (int) $matches[2];
   662 		if ($return->status_code >= 200 && $return->status_code < 300) {
   665 		if ($return->status_code >= 200 && $return->status_code < 300) {
   663 			$return->success = true;
   666 			$return->success = true;
   664 		}
   667 		}
   665 
   668 
   666 		foreach ($headers as $header) {
   669 		foreach ($headers as $header) {
   667 			list($key, $value) = explode(':', $header, 2);
   670 			list($key, $value) = explode(':', $header, 2);
   668 			$value = trim($value);
   671 			$value             = trim($value);
   669 			preg_replace('#(\s+)#i', ' ', $value);
   672 			preg_replace('#(\s+)#i', ' ', $value);
   670 			$return->headers[$key] = $value;
   673 			$return->headers[$key] = $value;
   671 		}
   674 		}
   672 		if (isset($return->headers['transfer-encoding'])) {
   675 		if (isset($return->headers['transfer-encoding'])) {
   673 			$return->body = self::decode_chunked($return->body);
   676 			$return->body = self::decode_chunked($return->body);
   700 				$hook_args = array(
   703 				$hook_args = array(
   701 					&$location,
   704 					&$location,
   702 					&$req_headers,
   705 					&$req_headers,
   703 					&$req_data,
   706 					&$req_data,
   704 					&$options,
   707 					&$options,
   705 					$return
   708 					$return,
   706 				);
   709 				);
   707 				$options['hooks']->dispatch('requests.before_redirect', $hook_args);
   710 				$options['hooks']->dispatch('requests.before_redirect', $hook_args);
   708 				$redirected = self::request($location, $req_headers, $req_data, $options['type'], $options);
   711 				$redirected            = self::request($location, $req_headers, $req_data, $options['type'], $options);
   709 				$redirected->history[] = $return;
   712 				$redirected->history[] = $return;
   710 				return $redirected;
   713 				return $redirected;
   711 			}
   714 			}
   712 			elseif ($options['redirected'] >= $options['redirects']) {
   715 			elseif ($options['redirected'] >= $options['redirects']) {
   713 				throw new Requests_Exception('Too many redirects', 'toomanyredirects', $return);
   716 				throw new Requests_Exception('Too many redirects', 'toomanyredirects', $return);
   730 	 * @param array $request Request data as passed into {@see Requests::request_multiple()}
   733 	 * @param array $request Request data as passed into {@see Requests::request_multiple()}
   731 	 * @return null `$response` is either set to a Requests_Response instance, or a Requests_Exception object
   734 	 * @return null `$response` is either set to a Requests_Response instance, or a Requests_Exception object
   732 	 */
   735 	 */
   733 	public static function parse_multiple(&$response, $request) {
   736 	public static function parse_multiple(&$response, $request) {
   734 		try {
   737 		try {
   735 			$url = $request['url'];
   738 			$url      = $request['url'];
   736 			$headers = $request['headers'];
   739 			$headers  = $request['headers'];
   737 			$data = $request['data'];
   740 			$data     = $request['data'];
   738 			$options = $request['options'];
   741 			$options  = $request['options'];
   739 			$response = self::parse_response($response, $url, $headers, $data, $options);
   742 			$response = self::parse_response($response, $url, $headers, $data, $options);
   740 		}
   743 		}
   741 		catch (Requests_Exception $e) {
   744 		catch (Requests_Exception $e) {
   742 			$response = $e;
   745 			$response = $e;
   743 		}
   746 		}
   752 	 */
   755 	 */
   753 	protected static function decode_chunked($data) {
   756 	protected static function decode_chunked($data) {
   754 		if (!preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', trim($data))) {
   757 		if (!preg_match('/^([0-9a-f]+)(?:;(?:[\w-]*)(?:=(?:(?:[\w-]*)*|"(?:[^\r\n])*"))?)*\r\n/i', trim($data))) {
   755 			return $data;
   758 			return $data;
   756 		}
   759 		}
   757 
       
   758 
       
   759 
   760 
   760 		$decoded = '';
   761 		$decoded = '';
   761 		$encoded = $data;
   762 		$encoded = $data;
   762 
   763 
   763 		while (true) {
   764 		while (true) {
   772 				// Ignore trailer headers
   773 				// Ignore trailer headers
   773 				return $decoded;
   774 				return $decoded;
   774 			}
   775 			}
   775 
   776 
   776 			$chunk_length = strlen($matches[0]);
   777 			$chunk_length = strlen($matches[0]);
   777 			$decoded .= substr($encoded, $chunk_length, $length);
   778 			$decoded     .= substr($encoded, $chunk_length, $length);
   778 			$encoded = substr($encoded, $chunk_length + $length + 2);
   779 			$encoded      = substr($encoded, $chunk_length + $length + 2);
   779 
   780 
   780 			if (trim($encoded) === '0' || empty($encoded)) {
   781 			if (trim($encoded) === '0' || empty($encoded)) {
   781 				return $decoded;
   782 				return $decoded;
   782 			}
   783 			}
   783 		}
   784 		}
   789 
   790 
   790 	/**
   791 	/**
   791 	 * Convert a key => value array to a 'key: value' array for headers
   792 	 * Convert a key => value array to a 'key: value' array for headers
   792 	 *
   793 	 *
   793 	 * @param array $array Dictionary of header values
   794 	 * @param array $array Dictionary of header values
   794 	 * @return string[] List of headers
   795 	 * @return array List of headers
   795 	 */
   796 	 */
   796 	public static function flatten($array) {
   797 	public static function flatten($array) {
   797 		$return = array();
   798 		$return = array();
   798 		foreach ($array as $key => $value) {
   799 		foreach ($array as $key => $value) {
   799 			$return[] = sprintf('%s: %s', $key, $value);
   800 			$return[] = sprintf('%s: %s', $key, $value);
   805 	 * Convert a key => value array to a 'key: value' array for headers
   806 	 * Convert a key => value array to a 'key: value' array for headers
   806 	 *
   807 	 *
   807 	 * @codeCoverageIgnore
   808 	 * @codeCoverageIgnore
   808 	 * @deprecated Misspelling of {@see Requests::flatten}
   809 	 * @deprecated Misspelling of {@see Requests::flatten}
   809 	 * @param array $array Dictionary of header values
   810 	 * @param array $array Dictionary of header values
   810 	 * @return string[] List of headers
   811 	 * @return array List of headers
   811 	 */
   812 	 */
   812 	public static function flattern($array) {
   813 	public static function flattern($array) {
   813 		return self::flatten($array);
   814 		return self::flatten($array);
   814 	}
   815 	}
   815 
   816 
   826 		if (substr($data, 0, 2) !== "\x1f\x8b" && substr($data, 0, 2) !== "\x78\x9c") {
   827 		if (substr($data, 0, 2) !== "\x1f\x8b" && substr($data, 0, 2) !== "\x78\x9c") {
   827 			// Not actually compressed. Probably cURL ruining this for us.
   828 			// Not actually compressed. Probably cURL ruining this for us.
   828 			return $data;
   829 			return $data;
   829 		}
   830 		}
   830 
   831 
   831 		if (function_exists('gzdecode') && ($decoded = @gzdecode($data)) !== false) {
   832 		if (function_exists('gzdecode')) {
       
   833 			// phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.gzdecodeFound -- Wrapped in function_exists() for PHP 5.2.
       
   834 			$decoded = @gzdecode($data);
       
   835 			if ($decoded !== false) {
       
   836 				return $decoded;
       
   837 			}
       
   838 		}
       
   839 
       
   840 		if (function_exists('gzinflate')) {
       
   841 			$decoded = @gzinflate($data);
       
   842 			if ($decoded !== false) {
       
   843 				return $decoded;
       
   844 			}
       
   845 		}
       
   846 
       
   847 		$decoded = self::compatible_gzinflate($data);
       
   848 		if ($decoded !== false) {
   832 			return $decoded;
   849 			return $decoded;
   833 		}
   850 		}
   834 		elseif (function_exists('gzinflate') && ($decoded = @gzinflate($data)) !== false) {
   851 
   835 			return $decoded;
   852 		if (function_exists('gzuncompress')) {
   836 		}
   853 			$decoded = @gzuncompress($data);
   837 		elseif (($decoded = self::compatible_gzinflate($data)) !== false) {
   854 			if ($decoded !== false) {
   838 			return $decoded;
   855 				return $decoded;
   839 		}
   856 			}
   840 		elseif (function_exists('gzuncompress') && ($decoded = @gzuncompress($data)) !== false) {
       
   841 			return $decoded;
       
   842 		}
   857 		}
   843 
   858 
   844 		return $data;
   859 		return $data;
   845 	}
   860 	}
   846 
   861 
   859 	 * @since 2.8.1
   874 	 * @since 2.8.1
   860 	 * @link https://core.trac.wordpress.org/ticket/18273
   875 	 * @link https://core.trac.wordpress.org/ticket/18273
   861 	 * @link https://secure.php.net/manual/en/function.gzinflate.php#70875
   876 	 * @link https://secure.php.net/manual/en/function.gzinflate.php#70875
   862 	 * @link https://secure.php.net/manual/en/function.gzinflate.php#77336
   877 	 * @link https://secure.php.net/manual/en/function.gzinflate.php#77336
   863 	 *
   878 	 *
   864 	 * @param string $gzData String to decompress.
   879 	 * @param string $gz_data String to decompress.
   865 	 * @return string|bool False on failure.
   880 	 * @return string|bool False on failure.
   866 	 */
   881 	 */
   867 	public static function compatible_gzinflate($gzData) {
   882 	public static function compatible_gzinflate($gz_data) {
   868 		// Compressed data might contain a full zlib header, if so strip it for
   883 		// Compressed data might contain a full zlib header, if so strip it for
   869 		// gzinflate()
   884 		// gzinflate()
   870 		if (substr($gzData, 0, 3) == "\x1f\x8b\x08") {
   885 		if (substr($gz_data, 0, 3) === "\x1f\x8b\x08") {
   871 			$i = 10;
   886 			$i   = 10;
   872 			$flg = ord(substr($gzData, 3, 1));
   887 			$flg = ord(substr($gz_data, 3, 1));
   873 			if ($flg > 0) {
   888 			if ($flg > 0) {
   874 				if ($flg & 4) {
   889 				if ($flg & 4) {
   875 					list($xlen) = unpack('v', substr($gzData, $i, 2));
   890 					list($xlen) = unpack('v', substr($gz_data, $i, 2));
   876 					$i = $i + 2 + $xlen;
   891 					$i         += 2 + $xlen;
   877 				}
   892 				}
   878 				if ($flg & 8) {
   893 				if ($flg & 8) {
   879 					$i = strpos($gzData, "\0", $i) + 1;
   894 					$i = strpos($gz_data, "\0", $i) + 1;
   880 				}
   895 				}
   881 				if ($flg & 16) {
   896 				if ($flg & 16) {
   882 					$i = strpos($gzData, "\0", $i) + 1;
   897 					$i = strpos($gz_data, "\0", $i) + 1;
   883 				}
   898 				}
   884 				if ($flg & 2) {
   899 				if ($flg & 2) {
   885 					$i = $i + 2;
   900 					$i += 2;
   886 				}
   901 				}
   887 			}
   902 			}
   888 			$decompressed = self::compatible_gzinflate(substr($gzData, $i));
   903 			$decompressed = self::compatible_gzinflate(substr($gz_data, $i));
   889 			if (false !== $decompressed) {
   904 			if ($decompressed !== false) {
   890 				return $decompressed;
   905 				return $decompressed;
   891 			}
   906 			}
   892 		}
   907 		}
   893 
   908 
   894 		// If the data is Huffman Encoded, we must first strip the leading 2
   909 		// If the data is Huffman Encoded, we must first strip the leading 2
   900 		// See https://decompres.blogspot.com/ for a quick explanation of this
   915 		// See https://decompres.blogspot.com/ for a quick explanation of this
   901 		// data type
   916 		// data type
   902 		$huffman_encoded = false;
   917 		$huffman_encoded = false;
   903 
   918 
   904 		// low nibble of first byte should be 0x08
   919 		// low nibble of first byte should be 0x08
   905 		list(, $first_nibble)    = unpack('h', $gzData);
   920 		list(, $first_nibble) = unpack('h', $gz_data);
   906 
   921 
   907 		// First 2 bytes should be divisible by 0x1F
   922 		// First 2 bytes should be divisible by 0x1F
   908 		list(, $first_two_bytes) = unpack('n', $gzData);
   923 		list(, $first_two_bytes) = unpack('n', $gz_data);
   909 
   924 
   910 		if (0x08 == $first_nibble && 0 == ($first_two_bytes % 0x1F)) {
   925 		if ($first_nibble === 0x08 && ($first_two_bytes % 0x1F) === 0) {
   911 			$huffman_encoded = true;
   926 			$huffman_encoded = true;
   912 		}
   927 		}
   913 
   928 
   914 		if ($huffman_encoded) {
   929 		if ($huffman_encoded) {
   915 			if (false !== ($decompressed = @gzinflate(substr($gzData, 2)))) {
   930 			$decompressed = @gzinflate(substr($gz_data, 2));
       
   931 			if ($decompressed !== false) {
   916 				return $decompressed;
   932 				return $decompressed;
   917 			}
   933 			}
   918 		}
   934 		}
   919 
   935 
   920 		if ("\x50\x4b\x03\x04" == substr($gzData, 0, 4)) {
   936 		if (substr($gz_data, 0, 4) === "\x50\x4b\x03\x04") {
   921 			// ZIP file format header
   937 			// ZIP file format header
   922 			// Offset 6: 2 bytes, General-purpose field
   938 			// Offset 6: 2 bytes, General-purpose field
   923 			// Offset 26: 2 bytes, filename length
   939 			// Offset 26: 2 bytes, filename length
   924 			// Offset 28: 2 bytes, optional field length
   940 			// Offset 28: 2 bytes, optional field length
   925 			// Offset 30: Filename field, followed by optional field, followed
   941 			// Offset 30: Filename field, followed by optional field, followed
   926 			// immediately by data
   942 			// immediately by data
   927 			list(, $general_purpose_flag) = unpack('v', substr($gzData, 6, 2));
   943 			list(, $general_purpose_flag) = unpack('v', substr($gz_data, 6, 2));
   928 
   944 
   929 			// If the file has been compressed on the fly, 0x08 bit is set of
   945 			// If the file has been compressed on the fly, 0x08 bit is set of
   930 			// the general purpose field. We can use this to differentiate
   946 			// the general purpose field. We can use this to differentiate
   931 			// between a compressed document, and a ZIP file
   947 			// between a compressed document, and a ZIP file
   932 			$zip_compressed_on_the_fly = (0x08 == (0x08 & $general_purpose_flag));
   948 			$zip_compressed_on_the_fly = ((0x08 & $general_purpose_flag) === 0x08);
   933 
   949 
   934 			if (!$zip_compressed_on_the_fly) {
   950 			if (!$zip_compressed_on_the_fly) {
   935 				// Don't attempt to decode a compressed zip file
   951 				// Don't attempt to decode a compressed zip file
   936 				return $gzData;
   952 				return $gz_data;
   937 			}
   953 			}
   938 
   954 
   939 			// Determine the first byte of data, based on the above ZIP header
   955 			// Determine the first byte of data, based on the above ZIP header
   940 			// offsets:
   956 			// offsets:
   941 			$first_file_start = array_sum(unpack('v2', substr($gzData, 26, 4)));
   957 			$first_file_start = array_sum(unpack('v2', substr($gz_data, 26, 4)));
   942 			if (false !== ($decompressed = @gzinflate(substr($gzData, 30 + $first_file_start)))) {
   958 			$decompressed     = @gzinflate(substr($gz_data, 30 + $first_file_start));
       
   959 			if ($decompressed !== false) {
   943 				return $decompressed;
   960 				return $decompressed;
   944 			}
   961 			}
   945 			return false;
   962 			return false;
   946 		}
   963 		}
   947 
   964 
   948 		// Finally fall back to straight gzinflate
   965 		// Finally fall back to straight gzinflate
   949 		if (false !== ($decompressed = @gzinflate($gzData))) {
   966 		$decompressed = @gzinflate($gz_data);
       
   967 		if ($decompressed !== false) {
   950 			return $decompressed;
   968 			return $decompressed;
   951 		}
   969 		}
   952 
   970 
   953 		// Fallback for all above failing, not expected, but included for
   971 		// Fallback for all above failing, not expected, but included for
   954 		// debugging and preventing regressions and to track stats
   972 		// debugging and preventing regressions and to track stats
   955 		if (false !== ($decompressed = @gzinflate(substr($gzData, 2)))) {
   973 		$decompressed = @gzinflate(substr($gz_data, 2));
       
   974 		if ($decompressed !== false) {
   956 			return $decompressed;
   975 			return $decompressed;
   957 		}
   976 		}
   958 
   977 
   959 		return false;
   978 		return false;
   960 	}
   979 	}