| @@ -12,61 +12,171 @@ | ||
| 12 | 12 | const ASSETS_REGEX = '/url\s*\(\s*(?!["\']?data:)(?![\'|\"]?[\#|\%|])([^)]+)\s*\)([^;},\s]*)/i'; |
| 13 | 13 | |
| 14 | 14 | /** |
| 15 | 15 | * Font-face regex-fu from HamZa at: https://stackoverflow.com/a/21395083 |
| 16 | - * ~ | |
| 17 | - * @font-face\s* # Match @font-face and some spaces | |
| 18 | - * ( # Start group 1 | |
| 19 | - * \{ # Match { | |
| 20 | - * (?: # A non-capturing group | |
| 21 | - * [^{}]+ # Match anything except {} one or more times | |
| 22 | - * | # Or | |
| 23 | - * (?1) # Recurse/rerun the expression of group 1 | |
| 24 | - * )* # Repeat 0 or more times | |
| 25 | - * \} # Match } | |
| 26 | - * ) # End group 1 | |
| 27 | - * ~xs'; | |
| 28 | 16 | */ |
| 29 | 17 | const FONT_FACE_REGEX = '~@font-face\s*(\{(?:[^{}]+|(?1))*\})~xsi'; // added `i` flag for case-insensitivity. |
| 30 | 18 | |
| 31 | - private $css = array(); | |
| 32 | - private $csscode = array(); | |
| 33 | - private $url = array(); | |
| 34 | - private $restofcontent = ''; | |
| 35 | - private $datauris = false; | |
| 36 | - private $hashmap = array(); | |
| 19 | + /** | |
| 20 | + * Store CSS. | |
| 21 | + * | |
| 22 | + * @var array | |
| 23 | + */ | |
| 24 | + private $css = array(); | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * To store CSS code | |
| 28 | + * | |
| 29 | + * @var array | |
| 30 | + */ | |
| 31 | + private $csscode = array(); | |
| 32 | + | |
| 33 | + /** | |
| 34 | + * To store urls | |
| 35 | + * | |
| 36 | + * @var array | |
| 37 | + */ | |
| 38 | + private $url = array(); | |
| 39 | + | |
| 40 | + /** | |
| 41 | + * String to store rest of content (when old setting "only in head" is used) | |
| 42 | + * | |
| 43 | + * @var string | |
| 44 | + */ | |
| 45 | + private $restofcontent = ''; | |
| 46 | + | |
| 47 | + /** | |
| 48 | + * Setting to change small images to inline CSS | |
| 49 | + * | |
| 50 | + * @var bool | |
| 51 | + */ | |
| 52 | + private $datauris = false; | |
| 53 | + | |
| 54 | + /** | |
| 55 | + * Array to store hashmap | |
| 56 | + * | |
| 57 | + * @var array | |
| 58 | + */ | |
| 59 | + private $hashmap = array(); | |
| 60 | + | |
| 61 | + /** | |
| 62 | + * Flag to indicate if CSS is already minified | |
| 63 | + * | |
| 64 | + * @var bool | |
| 65 | + */ | |
| 37 | 66 | private $alreadyminified = false; |
| 38 | - private $aggregate = true; | |
| 39 | - private $inline = false; | |
| 40 | - private $defer = false; | |
| 41 | - private $defer_inline = false; | |
| 42 | - private $whitelist = ''; | |
| 43 | - private $cssinlinesize = ''; | |
| 44 | - private $cssremovables = array(); | |
| 45 | - private $include_inline = false; | |
| 46 | - private $inject_min_late = ''; | |
| 47 | - private $dontmove = array(); | |
| 48 | - private $options = array(); | |
| 67 | + | |
| 68 | + /** | |
| 69 | + * Setting if CSS should be aggregated | |
| 70 | + * | |
| 71 | + * @var bool | |
| 72 | + */ | |
| 73 | + private $aggregate = true; | |
| 74 | + | |
| 75 | + /** | |
| 76 | + * Setting if all CSS should be inlined | |
| 77 | + * | |
| 78 | + * @var bool | |
| 79 | + */ | |
| 80 | + private $inline = false; | |
| 81 | + | |
| 82 | + /** | |
| 83 | + * Setting if CSS should be deferred | |
| 84 | + * | |
| 85 | + * @var bool | |
| 86 | + */ | |
| 87 | + private $defer = false; | |
| 88 | + | |
| 89 | + /** | |
| 90 | + * Setting for to be inlined CSS. | |
| 91 | + * | |
| 92 | + * @var string | |
| 93 | + */ | |
| 94 | + private $defer_inline = ''; | |
| 95 | + | |
| 96 | + /** | |
| 97 | + * Setting for allowlist of what should be aggregated. | |
| 98 | + * | |
| 99 | + * @var string | |
| 100 | + */ | |
| 101 | + private $allowlist = ''; | |
| 102 | + | |
| 103 | + /** | |
| 104 | + * Setting (only filter) for size under which CSS should be inlined instead of linked. | |
| 105 | + * | |
| 106 | + * @var string | |
| 107 | + */ | |
| 108 | + private $cssinlinesize = ''; | |
| 109 | + | |
| 110 | + /** | |
| 111 | + * Setting (only filter) of CSS that can be removed. | |
| 112 | + * | |
| 113 | + * @var array | |
| 114 | + */ | |
| 115 | + private $cssremovables = array(); | |
| 116 | + | |
| 117 | + /** | |
| 118 | + * Setting: should inline CSS be aggregated. | |
| 119 | + * | |
| 120 | + * @var bool | |
| 121 | + */ | |
| 122 | + private $include_inline = false; | |
| 123 | + | |
| 124 | + /** | |
| 125 | + * Setting (only filter) if minified CSS can be injected after minificatoin of aggregated CSS. | |
| 126 | + * | |
| 127 | + * @var bool | |
| 128 | + */ | |
| 129 | + private $inject_min_late = true; | |
| 130 | + | |
| 131 | + /** | |
| 132 | + * Holds all exclusions. | |
| 133 | + * | |
| 134 | + * @var array | |
| 135 | + */ | |
| 136 | + private $dontmove = array(); | |
| 137 | + | |
| 138 | + /** | |
| 139 | + * Holds all options. | |
| 140 | + * | |
| 141 | + * @var array | |
| 142 | + */ | |
| 143 | + private $options = array(); | |
| 144 | + | |
| 145 | + /** | |
| 146 | + * Setting; should excluded CSS-files be minified. | |
| 147 | + * | |
| 148 | + * @var bool | |
| 149 | + */ | |
| 49 | 150 | private $minify_excluded = true; |
| 50 | 151 | |
| 51 | - // public $cdn_url; // Used all over the place implicitly, so will have to be either public or protected :/ . | |
| 152 | + /** | |
| 153 | + * Setting (filter only); should all media-attributes be forced to "all". | |
| 154 | + * | |
| 155 | + * @var bool | |
| 156 | + */ | |
| 157 | + private $media_force_all = false; | |
| 52 | 158 | |
| 53 | - // Reads the page and collects style tags. | |
| 159 | + /** | |
| 160 | + * Reads the page and collects style tags. | |
| 161 | + * | |
| 162 | + * @param array $options all options. | |
| 163 | + */ | |
| 54 | 164 | public function read( $options ) |
| 55 | 165 | { |
| 56 | - $noptimizeCSS = apply_filters( 'autoptimize_filter_css_noptimize', false, $this->content ); | |
| 57 | - if ( $noptimizeCSS ) { | |
| 166 | + $noptimize_css = apply_filters( 'autoptimize_filter_css_noptimize', false, $this->content ); | |
| 167 | + if ( $noptimize_css || false === autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_css_optimize' )) { | |
| 58 | 168 | return false; |
| 59 | 169 | } |
| 60 | 170 | |
| 61 | - $whitelistCSS = apply_filters( 'autoptimize_filter_css_whitelist', '', $this->content ); | |
| 62 | - if ( ! empty( $whitelistCSS ) ) { | |
| 63 | - $this->whitelist = array_filter( array_map( 'trim', explode( ',', $whitelistCSS ) ) ); | |
| 171 | + $allowlist_css = apply_filters( 'autoptimize_filter_css_allowlist', '', $this->content ); | |
| 172 | + if ( ! empty( $allowlist_css ) ) { | |
| 173 | + $this->allowlist = array_filter( array_map( 'trim', explode( ',', $allowlist_css ) ) ); | |
| 64 | 174 | } |
| 65 | 175 | |
| 66 | - $removableCSS = apply_filters( 'autoptimize_filter_css_removables', '' ); | |
| 67 | - if ( ! empty( $removableCSS ) ) { | |
| 68 | - $this->cssremovables = array_filter( array_map( 'trim', explode( ',', $removableCSS ) ) ); | |
| 176 | + $removable_css = apply_filters( 'autoptimize_filter_css_removables', '' ); | |
| 177 | + if ( ! empty( $removable_css ) ) { | |
| 178 | + $this->cssremovables = array_filter( array_map( 'trim', explode( ',', $removable_css ) ) ); | |
| 69 | 179 | } |
| 70 | 180 | |
| 71 | 181 | $this->cssinlinesize = apply_filters( 'autoptimize_filter_css_inlinesize', 256 ); |
| 72 | 182 | |
| @@ -87,8 +197,10 @@ | ||
| 87 | 197 | // Returning true for "dontaggregate" turns off aggregation. |
| 88 | 198 | if ( $this->aggregate && apply_filters( 'autoptimize_filter_css_dontaggregate', false ) ) { |
| 89 | 199 | $this->aggregate = false; |
| 90 | 200 | } |
| 201 | + // and the filter that should have been there to begin with. | |
| 202 | + $this->aggregate = apply_filters( 'autoptimize_filter_css_aggregate', $this->aggregate ); | |
| 91 | 203 | |
| 92 | 204 | // include inline? |
| 93 | 205 | if ( apply_filters( 'autoptimize_css_include_inline', $options['include_inline'] ) ) { |
| 94 | 206 | $this->include_inline = true; |
| @@ -94,11 +206,11 @@ | ||
| 94 | 206 | $this->include_inline = true; |
| 95 | 207 | } |
| 96 | 208 | |
| 97 | 209 | // List of CSS strings which are excluded from autoptimization. |
| 98 | - $excludeCSS = apply_filters( 'autoptimize_filter_css_exclude', $options['css_exclude'], $this->content ); | |
| 99 | - if ( '' !== $excludeCSS ) { | |
| 100 | - $this->dontmove = array_filter( array_map( 'trim', explode( ',', $excludeCSS ) ) ); | |
| 210 | + $exclude_css = apply_filters( 'autoptimize_filter_css_exclude', $options['css_exclude'], $this->content ); | |
| 211 | + if ( '' !== $exclude_css ) { | |
| 212 | + $this->dontmove = array_filter( array_map( 'trim', explode( ',', $exclude_css ) ) ); | |
| 101 | 213 | } else { |
| 102 | 214 | $this->dontmove = array(); |
| 103 | 215 | } |
| 104 | 216 | |
| @@ -104,16 +216,24 @@ | ||
| 104 | 216 | |
| 105 | 217 | // forcefully exclude CSS with data-noptimize attrib. |
| 106 | 218 | $this->dontmove[] = 'data-noptimize'; |
| 107 | 219 | |
| 220 | + // forcefully exclude inline CSS with ".wp-container-" which due to the random-ish nature busts AO's cache continuously. | |
| 221 | + $this->dontmove[] = '.wp-container-'; | |
| 222 | + | |
| 108 | 223 | // Should we defer css? |
| 109 | 224 | // value: true / false. |
| 110 | 225 | $this->defer = $options['defer']; |
| 111 | 226 | $this->defer = apply_filters( 'autoptimize_filter_css_defer', $this->defer, $this->content ); |
| 112 | 227 | |
| 228 | + // If page/ post check post_meta to see if optimize is off. | |
| 229 | + if ( $this->defer && false === autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_ccss' ) ) { | |
| 230 | + $this->defer = false; | |
| 231 | + } | |
| 232 | + | |
| 113 | 233 | // Should we inline while deferring? |
| 114 | 234 | // value: inlined CSS. |
| 115 | - $this->defer_inline = apply_filters( 'autoptimize_filter_css_defer_inline', $options['defer_inline'], $this->content ); | |
| 235 | + $this->defer_inline = apply_filters( 'autoptimize_filter_css_defer_inline', $this->sanitize_css( $options['defer_inline'] ), $this->content ); | |
| 116 | 236 | |
| 117 | 237 | // Should we inline? |
| 118 | 238 | // value: true / false. |
| 119 | 239 | $this->inline = $options['inline']; |
| @@ -130,17 +250,20 @@ | ||
| 130 | 250 | $this->minify_excluded = false; |
| 131 | 251 | } |
| 132 | 252 | $this->minify_excluded = apply_filters( 'autoptimize_filter_css_minify_excluded', $this->minify_excluded, '' ); |
| 133 | 253 | |
| 254 | + // should we force all media-attributes to all? | |
| 255 | + $this->media_force_all = apply_filters( 'autoptimize_filter_css_tagmedia_forceall', false ); | |
| 256 | + | |
| 134 | 257 | // noptimize me. |
| 135 | 258 | $this->content = $this->hide_noptimize( $this->content ); |
| 136 | 259 | |
| 137 | 260 | // Exclude (no)script, as those may contain CSS which should be left as is. |
| 138 | 261 | $this->content = $this->replace_contents_with_marker_if_exists( |
| 139 | - 'SCRIPT', | |
| 140 | - '<script', | |
| 141 | - '#<(?:no)?script.*?<\/(?:no)?script>#is', | |
| 142 | - $this->content | |
| 262 | + 'SCRIPT', | |
| 263 | + '<script', | |
| 264 | + '#<(?:no)?script.*?<\/(?:no)?script>#is', | |
| 265 | + $this->content | |
| 143 | 266 | ); |
| 144 | 267 | |
| 145 | 268 | // Save IE hacks. |
| 146 | 269 | $this->content = $this->hide_iehacks( $this->content ); |
| @@ -157,17 +280,20 @@ | ||
| 157 | 280 | } elseif ( $this->ismovable( $tag ) ) { |
| 158 | 281 | // Get the media. |
| 159 | 282 | if ( false !== strpos( $tag, 'media=' ) ) { |
| 160 | 283 | preg_match( '#media=(?:"|\')([^>]*)(?:"|\')#Ui', $tag, $medias ); |
| 161 | - $medias = explode( ',', $medias[1] ); | |
| 162 | - $media = array(); | |
| 163 | - foreach ( $medias as $elem ) { | |
| 164 | - /* $media[] = current(explode(' ',trim($elem),2)); */ | |
| 165 | - if ( empty( $elem ) ) { | |
| 166 | - $elem = 'all'; | |
| 284 | + if ( !empty( $medias ) ) { | |
| 285 | + $medias = explode( ',', $medias[1] ); | |
| 286 | + $media = array(); | |
| 287 | + foreach ( $medias as $elem ) { | |
| 288 | + if ( empty( $elem ) ) { | |
| 289 | + $elem = 'all'; | |
| 290 | + } | |
| 291 | + | |
| 292 | + $media[] = $elem; | |
| 167 | 293 | } |
| 168 | - | |
| 169 | - $media[] = $elem; | |
| 294 | + } else { | |
| 295 | + $media = array( 'all' ); | |
| 170 | 296 | } |
| 171 | 297 | } else { |
| 172 | 298 | // No media specified - applies to all. |
| 173 | 299 | $media = array( 'all' ); |
| @@ -172,8 +298,13 @@ | ||
| 172 | 298 | // No media specified - applies to all. |
| 173 | 299 | $media = array( 'all' ); |
| 174 | 300 | } |
| 175 | 301 | |
| 302 | + // forcing media attribute to all to merge all in one file. | |
| 303 | + if ( $this->media_force_all ) { | |
| 304 | + $media = array( 'all' ); | |
| 305 | + } | |
| 306 | + | |
| 176 | 307 | $media = apply_filters( 'autoptimize_filter_css_tagmedia', $media, $tag ); |
| 177 | 308 | |
| 178 | 309 | if ( preg_match( '#<link.*href=("|\')(.*)("|\')#Usmi', $tag, $source ) ) { |
| 179 | 310 | // <link>. |
| @@ -185,9 +316,9 @@ | ||
| 185 | 316 | $this->css[] = array( $media, $path ); |
| 186 | 317 | } else { |
| 187 | 318 | // Link is dynamic (.php etc). |
| 188 | 319 | $new_tag = $this->optionally_defer_excluded( $tag, 'none' ); |
| 189 | - if ( $new_tag !== '' && $new_tag !== $tag ) { | |
| 320 | + if ( '' !== $new_tag && $new_tag !== $tag ) { | |
| 190 | 321 | $this->content = str_replace( $tag, $new_tag, $this->content ); |
| 191 | 322 | } |
| 192 | 323 | $tag = ''; |
| 193 | 324 | } |
| @@ -199,9 +330,9 @@ | ||
| 199 | 330 | // And re-hide them to be able to to the removal based on tag. |
| 200 | 331 | $tag = $this->hide_comments( $tag ); |
| 201 | 332 | |
| 202 | 333 | if ( $this->include_inline ) { |
| 203 | - $code = preg_replace( '#^.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*$#sm', '$1', $code[1] ); | |
| 334 | + $code = preg_replace( '#^.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*$#sm', '$1', $code[1] ); | |
| 204 | 335 | $this->css[] = array( $media, 'INLINE;' . $code ); |
| 205 | 336 | } else { |
| 206 | 337 | $tag = ''; |
| 207 | 338 | } |
| @@ -225,17 +356,28 @@ | ||
| 225 | 356 | $minified_url = $this->minify_single( $path ); |
| 226 | 357 | if ( ! empty( $minified_url ) ) { |
| 227 | 358 | // Replace orig URL with cached minified URL. |
| 228 | 359 | $new_tag = str_replace( $url, $minified_url, $tag ); |
| 360 | + } elseif ( apply_filters( 'autoptimize_filter_ccsjs_remove_empty_minified_url', false ) ) { | |
| 361 | + // Remove the original style tag, because cache content is empty but only if | |
| 362 | + // filter is true-ed because $minified_url is also false if file is minified already. | |
| 363 | + $new_tag = ''; | |
| 229 | 364 | } |
| 230 | 365 | } |
| 231 | 366 | } |
| 232 | 367 | |
| 233 | - // Optionally defer (preload) non-aggregated CSS. | |
| 234 | - $new_tag = $this->optionally_defer_excluded( $new_tag, $url ); | |
| 368 | + if ( '' !== $new_tag ) { | |
| 369 | + // Optionally defer (preload) non-aggregated CSS. | |
| 370 | + $new_tag = $this->optionally_defer_excluded( $new_tag, $url ); | |
| 371 | + | |
| 372 | + // Check if we still need to CDN (esp. for already minified resources). | |
| 373 | + if ( ! empty( $this->cdn_url ) || has_filter( 'autoptimize_filter_base_replace_cdn' ) ) { | |
| 374 | + $new_tag = str_replace( $url, $this->url_replace_cdn( $url ), $new_tag ); | |
| 375 | + } | |
| 376 | + } | |
| 235 | 377 | |
| 236 | 378 | // And replace! |
| 237 | - if ( $new_tag !== '' && $new_tag !== $tag ) { | |
| 379 | + if ( ( '' !== $new_tag && $new_tag !== $tag ) || ( '' === $new_tag && apply_filters( 'autoptimize_filter_css_remove_empty_files', false ) ) ) { | |
| 238 | 380 | $this->content = str_replace( $tag, $new_tag, $this->content ); |
| 239 | 381 | } |
| 240 | 382 | } |
| 241 | 383 | } |
| @@ -258,29 +400,39 @@ | ||
| 258 | 400 | */ |
| 259 | 401 | private function optionally_defer_excluded( $tag, $url = '' ) |
| 260 | 402 | { |
| 261 | 403 | // Defer single CSS if "inline & defer" is ON and there is inline CSS. |
| 262 | - if ( $this->defer && ! empty( $this->defer_inline ) ) { | |
| 263 | - // Get/ set (via filter) the JS to be triggers onload of the preloaded CSS. | |
| 264 | - $_preload_onload = apply_filters( | |
| 265 | - 'autoptimize_filter_css_preload_onload', | |
| 266 | - "this.onload=null;this.rel='stylesheet'", | |
| 267 | - $url | |
| 268 | - ); | |
| 269 | - // Adapt original <link> element for CSS to be preloaded and add <noscript>-version for fallback. | |
| 270 | - $new_tag = '<noscript>' . autoptimizeUtils::remove_id_from_node( $tag ) . '</noscript>' . str_replace( | |
| 271 | - array( | |
| 272 | - "rel='stylesheet'", | |
| 273 | - 'rel="stylesheet"', | |
| 274 | - ), | |
| 275 | - "rel='preload' as='style' onload=\"" . $_preload_onload . "\"", | |
| 276 | - $tag | |
| 277 | - ); | |
| 278 | - } else { | |
| 279 | - $new_tag = $tag; | |
| 404 | + if ( ! empty( $tag ) && false === strpos( $tag, ' onload=' ) && $this->defer && ! empty( $this->defer_inline ) && apply_filters( 'autoptimize_filter_css_defer_excluded', true, $tag ) ) { | |
| 405 | + // get media attribute and based on that create onload JS attribute value. | |
| 406 | + if ( false === strpos( $tag, 'media=' ) ) { | |
| 407 | + $tag = str_replace( '<link', "<link media='all'", $tag ); | |
| 408 | + } | |
| 409 | + | |
| 410 | + preg_match( '#media=(?:"|\')([^>]*)(?:"|\')#Ui', $tag, $_medias ); | |
| 411 | + $_media = $_medias[1]; | |
| 412 | + $_preload_onload = autoptimizeConfig::get_ao_css_preload_onload( $_media ); | |
| 413 | + | |
| 414 | + if ( 'print' !== $_media ) { | |
| 415 | + // If not media=print, adapt original <link> element for CSS to be preloaded and add <noscript>-version for fallback. | |
| 416 | + $new_tag = '<noscript>' . autoptimizeUtils::remove_id_from_node( $tag ) . '</noscript>' . str_replace( | |
| 417 | + $_medias[0], | |
| 418 | + "media='print' onload=\"" . $_preload_onload . '"', | |
| 419 | + $tag | |
| 420 | + ); | |
| 421 | + | |
| 422 | + // Optionally (but default false) preload the (excluded) CSS-file. | |
| 423 | + if ( apply_filters( 'autoptimize_fitler_css_preload_and_print', false ) && 'none' !== $url ) { | |
| 424 | + $new_tag = '<link rel="preload" as="stylesheet" href="' . $url . '"/>' . $new_tag; | |
| 425 | + } | |
| 426 | + } else { | |
| 427 | + $new_tag = $tag; | |
| 428 | + } | |
| 429 | + | |
| 430 | + return $new_tag; | |
| 280 | 431 | } |
| 281 | 432 | |
| 282 | - return $new_tag; | |
| 433 | + // Return unchanged $tag. | |
| 434 | + return $tag; | |
| 283 | 435 | } |
| 284 | 436 | |
| 285 | 437 | /** |
| 286 | 438 | * Checks if the local file referenced by $path is a valid |
| @@ -285,9 +437,9 @@ | ||
| 285 | 437 | /** |
| 286 | 438 | * Checks if the local file referenced by $path is a valid |
| 287 | 439 | * candidate for being inlined into a data: URI |
| 288 | 440 | * |
| 289 | - * @param string $path | |
| 441 | + * @param string $path image path. | |
| 290 | 442 | * @return boolean |
| 291 | 443 | */ |
| 292 | 444 | private function is_datauri_candidate( $path ) |
| 293 | 445 | { |
| @@ -338,14 +490,14 @@ | ||
| 338 | 490 | |
| 339 | 491 | private function check_datauri_exclude_list( $url ) |
| 340 | 492 | { |
| 341 | 493 | static $exclude_list = null; |
| 342 | - $no_datauris = array(); | |
| 494 | + static $no_datauris = array(); | |
| 343 | 495 | |
| 344 | 496 | // Again, skip doing certain stuff repeatedly when loop-called. |
| 345 | 497 | if ( null === $exclude_list ) { |
| 346 | 498 | $exclude_list = apply_filters( 'autoptimize_filter_css_datauri_exclude', '' ); |
| 347 | - $no_datauris = array_filter( array_map( 'trim', explode( ',', $exclude_list ) ) ); | |
| 499 | + $no_datauris = array_filter( array_map( 'trim', explode( ',', $exclude_list ) ) ); | |
| 348 | 500 | } |
| 349 | 501 | |
| 350 | 502 | $matched = false; |
| 351 | 503 | |
| @@ -376,20 +528,20 @@ | ||
| 376 | 528 | return $result; |
| 377 | 529 | } |
| 378 | 530 | } |
| 379 | 531 | |
| 380 | - $hash = md5( $path ); | |
| 532 | + $hash = md5( $path ); | |
| 381 | 533 | $check = new autoptimizeCache( $hash, 'img' ); |
| 382 | 534 | if ( $check->check() ) { |
| 383 | 535 | // we have the base64 image in cache. |
| 384 | - $headAndData = $check->retrieve(); | |
| 385 | - $_base64data = explode( ';base64,', $headAndData ); | |
| 386 | - $base64data = $_base64data[1]; | |
| 536 | + $head_and_data = $check->retrieve(); | |
| 537 | + $_base64data = explode( ';base64,', $head_and_data ); | |
| 538 | + $base64data = $_base64data[1]; | |
| 387 | 539 | unset( $_base64data ); |
| 388 | 540 | } else { |
| 389 | 541 | // It's an image and we don't have it in cache, get the type by extension. |
| 390 | 542 | $exploded_path = explode( '.', $path ); |
| 391 | - $type = end( $exploded_path ); | |
| 543 | + $type = end( $exploded_path ); | |
| 392 | 544 | |
| 393 | 545 | switch ( $type ) { |
| 394 | 546 | case 'jpg': |
| 395 | 547 | case 'jpeg': |
| @@ -411,17 +563,20 @@ | ||
| 411 | 563 | $dataurihead = 'data:application/octet-stream;base64,'; |
| 412 | 564 | } |
| 413 | 565 | |
| 414 | 566 | // Encode the data. |
| 415 | - $base64data = base64_encode( file_get_contents( $path ) ); | |
| 416 | - $headAndData = $dataurihead . $base64data; | |
| 567 | + $base64data = base64_encode( file_get_contents( $path ) ); | |
| 568 | + $head_and_data = $dataurihead . $base64data; | |
| 417 | 569 | |
| 418 | 570 | // Save in cache. |
| 419 | - $check->cache( $headAndData, 'text/plain' ); | |
| 571 | + $check->cache( $head_and_data, 'text/plain' ); | |
| 420 | 572 | } |
| 421 | 573 | unset( $check ); |
| 422 | 574 | |
| 423 | - return array( 'full' => $headAndData, 'base64data' => $base64data ); | |
| 575 | + return array( | |
| 576 | + 'full' => $head_and_data, | |
| 577 | + 'base64data' => $base64data, | |
| 578 | + ); | |
| 424 | 579 | } |
| 425 | 580 | |
| 426 | 581 | /** |
| 427 | 582 | * Given an array of key/value pairs to replace in $string, |
| @@ -426,10 +581,10 @@ | ||
| 426 | 581 | /** |
| 427 | 582 | * Given an array of key/value pairs to replace in $string, |
| 428 | 583 | * it does so by replacing the longest-matching strings first. |
| 429 | 584 | * |
| 430 | - * @param string $string | |
| 431 | - * @param array $replacements | |
| 585 | + * @param string $string string in which to replace. | |
| 586 | + * @param array $replacements to be replaced strings and replacement. | |
| 432 | 587 | * |
| 433 | 588 | * @return string |
| 434 | 589 | */ |
| 435 | 590 | protected static function replace_longest_matches_first( $string, $replacements = array() ) |
| @@ -483,9 +638,9 @@ | ||
| 483 | 638 | if ( ! empty( $this->cdn_url ) ) { |
| 484 | 639 | $replacement_url = $this->url_replace_cdn( $url ); |
| 485 | 640 | // Prepare replacements array. |
| 486 | 641 | $replacements[ $url_src_matches[1][ $count ] ] = str_replace( |
| 487 | - $original_url, $replacement_url, $url_src_matches[1][$count] | |
| 642 | + $original_url, $replacement_url, $url_src_matches[1][ $count ] | |
| 488 | 643 | ); |
| 489 | 644 | } |
| 490 | 645 | } |
| 491 | 646 | } |
| @@ -499,9 +654,9 @@ | ||
| 499 | 654 | * "Hides" @font-face declarations by replacing them with `%%FONTFACE%%` markers. |
| 500 | 655 | * Also does CDN replacement of any font-urls within those declarations if the `autoptimize_filter_css_fonts_cdn` |
| 501 | 656 | * filter is used. |
| 502 | 657 | * |
| 503 | - * @param string $code | |
| 658 | + * @param string $code HTML being processed to hide fonts. | |
| 504 | 659 | * @return string |
| 505 | 660 | */ |
| 506 | 661 | public function hide_fontface_and_maybe_cdn( $code ) |
| 507 | 662 | { |
| @@ -521,9 +676,9 @@ | ||
| 521 | 676 | } |
| 522 | 677 | |
| 523 | 678 | // Replace declaration with its base64 encoded string. |
| 524 | 679 | $replacement = self::build_marker( 'FONTFACE', $full_match ); |
| 525 | - $code = str_replace( $match_search, $replacement, $code ); | |
| 680 | + $code = str_replace( $match_search, $replacement, $code ); | |
| 526 | 681 | } |
| 527 | 682 | } |
| 528 | 683 | |
| 529 | 684 | return $code; |
| @@ -532,9 +687,9 @@ | ||
| 532 | 687 | /** |
| 533 | 688 | * Restores original @font-face declarations that have been "hidden" |
| 534 | 689 | * using `hide_fontface_and_maybe_cdn()`. |
| 535 | 690 | * |
| 536 | - * @param string $code | |
| 691 | + * @param string $code HTML being processed to unhide fonts. | |
| 537 | 692 | * @return string |
| 538 | 693 | */ |
| 539 | 694 | public function restore_fontface( $code ) |
| 540 | 695 | { |
| @@ -540,9 +695,14 @@ | ||
| 540 | 695 | { |
| 541 | 696 | return $this->restore_marked_content( 'FONTFACE', $code ); |
| 542 | 697 | } |
| 543 | 698 | |
| 544 | - // Re-write (and/or inline) referenced assets. | |
| 699 | + /** | |
| 700 | + * Re-write (and/or inline) referenced assets. | |
| 701 | + * | |
| 702 | + * @param string $code HTML being processed rewrite assets. | |
| 703 | + * @return string | |
| 704 | + */ | |
| 545 | 705 | public function rewrite_assets( $code ) |
| 546 | 706 | { |
| 547 | 707 | // Handle @font-face rules by hiding and processing them separately. |
| 548 | 708 | $code = $this->hide_fontface_and_maybe_cdn( $code ); |
| @@ -558,9 +718,10 @@ | ||
| 558 | 718 | */ |
| 559 | 719 | |
| 560 | 720 | // Re-write (and/or inline) URLs to point them to the CDN host. |
| 561 | 721 | $url_src_matches = array(); |
| 562 | - $imgreplace = array(); | |
| 722 | + $imgreplace = array(); | |
| 723 | + | |
| 563 | 724 | // Matches and captures anything specified within the literal `url()` and excludes those containing data: URIs. |
| 564 | 725 | preg_match_all( self::ASSETS_REGEX, $code, $url_src_matches ); |
| 565 | 726 | if ( is_array( $url_src_matches ) && ! empty( $url_src_matches ) ) { |
| 566 | 727 | foreach ( $url_src_matches[1] as $count => $original_url ) { |
| @@ -580,17 +741,17 @@ | ||
| 580 | 741 | $excluded = $this->check_datauri_exclude_list( $ipath ); |
| 581 | 742 | if ( ! $excluded ) { |
| 582 | 743 | $is_datauri_candidate = $this->is_datauri_candidate( $ipath ); |
| 583 | 744 | if ( $is_datauri_candidate ) { |
| 584 | - $datauri = $this->build_or_get_datauri_image( $ipath ); | |
| 585 | - $base64data = $datauri['base64data']; | |
| 745 | + $datauri = $this->build_or_get_datauri_image( $ipath ); | |
| 746 | + $base64data = $datauri['base64data']; | |
| 586 | 747 | // Add it to the list for replacement. |
| 587 | 748 | $imgreplace[ $url_src_matches[1][ $count ] ] = str_replace( |
| 588 | - $original_url, | |
| 589 | - $datauri['full'], | |
| 590 | - $url_src_matches[1][$count] | |
| 749 | + $original_url, | |
| 750 | + $datauri['full'], | |
| 751 | + $url_src_matches[1][ $count ] | |
| 591 | 752 | ); |
| 592 | - $inlined = true; | |
| 753 | + $inlined = true; | |
| 593 | 754 | } |
| 594 | 755 | } |
| 595 | 756 | } |
| 596 | 757 | |
| @@ -601,11 +762,11 @@ | ||
| 601 | 762 | * being inlined for whatever reason above. |
| 602 | 763 | */ |
| 603 | 764 | if ( ! $inlined && ( ! empty( $this->cdn_url ) || has_filter( 'autoptimize_filter_base_replace_cdn' ) ) ) { |
| 604 | 765 | // Just do the "simple" CDN replacement. |
| 605 | - $replacement_url = $this->url_replace_cdn( $url ); | |
| 766 | + $replacement_url = $this->url_replace_cdn( $url ); | |
| 606 | 767 | $imgreplace[ $url_src_matches[1][ $count ] ] = str_replace( |
| 607 | - $original_url, $replacement_url, $url_src_matches[1][$count] | |
| 768 | + $original_url, $replacement_url, $url_src_matches[1][ $count ] | |
| 608 | 769 | ); |
| 609 | 770 | } |
| 610 | 771 | } |
| 611 | 772 | } |
| @@ -617,9 +778,11 @@ | ||
| 617 | 778 | |
| 618 | 779 | return $code; |
| 619 | 780 | } |
| 620 | 781 | |
| 621 | - // Joins and optimizes CSS. | |
| 782 | + /** | |
| 783 | + * Joins and optimizes CSS. | |
| 784 | + */ | |
| 622 | 785 | public function minify() |
| 623 | 786 | { |
| 624 | 787 | foreach ( $this->css as $group ) { |
| 625 | 788 | list( $media, $css ) = $group; |
| @@ -624,27 +787,27 @@ | ||
| 624 | 787 | foreach ( $this->css as $group ) { |
| 625 | 788 | list( $media, $css ) = $group; |
| 626 | 789 | if ( preg_match( '#^INLINE;#', $css ) ) { |
| 627 | 790 | // <style>. |
| 628 | - $css = preg_replace( '#^INLINE;#', '', $css ); | |
| 629 | - $css = self::fixurls( ABSPATH . 'index.php', $css ); // ABSPATH already contains a trailing slash. | |
| 791 | + $css = preg_replace( '#^INLINE;#', '', $css ); | |
| 792 | + $css = self::fixurls( ABSPATH . 'index.php', $css ); // ABSPATH already contains a trailing slash. | |
| 630 | 793 | $tmpstyle = apply_filters( 'autoptimize_css_individual_style', $css, '' ); |
| 631 | 794 | if ( has_filter( 'autoptimize_css_individual_style' ) && ! empty( $tmpstyle ) ) { |
| 632 | - $css = $tmpstyle; | |
| 795 | + $css = $tmpstyle; | |
| 633 | 796 | $this->alreadyminified = true; |
| 634 | 797 | } |
| 635 | 798 | } else { |
| 636 | 799 | // <link> |
| 637 | 800 | if ( false !== $css && file_exists( $css ) && is_readable( $css ) ) { |
| 638 | - $cssPath = $css; | |
| 639 | - $css = self::fixurls( $cssPath, file_get_contents( $cssPath ) ); | |
| 640 | - $css = preg_replace( '/\x{EF}\x{BB}\x{BF}/', '', $css ); | |
| 641 | - $tmpstyle = apply_filters( 'autoptimize_css_individual_style', $css, $cssPath ); | |
| 801 | + $css_path = $css; | |
| 802 | + $css = self::fixurls( $css_path, file_get_contents( $css_path ) ); | |
| 803 | + $css = preg_replace( '/\x{EF}\x{BB}\x{BF}/', '', $css ); | |
| 804 | + $tmpstyle = apply_filters( 'autoptimize_css_individual_style', $css, $css_path ); | |
| 642 | 805 | if ( has_filter( 'autoptimize_css_individual_style' ) && ! empty( $tmpstyle ) ) { |
| 643 | - $css = $tmpstyle; | |
| 806 | + $css = $tmpstyle; | |
| 644 | 807 | $this->alreadyminified = true; |
| 645 | - } elseif ( $this->can_inject_late( $cssPath, $css ) ) { | |
| 646 | - $css = self::build_injectlater_marker( $cssPath, md5( $css ) ); | |
| 808 | + } elseif ( $this->can_inject_late( $css_path, $css ) ) { | |
| 809 | + $css = self::build_injectlater_marker( $css_path, md5( $css ) ); | |
| 647 | 810 | } |
| 648 | 811 | } else { |
| 649 | 812 | // Couldn't read CSS. Maybe getpath isn't working? |
| 650 | 813 | $css = ''; |
| @@ -652,12 +815,12 @@ | ||
| 652 | 815 | } |
| 653 | 816 | |
| 654 | 817 | foreach ( $media as $elem ) { |
| 655 | 818 | if ( ! empty( $css ) ) { |
| 656 | - if ( ! isset( $this->csscode[$elem] ) ) { | |
| 657 | - $this->csscode[$elem] = ''; | |
| 819 | + if ( ! isset( $this->csscode[ $elem ] ) ) { | |
| 820 | + $this->csscode[ $elem ] = ''; | |
| 658 | 821 | } |
| 659 | - $this->csscode[$elem] .= "\n/*FILESTART*/" . $css; | |
| 822 | + $this->csscode[ $elem ] .= "\n/*FILESTART*/" . $css; | |
| 660 | 823 | } |
| 661 | 824 | } |
| 662 | 825 | } |
| 663 | 826 | |
| @@ -670,15 +833,15 @@ | ||
| 670 | 833 | foreach ( $md5list as $med => $sum ) { |
| 671 | 834 | // If same code. |
| 672 | 835 | if ( $sum === $md5sum ) { |
| 673 | 836 | // Add the merged code. |
| 674 | - $medianame = $med . ', ' . $media; | |
| 675 | - $this->csscode[$medianame] = $code; | |
| 676 | - $md5list[$medianame] = $md5list[$med]; | |
| 677 | - unset( $this->csscode[$med], $this->csscode[$media], $md5list[$med] ); | |
| 837 | + $medianame = $med . ', ' . $media; | |
| 838 | + $this->csscode[ $medianame ] = $code; | |
| 839 | + $md5list[ $medianame ] = $md5list[ $med ]; | |
| 840 | + unset( $this->csscode[ $med ], $this->csscode[ $media ], $md5list[ $med ] ); | |
| 678 | 841 | } |
| 679 | 842 | } |
| 680 | - $md5list[$medianame] = $md5sum; | |
| 843 | + $md5list[ $medianame ] = $md5sum; | |
| 681 | 844 | } |
| 682 | 845 | unset( $tmpcss ); |
| 683 | 846 | |
| 684 | 847 | // Manage @imports, while is for recursive import management. |
| @@ -691,20 +854,20 @@ | ||
| 691 | 854 | $thiscss_nocomments = preg_replace( '#/\*.*\*/#Us', '', $thiscss ); |
| 692 | 855 | while ( preg_match_all( '#@import +(?:url)?(?:(?:\((["\']?)(?:[^"\')]+)\1\)|(["\'])(?:[^"\']+)\2)(?:[^,;"\']+(?:,[^,;"\']+)*)?)(?:;)#mi', $thiscss_nocomments, $matches ) ) { |
| 693 | 856 | foreach ( $matches[0] as $import ) { |
| 694 | 857 | if ( $this->isremovable( $import, $this->cssremovables ) ) { |
| 695 | - $thiscss = str_replace( $import, '', $thiscss ); | |
| 858 | + $thiscss = str_replace( $import, '', $thiscss ); | |
| 696 | 859 | $import_ok = true; |
| 697 | 860 | } else { |
| 698 | - $url = trim( preg_replace( '#^.*((?:https?:|ftp:)?//.*\.css).*$#', '$1', trim( $import ) ), " \t\n\r\0\x0B\"'" ); | |
| 699 | - $path = $this->getpath( $url ); | |
| 861 | + $url = trim( preg_replace( '#^.*((?:https?:|ftp:)?//.*\.css).*$#', '$1', trim( $import ) ), " \t\n\r\0\x0B\"'" ); | |
| 862 | + $path = $this->getpath( $url ); | |
| 700 | 863 | $import_ok = false; |
| 701 | 864 | if ( file_exists( $path ) && is_readable( $path ) ) { |
| 702 | - $code = addcslashes( self::fixurls( $path, file_get_contents( $path ) ), "\\" ); | |
| 703 | - $code = preg_replace( '/\x{EF}\x{BB}\x{BF}/', '', $code ); | |
| 865 | + $code = addcslashes( self::fixurls( $path, file_get_contents( $path ) ), '\\' ); | |
| 866 | + $code = preg_replace( '/\x{EF}\x{BB}\x{BF}/', '', $code ); | |
| 704 | 867 | $tmpstyle = apply_filters( 'autoptimize_css_individual_style', $code, '' ); |
| 705 | 868 | if ( has_filter( 'autoptimize_css_individual_style' ) && ! empty( $tmpstyle ) ) { |
| 706 | - $code = $tmpstyle; | |
| 869 | + $code = $tmpstyle; | |
| 707 | 870 | $this->alreadyminified = true; |
| 708 | 871 | } elseif ( $this->can_inject_late( $path, $code ) ) { |
| 709 | 872 | $code = self::build_injectlater_marker( $path, md5( $code ) ); |
| 710 | 873 | } |
| @@ -709,11 +872,11 @@ | ||
| 709 | 872 | $code = self::build_injectlater_marker( $path, md5( $code ) ); |
| 710 | 873 | } |
| 711 | 874 | |
| 712 | 875 | if ( ! empty( $code ) ) { |
| 713 | - $tmp_thiscss = preg_replace( '#(/\*FILESTART\*/.*)' . preg_quote( $import, '#' ) . '#Us', '/*FILESTART2*/' . $code . '$1', $thiscss ); | |
| 876 | + $tmp_thiscss = str_replace( $import, stripcslashes( $code ), $thiscss ); | |
| 714 | 877 | if ( ! empty( $tmp_thiscss ) ) { |
| 715 | - $thiscss = $tmp_thiscss; | |
| 878 | + $thiscss = $tmp_thiscss; | |
| 716 | 879 | $import_ok = true; |
| 717 | 880 | unset( $tmp_thiscss ); |
| 718 | 881 | } |
| 719 | 882 | } |
| @@ -749,10 +912,10 @@ | ||
| 749 | 912 | $hash = md5( $code ); |
| 750 | 913 | do_action( 'autoptimize_action_css_hash', $hash ); |
| 751 | 914 | $ccheck = new autoptimizeCache( $hash, 'css' ); |
| 752 | 915 | if ( $ccheck->check() ) { |
| 753 | - $code = $ccheck->retrieve(); | |
| 754 | - $this->hashmap[md5( $code )] = $hash; | |
| 916 | + $code = $ccheck->retrieve(); | |
| 917 | + $this->hashmap[ md5( $code ) ] = $hash; | |
| 755 | 918 | continue; |
| 756 | 919 | } |
| 757 | 920 | unset( $ccheck ); |
| 758 | 921 | |
| @@ -771,9 +934,9 @@ | ||
| 771 | 934 | $code = $tmp_code; |
| 772 | 935 | unset( $tmp_code ); |
| 773 | 936 | } |
| 774 | 937 | |
| 775 | - $this->hashmap[md5( $code )] = $hash; | |
| 938 | + $this->hashmap[ md5( $code ) ] = $hash; | |
| 776 | 939 | } |
| 777 | 940 | |
| 778 | 941 | unset( $code ); |
| 779 | 942 | return true; |
| @@ -797,29 +960,37 @@ | ||
| 797 | 960 | |
| 798 | 961 | return $code; |
| 799 | 962 | } |
| 800 | 963 | |
| 801 | - // Caches the CSS in uncompressed, deflated and gzipped form. | |
| 964 | + /** | |
| 965 | + * Caches the CSS in uncompressed, deflated and gzipped form. | |
| 966 | + */ | |
| 802 | 967 | public function cache() |
| 803 | 968 | { |
| 804 | 969 | // CSS cache. |
| 805 | 970 | foreach ( $this->csscode as $media => $code ) { |
| 806 | - $md5 = $this->hashmap[md5( $code )]; | |
| 971 | + if ( empty( $code ) ) { | |
| 972 | + continue; | |
| 973 | + } | |
| 974 | + | |
| 975 | + $md5 = $this->hashmap[ md5( $code ) ]; | |
| 807 | 976 | $cache = new autoptimizeCache( $md5, 'css' ); |
| 808 | 977 | if ( ! $cache->check() ) { |
| 809 | 978 | // Cache our code. |
| 810 | 979 | $cache->cache( $code, 'text/css' ); |
| 811 | 980 | } |
| 812 | - $this->url[$media] = AUTOPTIMIZE_CACHE_URL . $cache->getname(); | |
| 981 | + $this->url[ $media ] = AUTOPTIMIZE_CACHE_URL . $cache->getname(); | |
| 813 | 982 | } |
| 814 | 983 | } |
| 815 | 984 | |
| 816 | - // Returns the content. | |
| 985 | + /** | |
| 986 | + * Returns the content. | |
| 987 | + */ | |
| 817 | 988 | public function getcontent() |
| 818 | 989 | { |
| 819 | 990 | // Restore the full content (only applies when "autoptimize_filter_css_justhead" filter is true). |
| 820 | 991 | if ( ! empty( $this->restofcontent ) ) { |
| 821 | - $this->content .= $this->restofcontent; | |
| 992 | + $this->content .= $this->restofcontent; | |
| 822 | 993 | $this->restofcontent = ''; |
| 823 | 994 | } |
| 824 | 995 | |
| 825 | 996 | // type is not added by default. |
| @@ -828,29 +999,29 @@ | ||
| 828 | 999 | $type_css = 'type="text/css" '; |
| 829 | 1000 | } |
| 830 | 1001 | |
| 831 | 1002 | // Inject the new stylesheets. |
| 832 | - $replaceTag = array( '<title', 'before' ); | |
| 833 | - $replaceTag = apply_filters( 'autoptimize_filter_css_replacetag', $replaceTag, $this->content ); | |
| 1003 | + $replace_tag = array( '<title', 'before' ); | |
| 1004 | + $replace_tag = apply_filters( 'autoptimize_filter_css_replacetag', $replace_tag, $this->content ); | |
| 834 | 1005 | |
| 835 | 1006 | if ( $this->inline ) { |
| 836 | 1007 | foreach ( $this->csscode as $media => $code ) { |
| 837 | - $this->inject_in_html( '<style ' . $type_css . 'media="' . $media . '">' . $code . '</style>', $replaceTag ); | |
| 1008 | + $this->inject_in_html( apply_filters( 'autoptimize_filter_css_bodyreplacementpayload', '<style ' . $type_css . 'media="' . $media . '">' . $code . '</style>' ), $replace_tag ); | |
| 838 | 1009 | } |
| 839 | 1010 | } else { |
| 840 | 1011 | if ( $this->defer ) { |
| 841 | - $preloadCssBlock = ''; | |
| 1012 | + $preload_css_block = ''; | |
| 842 | 1013 | $inlined_ccss_block = ''; |
| 843 | - $noScriptCssBlock = "<noscript id=\"aonoscrcss\">"; | |
| 1014 | + $noscript_css_block = '<noscript id="aonoscrcss">'; | |
| 844 | 1015 | |
| 845 | 1016 | $defer_inline_code = $this->defer_inline; |
| 846 | 1017 | if ( ! empty( $defer_inline_code ) ) { |
| 847 | 1018 | if ( apply_filters( 'autoptimize_filter_css_critcss_minify', true ) ) { |
| 848 | - $iCssHash = md5( $defer_inline_code ); | |
| 849 | - $iCssCache = new autoptimizeCache( $iCssHash, 'css' ); | |
| 850 | - if ( $iCssCache->check() ) { | |
| 1019 | + $icss_hash = md5( $defer_inline_code ); | |
| 1020 | + $icss_cache = new autoptimizeCache( $icss_hash, 'css' ); | |
| 1021 | + if ( $icss_cache->check() ) { | |
| 851 | 1022 | // we have the optimized inline CSS in cache. |
| 852 | - $defer_inline_code = $iCssCache->retrieve(); | |
| 1023 | + $defer_inline_code = $icss_cache->retrieve(); | |
| 853 | 1024 | } else { |
| 854 | 1025 | $cssmin = new autoptimizeCSSmin(); |
| 855 | 1026 | $tmp_code = trim( $cssmin->run( $defer_inline_code ) ); |
| 856 | 1027 | |
| @@ -855,9 +1026,9 @@ | ||
| 855 | 1026 | $tmp_code = trim( $cssmin->run( $defer_inline_code ) ); |
| 856 | 1027 | |
| 857 | 1028 | if ( ! empty( $tmp_code ) ) { |
| 858 | 1029 | $defer_inline_code = $tmp_code; |
| 859 | - $iCssCache->cache( $defer_inline_code, 'text/css' ); | |
| 1030 | + $icss_cache->cache( $defer_inline_code, 'text/css' ); | |
| 860 | 1031 | unset( $tmp_code ); |
| 861 | 1032 | } |
| 862 | 1033 | } |
| 863 | 1034 | } |
| @@ -870,33 +1041,29 @@ | ||
| 870 | 1041 | foreach ( $this->url as $media => $url ) { |
| 871 | 1042 | $url = $this->url_replace_cdn( $url ); |
| 872 | 1043 | |
| 873 | 1044 | // Add the stylesheet either deferred (import at bottom) or normal links in head. |
| 874 | - if ( $this->defer ) { | |
| 875 | - $preloadOnLoad = autoptimizeConfig::get_ao_css_preload_onload(); | |
| 1045 | + if ( $this->defer && 'print' !== $media ) { | |
| 1046 | + $preload_onload = autoptimizeConfig::get_ao_css_preload_onload( $media ); | |
| 876 | 1047 | |
| 877 | - $preloadCssBlock .= '<link rel="preload" as="style" media="' . $media . '" href="' . $url . '" onload="' . $preloadOnLoad . '" />'; | |
| 878 | - $noScriptCssBlock .= '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet" />'; | |
| 1048 | + $preload_css_block .= apply_filters( 'autoptimize_filter_css_single_deferred_link', '<link rel="stylesheet" media="print" href="' . $url . '" onload="' . $preload_onload . '" />' ); | |
| 1049 | + if ( apply_filters( 'autoptimize_fitler_css_preload_and_print', false ) ) { | |
| 1050 | + $preload_css_block = '<link rel="preload" as="stylesheet" href="' . $url . '"/>' . $preload_css_block; | |
| 1051 | + } | |
| 1052 | + $noscript_css_block .= '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet" />'; | |
| 879 | 1053 | } else { |
| 880 | - if ( strlen( $this->csscode[$media] ) > $this->cssinlinesize ) { | |
| 881 | - $this->inject_in_html( '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet" />', $replaceTag ); | |
| 882 | - } elseif ( strlen( $this->csscode[$media] ) > 0 ) { | |
| 883 | - $this->inject_in_html( '<style ' . $type_css . 'media="' . $media . '">' . $this->csscode[$media] . '</style>', $replaceTag ); | |
| 1054 | + if ( strlen( $this->csscode[ $media ] ) > $this->cssinlinesize ) { | |
| 1055 | + $this->inject_in_html( apply_filters( 'autoptimize_filter_css_bodyreplacementpayload', '<link ' . $type_css . 'media="' . $media . '" href="' . $url . '" rel="stylesheet" />' ), $replace_tag ); | |
| 1056 | + } elseif ( strlen( $this->csscode[ $media ] ) > 0 ) { | |
| 1057 | + $this->inject_in_html( apply_filters( 'autoptimize_filter_css_bodyreplacementpayload', '<style ' . $type_css . 'media="' . $media . '">' . $this->csscode[ $media ] . '</style>' ), $replace_tag ); | |
| 884 | 1058 | } |
| 885 | 1059 | } |
| 886 | 1060 | } |
| 887 | 1061 | |
| 888 | 1062 | if ( $this->defer ) { |
| 889 | - $preload_polyfill = autoptimizeConfig::get_ao_css_preload_polyfill(); | |
| 890 | - $noScriptCssBlock .= '</noscript>'; | |
| 1063 | + $noscript_css_block .= '</noscript>'; | |
| 891 | 1064 | // Inject inline critical CSS, the preloaded full CSS and the noscript-CSS. |
| 892 | - $this->inject_in_html( $inlined_ccss_block . $preloadCssBlock . $noScriptCssBlock, $replaceTag ); | |
| 893 | - | |
| 894 | - // Adds preload polyfill at end of body tag. | |
| 895 | - $this->inject_in_html( | |
| 896 | - apply_filters( 'autoptimize_css_preload_polyfill', $preload_polyfill ), | |
| 897 | - apply_filters( 'autoptimize_css_preload_polyfill_injectat', array( '</body>', 'before' ) ) | |
| 898 | - ); | |
| 1065 | + $this->inject_in_html( apply_filters( 'autoptimize_filter_css_bodyreplacementpayload', $inlined_ccss_block . $preload_css_block . $noscript_css_block ), $replace_tag ); | |
| 899 | 1066 | } |
| 900 | 1067 | } |
| 901 | 1068 | |
| 902 | 1069 | // restore comments. |
| @@ -914,8 +1081,14 @@ | ||
| 914 | 1081 | // Return the modified stylesheet. |
| 915 | 1082 | return $this->content; |
| 916 | 1083 | } |
| 917 | 1084 | |
| 1085 | + /** | |
| 1086 | + * Make sure URL's are absolute iso relative to original CSS location. | |
| 1087 | + * | |
| 1088 | + * @param string $file filename of optimized CSS-file. | |
| 1089 | + * @param string $code CSS-code in which to fix URL's. | |
| 1090 | + */ | |
| 918 | 1091 | static function fixurls( $file, $code ) |
| 919 | 1092 | { |
| 920 | 1093 | // Switch all imports to the url() syntax. |
| 921 | 1094 | $code = preg_replace( '#@import ("|\')(.+?)\.css.*?("|\')#', '@import url("${2}.css")', $code ); |
| @@ -922,9 +1095,9 @@ | ||
| 922 | 1095 | |
| 923 | 1096 | if ( preg_match_all( self::ASSETS_REGEX, $code, $matches ) ) { |
| 924 | 1097 | $file = str_replace( WP_ROOT_DIR, '/', $file ); |
| 925 | 1098 | /** |
| 926 | - * rollback as per https://github.com/futtta/autoptimize/issues/94 | |
| 1099 | + * Rollback as per https://github.com/futtta/autoptimize/issues/94 | |
| 927 | 1100 | * $file = str_replace( AUTOPTIMIZE_WP_CONTENT_NAME, '', $file ); |
| 928 | 1101 | */ |
| 929 | 1102 | $dir = dirname( $file ); // Like /themes/expound/css. |
| 930 | 1103 | |
| @@ -938,27 +1111,27 @@ | ||
| 938 | 1111 | |
| 939 | 1112 | $replace = array(); |
| 940 | 1113 | foreach ( $matches[1] as $k => $url ) { |
| 941 | 1114 | // Remove quotes. |
| 942 | - $url = trim( $url, " \t\n\r\0\x0B\"'" ); | |
| 943 | - $noQurl = trim( $url, "\"'" ); | |
| 944 | - if ( $url !== $noQurl ) { | |
| 945 | - $removedQuotes = true; | |
| 1115 | + $url = trim( $url, " \t\n\r\0\x0B\"'" ); | |
| 1116 | + $no_q_url = trim( $url, "\"'" ); | |
| 1117 | + if ( $url !== $no_q_url ) { | |
| 1118 | + $removed_quotes = true; | |
| 946 | 1119 | } else { |
| 947 | - $removedQuotes = false; | |
| 1120 | + $removed_quotes = false; | |
| 948 | 1121 | } |
| 949 | 1122 | |
| 950 | - if ( '' === $noQurl ) { | |
| 1123 | + if ( '' === $no_q_url ) { | |
| 951 | 1124 | continue; |
| 952 | 1125 | } |
| 953 | 1126 | |
| 954 | - $url = $noQurl; | |
| 1127 | + $url = $no_q_url; | |
| 955 | 1128 | if ( '/' === $url[0] || preg_match( '#^(https?://|ftp://|data:)#i', $url ) ) { |
| 956 | 1129 | // URL is protocol-relative, host-relative or something we don't touch. |
| 957 | 1130 | continue; |
| 958 | - } else { | |
| 959 | - // Relative URL. | |
| 960 | - /** | |
| 1131 | + } else { // Relative URL. | |
| 1132 | + | |
| 1133 | + /* | |
| 961 | 1134 | * rollback as per https://github.com/futtta/autoptimize/issues/94 |
| 962 | 1135 | * $newurl = preg_replace( '/https?:/', '', str_replace( ' ', '%20', AUTOPTIMIZE_WP_CONTENT_URL . str_replace( '//', '/', $dir . '/' . $url ) ) ); |
| 963 | 1136 | */ |
| 964 | 1137 | $newurl = preg_replace( '/https?:/', '', str_replace( ' ', '%20', AUTOPTIMIZE_WP_ROOT_URL . str_replace( '//', '/', $dir . '/' . $url ) ) ); |
| @@ -968,15 +1141,15 @@ | ||
| 968 | 1141 | * Hash the url + whatever was behind potentially for replacement |
| 969 | 1142 | * We must do this, or different css classes referencing the same bg image (but |
| 970 | 1143 | * different parts of it, say, in sprites and such) loose their stuff... |
| 971 | 1144 | */ |
| 972 | - $hash = md5( $url . $matches[2][$k] ); | |
| 973 | - $code = str_replace( $matches[0][$k], $hash, $code ); | |
| 1145 | + $hash = md5( $url . $matches[2][ $k ] ); | |
| 1146 | + $code = str_replace( $matches[0][ $k ], $hash, $code ); | |
| 974 | 1147 | |
| 975 | - if ( $removedQuotes ) { | |
| 976 | - $replace[$hash] = "url('" . $newurl . "')" . $matches[2][$k]; | |
| 1148 | + if ( $removed_quotes ) { | |
| 1149 | + $replace[ $hash ] = "url('" . $newurl . "')" . $matches[2][ $k ]; | |
| 977 | 1150 | } else { |
| 978 | - $replace[$hash] = 'url(' . $newurl . ')' . $matches[2][$k]; | |
| 1151 | + $replace[ $hash ] = 'url(' . $newurl . ')' . $matches[2][ $k ]; | |
| 979 | 1152 | } |
| 980 | 1153 | } |
| 981 | 1154 | } |
| 982 | 1155 | |
| @@ -991,15 +1164,15 @@ | ||
| 991 | 1164 | if ( ! $this->aggregate ) { |
| 992 | 1165 | return false; |
| 993 | 1166 | } |
| 994 | 1167 | |
| 995 | - if ( ! empty( $this->whitelist ) ) { | |
| 996 | - foreach ( $this->whitelist as $match ) { | |
| 1168 | + if ( ! empty( $this->allowlist ) ) { | |
| 1169 | + foreach ( $this->allowlist as $match ) { | |
| 997 | 1170 | if ( false !== strpos( $tag, $match ) ) { |
| 998 | 1171 | return true; |
| 999 | 1172 | } |
| 1000 | 1173 | } |
| 1001 | - // no match with whitelist. | |
| 1174 | + // no match with allowlist. | |
| 1002 | 1175 | return false; |
| 1003 | 1176 | } else { |
| 1004 | 1177 | if ( is_array( $this->dontmove ) && ! empty( $this->dontmove ) ) { |
| 1005 | 1178 | foreach ( $this->dontmove as $match ) { |
| @@ -1014,15 +1187,15 @@ | ||
| 1014 | 1187 | return true; |
| 1015 | 1188 | } |
| 1016 | 1189 | } |
| 1017 | 1190 | |
| 1018 | - private function can_inject_late( $cssPath, $css ) | |
| 1191 | + private function can_inject_late( $css_path, $css ) | |
| 1019 | 1192 | { |
| 1020 | - $consider_minified_array = apply_filters( 'autoptimize_filter_css_consider_minified', false, $cssPath ); | |
| 1193 | + $consider_minified_array = apply_filters( 'autoptimize_filter_css_consider_minified', false, $css_path ); | |
| 1021 | 1194 | if ( true !== $this->inject_min_late ) { |
| 1022 | 1195 | // late-inject turned off. |
| 1023 | 1196 | return false; |
| 1024 | - } elseif ( ( false === strpos( $cssPath, 'min.css' ) ) && ( str_replace( $consider_minified_array, '', $cssPath ) === $cssPath ) ) { | |
| 1197 | + } elseif ( ( false === strpos( $css_path, 'min.css' ) ) && ( str_replace( $consider_minified_array, '', $css_path ) === $css_path ) ) { | |
| 1025 | 1198 | // file not minified based on filename & filter. |
| 1026 | 1199 | return false; |
| 1027 | 1200 | } elseif ( false !== strpos( $css, '@import' ) ) { |
| 1028 | 1201 | // can't late-inject files with imports as those need to be aggregated. |
| @@ -1029,9 +1202,9 @@ | ||
| 1029 | 1202 | return false; |
| 1030 | 1203 | } elseif ( ( false !== strpos( $css, '@font-face' ) ) && ( apply_filters( 'autoptimize_filter_css_fonts_cdn', false ) === true ) && ( ! empty( $this->cdn_url ) ) ) { |
| 1031 | 1204 | // don't late-inject CSS with font-src's if fonts are set to be CDN'ed. |
| 1032 | 1205 | return false; |
| 1033 | - } elseif ( ( ( $this->datauris == true ) || ( ! empty( $this->cdn_url ) ) ) && preg_match( '#background[^;}]*url\(#Ui', $css ) ) { | |
| 1206 | + } elseif ( ( ( true == $this->datauris ) || ( ! empty( $this->cdn_url ) ) ) && preg_match( '#background[^;}]*url\(#Ui', $css ) ) { | |
| 1034 | 1207 | // don't late-inject CSS with images if CDN is set OR if image inlining is on. |
| 1035 | 1208 | return false; |
| 1036 | 1209 | } else { |
| 1037 | 1210 | // phew, all is safe, we can late-inject. |
| @@ -1052,8 +1225,15 @@ | ||
| 1052 | 1225 | { |
| 1053 | 1226 | $contents = $this->prepare_minify_single( $filepath ); |
| 1054 | 1227 | |
| 1055 | 1228 | if ( empty( $contents ) ) { |
| 1229 | + // if aggregate is off and CCSS is used but all files are minified already, then we | |
| 1230 | + // must make sure the autoptimize_action_css_hash action still fires for CCSS's sake. | |
| 1231 | + $ao_ccss_key = get_option( 'autoptimize_ccss_key', '' ); | |
| 1232 | + if ( false === $this->aggregate && isset( $ao_ccss_key ) && ! empty( $ao_ccss_key ) ) { | |
| 1233 | + $hash = 'single_' . md5( file_get_contents( $filepath ) ); | |
| 1234 | + do_action( 'autoptimize_action_css_hash', $hash ); | |
| 1235 | + } | |
| 1056 | 1236 | return false; |
| 1057 | 1237 | } |
| 1058 | 1238 | |
| 1059 | 1239 | // Check cache. |
| @@ -1071,8 +1251,17 @@ | ||
| 1071 | 1251 | $contents = $this->restore_fontface( $contents ); |
| 1072 | 1252 | // Now minify... |
| 1073 | 1253 | $cssmin = new autoptimizeCSSmin(); |
| 1074 | 1254 | $contents = trim( $cssmin->run( $contents ) ); |
| 1255 | + | |
| 1256 | + // Check if minified cache content is empty. | |
| 1257 | + if ( empty( $contents ) ) { | |
| 1258 | + return false; | |
| 1259 | + } | |
| 1260 | + | |
| 1261 | + // Filter contents of excluded minified CSS. | |
| 1262 | + $contents = apply_filters( 'autoptimize_filter_css_single_after_minify', $contents ); | |
| 1263 | + | |
| 1075 | 1264 | // Store in cache. |
| 1076 | 1265 | $cache->cache( $contents, 'text/css' ); |
| 1077 | 1266 | } |
| 1078 | 1267 | |
| @@ -1102,13 +1291,33 @@ | ||
| 1102 | 1291 | } |
| 1103 | 1292 | |
| 1104 | 1293 | public function setOption( $name, $value ) |
| 1105 | 1294 | { |
| 1106 | - $this->options[$name] = $value; | |
| 1107 | - $this->$name = $value; | |
| 1295 | + $this->options[ $name ] = $value; | |
| 1296 | + $this->$name = $value; | |
| 1108 | 1297 | } |
| 1109 | 1298 | |
| 1110 | 1299 | public function getOption( $name ) |
| 1111 | 1300 | { |
| 1112 | - return $this->options[$name]; | |
| 1301 | + return $this->options[ $name ]; | |
| 1302 | + } | |
| 1303 | + | |
| 1304 | + /** | |
| 1305 | + * Sanitize user-provided CSS. | |
| 1306 | + * | |
| 1307 | + * For now just strip_tags (the WordPress way) and preg_replace to escape < in certain cases but might do full CSS escaping in the future, see: | |
| 1308 | + * https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#rule-4-css-encode-and-strictly-validate-before-inserting-untrusted-data-into-html-style-property-values | |
| 1309 | + * https://github.com/twigphp/Twig/blob/3.x/src/Extension/EscaperExtension.php#L300-L319 | |
| 1310 | + * https://github.com/laminas/laminas-escaper/blob/2.8.x/src/Escaper.php#L205-L221 | |
| 1311 | + * | |
| 1312 | + * @param string $css the to be sanitized CSS | |
| 1313 | + * @return string sanitized CSS. | |
| 1314 | + */ | |
| 1315 | + public static function sanitize_css( $css ) | |
| 1316 | + { | |
| 1317 | + $css = wp_strip_all_tags( $css ); | |
| 1318 | + if ( strpos( $css, '<' ) !== false ) { | |
| 1319 | + $css = preg_replace( '#<(\/?\w+)#', '\00003C$1', $css ); | |
| 1320 | + } | |
| 1321 | + return $css; | |
| 1113 | 1322 | } |
| 1114 | 1323 | } |