PluginProbe ʕ •ᴥ•ʔ
LiteSpeed Cache / 1.9.1.1
LiteSpeed Cache v1.9.1.1
trunk 1.0.15 1.9.1.1 2.9.9.2 3.6.4 4.6 5.7.0.1 6.5.4 7.0.0.1 7.0.1 7.1 7.2 7.3 7.3.0.1 7.4 7.5 7.5.0.1 7.6 7.6.1 7.6.2 7.7 7.8 7.8.0.1 7.8.1
litespeed-cache / lib / html_min.class.php
litespeed-cache / lib Last commit date
litespeed 8 years ago css_min.class.php 8 years ago css_min.colors.class.php 8 years ago css_min.utils.class.php 8 years ago html_min.class.php 8 years ago js_min.class.php 8 years ago litespeed-php-compatibility.func.php 8 years ago object-cache.php 8 years ago url_rewritter.class.php 8 years ago
html_min.class.php
259 lines
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 * @var boolean
23 */
24 protected $_jsCleanComments = true;
25
26 /**
27 * "Minify" an HTML page
28 *
29 * @param string $html
30 *
31 * @param array $options
32 *
33 * 'cssMinifier' : (optional) callback function to process content of STYLE
34 * elements.
35 *
36 * 'jsMinifier' : (optional) callback function to process content of SCRIPT
37 * elements. Note: the type attribute is ignored.
38 *
39 * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
40 * unset, minify will sniff for an XHTML doctype.
41 *
42 * @return string
43 */
44 public static function minify($html, $options = array())
45 {
46 $min = new self($html, $options);
47
48 return $min->process();
49 }
50
51 /**
52 * Create a minifier object
53 *
54 * @param string $html
55 *
56 * @param array $options
57 *
58 * 'cssMinifier' : (optional) callback function to process content of STYLE
59 * elements.
60 *
61 * 'jsMinifier' : (optional) callback function to process content of SCRIPT
62 * elements. Note: the type attribute is ignored.
63 *
64 * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
65 *
66 * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
67 * unset, minify will sniff for an XHTML doctype.
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['jsCleanComments'])) {
82 $this->_jsCleanComments = (bool)$options['jsCleanComments'];
83 }
84 }
85
86 /**
87 * Minify the markeup given in the constructor
88 *
89 * @return string
90 */
91 public function process()
92 {
93 if ($this->_isXhtml === null) {
94 $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
95 }
96
97 $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
98 $this->_placeholders = array();
99
100 // replace SCRIPTs (and minify) with placeholders
101 $this->_html = preg_replace_callback(
102 '/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
103 ,array($this, '_removeScriptCB')
104 ,$this->_html);
105
106 // replace STYLEs (and minify) with placeholders
107 $this->_html = preg_replace_callback(
108 '/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/i'
109 ,array($this, '_removeStyleCB')
110 ,$this->_html);
111
112 // remove HTML comments (not containing IE conditional comments).
113 $this->_html = preg_replace_callback(
114 '/<!--([\\s\\S]*?)-->/'
115 ,array($this, '_commentCB')
116 ,$this->_html);
117
118 // replace PREs with placeholders
119 $this->_html = preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
120 ,array($this, '_removePreCB')
121 ,$this->_html);
122
123 // replace TEXTAREAs with placeholders
124 $this->_html = preg_replace_callback(
125 '/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
126 ,array($this, '_removeTextareaCB')
127 ,$this->_html);
128
129 // trim each line.
130 // @todo take into account attribute values that span multiple lines.
131 $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
132
133 // remove ws around block/undisplayed elements
134 $this->_html = preg_replace('/\\s+(<\\/?(?:area|article|aside|base(?:font)?|blockquote|body'
135 .'|canvas|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|figcaption|figure|footer|form'
136 .'|frame(?:set)?|h[1-6]|head|header|hgroup|hr|html|legend|li|link|main|map|menu|meta|nav'
137 .'|ol|opt(?:group|ion)|output|p|param|section|t(?:able|body|head|d|h||r|foot|itle)'
138 .'|ul|video)\\b[^>]*>)/i', '$1', $this->_html);
139
140 // remove ws outside of all elements
141 $this->_html = preg_replace(
142 '/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?</'
143 ,'>$1$2$3<'
144 ,$this->_html);
145
146 // use newlines before 1st attribute in open tags (to limit line lengths)
147 // $this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
148
149 // fill placeholders
150 $this->_html = str_replace(
151 array_keys($this->_placeholders)
152 ,array_values($this->_placeholders)
153 ,$this->_html
154 );
155 // issue 229: multi-pass to catch scripts that didn't get replaced in textareas
156 $this->_html = str_replace(
157 array_keys($this->_placeholders)
158 ,array_values($this->_placeholders)
159 ,$this->_html
160 );
161
162 return $this->_html;
163 }
164
165 protected function _commentCB($m)
166 {
167 return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
168 ? $m[0]
169 : '';
170 }
171
172 protected function _reservePlace($content)
173 {
174 $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
175 $this->_placeholders[$placeholder] = $content;
176
177 return $placeholder;
178 }
179
180 protected $_isXhtml = null;
181 protected $_replacementHash = null;
182 protected $_placeholders = array();
183 protected $_cssMinifier = null;
184 protected $_jsMinifier = null;
185
186 protected function _removePreCB($m)
187 {
188 return $this->_reservePlace("<pre{$m[1]}");
189 }
190
191 protected function _removeTextareaCB($m)
192 {
193 return $this->_reservePlace("<textarea{$m[1]}");
194 }
195
196 protected function _removeStyleCB($m)
197 {
198 $openStyle = "<style{$m[1]}";
199 $css = $m[2];
200 // remove HTML comments
201 $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
202
203 // remove CDATA section markers
204 $css = $this->_removeCdata($css);
205
206 // minify
207 $minifier = $this->_cssMinifier
208 ? $this->_cssMinifier
209 : 'trim';
210 $css = call_user_func($minifier, $css);
211
212 return $this->_reservePlace($this->_needsCdata($css)
213 ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
214 : "{$openStyle}{$css}</style>"
215 );
216 }
217
218 protected function _removeScriptCB($m)
219 {
220 $openScript = "<script{$m[2]}";
221 $js = $m[3];
222
223 // whitespace surrounding? preserve at least one space
224 $ws1 = ($m[1] === '') ? '' : ' ';
225 $ws2 = ($m[4] === '') ? '' : ' ';
226
227 // remove HTML comments (and ending "//" if present)
228 if ($this->_jsCleanComments) {
229 $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
230 }
231
232 // remove CDATA section markers
233 $js = $this->_removeCdata($js);
234
235 // minify
236 $minifier = $this->_jsMinifier
237 ? $this->_jsMinifier
238 : 'trim';
239 $js = call_user_func($minifier, $js);
240
241 return $this->_reservePlace($this->_needsCdata($js)
242 ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
243 : "{$ws1}{$openScript}{$js}</script>{$ws2}"
244 );
245 }
246
247 protected function _removeCdata($str)
248 {
249 return (false !== strpos($str, '<![CDATA['))
250 ? str_replace(array('<![CDATA[', ']]>'), '', $str)
251 : $str;
252 }
253
254 protected function _needsCdata($str)
255 {
256 return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
257 }
258 }
259