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