PluginProbe
Autoptimize / 0.2
Autoptimize v0.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 0.2, at classes/minify-html.php

246 lines 7.6 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 * @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 }
246