]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Minify/extlib/minify/min/lib/Minify/HTML.php
Added minify plugin
[quix0rs-gnu-social.git] / plugins / Minify / extlib / minify / min / lib / Minify / HTML.php
1 <?php
2 /**
3  * Class Minify_HTML  
4  * @package Minify
5  */
6
7 /**
8  * Compress HTML
9  *
10  * This is a heavy regex-based removal of whitespace, unnecessary comments and 
11  * tokens. IE conditional comments are preserved. There are also options to have
12  * STYLE and SCRIPT blocks compressed by callback functions. 
13  * 
14  * A test suite is available.
15  * 
16  * @package Minify
17  * @author Stephen Clay <steve@mrclay.org>
18  */
19 class Minify_HTML {
20
21     /**
22      * "Minify" an HTML page
23      *
24      * @param string $html
25      *
26      * @param array $options
27      *
28      * 'cssMinifier' : (optional) callback function to process content of STYLE
29      * elements.
30      * 
31      * 'jsMinifier' : (optional) callback function to process content of SCRIPT
32      * elements. Note: the type attribute is ignored.
33      * 
34      * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
35      * unset, minify will sniff for an XHTML doctype.
36      * 
37      * @return string
38      */
39     public static function minify($html, $options = array()) {
40         $min = new Minify_HTML($html, $options);
41         return $min->process();
42     }
43     
44     
45     /**
46      * Create a minifier object
47      *
48      * @param string $html
49      *
50      * @param array $options
51      *
52      * 'cssMinifier' : (optional) callback function to process content of STYLE
53      * elements.
54      * 
55      * 'jsMinifier' : (optional) callback function to process content of SCRIPT
56      * elements. Note: the type attribute is ignored.
57      * 
58      * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
59      * unset, minify will sniff for an XHTML doctype.
60      * 
61      * @return null
62      */
63     public function __construct($html, $options = array())
64     {
65         $this->_html = str_replace("\r\n", "\n", trim($html));
66         if (isset($options['xhtml'])) {
67             $this->_isXhtml = (bool)$options['xhtml'];
68         }
69         if (isset($options['cssMinifier'])) {
70             $this->_cssMinifier = $options['cssMinifier'];
71         }
72         if (isset($options['jsMinifier'])) {
73             $this->_jsMinifier = $options['jsMinifier'];
74         }
75     }
76     
77     
78     /**
79      * Minify the markeup given in the constructor
80      * 
81      * @return string
82      */
83     public function process()
84     {
85         if ($this->_isXhtml === null) {
86             $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
87         }
88         
89         $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
90         $this->_placeholders = array();
91         
92         // replace SCRIPTs (and minify) with placeholders
93         $this->_html = preg_replace_callback(
94             '/(\\s*)(<script\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
95             ,array($this, '_removeScriptCB')
96             ,$this->_html);
97         
98         // replace STYLEs (and minify) with placeholders
99         $this->_html = preg_replace_callback(
100             '/\\s*(<style\\b[^>]*?>)([\\s\\S]*?)<\\/style>\\s*/i'
101             ,array($this, '_removeStyleCB')
102             ,$this->_html);
103         
104         // remove HTML comments (not containing IE conditional comments).
105         $this->_html = preg_replace_callback(
106             '/<!--([\\s\\S]*?)-->/'
107             ,array($this, '_commentCB')
108             ,$this->_html);
109         
110         // replace PREs with placeholders
111         $this->_html = preg_replace_callback('/\\s*(<pre\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
112             ,array($this, '_removePreCB')
113             ,$this->_html);
114         
115         // replace TEXTAREAs with placeholders
116         $this->_html = preg_replace_callback(
117             '/\\s*(<textarea\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
118             ,array($this, '_removeTextareaCB')
119             ,$this->_html);
120         
121         // trim each line.
122         // @todo take into account attribute values that span multiple lines.
123         $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
124         
125         // remove ws around block/undisplayed elements
126         $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
127             .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
128             .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
129             .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
130             .'|ul)\\b[^>]*>)/i', '$1', $this->_html);
131         
132         // remove ws outside of all elements
133         $this->_html = preg_replace_callback(
134             '/>([^<]+)</'
135             ,array($this, '_outsideTagCB')
136             ,$this->_html);
137         
138         // use newlines before 1st attribute in open tags (to limit line lengths)
139         $this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
140         
141         // fill placeholders
142         $this->_html = str_replace(
143             array_keys($this->_placeholders)
144             ,array_values($this->_placeholders)
145             ,$this->_html
146         );
147         return $this->_html;
148     }
149     
150     protected function _commentCB($m)
151     {
152         return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
153             ? $m[0]
154             : '';
155     }
156     
157     protected function _reservePlace($content)
158     {
159         $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
160         $this->_placeholders[$placeholder] = $content;
161         return $placeholder;
162     }
163
164     protected $_isXhtml = null;
165     protected $_replacementHash = null;
166     protected $_placeholders = array();
167     protected $_cssMinifier = null;
168     protected $_jsMinifier = null;
169
170     protected function _outsideTagCB($m)
171     {
172         return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<';
173     }
174     
175     protected function _removePreCB($m)
176     {
177         return $this->_reservePlace($m[1]);
178     }
179     
180     protected function _removeTextareaCB($m)
181     {
182         return $this->_reservePlace($m[1]);
183     }
184
185     protected function _removeStyleCB($m)
186     {
187         $openStyle = $m[1];
188         $css = $m[2];
189         // remove HTML comments
190         $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
191         
192         // remove CDATA section markers
193         $css = $this->_removeCdata($css);
194         
195         // minify
196         $minifier = $this->_cssMinifier
197             ? $this->_cssMinifier
198             : 'trim';
199         $css = call_user_func($minifier, $css);
200         
201         return $this->_reservePlace($this->_needsCdata($css)
202             ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
203             : "{$openStyle}{$css}</style>"
204         );
205     }
206
207     protected function _removeScriptCB($m)
208     {
209         $openScript = $m[2];
210         $js = $m[3];
211         
212         // whitespace surrounding? preserve at least one space
213         $ws1 = ($m[1] === '') ? '' : ' ';
214         $ws2 = ($m[4] === '') ? '' : ' ';
215  
216         // remove HTML comments (and ending "//" if present)
217         $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
218             
219         // remove CDATA section markers
220         $js = $this->_removeCdata($js);
221         
222         // minify
223         $minifier = $this->_jsMinifier
224             ? $this->_jsMinifier
225             : 'trim'; 
226         $js = call_user_func($minifier, $js);
227         
228         return $this->_reservePlace($this->_needsCdata($js)
229             ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
230             : "{$ws1}{$openScript}{$js}</script>{$ws2}"
231         );
232     }
233
234     protected function _removeCdata($str)
235     {
236         return (false !== strpos($str, '<![CDATA['))
237             ? str_replace(array('<![CDATA[', ']]>'), '', $str)
238             : $str;
239     }
240     
241     protected function _needsCdata($str)
242     {
243         return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
244     }
245 }