PluginProbe
ezCache / 1.3.11
ezCache v1.3.11
2.6.5 2.6.4 2.6.2 2.6.3 2.6.1 2.6.0 2.5.6 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5 2.2.1 2.2.2 trunk 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.3 1.3.1 1.3.10 1.3.11 All 49 releases
ezcache / includes / ThirdParty / Minify_HTML.php

Minify_HTML.php in ezCache 1.3.11, at includes/ThirdParty/Minify_HTML.php

288 lines 8.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Upress\EzCache\ThirdParty;
3
4 /**
5 * Class Minify_HTML
6 * @package Minify
7 */
8
9 /**
10 * Compress HTML
11 *
12 * This is a heavy regex-based removal of whitespace, unnecessary comments and
13 * tokens. IE conditional comments are preserved. There are also options to have
14 * STYLE and SCRIPT blocks compressed by callback functions.
15 *
16 * A test suite is available.
17 *
18 * @package Minify
19 * @author Stephen Clay <steve@mrclay.org>
20 */
21 class Minify_HTML
22 {
23 /**
24 * @var boolean
25 */
26 protected $_jsCleanComments = true;
27 /**
28 * @var boolean
29 */
30 protected $_htmlCleanComments = true;
31
32 /**
33 * "Minify" an HTML page
34 *
35 * @param string $html
36 *
37 * @param array $options
38 *
39 * 'cssMinifier' : (optional) callback function to process content of STYLE
40 * elements.
41 *
42 * 'jsMinifier' : (optional) callback function to process content of SCRIPT
43 * elements. Note: the type attribute is ignored.
44 *
45 * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
46 * unset, minify will sniff for an XHTML doctype.
47 *
48 * @return string
49 */
50 public static function minify($html, $options = array())
51 {
52 $min = new self($html, $options);
53
54 return $min->process();
55 }
56
57 /**
58 * Create a minifier object
59 *
60 * @param string $html
61 *
62 * @param array $options
63 *
64 * 'cssMinifier' : (optional) callback function to process content of STYLE
65 * elements.
66 *
67 * 'jsMinifier' : (optional) callback function to process content of SCRIPT
68 * elements. Note: the type attribute is ignored.
69 *
70 * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
71 *
72 * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
73 * unset, minify will sniff for an XHTML doctype.
74 */
75 public function __construct($html, $options = array())
76 {
77 $this->_html = str_replace("\r\n", "\n", trim($html));
78 if (isset($options['xhtml'])) {
79 $this->_isXhtml = (bool)$options['xhtml'];
80 }
81 if (isset($options['cssMinifier'])) {
82 $this->_cssMinifier = $options['cssMinifier'];
83 }
84 if (isset($options['jsMinifier'])) {
85 $this->_jsMinifier = $options['jsMinifier'];
86 }
87 if (isset($options['jsCleanComments'])) {
88 $this->_jsCleanComments = (bool)$options['jsCleanComments'];
89 }
90
91 if (isset($options['htmlCleanComments'])) {
92 $this->_htmlCleanComments = (bool)$options['htmlCleanComments'];
93 }
94 }
95
96 /**
97 * Minify the markeup given in the constructor
98 *
99 * @return string
100 */
101 public function process()
102 {
103 if ($this->_isXhtml === null) {
104 $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
105 }
106
107 $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
108 $this->_placeholders = array();
109
110 // replace SCRIPTs (and minify) with placeholders
111 $this->_html = preg_replace_callback(
112 '/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/ui'
113 ,array($this, '_removeScriptCB')
114 ,$this->_html);
115
116 // replace STYLEs (and minify) with placeholders
117 $this->_html = preg_replace_callback(
118 '/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/ui'
119 ,array($this, '_removeStyleCB')
120 ,$this->_html);
121
122 if($this->_htmlCleanComments) {
123 // remove HTML comments (not containing IE conditional comments).
124 $this->_html = preg_replace_callback(
125 '/<!--([\\s\\S]*?)-->/u'
126 , array( $this, '_commentCB' )
127 , $this->_html );
128 }
129
130 // replace PREs with placeholders
131 $this->_html = preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/ui'
132 ,array($this, '_removePreCB')
133 ,$this->_html);
134
135 // replace TEXTAREAs with placeholders
136 $this->_html = preg_replace_callback(
137 '/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/ui'
138 ,array($this, '_removeTextareaCB')
139 ,$this->_html);
140
141 // trim each line.
142 // @todo take into account attribute values that span multiple lines.
143 $this->_html = preg_replace('/^\\s+|\\s+$/um', '', $this->_html);
144
145 // remove ws around block/undisplayed elements
146 $this->_html = preg_replace('/\\s+(<\\/?(?:area|article|aside|base(?:font)?|blockquote|body'
147 .'|canvas|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|figcaption|figure|footer|form'
148 .'|frame(?:set)?|h[1-6]|head|header|hgroup|hr|html|legend|li|link|main|map|menu|meta|nav'
149 .'|ol|opt(?:group|ion)|output|p|param|section|t(?:able|body|head|d|h||r|foot|itle)'
150 .'|ul|video)\\b[^>]*>)/ui', '$1', $this->_html);
151
152 // remove ws outside of all elements
153 $this->_html = preg_replace_callback(
154 '/>([^<]+)</'
155 ,array($this, '_outsideTagCB')
156 ,$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 // issue 229: multi-pass to catch scripts that didn't get replaced in textareas
165 $this->_html = str_replace(
166 array_keys($this->_placeholders)
167 ,array_values($this->_placeholders)
168 ,$this->_html
169 );
170
171 return $this->_html;
172 }
173
174 protected function _commentCB($m)
175 {
176 return (
177 0 === strpos($m[1], '[') ||
178 false !== strpos($m[1], '<![') ||
179 0 === strpos($m[1], 'esi') ||
180 0 === strpos($m[1], 'noindex') ||
181 0 === strpos($m[1], '/noindex') ||
182 0 === strpos($m[1], 'start_content') ||
183 0 === strpos($m[1], 'end_content') ||
184 0 === strpos(trim($m[1]), 'ko') ||
185 0 === strpos(trim($m[1]), '/ko')
186 )
187 ? $m[0]
188 : '';
189 }
190
191 protected function _reservePlace($content)
192 {
193 $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
194 $this->_placeholders[$placeholder] = $content;
195
196 return $placeholder;
197 }
198
199 protected $_isXhtml = null;
200 protected $_replacementHash = null;
201 protected $_placeholders = array();
202 protected $_cssMinifier = null;
203 protected $_jsMinifier = null;
204
205 protected function _outsideTagCB($m)
206 {
207 return '>' . preg_replace('/^\\s+|\\s+$/u', ' ', $m[1]) . '<';
208 }
209
210 protected function _removePreCB($m)
211 {
212 return $this->_reservePlace("<pre{$m[1]}");
213 }
214
215 protected function _removeTextareaCB($m)
216 {
217 return $this->_reservePlace("<textarea{$m[1]}");
218 }
219
220 protected function _removeStyleCB($m)
221 {
222 $openStyle = "<style{$m[1]}";
223 $css = $m[2];
224 // remove HTML comments
225 $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/u', '', $css);
226
227 // remove CDATA section markers
228 $data = $this->_removeCdata($css);
229
230 // minify
231 $minifier = $this->_cssMinifier
232 ? $this->_cssMinifier
233 : 'trim';
234 $css = call_user_func($minifier, $data['content']);
235
236 return $this->_reservePlace( $data['cdata']
237 ? "{$openStyle}/* <![CDATA[ */ {$css} /* ]]> */</style>"
238 : "{$openStyle}{$css}</style>"
239 );
240 }
241
242 protected function _removeScriptCB($m)
243 {
244 $openScript = "<script{$m[2]}";
245 $js = $m[3];
246
247 // whitespace surrounding? preserve at least one space
248 $ws1 = ($m[1] === '') ? '' : ' ';
249 $ws2 = ($m[4] === '') ? '' : ' ';
250
251 // remove HTML comments (and ending "//" if present)
252 if ($this->_jsCleanComments) {
253 $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/u', '', $js);
254 }
255
256 // remove CDATA section markers
257 $data = $this->_removeCdata($js);
258
259 // minify
260 $minifier = $this->_jsMinifier
261 ? $this->_jsMinifier
262 : 'trim';
263 $js = call_user_func($minifier, $data['content']);
264
265 return $this->_reservePlace($data['cdata']
266 ? "{$ws1}{$openScript}/* <![CDATA[ */ {$js} /* ]]> */</script>{$ws2}"
267 : "{$ws1}{$openScript}{$js}</script>{$ws2}"
268 );
269 }
270
271 protected function _removeCdata($str)
272 {
273 $data = array();
274
275 if ( false !== strpos( $str, '<![CDATA[' ) ) {
276 $data['content'] = str_replace( array( '/* <![CDATA[ */', '/* ]]> */' ), '', $str );
277 $data['cdata'] = true;
278
279 } else {
280 $data['content'] = $str;
281 $data['cdata'] = false;
282 }
283
284 return $data;
285 }
286
287 }
288