10 * Surcharge du minifieur HTML
12 * Surcharge pour ne pas manger les commentaires <!--extra-->
13 * qui servent parfois aux plugins, et parfois même après
14 * le passage du compacteur HTML
16 * C'était le cas du bouton statistiques du formulaire admin par exemple
18 * On permet de conserver également tout commentaire commençant par <!--keepme: -->
20 * @package SPIP\Compresseur\Minifieur
22 class Minify_HTML_SPIP
extends Minify_HTML
{
30 * @param array $options
31 * Tableau d'option avec les index possibles
32 * - 'cssMinifier' : (optional) callback function to process content of STYLE
34 * - 'jsMinifier' : (optional) callback function to process content of SCRIPT
35 * elements. Note: the type attribute is ignored.
36 * - 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
37 * unset, minify will sniff for an XHTML doctype.
41 public static function minify($html, $options = array()) {
42 $min = new Minify_HTML_SPIP($html, $options);
43 return $min->process();
47 * Minification des commentaires
49 * Le cas <!--extra--> doit être conservé dans les commentaires,
50 * tout comme <!--keepme: xxx -->
52 * @param array $m Matches du preg_match d'un commentaire HTML
53 * @return string Contenu minifié
55 protected function _commentCB($m)
57 if ($m[1] === 'extra') return $m[0];
58 if ($m[1] AND $m[1][0] === 'k' AND substr($m[1],0,7) === 'keepme:') return $m[0];
59 return parent
::_commentCB($m);
67 * This is a heavy regex-based removal of whitespace, unnecessary comments and
68 * tokens. IE conditional comments are preserved. There are also options to have
69 * STYLE and SCRIPT blocks compressed by callback functions.
70 * https://github.com/mrclay/minify/blob/master/min/lib/Minify/HTML.php
72 * A test suite is available.
75 * @author Stephen Clay <steve@mrclay.org>
81 protected $_jsCleanComments = true;
84 * "Minify" an HTML page
88 * @param array $options
90 * 'cssMinifier' : (optional) callback function to process content of STYLE
93 * 'jsMinifier' : (optional) callback function to process content of SCRIPT
94 * elements. Note: the type attribute is ignored.
96 * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
97 * unset, minify will sniff for an XHTML doctype.
101 public static function minify($html, $options = array()) {
102 $min = new self($html, $options);
103 return $min->process();
108 * Create a minifier object
110 * @param string $html
112 * @param array $options
114 * 'cssMinifier' : (optional) callback function to process content of STYLE
117 * 'jsMinifier' : (optional) callback function to process content of SCRIPT
118 * elements. Note: the type attribute is ignored.
120 * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
122 * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
123 * unset, minify will sniff for an XHTML doctype.
127 public function __construct($html, $options = array())
129 $this->_html
= str_replace("\r\n", "\n", trim($html));
130 if (isset($options['xhtml'])) {
131 $this->_isXhtml
= (bool)$options['xhtml'];
133 if (isset($options['cssMinifier'])) {
134 $this->_cssMinifier
= $options['cssMinifier'];
136 if (isset($options['jsMinifier'])) {
137 $this->_jsMinifier
= $options['jsMinifier'];
139 if (isset($options['jsCleanComments'])) {
140 $this->_jsCleanComments
= (bool)$options['jsCleanComments'];
146 * Minify the markeup given in the constructor
150 public function process()
152 if ($this->_isXhtml
=== null) {
153 $this->_isXhtml
= (false !== strpos($this->_html
, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
156 $this->_replacementHash
= 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
157 $this->_placeholders
= array();
159 // replace SCRIPTs (and minify) with placeholders
160 $this->_html
= preg_replace_callback(
161 '/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
162 ,array($this, '_removeScriptCB')
165 // replace STYLEs (and minify) with placeholders
166 $this->_html
= preg_replace_callback(
167 '/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/i'
168 ,array($this, '_removeStyleCB')
171 // remove HTML comments (not containing IE conditional comments).
172 $this->_html
= preg_replace_callback(
173 '/<!--([\\s\\S]*?)-->/'
174 ,array($this, '_commentCB')
177 // replace PREs with placeholders
178 $this->_html
= preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
179 ,array($this, '_removePreCB')
182 // replace TEXTAREAs with placeholders
183 $this->_html
= preg_replace_callback(
184 '/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
185 ,array($this, '_removeTextareaCB')
188 // replace INPUTs with placeholders
189 $this->_html
= preg_replace_callback(
190 '/\\s*<input(\\b[^>]*?>)\\s*/i'
191 ,array($this, '_removeInputCB')
195 // @todo take into account attribute values that span multiple lines.
196 // 2 regexp because merging un /^\\s+|\\s+$/m also del a lot of newline chars ???
197 $this->_html
= preg_replace('/\\s+$/m', '', $this->_html
);
198 $this->_html
= preg_replace('/^\\s+/m', '', $this->_html
);
200 // remove ws around block/undisplayed elements
201 $this->_html
= preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
202 .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
203 .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
204 .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
205 .'|ul)\\b[^>]*>)/i', '$1', $this->_html
);
207 // remove ws outside of all elements
208 $this->_html
= preg_replace(
209 '/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?</'
213 // use newlines before 1st attribute in open tags (to limit line lengths)
214 $this->_html
= preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html
);
217 $this->_html
= str_replace(
218 array_keys($this->_placeholders
)
219 ,array_values($this->_placeholders
)
222 // issue 229: multi-pass to catch scripts that didn't get replaced in textareas
223 $this->_html
= str_replace(
224 array_keys($this->_placeholders
)
225 ,array_values($this->_placeholders
)
231 protected function _commentCB($m)
233 return (0 === strpos($m[1], '[') ||
false !== strpos($m[1], '<!['))
238 protected function _reservePlace($content)
240 $placeholder = '%' . $this->_replacementHash
. count($this->_placeholders
) . '%';
241 $this->_placeholders
[$placeholder] = $content;
245 protected $_isXhtml = null;
246 protected $_replacementHash = null;
247 protected $_placeholders = array();
248 protected $_cssMinifier = null;
249 protected $_jsMinifier = null;
251 protected function _removePreCB($m)
253 return $this->_reservePlace("<pre{$m[1]}");
256 protected function _removeInputCB($m)
258 return $this->_reservePlace("<input{$m[1]}");
261 protected function _removeTextareaCB($m)
263 return $this->_reservePlace("<textarea{$m[1]}");
266 protected function _removeStyleCB($m)
268 $openStyle = "<style{$m[1]}";
270 // remove HTML comments
271 $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
273 // remove CDATA section markers
274 $css = $this->_removeCdata($css);
277 $minifier = $this->_cssMinifier
278 ?
$this->_cssMinifier
280 $css = call_user_func($minifier, $css);
282 return $this->_reservePlace($this->_needsCdata($css)
283 ?
"{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
284 : "{$openStyle}{$css}</style>"
288 protected function _removeScriptCB($m)
290 $openScript = "<script{$m[2]}";
293 // whitespace surrounding? preserve at least one space
294 $ws1 = ($m[1] === '') ?
'' : ' ';
295 $ws2 = ($m[4] === '') ?
'' : ' ';
297 // remove HTML comments (and ending "//" if present)
298 if ($this->_jsCleanComments
) {
299 $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
302 // remove CDATA section markers
303 $js = $this->_removeCdata($js);
306 $minifier = $this->_jsMinifier
309 $js = call_user_func($minifier, $js);
311 return $this->_reservePlace($this->_needsCdata($js)
312 ?
"{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
313 : "{$ws1}{$openScript}{$js}</script>{$ws2}"
317 protected function _removeCdata($str)
319 return (false !== strpos($str, '<![CDATA['))
320 ?
str_replace(array('<![CDATA[', ']]>'), '', $str)
324 protected function _needsCdata($str)
326 return ($this->_isXhtml
&& preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));