class-wp-http-encoding.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. <?php
  2. /**
  3. * HTTP API: WP_Http_Encoding class
  4. *
  5. * @package WordPress
  6. * @subpackage HTTP
  7. * @since 4.4.0
  8. */
  9. /**
  10. * Core class used to implement deflate and gzip transfer encoding support for HTTP requests.
  11. *
  12. * Includes RFC 1950, RFC 1951, and RFC 1952.
  13. *
  14. * @since 2.8.0
  15. */
  16. class WP_Http_Encoding {
  17. /**
  18. * Compress raw string using the deflate format.
  19. *
  20. * Supports the RFC 1951 standard.
  21. *
  22. * @since 2.8.0
  23. *
  24. * @param string $raw String to compress.
  25. * @param int $level Optional, default is 9. Compression level, 9 is highest.
  26. * @param string $supports Optional, not used. When implemented it will choose the right compression based on what the server supports.
  27. * @return string|false False on failure.
  28. */
  29. public static function compress( $raw, $level = 9, $supports = null ) {
  30. return gzdeflate( $raw, $level );
  31. }
  32. /**
  33. * Decompression of deflated string.
  34. *
  35. * Will attempt to decompress using the RFC 1950 standard, and if that fails
  36. * then the RFC 1951 standard deflate will be attempted. Finally, the RFC
  37. * 1952 standard gzip decode will be attempted. If all fail, then the
  38. * original compressed string will be returned.
  39. *
  40. * @since 2.8.0
  41. *
  42. * @param string $compressed String to decompress.
  43. * @param int $length The optional length of the compressed data.
  44. * @return string|bool False on failure.
  45. */
  46. public static function decompress( $compressed, $length = null ) {
  47. if ( empty( $compressed ) ) {
  48. return $compressed;
  49. }
  50. $decompressed = @gzinflate( $compressed );
  51. if ( false !== $decompressed ) {
  52. return $decompressed;
  53. }
  54. $decompressed = self::compatible_gzinflate( $compressed );
  55. if ( false !== $decompressed ) {
  56. return $decompressed;
  57. }
  58. $decompressed = @gzuncompress( $compressed );
  59. if ( false !== $decompressed ) {
  60. return $decompressed;
  61. }
  62. if ( function_exists( 'gzdecode' ) ) {
  63. $decompressed = @gzdecode( $compressed );
  64. if ( false !== $decompressed ) {
  65. return $decompressed;
  66. }
  67. }
  68. return $compressed;
  69. }
  70. /**
  71. * Decompression of deflated string while staying compatible with the majority of servers.
  72. *
  73. * Certain Servers will return deflated data with headers which PHP's gzinflate()
  74. * function cannot handle out of the box. The following function has been created from
  75. * various snippets on the gzinflate() PHP documentation.
  76. *
  77. * Warning: Magic numbers within. Due to the potential different formats that the compressed
  78. * data may be returned in, some "magic offsets" are needed to ensure proper decompression
  79. * takes place. For a simple progmatic way to determine the magic offset in use, see:
  80. * https://core.trac.wordpress.org/ticket/18273
  81. *
  82. * @since 2.8.1
  83. * @link https://core.trac.wordpress.org/ticket/18273
  84. * @link https://secure.php.net/manual/en/function.gzinflate.php#70875
  85. * @link https://secure.php.net/manual/en/function.gzinflate.php#77336
  86. *
  87. * @param string $gzData String to decompress.
  88. * @return string|bool False on failure.
  89. */
  90. public static function compatible_gzinflate( $gzData ) {
  91. // Compressed data might contain a full header, if so strip it for gzinflate().
  92. if ( substr( $gzData, 0, 3 ) == "\x1f\x8b\x08" ) {
  93. $i = 10;
  94. $flg = ord( substr( $gzData, 3, 1 ) );
  95. if ( $flg > 0 ) {
  96. if ( $flg & 4 ) {
  97. list($xlen) = unpack( 'v', substr( $gzData, $i, 2 ) );
  98. $i = $i + 2 + $xlen;
  99. }
  100. if ( $flg & 8 ) {
  101. $i = strpos( $gzData, "\0", $i ) + 1;
  102. }
  103. if ( $flg & 16 ) {
  104. $i = strpos( $gzData, "\0", $i ) + 1;
  105. }
  106. if ( $flg & 2 ) {
  107. $i = $i + 2;
  108. }
  109. }
  110. $decompressed = @gzinflate( substr( $gzData, $i, -8 ) );
  111. if ( false !== $decompressed ) {
  112. return $decompressed;
  113. }
  114. }
  115. // Compressed data from java.util.zip.Deflater amongst others.
  116. $decompressed = @gzinflate( substr( $gzData, 2 ) );
  117. if ( false !== $decompressed ) {
  118. return $decompressed;
  119. }
  120. return false;
  121. }
  122. /**
  123. * What encoding types to accept and their priority values.
  124. *
  125. * @since 2.8.0
  126. *
  127. * @param string $url
  128. * @param array $args
  129. * @return string Types of encoding to accept.
  130. */
  131. public static function accept_encoding( $url, $args ) {
  132. $type = array();
  133. $compression_enabled = self::is_available();
  134. if ( ! $args['decompress'] ) { // Decompression specifically disabled.
  135. $compression_enabled = false;
  136. } elseif ( $args['stream'] ) { // Disable when streaming to file.
  137. $compression_enabled = false;
  138. } elseif ( isset( $args['limit_response_size'] ) ) { // If only partial content is being requested, we won't be able to decompress it.
  139. $compression_enabled = false;
  140. }
  141. if ( $compression_enabled ) {
  142. if ( function_exists( 'gzinflate' ) ) {
  143. $type[] = 'deflate;q=1.0';
  144. }
  145. if ( function_exists( 'gzuncompress' ) ) {
  146. $type[] = 'compress;q=0.5';
  147. }
  148. if ( function_exists( 'gzdecode' ) ) {
  149. $type[] = 'gzip;q=0.5';
  150. }
  151. }
  152. /**
  153. * Filters the allowed encoding types.
  154. *
  155. * @since 3.6.0
  156. *
  157. * @param array $type Encoding types allowed. Accepts 'gzinflate',
  158. * 'gzuncompress', 'gzdecode'.
  159. * @param string $url URL of the HTTP request.
  160. * @param array $args HTTP request arguments.
  161. */
  162. $type = apply_filters( 'wp_http_accept_encoding', $type, $url, $args );
  163. return implode( ', ', $type );
  164. }
  165. /**
  166. * What encoding the content used when it was compressed to send in the headers.
  167. *
  168. * @since 2.8.0
  169. *
  170. * @return string Content-Encoding string to send in the header.
  171. */
  172. public static function content_encoding() {
  173. return 'deflate';
  174. }
  175. /**
  176. * Whether the content be decoded based on the headers.
  177. *
  178. * @since 2.8.0
  179. *
  180. * @param array|string $headers All of the available headers.
  181. * @return bool
  182. */
  183. public static function should_decode( $headers ) {
  184. if ( is_array( $headers ) ) {
  185. if ( array_key_exists( 'content-encoding', $headers ) && ! empty( $headers['content-encoding'] ) ) {
  186. return true;
  187. }
  188. } elseif ( is_string( $headers ) ) {
  189. return ( stripos( $headers, 'content-encoding:' ) !== false );
  190. }
  191. return false;
  192. }
  193. /**
  194. * Whether decompression and compression are supported by the PHP version.
  195. *
  196. * Each function is tested instead of checking for the zlib extension, to
  197. * ensure that the functions all exist in the PHP version and aren't
  198. * disabled.
  199. *
  200. * @since 2.8.0
  201. *
  202. * @return bool
  203. */
  204. public static function is_available() {
  205. return ( function_exists( 'gzuncompress' ) || function_exists( 'gzdeflate' ) || function_exists( 'gzinflate' ) );
  206. }
  207. }