fbb9c897928335b00315059165f8f4bb44d17b07
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.
71 * A test suite is available.
73 * @link https://github.com/mrclay/minify/blob/master/min/lib/Minify/HTML.php
75 * @note Librairie modifiée pour prendre en compte les attributs avec multilignes.
78 * @author Stephen Clay <steve@mrclay.org>
84 protected $_jsCleanComments = true;
87 * "Minify" an HTML page
91 * @param array $options
93 * 'cssMinifier' : (optional) callback function to process content of STYLE
96 * 'jsMinifier' : (optional) callback function to process content of SCRIPT
97 * elements. Note: the type attribute is ignored.
99 * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
100 * unset, minify will sniff for an XHTML doctype.
104 public static function minify($html, $options = array()) {
105 $min = new self($html, $options);
106 return $min->process();
111 * Create a minifier object
113 * @param string $html
115 * @param array $options
117 * 'cssMinifier' : (optional) callback function to process content of STYLE
120 * 'jsMinifier' : (optional) callback function to process content of SCRIPT
121 * elements. Note: the type attribute is ignored.
123 * 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
125 * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
126 * unset, minify will sniff for an XHTML doctype.
128 public function __construct($html, $options = array())
130 $this->_html
= str_replace("\r\n", "\n", trim($html));
131 if (isset($options['xhtml'])) {
132 $this->_isXhtml
= (bool)$options['xhtml'];
134 if (isset($options['cssMinifier'])) {
135 $this->_cssMinifier
= $options['cssMinifier'];
137 if (isset($options['jsMinifier'])) {
138 $this->_jsMinifier
= $options['jsMinifier'];
140 if (isset($options['jsCleanComments'])) {
141 $this->_jsCleanComments
= (bool)$options['jsCleanComments'];
147 * Minify the markeup given in the constructor
151 public function process()
153 if ($this->_isXhtml
=== null) {
154 $this->_isXhtml
= (false !== strpos($this->_html
, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
157 $this->_replacementHash
= 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
158 $this->_placeholders
= array();
160 // replace SCRIPTs (and minify) with placeholders
161 $this->_html
= preg_replace_callback(
162 '/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
163 ,array($this, '_removeScriptCB')
166 // replace STYLEs (and minify) with placeholders
167 $this->_html
= preg_replace_callback(
168 '/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/i'
169 ,array($this, '_removeStyleCB')
172 // remove HTML comments (not containing IE conditional comments).
173 $this->_html
= preg_replace_callback(
174 '/<!--([\\s\\S]*?)-->/'
175 ,array($this, '_commentCB')
178 // replace PREs with placeholders
179 $this->_html
= preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
180 ,array($this, '_removePreCB')
183 // replace TEXTAREAs with placeholders
184 $this->_html
= preg_replace_callback(
185 '/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
186 ,array($this, '_removeTextareaCB')
189 // replace INPUTs with placeholders
190 $this->_html
= preg_replace_callback(
191 '/\\s*<input(\\b[^>]*?>)\\s*/i'
192 ,array($this, '_removeInputCB')
196 // @todo take into account attribute values that span multiple lines.
197 // 2 regexp because merging un /^\\s+|\\s+$/m also del a lot of newline chars ???
198 $this->_html
= preg_replace('/\\s+$/m', '', $this->_html
);
199 $this->_html
= preg_replace('/^\\s+/m', '', $this->_html
);
201 // remove ws around block/undisplayed elements
202 $this->_html
= preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
203 .'|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
204 .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
205 .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
206 .'|ul)\\b[^>]*>)/i', '$1', $this->_html
);
208 // remove ws outside of all elements
209 $this->_html
= preg_replace(
210 '/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?</'
214 // use newlines before 1st attribute in open tags (to limit line lengths)
215 $this->_html
= preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html
);
218 $this->_html
= str_replace(
219 array_keys($this->_placeholders
)
220 ,array_values($this->_placeholders
)
223 // issue 229: multi-pass to catch scripts that didn't get replaced in textareas
224 $this->_html
= str_replace(
225 array_keys($this->_placeholders
)
226 ,array_values($this->_placeholders
)
232 protected function _commentCB($m)
234 return (0 === strpos($m[1], '[') ||
false !== strpos($m[1], '<!['))
239 protected function _reservePlace($content)
241 $placeholder = '%' . $this->_replacementHash
. count($this->_placeholders
) . '%';
242 $this->_placeholders
[$placeholder] = $content;
246 protected $_isXhtml = null;
247 protected $_replacementHash = null;
248 protected $_placeholders = array();
249 protected $_cssMinifier = null;
250 protected $_jsMinifier = null;
252 protected function _removePreCB($m)
254 return $this->_reservePlace("<pre{$m[1]}");
257 protected function _removeInputCB($m)
259 return $this->_reservePlace("<input{$m[1]}");
262 protected function _removeTextareaCB($m)
264 return $this->_reservePlace("<textarea{$m[1]}");
267 protected function _removeStyleCB($m)
269 $openStyle = "<style{$m[1]}";
271 // remove HTML comments
272 $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
274 // remove CDATA section markers
275 $css = $this->_removeCdata($css);
278 $minifier = $this->_cssMinifier
279 ?
$this->_cssMinifier
281 $css = call_user_func($minifier, $css);
283 return $this->_reservePlace($this->_needsCdata($css)
284 ?
"{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
285 : "{$openStyle}{$css}</style>"
289 protected function _removeScriptCB($m)
291 $openScript = "<script{$m[2]}";
294 // whitespace surrounding? preserve at least one space
295 $ws1 = ($m[1] === '') ?
'' : ' ';
296 $ws2 = ($m[4] === '') ?
'' : ' ';
298 // remove HTML comments (and ending "//" if present)
299 if ($this->_jsCleanComments
) {
300 $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
303 // remove CDATA section markers
304 $js = $this->_removeCdata($js);
307 $minifier = $this->_jsMinifier
310 $js = call_user_func($minifier, $js);
312 return $this->_reservePlace($this->_needsCdata($js)
313 ?
"{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
314 : "{$ws1}{$openScript}{$js}</script>{$ws2}"
318 protected function _removeCdata($str)
320 return (false !== strpos($str, '<![CDATA['))
321 ?
str_replace(array('<![CDATA[', ']]>'), '', $str)
325 protected function _needsCdata($str)
327 return ($this->_isXhtml
&& preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));