|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00019 class Minify_HTML { 00020 00039 public static function minify($html, $options = array()) { 00040 $min = new Minify_HTML($html, $options); 00041 return $min->process(); 00042 } 00043 00044 00063 public function __construct($html, $options = array()) 00064 { 00065 $this->_html = str_replace("\r\n", "\n", trim($html)); 00066 if (isset($options['xhtml'])) { 00067 $this->_isXhtml = (bool)$options['xhtml']; 00068 } 00069 if (isset($options['cssMinifier'])) { 00070 $this->_cssMinifier = $options['cssMinifier']; 00071 } 00072 if (isset($options['jsMinifier'])) { 00073 $this->_jsMinifier = $options['jsMinifier']; 00074 } 00075 } 00076 00077 00083 public function process() 00084 { 00085 if ($this->_isXhtml === null) { 00086 $this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML')); 00087 } 00088 00089 $this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']); 00090 $this->_placeholders = array(); 00091 00092 // replace SCRIPTs (and minify) with placeholders 00093 $this->_html = preg_replace_callback( 00094 '/(\\s*)(<script\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i' 00095 ,array($this, '_removeScriptCB') 00096 ,$this->_html); 00097 00098 // replace STYLEs (and minify) with placeholders 00099 $this->_html = preg_replace_callback( 00100 '/\\s*(<style\\b[^>]*?>)([\\s\\S]*?)<\\/style>\\s*/i' 00101 ,array($this, '_removeStyleCB') 00102 ,$this->_html); 00103 00104 // remove HTML comments (not containing IE conditional comments). 00105 $this->_html = preg_replace_callback( 00106 '/<!--([\\s\\S]*?)-->/' 00107 ,array($this, '_commentCB') 00108 ,$this->_html); 00109 00110 // replace PREs with placeholders 00111 $this->_html = preg_replace_callback('/\\s*(<pre\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i' 00112 ,array($this, '_removePreCB') 00113 ,$this->_html); 00114 00115 // replace TEXTAREAs with placeholders 00116 $this->_html = preg_replace_callback( 00117 '/\\s*(<textarea\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i' 00118 ,array($this, '_removeTextareaCB') 00119 ,$this->_html); 00120 00121 // trim each line. 00122 // @todo take into account attribute values that span multiple lines. 00123 $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html); 00124 00125 // remove ws around block/undisplayed elements 00126 $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body' 00127 .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form' 00128 .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta' 00129 .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)' 00130 .'|ul)\\b[^>]*>)/i', '$1', $this->_html); 00131 00132 // remove ws outside of all elements 00133 $this->_html = preg_replace_callback( 00134 '/>([^<]+)</' 00135 ,array($this, '_outsideTagCB') 00136 ,$this->_html); 00137 00138 // use newlines before 1st attribute in open tags (to limit line lengths) 00139 $this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html); 00140 00141 // fill placeholders 00142 $this->_html = str_replace( 00143 array_keys($this->_placeholders) 00144 ,array_values($this->_placeholders) 00145 ,$this->_html 00146 ); 00147 return $this->_html; 00148 } 00149 00150 protected function _commentCB($m) 00151 { 00152 return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<![')) 00153 ? $m[0] 00154 : ''; 00155 } 00156 00157 protected function _reservePlace($content) 00158 { 00159 $placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%'; 00160 $this->_placeholders[$placeholder] = $content; 00161 return $placeholder; 00162 } 00163 00164 protected $_isXhtml = null; 00165 protected $_replacementHash = null; 00166 protected $_placeholders = array(); 00167 protected $_cssMinifier = null; 00168 protected $_jsMinifier = null; 00169 00170 protected function _outsideTagCB($m) 00171 { 00172 return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<'; 00173 } 00174 00175 protected function _removePreCB($m) 00176 { 00177 return $this->_reservePlace($m[1]); 00178 } 00179 00180 protected function _removeTextareaCB($m) 00181 { 00182 return $this->_reservePlace($m[1]); 00183 } 00184 00185 protected function _removeStyleCB($m) 00186 { 00187 $openStyle = $m[1]; 00188 $css = $m[2]; 00189 // remove HTML comments 00190 $css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css); 00191 00192 // remove CDATA section markers 00193 $css = $this->_removeCdata($css); 00194 00195 // minify 00196 $minifier = $this->_cssMinifier 00197 ? $this->_cssMinifier 00198 : 'trim'; 00199 $css = call_user_func($minifier, $css); 00200 00201 return $this->_reservePlace($this->_needsCdata($css) 00202 ? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>" 00203 : "{$openStyle}{$css}</style>" 00204 ); 00205 } 00206 00207 protected function _removeScriptCB($m) 00208 { 00209 $openScript = $m[2]; 00210 $js = $m[3]; 00211 00212 // whitespace surrounding? preserve at least one space 00213 $ws1 = ($m[1] === '') ? '' : ' '; 00214 $ws2 = ($m[4] === '') ? '' : ' '; 00215 00216 // remove HTML comments (and ending "//" if present) 00217 $js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js); 00218 00219 // remove CDATA section markers 00220 $js = $this->_removeCdata($js); 00221 00222 // minify 00223 $minifier = $this->_jsMinifier 00224 ? $this->_jsMinifier 00225 : 'trim'; 00226 $js = call_user_func($minifier, $js); 00227 00228 return $this->_reservePlace($this->_needsCdata($js) 00229 ? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}" 00230 : "{$ws1}{$openScript}{$js}</script>{$ws2}" 00231 ); 00232 } 00233 00234 protected function _removeCdata($str) 00235 { 00236 return (false !== strpos($str, '<![CDATA[')) 00237 ? str_replace(array('<![CDATA[', ']]>'), '', $str) 00238 : $str; 00239 } 00240 00241 protected function _needsCdata($str) 00242 { 00243 return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str)); 00244 } 00245 }