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 | if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly |
||
| 3 | |||
| 4 | class autoptimizeStyles extends autoptimizeBase {
|
||
| 5 | |||
| 6 | const ASSETS_REGEX = '/url\s*\(\s*(?!["\']?data:)(?![\'|\"]?[\#|\%|])([^)]+)\s*\)([^;},\s]*)/i'; |
||
| 7 | |||
| 8 | private $css = array(); |
||
| 9 | private $csscode = array(); |
||
| 10 | private $url = array(); |
||
| 11 | private $restofcontent = ''; |
||
| 12 | private $datauris = false; |
||
| 13 | private $hashmap = array(); |
||
| 14 | private $alreadyminified = false; |
||
| 15 | private $inline = false; |
||
| 16 | private $defer = false; |
||
| 17 | private $defer_inline = false; |
||
| 18 | private $whitelist = ''; |
||
| 19 | private $cssinlinesize = ''; |
||
| 20 | private $cssremovables = array(); |
||
| 21 | private $include_inline = false; |
||
| 22 | private $inject_min_late = ''; |
||
| 23 | |||
| 24 | //Reads the page and collects style tags |
||
| 25 | public function read($options) {
|
||
| 26 | $noptimizeCSS = apply_filters( 'autoptimize_filter_css_noptimize', false, $this->content ); |
||
| 27 | if ($noptimizeCSS) return false; |
||
| 28 | |||
| 29 | $whitelistCSS = apply_filters( 'autoptimize_filter_css_whitelist', '', $this->content ); |
||
| 30 | if (!empty($whitelistCSS)) {
|
||
| 31 | $this->whitelist = array_filter(array_map('trim',explode(",",$whitelistCSS)));
|
||
| 32 | } |
||
| 33 | |||
| 34 | $removableCSS = apply_filters( 'autoptimize_filter_css_removables','' ); |
||
| 35 | View Code Duplication | if (!empty($removableCSS)) {
|
|
| 36 | $this->cssremovables = array_filter(array_map('trim',explode(",",$removableCSS)));
|
||
| 37 | } |
||
| 38 | |||
| 39 | $this->cssinlinesize = apply_filters('autoptimize_filter_css_inlinesize',256);
|
||
| 40 | |||
| 41 | // filter to "late inject minified CSS", default to true for now (it is faster) |
||
| 42 | $this->inject_min_late = apply_filters('autoptimize_filter_css_inject_min_late',true);
|
||
| 43 | |||
| 44 | // Remove everything that's not the header |
||
| 45 | View Code Duplication | if ( apply_filters('autoptimize_filter_css_justhead',$options['justhead']) == true ) {
|
|
| 46 | $content = explode('</head>',$this->content,2);
|
||
| 47 | $this->content = $content[0].'</head>'; |
||
| 48 | $this->restofcontent = $content[1]; |
||
| 49 | } |
||
| 50 | |||
| 51 | // include inline? |
||
| 52 | if( apply_filters('autoptimize_css_include_inline',$options['include_inline']) == true ) {
|
||
| 53 | $this->include_inline = true; |
||
| 54 | } |
||
| 55 | |||
| 56 | // what CSS shouldn't be autoptimized |
||
| 57 | $excludeCSS = $options['css_exclude']; |
||
| 58 | $excludeCSS = apply_filters( 'autoptimize_filter_css_exclude', $excludeCSS, $this->content ); |
||
| 59 | if ($excludeCSS!=="") {
|
||
| 60 | $this->dontmove = array_filter(array_map('trim',explode(",",$excludeCSS)));
|
||
| 61 | } else {
|
||
| 62 | $this->dontmove = array(); |
||
| 63 | } |
||
| 64 | |||
| 65 | // forcefully exclude CSS with data-noptimize attrib |
||
| 66 | $this->dontmove[]="data-noptimize"; |
||
| 67 | |||
| 68 | // should we defer css? |
||
| 69 | // value: true/ false |
||
| 70 | $this->defer = $options['defer']; |
||
| 71 | $this->defer = apply_filters( 'autoptimize_filter_css_defer', $this->defer, $this->content ); |
||
| 72 | |||
| 73 | // should we inline while deferring? |
||
| 74 | // value: inlined CSS |
||
| 75 | $this->defer_inline = $options['defer_inline']; |
||
| 76 | $this->defer_inline = apply_filters( 'autoptimize_filter_css_defer_inline', $this->defer_inline, $this->content ); |
||
| 77 | |||
| 78 | // should we inline? |
||
| 79 | // value: true/ false |
||
| 80 | $this->inline = $options['inline']; |
||
| 81 | $this->inline = apply_filters( 'autoptimize_filter_css_inline', $this->inline, $this->content ); |
||
| 82 | |||
| 83 | // get cdn url |
||
| 84 | $this->cdn_url = $options['cdn_url']; |
||
| 85 | |||
| 86 | // Store data: URIs setting for later use |
||
| 87 | $this->datauris = $options['datauris']; |
||
| 88 | |||
| 89 | // noptimize me |
||
| 90 | $this->content = $this->hide_noptimize($this->content); |
||
| 91 | |||
| 92 | // exclude (no)script, as those may contain CSS which should be left as is |
||
| 93 | View Code Duplication | if ( strpos( $this->content, '<script' ) !== false ) {
|
|
| 94 | $this->content = preg_replace_callback( |
||
| 95 | '#<(?:no)?script.*?<\/(?:no)?script>#is', |
||
| 96 | create_function( |
||
|
0 ignored issues
–
show
|
|||
| 97 | '$matches', |
||
| 98 | 'return "%%SCRIPT".AUTOPTIMIZE_HASH."%%".base64_encode($matches[0])."%%SCRIPT%%";' |
||
| 99 | ), |
||
| 100 | $this->content |
||
| 101 | ); |
||
| 102 | } |
||
| 103 | |||
| 104 | // Save IE hacks |
||
| 105 | $this->content = $this->hide_iehacks($this->content); |
||
| 106 | |||
| 107 | // hide comments |
||
| 108 | $this->content = $this->hide_comments($this->content); |
||
| 109 | |||
| 110 | // Get <style> and <link> |
||
| 111 | if(preg_match_all('#(<style[^>]*>.*</style>)|(<link[^>]*stylesheet[^>]*>)#Usmi',$this->content,$matches)) {
|
||
| 112 | foreach($matches[0] as $tag) {
|
||
| 113 | if ($this->isremovable($tag,$this->cssremovables)) {
|
||
| 114 | $this->content = str_replace($tag,'',$this->content); |
||
| 115 | } else if ($this->ismovable($tag)) {
|
||
| 116 | // Get the media |
||
| 117 | if(strpos($tag,'media=')!==false) {
|
||
| 118 | preg_match('#media=(?:"|\')([^>]*)(?:"|\')#Ui',$tag,$medias);
|
||
| 119 | $medias = explode(',',$medias[1]);
|
||
| 120 | $media = array(); |
||
| 121 | foreach($medias as $elem) {
|
||
| 122 | if (empty($elem)) { $elem="all"; }
|
||
| 123 | $media[] = $elem; |
||
| 124 | } |
||
| 125 | } else {
|
||
| 126 | // No media specified - applies to all |
||
| 127 | $media = array('all');
|
||
| 128 | } |
||
| 129 | $media = apply_filters( 'autoptimize_filter_css_tagmedia',$media,$tag ); |
||
| 130 | |||
| 131 | if(preg_match('#<link.*href=("|\')(.*)("|\')#Usmi',$tag,$source)) {
|
||
| 132 | // <link> |
||
| 133 | $explUrl = explode('?',$source[2],2);
|
||
| 134 | $url = $explUrl[0]; |
||
| 135 | $path = $this->getpath($url); |
||
| 136 | |||
| 137 | if($path!==false && preg_match('#\.css$#',$path)) {
|
||
| 138 | // Good link |
||
| 139 | $this->css[] = array($media,$path); |
||
| 140 | }else{
|
||
| 141 | // Link is dynamic (.php etc) |
||
| 142 | $tag = ''; |
||
| 143 | } |
||
| 144 | } else {
|
||
| 145 | // inline css in style tags can be wrapped in comment tags, so restore comments |
||
| 146 | $tag = $this->restore_comments($tag); |
||
| 147 | preg_match('#<style.*>(.*)</style>#Usmi',$tag,$code);
|
||
| 148 | |||
| 149 | // and re-hide them to be able to to the removal based on tag |
||
| 150 | $tag = $this->hide_comments($tag); |
||
| 151 | |||
| 152 | if ( $this->include_inline ) {
|
||
| 153 | $code = preg_replace('#^.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*$#sm','$1',$code[1]);
|
||
| 154 | $this->css[] = array($media,'INLINE;'.$code); |
||
| 155 | } else {
|
||
| 156 | $tag = ''; |
||
| 157 | } |
||
| 158 | } |
||
| 159 | |||
| 160 | // Remove the original style tag |
||
| 161 | $this->content = str_replace($tag,'',$this->content); |
||
| 162 | } else {
|
||
| 163 | // excluded CSS, minify if getpath |
||
| 164 | if (preg_match('#<link.*href=("|\')(.*)("|\')#Usmi',$tag,$source)) {
|
||
| 165 | $explUrl = explode('?',$source[2],2);
|
||
| 166 | $url = $explUrl[0]; |
||
| 167 | $path = $this->getpath($url); |
||
| 168 | |||
| 169 | if ($path && apply_filters('autoptimize_filter_css_minify_excluded',false)) {
|
||
| 170 | $_CachedMinifiedUrl = $this->minify_single($path); |
||
| 171 | |||
| 172 | if (!empty($_CachedMinifiedUrl)) {
|
||
| 173 | // replace orig URL with URL to cache |
||
| 174 | $newTag = str_replace($url, $_CachedMinifiedUrl, $tag); |
||
| 175 | } else {
|
||
| 176 | $newTag = $tag; |
||
| 177 | } |
||
| 178 | |||
| 179 | // remove querystring from URL |
||
| 180 | View Code Duplication | if ( !empty($explUrl[1]) ) {
|
|
| 181 | $newTag = str_replace("?".$explUrl[1],"",$newTag);
|
||
| 182 | } |
||
| 183 | |||
| 184 | // and replace |
||
| 185 | $this->content = str_replace($tag,$newTag,$this->content); |
||
| 186 | } |
||
| 187 | } |
||
| 188 | } |
||
| 189 | } |
||
| 190 | return true; |
||
| 191 | } |
||
| 192 | // Really, no styles? |
||
| 193 | return false; |
||
| 194 | } |
||
| 195 | |||
| 196 | // Joins and optimizes CSS |
||
| 197 | public function minify() {
|
||
| 198 | foreach($this->css as $group) {
|
||
| 199 | list($media,$css) = $group; |
||
| 200 | if(preg_match('#^INLINE;#',$css)) {
|
||
| 201 | // <style> |
||
| 202 | $css = preg_replace('#^INLINE;#','',$css);
|
||
| 203 | $css = $this->fixurls(ABSPATH.'/index.php',$css); |
||
| 204 | $tmpstyle = apply_filters( 'autoptimize_css_individual_style', $css, "" ); |
||
| 205 | if ( has_filter('autoptimize_css_individual_style') && !empty($tmpstyle) ) {
|
||
| 206 | $css=$tmpstyle; |
||
| 207 | $this->alreadyminified=true; |
||
| 208 | } |
||
| 209 | } else {
|
||
| 210 | //<link> |
||
| 211 | if($css !== false && file_exists($css) && is_readable($css)) {
|
||
| 212 | $cssPath = $css; |
||
| 213 | $cssContents = file_get_contents($cssPath); |
||
| 214 | $cssHash = md5($cssContents); |
||
| 215 | $css = $this->fixurls($cssPath,$cssContents); |
||
| 216 | $css = preg_replace('/\x{EF}\x{BB}\x{BF}/','',$css);
|
||
| 217 | $tmpstyle = apply_filters( 'autoptimize_css_individual_style', $css, $cssPath ); |
||
| 218 | View Code Duplication | if (has_filter('autoptimize_css_individual_style') && !empty($tmpstyle)) {
|
|
| 219 | $css=$tmpstyle; |
||
| 220 | $this->alreadyminified=true; |
||
| 221 | } else if ($this->can_inject_late($cssPath,$css)) {
|
||
| 222 | $css="/*!%%INJECTLATER%%".base64_encode($cssPath)."|".$cssHash."%%INJECTLATER%%*/"; |
||
| 223 | } |
||
| 224 | } else {
|
||
| 225 | // Couldn't read CSS. Maybe getpath isn't working? |
||
| 226 | $css = ''; |
||
| 227 | } |
||
| 228 | } |
||
| 229 | |||
| 230 | foreach($media as $elem) {
|
||
| 231 | if(!isset($this->csscode[$elem])) |
||
| 232 | $this->csscode[$elem] = ''; |
||
| 233 | $this->csscode[$elem] .= "\n/*FILESTART*/".$css; |
||
| 234 | } |
||
| 235 | } |
||
| 236 | |||
| 237 | // Check for duplicate code |
||
| 238 | $md5list = array(); |
||
| 239 | $tmpcss = $this->csscode; |
||
| 240 | foreach($tmpcss as $media => $code) {
|
||
| 241 | $md5sum = md5($code); |
||
| 242 | $medianame = $media; |
||
| 243 | foreach($md5list as $med => $sum) {
|
||
| 244 | // If same code |
||
| 245 | if($sum === $md5sum) {
|
||
| 246 | //Add the merged code |
||
| 247 | $medianame = $med.', '.$media; |
||
| 248 | $this->csscode[$medianame] = $code; |
||
| 249 | $md5list[$medianame] = $md5list[$med]; |
||
| 250 | unset($this->csscode[$med], $this->csscode[$media]); |
||
| 251 | unset($md5list[$med]); |
||
| 252 | } |
||
| 253 | } |
||
| 254 | $md5list[$medianame] = $md5sum; |
||
| 255 | } |
||
| 256 | unset($tmpcss); |
||
| 257 | |||
| 258 | // Manage @imports, while is for recursive import management |
||
| 259 | foreach ($this->csscode as &$thiscss) {
|
||
| 260 | // Flag to trigger import reconstitution and var to hold external imports |
||
| 261 | $fiximports = false; |
||
| 262 | $external_imports = ""; |
||
| 263 | |||
| 264 | // remove comments to avoid importing commented-out imports |
||
| 265 | $thiscss_nocomments = preg_replace('#/\*.*\*/#Us','',$thiscss);
|
||
| 266 | |||
| 267 | while(preg_match_all('#@import +(?:url)?(?:(?:\\(([\"\']?)(?:[^\"\')]+)\\1\\)|([\"\'])(?:[^\"\']+)\\2)(?:[^,;\"\']+(?:,[^,;\"\']+)*)?)(?:;)#m',$thiscss_nocomments,$matches)) {
|
||
| 268 | foreach($matches[0] as $import) {
|
||
| 269 | if ($this->isremovable($import,$this->cssremovables)) {
|
||
| 270 | $thiscss = str_replace($import,'',$thiscss); |
||
| 271 | $import_ok = true; |
||
| 272 | } else {
|
||
| 273 | $url = trim(preg_replace('#^.*((?:https?:|ftp:)?//.*\.css).*$#','$1',trim($import))," \t\n\r\0\x0B\"'");
|
||
| 274 | $path = $this->getpath($url); |
||
| 275 | $import_ok = false; |
||
| 276 | if (file_exists($path) && is_readable($path)) {
|
||
| 277 | $code = addcslashes($this->fixurls($path,file_get_contents($path)),"\\"); |
||
| 278 | $code = preg_replace('/\x{EF}\x{BB}\x{BF}/','',$code);
|
||
| 279 | $tmpstyle = apply_filters( 'autoptimize_css_individual_style', $code, "" ); |
||
| 280 | if ( has_filter('autoptimize_css_individual_style') && !empty($tmpstyle)) {
|
||
| 281 | $code=$tmpstyle; |
||
| 282 | $this->alreadyminified=true; |
||
| 283 | } else if ($this->can_inject_late($path,$code)) {
|
||
| 284 | $code="/*!%%INJECTLATER".AUTOPTIMIZE_HASH."%%".base64_encode($path)."|".md5($code)."%%INJECTLATER%%*/"; |
||
| 285 | } |
||
| 286 | |||
| 287 | if(!empty($code)) {
|
||
| 288 | $tmp_thiscss = preg_replace('#(/\*FILESTART\*/.*)'.preg_quote($import,'#').'#Us','/*FILESTART2*/'.$code.'$1',$thiscss);
|
||
| 289 | if (!empty($tmp_thiscss)) {
|
||
| 290 | $thiscss = $tmp_thiscss; |
||
| 291 | $import_ok = true; |
||
| 292 | unset($tmp_thiscss); |
||
| 293 | } |
||
| 294 | unset($code); |
||
| 295 | } |
||
| 296 | } |
||
| 297 | } |
||
| 298 | |||
| 299 | if (!$import_ok) {
|
||
| 300 | // external imports and general fall-back |
||
| 301 | $external_imports .= $import; |
||
| 302 | $thiscss = str_replace($import,'',$thiscss); |
||
| 303 | $fiximports = true; |
||
| 304 | } |
||
| 305 | } |
||
| 306 | $thiscss = preg_replace('#/\*FILESTART\*/#','',$thiscss);
|
||
| 307 | $thiscss = preg_replace('#/\*FILESTART2\*/#','/*FILESTART*/',$thiscss);
|
||
| 308 | |||
| 309 | // and update $thiscss_nocomments before going into next iteration in while loop |
||
| 310 | $thiscss_nocomments=preg_replace('#/\*.*\*/#Us','',$thiscss);
|
||
| 311 | } |
||
| 312 | unset($thiscss_nocomments); |
||
| 313 | |||
| 314 | // add external imports to top of aggregated CSS |
||
| 315 | if($fiximports) {
|
||
| 316 | $thiscss=$external_imports.$thiscss; |
||
| 317 | } |
||
| 318 | } |
||
| 319 | unset($thiscss); |
||
| 320 | |||
| 321 | // $this->csscode has all the uncompressed code now. |
||
| 322 | foreach($this->csscode as &$code) {
|
||
| 323 | // Check for already-minified code |
||
| 324 | $hash = md5($code); |
||
| 325 | do_action( 'autoptimize_action_css_hash', $hash ); |
||
| 326 | $ccheck = new autoptimizeCache($hash,'css'); |
||
| 327 | if($ccheck->check()) {
|
||
| 328 | $code = $ccheck->retrieve(); |
||
| 329 | $this->hashmap[md5($code)] = $hash; |
||
| 330 | continue; |
||
| 331 | } |
||
| 332 | unset($ccheck); |
||
| 333 | |||
| 334 | // Do the imaging! |
||
| 335 | $imgreplace = array(); |
||
| 336 | preg_match_all( self::ASSETS_REGEX, $code, $matches ); |
||
| 337 | |||
| 338 | if ( ($this->datauris == true) && (function_exists('base64_encode')) && (is_array($matches)) ) {
|
||
| 339 | foreach($matches[1] as $count => $quotedurl) {
|
||
| 340 | $iurl = trim($quotedurl," \t\n\r\0\x0B\"'"); |
||
| 341 | |||
| 342 | // if querystring, remove it from url |
||
| 343 | if (strpos($iurl,'?') !== false) { $iurl = strtok($iurl,'?'); }
|
||
| 344 | |||
| 345 | $ipath = $this->getpath($iurl); |
||
| 346 | |||
| 347 | $datauri_max_size = 4096; |
||
| 348 | $datauri_max_size = (int) apply_filters( 'autoptimize_filter_css_datauri_maxsize', $datauri_max_size ); |
||
| 349 | $datauri_exclude = apply_filters( 'autoptimize_filter_css_datauri_exclude', ""); |
||
| 350 | if (!empty($datauri_exclude)) {
|
||
| 351 | $no_datauris=array_filter(array_map('trim',explode(",",$datauri_exclude)));
|
||
| 352 | foreach ($no_datauris as $no_datauri) {
|
||
| 353 | if (strpos($iurl,$no_datauri)!==false) {
|
||
| 354 | $ipath=false; |
||
| 355 | break; |
||
| 356 | } |
||
| 357 | } |
||
| 358 | } |
||
| 359 | |||
| 360 | if($ipath != false && preg_match('#\.(jpe?g|png|gif|bmp)$#i',$ipath) && file_exists($ipath) && is_readable($ipath) && filesize($ipath) <= $datauri_max_size) {
|
||
| 361 | $ihash=md5($ipath); |
||
| 362 | $icheck = new autoptimizeCache($ihash,'img'); |
||
| 363 | if($icheck->check()) {
|
||
| 364 | // we have the base64 image in cache |
||
| 365 | $headAndData=$icheck->retrieve(); |
||
| 366 | $_base64data=explode(";base64,",$headAndData);
|
||
| 367 | $base64data=$_base64data[1]; |
||
| 368 | } else {
|
||
| 369 | // It's an image and we don't have it in cache, get the type |
||
| 370 | $explA=explode('.',$ipath);
|
||
| 371 | $type=end($explA); |
||
| 372 | |||
| 373 | switch($type) {
|
||
| 374 | case 'jpeg': |
||
| 375 | $dataurihead = 'data:image/jpeg;base64,'; |
||
| 376 | break; |
||
| 377 | case 'jpg': |
||
| 378 | $dataurihead = 'data:image/jpeg;base64,'; |
||
| 379 | break; |
||
| 380 | case 'gif': |
||
| 381 | $dataurihead = 'data:image/gif;base64,'; |
||
| 382 | break; |
||
| 383 | case 'png': |
||
| 384 | $dataurihead = 'data:image/png;base64,'; |
||
| 385 | break; |
||
| 386 | case 'bmp': |
||
| 387 | $dataurihead = 'data:image/bmp;base64,'; |
||
| 388 | break; |
||
| 389 | default: |
||
| 390 | $dataurihead = 'data:application/octet-stream;base64,'; |
||
| 391 | } |
||
| 392 | |||
| 393 | // Encode the data |
||
| 394 | $base64data = base64_encode(file_get_contents($ipath)); |
||
| 395 | $headAndData=$dataurihead.$base64data; |
||
| 396 | |||
| 397 | // Save in cache |
||
| 398 | $icheck->cache($headAndData,"text/plain"); |
||
| 399 | } |
||
| 400 | unset($icheck); |
||
| 401 | |||
| 402 | // Add it to the list for replacement |
||
| 403 | $imgreplace[$matches[0][$count]] = str_replace($quotedurl,$headAndData,$matches[0][$count]); |
||
| 404 | } else {
|
||
| 405 | // just cdn the URL if applicable |
||
| 406 | if (!empty($this->cdn_url)) {
|
||
| 407 | $imgreplace[$matches[0][$count]] = str_replace($quotedurl,$this->maybe_cdn_urls($quotedurl),$matches[0][$count]); |
||
| 408 | } |
||
| 409 | } |
||
| 410 | } |
||
| 411 | } else if ((is_array($matches)) && (!empty($this->cdn_url))) {
|
||
| 412 | // change urls to cdn-url |
||
| 413 | foreach($matches[1] as $count => $quotedurl) {
|
||
| 414 | $imgreplace[$matches[0][$count]] = str_replace($quotedurl,$this->maybe_cdn_urls($quotedurl),$matches[0][$count]); |
||
| 415 | } |
||
| 416 | } |
||
| 417 | |||
| 418 | if(!empty($imgreplace)) {
|
||
| 419 | $code = str_replace(array_keys($imgreplace),array_values($imgreplace),$code); |
||
| 420 | } |
||
| 421 | |||
| 422 | // Minify |
||
| 423 | if (($this->alreadyminified!==true) && (apply_filters( "autoptimize_css_do_minify", true))) {
|
||
| 424 | View Code Duplication | if (class_exists('Minify_CSS_Compressor')) {
|
|
| 425 | $tmp_code = trim(Minify_CSS_Compressor::process($code)); |
||
| 426 | } else if(class_exists('CSSmin')) {
|
||
| 427 | $cssmin = new CSSmin(); |
||
| 428 | if (method_exists($cssmin,"run")) {
|
||
| 429 | $tmp_code = trim($cssmin->run($code)); |
||
| 430 | } elseif (@is_callable(array($cssmin,"minify"))) {
|
||
| 431 | $tmp_code = trim(CssMin::minify($code)); |
||
| 432 | } |
||
| 433 | } |
||
| 434 | if (!empty($tmp_code)) {
|
||
| 435 | $code = $tmp_code; |
||
| 436 | unset($tmp_code); |
||
| 437 | } |
||
| 438 | } |
||
| 439 | |||
| 440 | $code = $this->inject_minified($code); |
||
| 441 | |||
| 442 | $tmp_code = apply_filters( 'autoptimize_css_after_minify', $code ); |
||
| 443 | if (!empty($tmp_code)) {
|
||
| 444 | $code = $tmp_code; |
||
| 445 | unset($tmp_code); |
||
| 446 | } |
||
| 447 | |||
| 448 | $this->hashmap[md5($code)] = $hash; |
||
| 449 | } |
||
| 450 | unset($code); |
||
| 451 | return true; |
||
| 452 | } |
||
| 453 | |||
| 454 | //Caches the CSS in uncompressed, deflated and gzipped form. |
||
| 455 | public function cache() {
|
||
| 456 | // CSS cache |
||
| 457 | foreach($this->csscode as $media => $code) {
|
||
| 458 | $md5 = $this->hashmap[md5($code)]; |
||
| 459 | |||
| 460 | $cache = new autoptimizeCache($md5,'css'); |
||
| 461 | if(!$cache->check()) {
|
||
| 462 | // Cache our code |
||
| 463 | $cache->cache($code,'text/css'); |
||
| 464 | } |
||
| 465 | $this->url[$media] = AUTOPTIMIZE_CACHE_URL.$cache->getname(); |
||
| 466 | } |
||
| 467 | } |
||
| 468 | |||
| 469 | //Returns the content |
||
| 470 | public function getcontent() {
|
||
| 471 | // restore IE hacks |
||
| 472 | $this->content = $this->restore_iehacks($this->content); |
||
| 473 | |||
| 474 | // restore comments |
||
| 475 | $this->content = $this->restore_comments($this->content); |
||
| 476 | |||
| 477 | // restore (no)script |
||
| 478 | View Code Duplication | if ( strpos( $this->content, '%%SCRIPT%%' ) !== false ) {
|
|
| 479 | $this->content = preg_replace_callback( |
||
| 480 | '#%%SCRIPT'.AUTOPTIMIZE_HASH.'%%(.*?)%%SCRIPT%%#is', |
||
| 481 | create_function( |
||
|
0 ignored issues
–
show
The use of
create_function is highly discouraged, better use a closure.
// Instead of
$function = create_function('$a, $b', 'return $a + $b');
// Better use
$function = function($a, $b) { return $a + $b; }
Loading history...
|
|||
| 482 | '$matches', |
||
| 483 | 'return base64_decode($matches[1]);' |
||
| 484 | ), |
||
| 485 | $this->content |
||
| 486 | ); |
||
| 487 | } |
||
| 488 | |||
| 489 | // restore noptimize |
||
| 490 | $this->content = $this->restore_noptimize($this->content); |
||
| 491 | |||
| 492 | //Restore the full content |
||
| 493 | if(!empty($this->restofcontent)) {
|
||
| 494 | $this->content .= $this->restofcontent; |
||
| 495 | $this->restofcontent = ''; |
||
| 496 | } |
||
| 497 | |||
| 498 | // Inject the new stylesheets |
||
| 499 | $replaceTag = array("<title","before");
|
||
| 500 | $replaceTag = apply_filters( 'autoptimize_filter_css_replacetag', $replaceTag, $this->content ); |
||
| 501 | |||
| 502 | if ($this->inline == true) {
|
||
| 503 | foreach($this->csscode as $media => $code) {
|
||
| 504 | $this->inject_in_html('<style type="text/css" media="'.$media.'">'.$code.'</style>',$replaceTag);
|
||
| 505 | } |
||
| 506 | } else {
|
||
| 507 | if ($this->defer == true) {
|
||
| 508 | $preloadCssBlock = ""; |
||
| 509 | $noScriptCssBlock = "<noscript id=\"aonoscrcss\">"; |
||
| 510 | $defer_inline_code=$this->defer_inline; |
||
| 511 | if(!empty($defer_inline_code)){
|
||
| 512 | if ( apply_filters( 'autoptimize_filter_css_critcss_minify', true ) ) {
|
||
| 513 | $iCssHash = md5($defer_inline_code); |
||
| 514 | $iCssCache = new autoptimizeCache($iCssHash,'css'); |
||
| 515 | if($iCssCache->check()) {
|
||
| 516 | // we have the optimized inline CSS in cache |
||
| 517 | $defer_inline_code=$iCssCache->retrieve(); |
||
| 518 | } else {
|
||
| 519 | if (class_exists('Minify_CSS_Compressor')) {
|
||
| 520 | $tmp_code = trim(Minify_CSS_Compressor::process($defer_inline_code)); |
||
| 521 | } else if(class_exists('CSSmin')) {
|
||
| 522 | $cssmin = new CSSmin(); |
||
| 523 | $tmp_code = trim($cssmin->run($defer_inline_code)); |
||
| 524 | } |
||
| 525 | if (!empty($tmp_code)) {
|
||
| 526 | $defer_inline_code = $tmp_code; |
||
| 527 | $iCssCache->cache($defer_inline_code,"text/css"); |
||
| 528 | unset($tmp_code); |
||
| 529 | } |
||
| 530 | } |
||
| 531 | } |
||
| 532 | $code_out='<style type="text/css" id="aoatfcss" media="all">'.$defer_inline_code.'</style>'; |
||
| 533 | $this->inject_in_html($code_out,$replaceTag); |
||
| 534 | } |
||
| 535 | } |
||
| 536 | |||
| 537 | foreach($this->url as $media => $url) {
|
||
| 538 | $url = $this->url_replace_cdn($url); |
||
| 539 | |||
| 540 | //Add the stylesheet either deferred (import at bottom) or normal links in head |
||
| 541 | if($this->defer == true) {
|
||
| 542 | |||
| 543 | // Filter to modify the onload attribute - passes value and the stylesheet url |
||
| 544 | $preloadOnLoad = apply_filters('autoptimize_filter_css_preload_onload', "this.rel='stylesheet'", $url);
|
||
| 545 | |||
| 546 | $preloadCssBlock .= '<link rel="preload" as="style" media="'.$media.'" href="'.$url.'" onload="'.$preloadOnLoad.'" />'; |
||
| 547 | $noScriptCssBlock .= '<link type="text/css" media="'.$media.'" href="'.$url.'" rel="stylesheet" />'; |
||
| 548 | |||
| 549 | } else {
|
||
| 550 | if (strlen($this->csscode[$media]) > $this->cssinlinesize) {
|
||
| 551 | $this->inject_in_html('<link type="text/css" media="'.$media.'" href="'.$url.'" rel="stylesheet" />',$replaceTag);
|
||
| 552 | } else if (strlen($this->csscode[$media])>0) {
|
||
| 553 | $this->inject_in_html('<style type="text/css" media="'.$media.'">'.$this->csscode[$media].'</style>',$replaceTag);
|
||
| 554 | } |
||
| 555 | } |
||
| 556 | } |
||
| 557 | |||
| 558 | if($this->defer == true) {
|
||
| 559 | $preloadPolyfill = '<script data-cfasync=\'false\'>/*! loadCSS. [c]2017 Filament Group, Inc. MIT License */ |
||
| 560 | !function(a){"use strict";var b=function(b,c,d){function e(a){return h.body?a():void setTimeout(function(){e(a)})}function f(){i.addEventListener&&i.removeEventListener("load",f),i.media=d||"all"}var g,h=a.document,i=h.createElement("link");if(c)g=c;else{var j=(h.body||h.getElementsByTagName("head")[0]).childNodes;g=j[j.length-1]}var k=h.styleSheets;i.rel="stylesheet",i.href=b,i.media="only x",e(function(){g.parentNode.insertBefore(i,c?g:g.nextSibling)});var l=function(a){for(var b=i.href,c=k.length;c--;)if(k[c].href===b)return a();setTimeout(function(){l(a)})};return i.addEventListener&&i.addEventListener("load",f),i.onloadcssdefined=l,l(f),i};"undefined"!=typeof exports?exports.loadCSS=b:a.loadCSS=b}("undefined"!=typeof global?global:this);
|
||
| 561 | /*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */ |
||
| 562 | !function(a){if(a.loadCSS){var b=loadCSS.relpreload={};if(b.support=function(){try{return a.document.createElement("link").relList.supports("preload")}catch(b){return!1}},b.poly=function(){for(var b=a.document.getElementsByTagName("link"),c=0;c<b.length;c++){var d=b[c];"preload"===d.rel&&"style"===d.getAttribute("as")&&(a.loadCSS(d.href,d,d.getAttribute("media")),d.rel=null)}},!b.support()){b.poly();var c=a.setInterval(b.poly,300);a.addEventListener&&a.addEventListener("load",function(){b.poly(),a.clearInterval(c)}),a.attachEvent&&a.attachEvent("onload",function(){a.clearInterval(c)})}}}(this);</script>';
|
||
| 563 | $noScriptCssBlock .= "</noscript>"; |
||
| 564 | $this->inject_in_html($preloadCssBlock.$noScriptCssBlock,$replaceTag); |
||
| 565 | |||
| 566 | // Adds preload polyfill at end of body tag |
||
| 567 | $this->inject_in_html( |
||
| 568 | apply_filters('autoptimize_css_preload_polyfill', $preloadPolyfill),
|
||
| 569 | array('</body>','before')
|
||
| 570 | ); |
||
| 571 | } |
||
| 572 | } |
||
| 573 | |||
| 574 | //Return the modified stylesheet |
||
| 575 | return $this->content; |
||
| 576 | } |
||
| 577 | |||
| 578 | static function fixurls($file, $code) {
|
||
| 579 | // Switch all imports to the url() syntax |
||
| 580 | $code = preg_replace( '#@import ("|\')(.+?)\.css.*?("|\')#', '@import url("${2}.css")', $code );
|
||
| 581 | |||
| 582 | if ( preg_match_all( self::ASSETS_REGEX, $code, $matches ) ) {
|
||
| 583 | $file = str_replace( WP_ROOT_DIR, '/', $file ); |
||
| 584 | $dir = dirname( $file ); // Like /themes/expound/css |
||
| 585 | |||
| 586 | // $dir should not contain backslashes, since it's used to replace |
||
| 587 | // urls, but it can contain them when running on Windows because |
||
| 588 | // fixurls() is sometimes called with `ABSPATH . 'index.php'` |
||
| 589 | $dir = str_replace( '\\', '/', $dir ); |
||
| 590 | unset( $file ); // not used below at all |
||
| 591 | |||
| 592 | $replace = array(); |
||
| 593 | foreach ( $matches[1] as $k => $url ) {
|
||
| 594 | // Remove quotes |
||
| 595 | $url = trim( $url," \t\n\r\0\x0B\"'" ); |
||
| 596 | $noQurl = trim( $url, "\"'" ); |
||
| 597 | if ( $url !== $noQurl ) {
|
||
| 598 | $removedQuotes = true; |
||
| 599 | } else {
|
||
| 600 | $removedQuotes = false; |
||
| 601 | } |
||
| 602 | |||
| 603 | if ( '' === $noQurl ) {
|
||
| 604 | continue; |
||
| 605 | } |
||
| 606 | |||
| 607 | $url = $noQurl; |
||
| 608 | if ( '/' === $url{0} || preg_match( '#^(https?://|ftp://|data:)#i', $url ) ) {
|
||
| 609 | // URL is protocol-relative, host-relative or something we don't touch |
||
| 610 | continue; |
||
| 611 | } else {
|
||
| 612 | // Relative URL |
||
| 613 | $newurl = preg_replace( '/https?:/', '', str_replace( ' ', '%20', AUTOPTIMIZE_WP_ROOT_URL . str_replace( '//', '/', $dir . '/' . $url ) ) ); |
||
| 614 | |||
| 615 | // Hash the url + whatever was behind potentially for replacement |
||
| 616 | // We must do this, or different css classes referencing the same bg image (but |
||
| 617 | // different parts of it, say, in sprites and such) loose their stuff... |
||
| 618 | $hash = md5( $url . $matches[2][$k] ); |
||
| 619 | $code = str_replace( $matches[0][$k], $hash, $code ); |
||
| 620 | |||
| 621 | if ( $removedQuotes ) {
|
||
| 622 | $replace[$hash] = "url('" . $newurl . "')" . $matches[2][$k];
|
||
| 623 | } else {
|
||
| 624 | $replace[$hash] = 'url(' . $newurl . ')' . $matches[2][$k];
|
||
| 625 | } |
||
| 626 | } |
||
| 627 | } |
||
| 628 | |||
| 629 | if ( ! empty( $replace ) ) {
|
||
| 630 | // Sort the replacements array by key length in desc order (so that the longest strings are replaced first) |
||
| 631 | $keys = array_map( 'strlen', array_keys( $replace ) ); |
||
| 632 | array_multisort( $keys, SORT_DESC, $replace ); |
||
| 633 | |||
| 634 | // Replace URLs found within $code |
||
| 635 | $code = str_replace( array_keys( $replace ), array_values( $replace ), $code ); |
||
| 636 | } |
||
| 637 | } |
||
| 638 | |||
| 639 | return $code; |
||
| 640 | } |
||
| 641 | |||
| 642 | private function ismovable($tag) {
|
||
| 643 | if ( apply_filters('autoptimize_filter_css_dontaggregate', false) ) {
|
||
| 644 | return false; |
||
| 645 | } else if (!empty($this->whitelist)) {
|
||
| 646 | foreach ($this->whitelist as $match) {
|
||
| 647 | if(strpos($tag,$match)!==false) {
|
||
| 648 | return true; |
||
| 649 | } |
||
| 650 | } |
||
| 651 | // no match with whitelist |
||
| 652 | return false; |
||
| 653 | } else {
|
||
| 654 | if (is_array($this->dontmove)) {
|
||
| 655 | foreach($this->dontmove as $match) {
|
||
| 656 | if(strpos($tag,$match)!==false) {
|
||
| 657 | //Matched something |
||
| 658 | return false; |
||
| 659 | } |
||
| 660 | } |
||
| 661 | } |
||
| 662 | |||
| 663 | //If we're here it's safe to move |
||
| 664 | return true; |
||
| 665 | } |
||
| 666 | } |
||
| 667 | |||
| 668 | private function can_inject_late($cssPath,$css) {
|
||
| 669 | $consider_minified_array = apply_filters('autoptimize_filter_css_consider_minified', false, $cssPath);
|
||
| 670 | if ( $this->inject_min_late !== true ) {
|
||
| 671 | // late-inject turned off |
||
| 672 | return false; |
||
| 673 | } else if ( (strpos($cssPath,"min.css") === false) && ( str_replace($consider_minified_array, '', $cssPath) === $cssPath ) ) {
|
||
| 674 | // file not minified based on filename & filter |
||
| 675 | return false; |
||
| 676 | } else if ( strpos($css,"@import") !== false ) {
|
||
| 677 | // can't late-inject files with imports as those need to be aggregated |
||
| 678 | return false; |
||
| 679 | } else if ( (strpos($css,"@font-face")!==false ) && ( apply_filters("autoptimize_filter_css_fonts_cdn",false)===true) && (!empty($this->cdn_url)) ) {
|
||
| 680 | // don't late-inject CSS with font-src's if fonts are set to be CDN'ed |
||
| 681 | return false; |
||
| 682 | } else if ( (($this->datauris == true) || (!empty($this->cdn_url))) && preg_match("#background[^;}]*url\(#Ui",$css) ) {
|
||
| 683 | // don't late-inject CSS with images if CDN is set OR is image inlining is on |
||
| 684 | return false; |
||
| 685 | } else {
|
||
| 686 | // phew, all is safe, we can late-inject |
||
| 687 | return true; |
||
| 688 | } |
||
| 689 | } |
||
| 690 | |||
| 691 | private function maybe_cdn_urls($inUrl) {
|
||
| 692 | $url = trim($inUrl," \t\n\r\0\x0B\"'"); |
||
| 693 | $urlPath = parse_url($url,PHP_URL_PATH); |
||
| 694 | |||
| 695 | // exclude fonts from CDN except if filter returns true |
||
| 696 | if ( !preg_match('#\.(woff2?|eot|ttf|otf)$#i',$urlPath) || apply_filters('autoptimize_filter_css_fonts_cdn',false) ) {
|
||
| 697 | $cdn_url = $this->url_replace_cdn($url); |
||
| 698 | } else {
|
||
| 699 | $cdn_url = $url; |
||
| 700 | } |
||
| 701 | |||
| 702 | return $cdn_url; |
||
| 703 | } |
||
| 704 | } |
||
| 705 |
create_functioncan pose a great security vulnerability as it is similar toeval, and could be used for arbitrary code execution. We highly recommend to use a closure instead.