HTTP_Encoder

Encode and send gzipped/deflated content.

Defined (1)

The class is defined in the following location(s).

/lib/Minify/HTTP/Encoder.php  
  1. class HTTP_Encoder { 
  2.  
  3. /** 
  4. * Should the encoder allow HTTP encoding to IE6?  
  5. *  
  6. * If you have many IE6 users and the bandwidth savings is worth troubling  
  7. * some of them, set this to true. 
  8. *  
  9. * By default, encoding is only offered to IE7+. When this is true,  
  10. * getAcceptedEncoding() will return an encoding for IE6 if its user agent 
  11. * string contains "SV1". This has been documented in many places as "safe",  
  12. * but there seem to be remaining, intermittent encoding bugs in patched  
  13. * IE6 on the wild web. 
  14. *  
  15. * @var bool 
  16. */ 
  17. public static $encodeToIe6 = true; 
  18.  
  19.  
  20. /** 
  21. * Default compression level for zlib operations 
  22. *  
  23. * This level is used if encode() is not given a $compressionLevel 
  24. *  
  25. * @var int 
  26. */ 
  27. public static $compressionLevel = 6; 
  28.  
  29.  
  30. /** 
  31. * Get an HTTP Encoder object 
  32. *  
  33. * @param array $spec options 
  34. *  
  35. * 'content': (string required) content to be encoded 
  36. *  
  37. * 'type': (string) if set, the Content-Type header will have this value. 
  38. *  
  39. * 'method: (string) only set this if you are forcing a particular encoding 
  40. * method. If not set, the best method will be chosen by getAcceptedEncoding() 
  41. * The available methods are 'gzip', 'deflate', 'compress', and '' (no 
  42. * encoding) 
  43. */ 
  44. public function __construct($spec)  
  45. $this->_useMbStrlen = (function_exists('mb_strlen') 
  46. && (ini_get('mbstring.func_overload') !== '') 
  47. && ((int)ini_get('mbstring.func_overload') & 2)); 
  48. $this->_content = $spec['content']; 
  49. $this->_headers['Content-Length'] = $this->_useMbStrlen 
  50. ? (string)mb_strlen($this->_content, '8bit') 
  51. : (string)strlen($this->_content); 
  52. if (isset($spec['type'])) { 
  53. $this->_headers['Content-Type'] = $spec['type']; 
  54. if (isset($spec['method']) 
  55. && in_array($spec['method'], array('gzip', 'deflate', ''))) 
  56. $this->_encodeMethod = array($spec['method'], $spec['method']); 
  57. } else { 
  58. $this->_encodeMethod = self::getAcceptedEncoding(); 
  59.  
  60. /** 
  61. * Get content in current form 
  62. *  
  63. * Call after encode() for encoded content. 
  64. *  
  65. * @return string 
  66. */ 
  67. public function getContent()  
  68. return $this->_content; 
  69.  
  70. /** 
  71. * Get array of output headers to be sent 
  72. *  
  73. * E.g. 
  74. * <code> 
  75. * array( 
  76. * 'Content-Length' => '615' 
  77. * , 'Content-Encoding' => 'x-gzip' 
  78. * , 'Vary' => 'Accept-Encoding' 
  79. * ) 
  80. * </code> 
  81. * @return array  
  82. */ 
  83. public function getHeaders() 
  84. return $this->_headers; 
  85.  
  86. /** 
  87. * Send output headers 
  88. *  
  89. * You must call this before headers are sent and it probably cannot be 
  90. * used in conjunction with zlib output buffering / mod_gzip. Errors are 
  91. * not handled purposefully. 
  92. *  
  93. * @see getHeaders() 
  94. */ 
  95. public function sendHeaders() 
  96. foreach ($this->_headers as $name => $val) { 
  97. header($name . ': ' . $val); 
  98.  
  99. /** 
  100. * Send output headers and content 
  101. *  
  102. * A shortcut for sendHeaders() and echo getContent() 
  103. * You must call this before headers are sent and it probably cannot be 
  104. * used in conjunction with zlib output buffering / mod_gzip. Errors are 
  105. * not handled purposefully. 
  106. */ 
  107. public function sendAll() 
  108. $this->sendHeaders(); 
  109. echo $this->_content; 
  110.  
  111. /** 
  112. * Determine the client's best encoding method from the HTTP Accept-Encoding  
  113. * header. 
  114. *  
  115. * If no Accept-Encoding header is set, or the browser is IE before v6 SP2,  
  116. * this will return ('', ''), the "identity" encoding. 
  117. *  
  118. * A syntax-aware scan is done of the Accept-Encoding, so the method must 
  119. * be non 0. The methods are favored in order of gzip, deflate, then  
  120. * compress. Deflate is always smallest and generally faster, but is  
  121. * rarely sent by servers, so client support could be buggier. 
  122. *  
  123. * @param bool $allowCompress allow the older compress encoding 
  124. *  
  125. * @param bool $allowDeflate allow the more recent deflate encoding 
  126. *  
  127. * @return array two values, 1st is the actual encoding method, 2nd is the 
  128. * alias of that method to use in the Content-Encoding header (some browsers 
  129. * call gzip "x-gzip" etc.) 
  130. */ 
  131. public static function getAcceptedEncoding($allowCompress = true, $allowDeflate = true) 
  132. // @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html 
  133.  
  134. if (! isset($_SERVER['HTTP_ACCEPT_ENCODING']) 
  135. || \W3TC\Util_Environment::is_zlib_enabled() 
  136. || headers_sent() 
  137. || self::isBuggyIe()) 
  138. return array('', ''); 
  139. $ae = $_SERVER['HTTP_ACCEPT_ENCODING']; 
  140. // gzip checks (quick) 
  141. if (0 === strpos($ae, 'gzip, ') // most browsers 
  142. || 0 === strpos($ae, 'deflate, gzip, ') // opera 
  143. ) { 
  144. if (function_exists('gzencode')) 
  145. return array('gzip', 'gzip'); 
  146. // gzip checks (slow) 
  147. if (preg_match( 
  148. '@(?:^|, )\\s*((?:x-)?gzip)\\s*(?:$|, |;\\s*q=(?:0\\.|1))@' 
  149. , $ae 
  150. , $m)) { 
  151. return array('gzip', $m[1]); 
  152.  
  153. return array('', ''); 
  154.  
  155. /** 
  156. * Encode (compress) the content 
  157. *  
  158. * If the encode method is '' (none) or compression level is 0, or the 'zlib' 
  159. * extension isn't loaded, we return false. 
  160. *  
  161. * Then the appropriate gz_* function is called to compress the content. If 
  162. * this fails, false is returned. 
  163. *  
  164. * The header "Vary: Accept-Encoding" is added. If encoding is successful,  
  165. * the Content-Length header is updated, and Content-Encoding is also added. 
  166. *  
  167. * @param int $compressionLevel given to zlib functions. If not given, the 
  168. * class default will be used. 
  169. *  
  170. * @return bool success true if the content was actually compressed 
  171. */ 
  172. public function encode($compressionLevel = null) 
  173. if (! self::isBuggyIe()) { 
  174. $this->_headers['Vary'] = 'Accept-Encoding'; 
  175. if (null === $compressionLevel) { 
  176. $compressionLevel = self::$compressionLevel; 
  177. if ('' === $this->_encodeMethod[0] 
  178. || ($compressionLevel == 0) 
  179. || !extension_loaded('zlib')) 
  180. return false; 
  181. if ($this->_encodeMethod[0] === 'deflate') { 
  182. $encoded = gzdeflate($this->_content, $compressionLevel); 
  183. } elseif ($this->_encodeMethod[0] === 'gzip') { 
  184. $encoded = gzencode($this->_content, $compressionLevel); 
  185. } else { 
  186. $encoded = gzcompress($this->_content, $compressionLevel); 
  187. if (false === $encoded) { 
  188. return false; 
  189. $this->_headers['Content-Length'] = $this->_useMbStrlen 
  190. ? (string)mb_strlen($encoded, '8bit') 
  191. : (string)strlen($encoded); 
  192. $this->_headers['Content-Encoding'] = $this->_encodeMethod[1]; 
  193. $this->_content = $encoded; 
  194. return true; 
  195.  
  196. /** 
  197. * Encode and send appropriate headers and content 
  198. * This is a convenience method for common use of the class 
  199. *  
  200. * @param string $content 
  201. *  
  202. * @param int $compressionLevel given to zlib functions. If not given, the 
  203. * class default will be used. 
  204. *  
  205. * @return bool success true if the content was actually compressed 
  206. */ 
  207. public static function output($content, $compressionLevel = null) 
  208. if (null === $compressionLevel) { 
  209. $compressionLevel = self::$compressionLevel; 
  210. $he = new HTTP_Encoder(array('content' => $content)); 
  211. $ret = $he->encode($compressionLevel); 
  212. $he->sendAll(); 
  213. return $ret; 
  214.  
  215. /** 
  216. * Is the browser an IE version earlier than 6 SP2? 
  217. * @return bool 
  218. */ 
  219. public static function isBuggyIe() 
  220. if (empty($_SERVER['HTTP_USER_AGENT'])) { 
  221. return false; 
  222. $ua = $_SERVER['HTTP_USER_AGENT']; 
  223. // quick escape for non-IEs 
  224. if (0 !== strpos($ua, 'Mozilla/4.0 (compatible; MSIE ') 
  225. || false !== strpos($ua, 'Opera')) { 
  226. return false; 
  227. // no regex = faaast 
  228. $version = (float)substr($ua, 30); 
  229. return self::$encodeToIe6 
  230. ? ($version < 6 || ($version == 6 && false === strpos($ua, 'SV1'))) 
  231. : ($version < 7); 
  232.  
  233. protected $_content = ''; 
  234. protected $_headers = array(); 
  235. protected $_encodeMethod = array('', ''); 
  236. protected $_useMbStrlen = false;