PluginProbe
Autoptimize / 1.6.2
Autoptimize v1.6.2
2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 All 107 releases
autoptimize / classes / minify-html.php

minify-html.php in Autoptimize 1.6.2, at classes/minify-html.php

269 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 * 'keepComments' : (optional boolean) should the HTML comments be kept
38 * in the HTML Code?
39 *
40 * @return string
41 */
42 public static function minify($html, $options = array()) {
43 $min = new Minify_HTML($html, $options);
44 return $min->process();
45 }
46
47
48 /**
49 * Create a minifier object
50 *
51 * @param string $html
52 *
53 * @param array $options
54 *
55 * 'cssMinifier' : (optional) callback function to process content of STYLE
56 * elements.
57 *
58 * 'jsMinifier' : (optional) callback function to process content of SCRIPT
59 * elements. Note: the type attribute is ignored.
60 *
61 * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
62 * unset, minify will sniff for an XHTML doctype.
63 *
64 * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
65 * unset, minify will sniff for an XHTML doctype.
66 *
67 * @return null
68 */
69 public function __construct($html, $options = array())
70 {
71 $this->_html = str_replace("\r\n", "\n", trim($html));
72 if (isset($options['xhtml'])) {
73 $this->_isXhtml = (bool)$options['xhtml'];
74 }
75 if (isset($options['cssMinifier'])) {
76 $this->_cssMinifier = $options['cssMinifier'];
77 }
78 if (isset($options['jsMinifier'])) {
79 $this->_jsMinifier = $options['jsMinifier'];
80 }
81 if (isset($options['keepComments'])) {
82 $this->_keepComments = $options['keepComments'];
83 }
84 }
85
86
87 /**
88 * Minify the markeup given in the constructor
89 *
90 * @return string
91 */
92 public function process()
93 {
94 if ($this->_isXhtml === null) {
95 $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
96 }
97
98 $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
99 $this->_placeholders = array();
100
101 // replace SCRIPTs (and minify) with placeholders
102 $this->_html = preg_replace_callback(
103 '/(\\s*)(<script\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
104 ,array($this, '_removeScriptCB')
105 ,$this->_html);
106
107 // replace STYLEs (and minify) with placeholders
108 $this->_html = preg_replace_callback(
109 '/\\s*(<style\\b[^>]*?>)([\\s\\S]*?)<\\/style>\\s*/i'
110 ,array($this, '_removeStyleCB')
111 ,$this->_html);
112
113 // remove HTML comments (not containing IE conditional comments).
114 if ($this->_keepComments == false) {
115 $this->_html = preg_replace_callback(
116 '/<!--([\\s\\S]*?)-->/'
117 ,array($this, '_commentCB')
118 ,$this->_html);
119 }
120
121 // replace PREs with placeholders
122 $this->_html = preg_replace_callback('/\\s*(<pre\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
123 ,array($this, '_removePreCB')
124 ,$this->_html);
125
126 // replace TEXTAREAs with placeholders
127 $this->_html = preg_replace_callback(
128 '/\\s*(<textarea\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
129 ,array($this, '_removeTextareaCB')
130 ,$this->_html);
131
132 // replace data: URIs with placeholders
133 $this->_html = preg_replace_callback(
134 '/(=("|\')data:.*\\2)/Ui'
135 ,array($this, '_removeDataURICB')
136 ,$this->_html);
137
138 // trim each line.
139 // @todo take into account attribute values that span multiple lines.
140 $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
141
142 // remove ws around block/undisplayed elements
143 $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
144 .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
145 .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
146 .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
147 .'|ul)\\b[^>]*>)/i', '$1', $this->_html);
148
149 // remove ws outside of all elements
150 $this->_html = preg_replace_callback(
151 '/>([^<]+)</'
152 ,array($this, '_outsideTagCB')
153 ,$this->_html);
154
155 // use newlines before 1st attribute in open tags (to limit line lengths)
156 //$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
157
158 // fill placeholders
159 $this->_html = str_replace(
160 array_keys($this->_placeholders)
161 ,array_values($this->_placeholders)
162 ,$this->_html
163 );
164 return $this->_html;
165 }
166
167 protected function _commentCB($m)
168 {
169 return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
170 ? $m[0]
171 : '';
172 }
173
174 protected function _reservePlace($content)
175 {
176 $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
177 $this->_placeholders[$placeholder] = $content;
178 return $placeholder;
179 }
180
181 protected $_isXhtml = null;
182 protected $_replacementHash = null;
183 protected $_placeholders = array();
184 protected $_cssMinifier = null;
185 protected $_jsMinifier = null;
186 protected $_keepComments = false;
187
188 protected function _outsideTagCB($m)
189 {
190 return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<';
191 }
192
193 protected function _removePreCB($m)
194 {
195 return $this->_reservePlace($m[1]);
196 }
197
198 protected function _removeTextareaCB($m)
199 {
200 return $this->_reservePlace($m[1]);
201 }
202
203 protected function _removeDataURICB($m)
204 {
205 return $this->_reservePlace($m[1]);
206 }
207
208 protected function _removeStyleCB($m)
209 {
210 $openStyle = $m[1];
211 $css = $m[2];
212 // remove HTML comments
213 $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
214
215 // remove CDATA section markers
216 $css = $this->_removeCdata($css);
217
218 // minify
219 $minifier = $this->_cssMinifier
220 ? $this->_cssMinifier
221 : 'trim';
222 $css = call_user_func($minifier, $css);
223
224 return $this->_reservePlace($this->_needsCdata($css)
225 ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
226 : "{$openStyle}{$css}</style>"
227 );
228 }
229
230 protected function _removeScriptCB($m)
231 {
232 $openScript = $m[2];
233 $js = $m[3];
234
235 // whitespace surrounding? preserve at least one space
236 $ws1 = ($m[1] === '') ? '' : ' ';
237 $ws2 = ($m[4] === '') ? '' : ' ';
238
239 // remove HTML comments (and ending "//" if present)
240 $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
241
242 // remove CDATA section markers
243 $js = $this->_removeCdata($js);
244
245 // minify
246 $minifier = $this->_jsMinifier
247 ? $this->_jsMinifier
248 : 'trim';
249 $js = call_user_func($minifier, $js);
250
251 return $this->_reservePlace($this->_needsCdata($js)
252 ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
253 : "{$ws1}{$openScript}{$js}</script>{$ws2}"
254 );
255 }
256
257 protected function _removeCdata($str)
258 {
259 return (false !== strpos($str, '<![CDATA['))
260 ? str_replace(array('<![CDATA[', ']]>'), '', $str)
261 : $str;
262 }
263
264 protected function _needsCdata($str)
265 {
266 return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
267 }
268 }
269