futtta /
autoptimize
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 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 <[email protected]> |
||
| 18 | */ |
||
| 19 | class Minify_HTML { |
||
| 20 | |||
| 21 | /** |
||
| 22 | * "Minify" an HTML page |
||
| 23 | * |
||
| 24 | * @param string $html |
||
| 25 | * |
||
| 26 | * @param array $options |
||
| 27 | * |
||
| 28 | * 'cssMinifier' : (optional) callback function to process content of STYLE |
||
| 29 | * elements. |
||
| 30 | * |
||
| 31 | * 'jsMinifier' : (optional) callback function to process content of SCRIPT |
||
| 32 | * elements. Note: the type attribute is ignored. |
||
| 33 | * |
||
| 34 | * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If |
||
| 35 | * unset, minify will sniff for an XHTML doctype. |
||
| 36 | * |
||
| 37 | * 'keepComments' : (optional boolean) should the HTML comments be kept |
||
| 38 | * in the HTML Code? |
||
| 39 | * |
||
| 40 | * @return string |
||
| 41 | */ |
||
| 42 | public static function minify($html, $options = array()) { |
||
| 43 | $min = new Minify_HTML($html, $options); |
||
| 44 | return $min->process(); |
||
| 45 | } |
||
| 46 | |||
| 47 | |||
| 48 | /** |
||
| 49 | * Create a minifier object |
||
| 50 | * |
||
| 51 | * @param string $html |
||
| 52 | * |
||
| 53 | * @param array $options |
||
| 54 | * |
||
| 55 | * 'cssMinifier' : (optional) callback function to process content of STYLE |
||
| 56 | * elements. |
||
| 57 | * |
||
| 58 | * 'jsMinifier' : (optional) callback function to process content of SCRIPT |
||
| 59 | * elements. Note: the type attribute is ignored. |
||
| 60 | * |
||
| 61 | * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If |
||
| 62 | * unset, minify will sniff for an XHTML doctype. |
||
| 63 | * |
||
| 64 | * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If |
||
| 65 | * unset, minify will sniff for an XHTML doctype. |
||
| 66 | * |
||
| 67 | * @return null |
||
| 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['keepComments'])) { |
||
| 82 | $this->_keepComments = $options['keepComments']; |
||
| 83 | } |
||
| 84 | } |
||
| 85 | |||
| 86 | |||
| 87 | /** |
||
| 88 | * Minify the markeup given in the constructor |
||
| 89 | * |
||
| 90 | * @return string |
||
| 91 | */ |
||
| 92 | public function process() |
||
| 93 | { |
||
| 94 | View Code Duplication | if ($this->_isXhtml === null) { |
|
| 95 | $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML')); |
||
| 96 | } |
||
| 97 | |||
| 98 | $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']); |
||
| 99 | $this->_placeholders = array(); |
||
| 100 | |||
| 101 | // replace SCRIPTs (and minify) with placeholders |
||
| 102 | $this->_html = preg_replace_callback( |
||
| 103 | '/(\\s*)(<script\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i' |
||
| 104 | ,array($this, '_removeScriptCB') |
||
| 105 | ,$this->_html); |
||
| 106 | |||
| 107 | // replace STYLEs (and minify) with placeholders |
||
| 108 | $this->_html = preg_replace_callback( |
||
| 109 | '/\\s*(<style\\b[^>]*?>)([\\s\\S]*?)<\\/style>\\s*/i' |
||
| 110 | ,array($this, '_removeStyleCB') |
||
| 111 | ,$this->_html); |
||
| 112 | |||
| 113 | // remove HTML comments (not containing IE conditional comments). |
||
| 114 | if ($this->_keepComments == false) { |
||
| 115 | $this->_html = preg_replace_callback( |
||
| 116 | '/<!--([\\s\\S]*?)-->/' |
||
| 117 | ,array($this, '_commentCB') |
||
| 118 | ,$this->_html); |
||
| 119 | } |
||
| 120 | |||
| 121 | // replace PREs with placeholders |
||
| 122 | $this->_html = preg_replace_callback('/\\s*(<pre\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i' |
||
| 123 | ,array($this, '_removePreCB') |
||
| 124 | ,$this->_html); |
||
| 125 | |||
| 126 | // replace TEXTAREAs with placeholders |
||
| 127 | $this->_html = preg_replace_callback( |
||
| 128 | '/\\s*(<textarea\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i' |
||
| 129 | ,array($this, '_removeTextareaCB') |
||
| 130 | ,$this->_html); |
||
| 131 | |||
| 132 | // replace data: URIs with placeholders |
||
| 133 | $this->_html = preg_replace_callback( |
||
| 134 | '/(=("|\')data:.*\\2)/Ui' |
||
| 135 | ,array($this, '_removeDataURICB') |
||
| 136 | ,$this->_html); |
||
| 137 | |||
| 138 | // trim each line. |
||
| 139 | // replace by space instead of '' to avoid newline after opening tag getting zapped |
||
| 140 | $this->_html = preg_replace('/^\s+|\s+$/m', ' ', $this->_html); |
||
| 141 | |||
| 142 | // remove ws around block/undisplayed elements |
||
| 143 | $this->_html = preg_replace('/\\s+(<\\/?(?:area|article|aside|base(?:font)?|blockquote|body' |
||
| 144 | .'|canvas|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|figcaption|figure|footer|form' |
||
| 145 | .'|frame(?:set)?|h[1-6]|head|header|hgroup|hr|html|legend|li|link|main|map|menu|meta|nav' |
||
| 146 | .'|ol|opt(?:group|ion)|output|p|param|section|t(?:able|body|head|d|h||r|foot|itle)' |
||
| 147 | .'|ul|video)\\b[^>]*>)/i', '$1', $this->_html); |
||
| 148 | |||
| 149 | // remove ws outside of all elements |
||
| 150 | $this->_html = preg_replace_callback( |
||
| 151 | '/>([^<]+)</' |
||
| 152 | ,array($this, '_outsideTagCB') |
||
| 153 | ,$this->_html); |
||
| 154 | |||
| 155 | // use newlines before 1st attribute in open tags (to limit line lengths) |
||
| 156 | //$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html); |
||
|
0 ignored issues
–
show
|
|||
| 157 | |||
| 158 | // fill placeholders |
||
| 159 | $this->_html = str_replace( |
||
| 160 | array_keys($this->_placeholders) |
||
| 161 | ,array_values($this->_placeholders) |
||
| 162 | ,$this->_html |
||
| 163 | ); |
||
| 164 | return $this->_html; |
||
| 165 | } |
||
| 166 | |||
| 167 | protected function _commentCB($m) |
||
| 168 | { |
||
| 169 | return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<![')) |
||
| 170 | ? $m[0] |
||
| 171 | : ''; |
||
| 172 | } |
||
| 173 | |||
| 174 | protected function _reservePlace($content) |
||
| 175 | { |
||
| 176 | $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%'; |
||
| 177 | $this->_placeholders[$placeholder] = $content; |
||
| 178 | return $placeholder; |
||
| 179 | } |
||
| 180 | |||
| 181 | protected $_isXhtml = null; |
||
| 182 | protected $_replacementHash = null; |
||
| 183 | protected $_placeholders = array(); |
||
| 184 | protected $_cssMinifier = null; |
||
| 185 | protected $_jsMinifier = null; |
||
| 186 | protected $_keepComments = false; |
||
| 187 | |||
| 188 | protected function _outsideTagCB($m) |
||
| 189 | { |
||
| 190 | return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<'; |
||
| 191 | } |
||
| 192 | |||
| 193 | protected function _removePreCB($m) |
||
| 194 | { |
||
| 195 | return $this->_reservePlace($m[1]); |
||
| 196 | } |
||
| 197 | |||
| 198 | protected function _removeTextareaCB($m) |
||
| 199 | { |
||
| 200 | return $this->_reservePlace($m[1]); |
||
| 201 | } |
||
| 202 | |||
| 203 | protected function _removeDataURICB($m) |
||
| 204 | { |
||
| 205 | return $this->_reservePlace($m[1]); |
||
| 206 | } |
||
| 207 | |||
| 208 | View Code Duplication | protected function _removeStyleCB($m) |
|
| 209 | { |
||
| 210 | $openStyle = $m[1]; |
||
| 211 | $css = $m[2]; |
||
| 212 | // remove HTML comments |
||
| 213 | $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css); |
||
| 214 | |||
| 215 | // remove CDATA section markers |
||
| 216 | $css = $this->_removeCdata($css); |
||
| 217 | |||
| 218 | // minify |
||
| 219 | $minifier = $this->_cssMinifier |
||
| 220 | ? $this->_cssMinifier |
||
| 221 | : 'trim'; |
||
| 222 | $css = call_user_func($minifier, $css); |
||
| 223 | |||
| 224 | return $this->_reservePlace($this->_needsCdata($css) |
||
| 225 | ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>" |
||
| 226 | : "{$openStyle}{$css}</style>" |
||
| 227 | ); |
||
| 228 | } |
||
| 229 | |||
| 230 | View Code Duplication | protected function _removeScriptCB($m) |
|
| 231 | { |
||
| 232 | $openScript = $m[2]; |
||
| 233 | $js = $m[3]; |
||
| 234 | |||
| 235 | // whitespace surrounding? preserve at least one space |
||
| 236 | $ws1 = ($m[1] === '') ? '' : ' '; |
||
| 237 | $ws2 = ($m[4] === '') ? '' : ' '; |
||
| 238 | |||
| 239 | // remove HTML comments (and ending "//" if present) |
||
| 240 | $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js); |
||
| 241 | |||
| 242 | // remove CDATA section markers |
||
| 243 | $js = $this->_removeCdata($js); |
||
| 244 | |||
| 245 | // minify |
||
| 246 | $minifier = $this->_jsMinifier |
||
| 247 | ? $this->_jsMinifier |
||
| 248 | : 'trim'; |
||
| 249 | $js = call_user_func($minifier, $js); |
||
| 250 | |||
| 251 | return $this->_reservePlace($this->_needsCdata($js) |
||
| 252 | ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}" |
||
| 253 | : "{$ws1}{$openScript}{$js}</script>{$ws2}" |
||
| 254 | ); |
||
| 255 | } |
||
| 256 | |||
| 257 | protected function _removeCdata($str) |
||
| 258 | { |
||
| 259 | return (false !== strpos($str, '<![CDATA[')) |
||
| 260 | ? str_replace(array('/* <![CDATA[ */','/* ]]> */','/*<![CDATA[*/','/*]]>*/','<![CDATA[', ']]>'), '', $str) |
||
| 261 | : $str; |
||
| 262 | } |
||
| 263 | |||
| 264 | protected function _needsCdata($str) |
||
| 265 | { |
||
| 266 | return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str)); |
||
| 267 | } |
||
| 268 | } |
||
| 269 |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.