PluginProbe
ezCache / 1.2
ezCache v1.2
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.2, at includes/ThirdParty/Minify_HTML.php

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