PluginProbe
WP Super Minify • Minify, Compress and Cache HTML, CSS & JavaScript / trunk
WP Super Minify • Minify, Compress and Cache HTML, CSS & JavaScript vtrunk
trunk 1.0 1.1 1.2 1.3 1.3.1 1.3.2 1.4 1.5 1.5.1 1.6 2.0 2.0.1
wp-super-minify / includes / min / lib / HTTP / Encoder.php

Encoder.php in WP Super Minify • Minify, Compress and Cache HTML, CSS & JavaScript trunk, at includes/min/lib/HTTP/Encoder.php

340 lines 10.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class HTTP_Encoder
4 * @package Minify
5 * @subpackage HTTP
6 */
7
8 /**
9 * Encode and send gzipped/deflated content
10 *
11 * The "Vary: Accept-Encoding" header is sent. If the client allows encoding,
12 * Content-Encoding and Content-Length are added.
13 *
14 * <code>
15 * // Send a CSS file, compressed if possible
16 * $he = new HTTP_Encoder(array(
17 * 'content' => file_get_contents($cssFile)
18 * ,'type' => 'text/css'
19 * ));
20 * $he->encode();
21 * $he->sendAll();
22 * </code>
23 *
24 * <code>
25 * // Shortcut to encoding output
26 * header('Content-Type: text/css'); // needed if not HTML
27 * HTTP_Encoder::output($css);
28 * </code>
29 *
30 * <code>
31 * // Just sniff for the accepted encoding
32 * $encoding = HTTP_Encoder::getAcceptedEncoding();
33 * </code>
34 *
35 * For more control over headers, use getHeaders() and getData() and send your
36 * own output.
37 *
38 * Note: If you don't need header mgmt, use PHP's native gzencode, gzdeflate,
39 * and gzcompress functions for gzip, deflate, and compress-encoding
40 * respectively.
41 *
42 * @package Minify
43 * @subpackage HTTP
44 * @author Stephen Clay <steve@mrclay.org>
45 */
46 class HTTP_Encoder
47 {
48
49 /**
50 * Should the encoder allow HTTP encoding to IE6?
51 *
52 * If you have many IE6 users and the bandwidth savings is worth troubling
53 * some of them, set this to true.
54 *
55 * By default, encoding is only offered to IE7+. When this is true,
56 * getAcceptedEncoding() will return an encoding for IE6 if its user agent
57 * string contains "SV1". This has been documented in many places as "safe",
58 * but there seem to be remaining, intermittent encoding bugs in patched
59 * IE6 on the wild web.
60 *
61 * @var bool
62 */
63 public static $encodeToIe6 = true;
64
65 /**
66 * Default compression level for zlib operations
67 *
68 * This level is used if encode() is not given a $compressionLevel
69 *
70 * @var int
71 */
72 public static $compressionLevel = 6;
73
74 /**
75 * Get an HTTP Encoder object
76 *
77 * @param array $spec options
78 *
79 * 'content': (string required) content to be encoded
80 *
81 * 'type': (string) if set, the Content-Type header will have this value.
82 *
83 * 'method: (string) only set this if you are forcing a particular encoding
84 * method. If not set, the best method will be chosen by getAcceptedEncoding()
85 * The available methods are 'gzip', 'deflate', 'compress', and '' (no
86 * encoding)
87 */
88 public function __construct($spec)
89 {
90 $this->_useMbStrlen = (function_exists('mb_strlen')
91 && (ini_get('mbstring.func_overload') !== '')
92 && ((int)ini_get('mbstring.func_overload') & 2));
93 $this->_content = $spec['content'];
94 $this->_headers['Content-Length'] = $this->_useMbStrlen
95 ? (string)mb_strlen($this->_content, '8bit')
96 : (string)strlen($this->_content);
97 if (isset($spec['type'])) {
98 $this->_headers['Content-Type'] = $spec['type'];
99 }
100 if (isset($spec['method'])
101 && in_array($spec['method'], array('gzip', 'deflate', 'compress', ''))) {
102 $this->_encodeMethod = array($spec['method'], $spec['method']);
103 } else {
104 $this->_encodeMethod = self::getAcceptedEncoding();
105 }
106 }
107
108 /**
109 * Get content in current form
110 *
111 * Call after encode() for encoded content.
112 *
113 * @return string
114 */
115 public function getContent()
116 {
117 return $this->_content;
118 }
119
120 /**
121 * Get array of output headers to be sent
122 *
123 * E.g.
124 * <code>
125 * array(
126 * 'Content-Length' => '615'
127 * ,'Content-Encoding' => 'x-gzip'
128 * ,'Vary' => 'Accept-Encoding'
129 * )
130 * </code>
131 *
132 * @return array
133 */
134 public function getHeaders()
135 {
136 return $this->_headers;
137 }
138
139 /**
140 * Send output headers
141 *
142 * You must call this before headers are sent and it probably cannot be
143 * used in conjunction with zlib output buffering / mod_gzip. Errors are
144 * not handled purposefully.
145 *
146 * @see getHeaders()
147 */
148 public function sendHeaders()
149 {
150 foreach ($this->_headers as $name => $val) {
151 header($name . ': ' . $val);
152 }
153 }
154
155 /**
156 * Send output headers and content
157 *
158 * A shortcut for sendHeaders() and echo getContent()
159 *
160 * You must call this before headers are sent and it probably cannot be
161 * used in conjunction with zlib output buffering / mod_gzip. Errors are
162 * not handled purposefully.
163 */
164 public function sendAll()
165 {
166 $this->sendHeaders();
167 echo $this->_content;
168 }
169
170 /**
171 * Determine the client's best encoding method from the HTTP Accept-Encoding
172 * header.
173 *
174 * If no Accept-Encoding header is set, or the browser is IE before v6 SP2,
175 * this will return ('', ''), the "identity" encoding.
176 *
177 * A syntax-aware scan is done of the Accept-Encoding, so the method must
178 * be non 0. The methods are favored in order of gzip, deflate, then
179 * compress. Deflate is always smallest and generally faster, but is
180 * rarely sent by servers, so client support could be buggier.
181 *
182 * @param bool $allowCompress allow the older compress encoding
183 *
184 * @param bool $allowDeflate allow the more recent deflate encoding
185 *
186 * @return array two values, 1st is the actual encoding method, 2nd is the
187 * alias of that method to use in the Content-Encoding header (some browsers
188 * call gzip "x-gzip" etc.)
189 */
190 public static function getAcceptedEncoding($allowCompress = true, $allowDeflate = true)
191 {
192 // @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
193
194 if (! isset($_SERVER['HTTP_ACCEPT_ENCODING'])
195 || self::isBuggyIe()) {
196 return array('', '');
197 }
198 $ae = $_SERVER['HTTP_ACCEPT_ENCODING'];
199 // gzip checks (quick)
200 if (0 === strpos($ae, 'gzip,') // most browsers
201 || 0 === strpos($ae, 'deflate, gzip,') // opera
202 ) {
203 return array('gzip', 'gzip');
204 }
205 // gzip checks (slow)
206 if (preg_match(
207 '@(?:^|,)\\s*((?:x-)?gzip)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@',
208 $ae,
209 $m
210 )) {
211 return array('gzip', $m[1]);
212 }
213 if ($allowDeflate) {
214 // deflate checks
215 $aeRev = strrev($ae);
216 if (0 === strpos($aeRev, 'etalfed ,') // ie, webkit
217 || 0 === strpos($aeRev, 'etalfed,') // gecko
218 || 0 === strpos($ae, 'deflate,') // opera
219 // slow parsing
220 || preg_match(
221 '@(?:^|,)\\s*deflate\\s*(?:$|,|;\\s*q=(?:0\\.|1))@',
222 $ae
223 )) {
224 return array('deflate', 'deflate');
225 }
226 }
227 if ($allowCompress && preg_match(
228 '@(?:^|,)\\s*((?:x-)?compress)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@',
229 $ae,
230 $m
231 )) {
232 return array('compress', $m[1]);
233 }
234
235 return array('', '');
236 }
237
238 /**
239 * Encode (compress) the content
240 *
241 * If the encode method is '' (none) or compression level is 0, or the 'zlib'
242 * extension isn't loaded, we return false.
243 *
244 * Then the appropriate gz_* function is called to compress the content. If
245 * this fails, false is returned.
246 *
247 * The header "Vary: Accept-Encoding" is added. If encoding is successful,
248 * the Content-Length header is updated, and Content-Encoding is also added.
249 *
250 * @param int $compressionLevel given to zlib functions. If not given, the
251 * class default will be used.
252 *
253 * @return bool success true if the content was actually compressed
254 */
255 public function encode($compressionLevel = null)
256 {
257 if (! self::isBuggyIe()) {
258 $this->_headers['Vary'] = 'Accept-Encoding';
259 }
260 if (null === $compressionLevel) {
261 $compressionLevel = self::$compressionLevel;
262 }
263 if ('' === $this->_encodeMethod[0]
264 || ($compressionLevel == 0)
265 || !extension_loaded('zlib')) {
266 return false;
267 }
268 if ($this->_encodeMethod[0] === 'deflate') {
269 $encoded = gzdeflate($this->_content, $compressionLevel);
270 } elseif ($this->_encodeMethod[0] === 'gzip') {
271 $encoded = gzencode($this->_content, $compressionLevel);
272 } else {
273 $encoded = gzcompress($this->_content, $compressionLevel);
274 }
275 if (false === $encoded) {
276 return false;
277 }
278 $this->_headers['Content-Length'] = $this->_useMbStrlen
279 ? (string)mb_strlen($encoded, '8bit')
280 : (string)strlen($encoded);
281 $this->_headers['Content-Encoding'] = $this->_encodeMethod[1];
282 $this->_content = $encoded;
283
284 return true;
285 }
286
287 /**
288 * Encode and send appropriate headers and content
289 *
290 * This is a convenience method for common use of the class
291 *
292 * @param string $content
293 *
294 * @param int $compressionLevel given to zlib functions. If not given, the
295 * class default will be used.
296 *
297 * @return bool success true if the content was actually compressed
298 */
299 public static function output($content, $compressionLevel = null)
300 {
301 if (null === $compressionLevel) {
302 $compressionLevel = self::$compressionLevel;
303 }
304 $he = new HTTP_Encoder(array('content' => $content));
305 $ret = $he->encode($compressionLevel);
306 $he->sendAll();
307
308 return $ret;
309 }
310
311 /**
312 * Is the browser an IE version earlier than 6 SP2?
313 *
314 * @return bool
315 */
316 public static function isBuggyIe()
317 {
318 if (empty($_SERVER['HTTP_USER_AGENT'])) {
319 return false;
320 }
321 $ua = $_SERVER['HTTP_USER_AGENT'];
322 // quick escape for non-IEs
323 if (0 !== strpos($ua, 'Mozilla/4.0 (compatible; MSIE ')
324 || false !== strpos($ua, 'Opera')) {
325 return false;
326 }
327 // no regex = faaast
328 $version = (float)substr($ua, 30);
329
330 return self::$encodeToIe6
331 ? ($version < 6 || ($version == 6 && false === strpos($ua, 'SV1')))
332 : ($version < 7);
333 }
334
335 protected $_content = '';
336 protected $_headers = array();
337 protected $_encodeMethod = array('', '');
338 protected $_useMbStrlen = false;
339 }
340