class-wp-http-encoding.php 6.5 KB

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