content ); if ($noptimizeCSS) return false; $whitelistCSS = apply_filters( 'autoptimize_filter_css_whitelist', '', $this->content ); if (!empty($whitelistCSS)) { $this->whitelist = array_filter(array_map('trim',explode(",",$whitelistCSS))); } if ($options['nogooglefont'] == true) { $removableCSS = "fonts.googleapis.com"; } else { $removableCSS = ""; } $removableCSS = apply_filters( 'autoptimize_filter_css_removables', $removableCSS); if (!empty($removableCSS)) { $this->cssremovables = array_filter(array_map('trim',explode(",",$removableCSS))); } $this->cssinlinesize = apply_filters('autoptimize_filter_css_inlinesize',256); // filter to "late inject minified CSS", default to true for now (it is faster) $this->inject_min_late = apply_filters('autoptimize_filter_css_inject_min_late',true); // Remove everything that's not the header if ( apply_filters('autoptimize_filter_css_justhead',$options['justhead']) == true ) { $content = explode('',$this->content,2); $this->content = $content[0].''; $this->restofcontent = $content[1]; } // include inline? if( apply_filters('autoptimize_css_include_inline',$options['include_inline']) == true ) { $this->include_inline = true; } // what CSS shouldn't be autoptimized $excludeCSS = $options['css_exclude']; $excludeCSS = apply_filters( 'autoptimize_filter_css_exclude', $excludeCSS, $this->content ); if ($excludeCSS!=="") { $this->dontmove = array_filter(array_map('trim',explode(",",$excludeCSS))); } else { $this->dontmove = array(); } // forcefully exclude CSS with data-noptimize attrib $this->dontmove[]="data-noptimize"; // should we defer css? // value: true/ false $this->defer = $options['defer']; $this->defer = apply_filters( 'autoptimize_filter_css_defer', $this->defer, $this->content ); // should we inline while deferring? // value: inlined CSS $this->defer_inline = $options['defer_inline']; $this->defer_inline = apply_filters( 'autoptimize_filter_css_defer_inline', $this->defer_inline, $this->content ); // should we inline? // value: true/ false $this->inline = $options['inline']; $this->inline = apply_filters( 'autoptimize_filter_css_inline', $this->inline, $this->content ); // get cdn url $this->cdn_url = $options['cdn_url']; // Store data: URIs setting for later use $this->datauris = $options['datauris']; // noptimize me $this->content = $this->hide_noptimize($this->content); // exclude (no)script, as those may contain CSS which should be left as is if ( strpos( $this->content, 'content = preg_replace_callback( '#<(?:no)?script.*?<\/(?:no)?script>#is', create_function( '$matches', 'return "%%SCRIPT".AUTOPTIMIZE_HASH."%%".base64_encode($matches[0])."%%SCRIPT%%";' ), $this->content ); } // Save IE hacks $this->content = $this->hide_iehacks($this->content); // hide comments $this->content = $this->hide_comments($this->content); // Get )|(]*stylesheet[^>]*>)#Usmi',$this->content,$matches)) { foreach($matches[0] as $tag) { if ($this->isremovable($tag,$this->cssremovables)) { $this->content = str_replace($tag,'',$this->content); } else if ($this->ismovable($tag)) { // Get the media if(strpos($tag,'media=')!==false) { preg_match('#media=(?:"|\')([^>]*)(?:"|\')#Ui',$tag,$medias); $medias = explode(',',$medias[1]); $media = array(); foreach($medias as $elem) { if (empty($elem)) { $elem="all"; } $media[] = $elem; } } else { // No media specified - applies to all $media = array('all'); } $media = apply_filters( 'autoptimize_filter_css_tagmedia',$media,$tag ); if(preg_match('# $explUrl = explode('?',$source[2],2); $url = $explUrl[0]; $path = $this->getpath($url); if($path!==false && preg_match('#\.css$#',$path)) { // Good link $this->css[] = array($media,$path); }else{ // Link is dynamic (.php etc) $tag = ''; } } else { // inline css in style tags can be wrapped in comment tags, so restore comments $tag = $this->restore_comments($tag); preg_match('#(.*)#Usmi',$tag,$code); // and re-hide them to be able to to the removal based on tag $tag = $this->hide_comments($tag); if ( $this->include_inline ) { $code = preg_replace('#^.*.*$#sm','$1',$code[1]); $this->css[] = array($media,'INLINE;'.$code); } else { $tag = ''; } } // Remove the original style tag $this->content = str_replace($tag,'',$this->content); } else { // excluded CSS, minify if getpath if (preg_match('#getpath($url); if ($path && apply_filters('autoptimize_filter_css_minify_excluded',false)) { $_CachedMinifiedUrl = $this->minify_single($path); if (!empty($_CachedMinifiedUrl)) { // replace orig URL with URL to cache $newTag = str_replace($url, $_CachedMinifiedUrl, $tag); } else { $newTag = $tag; } // remove querystring from URL if ( !empty($explUrl[1]) ) { $newTag = str_replace("?".$explUrl[1],"",$newTag); } // and replace $this->content = str_replace($tag,$newTag,$this->content); } } } } return true; } // Really, no styles? return false; } // Joins and optimizes CSS public function minify() { foreach($this->css as $group) { list($media,$css) = $group; if(preg_match('#^INLINE;#',$css)) { // ',$replaceTag); } } else { if ($this->defer == true) { $preloadCssBlock = ""; $noScriptCssBlock = ""; $this->inject_in_html($preloadCssBlock.$noScriptCssBlock,$replaceTag); $this->inject_in_html($preloadPolyfill,array('','before')); } } //Return the modified stylesheet return $this->content; } static function fixurls($file, $code) { // Switch all imports to the url() syntax $code = preg_replace( '#@import ("|\')(.+?)\.css.*("|\')#', '@import url("${2}.css")', $code ); if ( preg_match_all( self::ASSETS_REGEX, $code, $matches ) ) { $file = str_replace( WP_ROOT_DIR, '/', $file ); $dir = dirname( $file ); // Like /themes/expound/css // $dir should not contain backslashes, since it's used to replace // urls, but it can contain them when running on Windows because // fixurls() is sometimes called with `ABSPATH . 'index.php'` $dir = str_replace( '\\', '/', $dir ); unset( $file ); // not used below at all $replace = array(); foreach ( $matches[1] as $k => $url ) { // Remove quotes $url = trim( $url," \t\n\r\0\x0B\"'" ); $noQurl = trim( $url, "\"'" ); if ( $url !== $noQurl ) { $removedQuotes = true; } else { $removedQuotes = false; } if ( '' === $noQurl ) { continue; } $url = $noQurl; if ( '/' === $url{0} || preg_match( '#^(https?://|ftp://|data:)#i', $url ) ) { // URL is protocol-relative, host-relative or something we don't touch continue; } else { // Relative URL $newurl = preg_replace( '/https?:/', '', str_replace( ' ', '%20', AUTOPTIMIZE_WP_ROOT_URL . str_replace( '//', '/', $dir . '/' . $url ) ) ); // Hash the url + whatever was behind potentially for replacement // We must do this, or different css classes referencing the same bg image (but // different parts of it, say, in sprites and such) loose their stuff... $hash = md5( $url . $matches[2][$k] ); $code = str_replace( $matches[0][$k], $hash, $code ); if ( $removedQuotes ) { $replace[$hash] = "url('" . $newurl . "')" . $matches[2][$k]; } else { $replace[$hash] = 'url(' . $newurl . ')' . $matches[2][$k]; } } } if ( ! empty( $replace ) ) { // Sort the replacements array by key length in desc order (so that the longest strings are replaced first) $keys = array_map( 'strlen', array_keys( $replace ) ); array_multisort( $keys, SORT_DESC, $replace ); // Replace URLs found within $code $code = str_replace( array_keys( $replace ), array_values( $replace ), $code ); } } return $code; } private function ismovable($tag) { if ( apply_filters('autoptimize_filter_css_dontaggregate', false) ) { return false; } else if (!empty($this->whitelist)) { foreach ($this->whitelist as $match) { if(strpos($tag,$match)!==false) { return true; } } // no match with whitelist return false; } else { if (is_array($this->dontmove)) { foreach($this->dontmove as $match) { if(strpos($tag,$match)!==false) { //Matched something return false; } } } //If we're here it's safe to move return true; } } private function can_inject_late($cssPath,$css) { $consider_minified_array = apply_filters('autoptimize_filter_css_consider_minified', false, $cssPath); if ( $this->inject_min_late !== true ) { // late-inject turned off return false; } else if ( (strpos($cssPath,"min.css") === false) && ( str_replace($consider_minified_array, '', $cssPath) === $cssPath ) ) { // file not minified based on filename & filter return false; } else if ( strpos($css,"@import") !== false ) { // can't late-inject files with imports as those need to be aggregated return false; } else if ( (strpos($css,"@font-face")!==false ) && ( apply_filters("autoptimize_filter_css_fonts_cdn",false)===true) && (!empty($this->cdn_url)) ) { // don't late-inject CSS with font-src's if fonts are set to be CDN'ed return false; } else if ( (($this->datauris == true) || (!empty($this->cdn_url))) && preg_match("#background[^;}]*url\(#Ui",$css) ) { // don't late-inject CSS with images if CDN is set OR is image inlining is on return false; } else { // phew, all is safe, we can late-inject return true; } } private function maybe_cdn_urls($inUrl) { $url = trim($inUrl," \t\n\r\0\x0B\"'"); $urlPath = parse_url($url,PHP_URL_PATH); // exclude fonts from CDN except if filter returns true if ( !preg_match('#\.(woff2?|eot|ttf|otf)$#i',$urlPath) || apply_filters('autoptimize_filter_css_fonts_cdn',false) ) { $cdn_url = $this->url_replace_cdn($url); } else { $cdn_url = $url; } return $cdn_url; } }