PluginProbe
Autoptimize / 3.1.16
Autoptimize v3.1.16
3.1.16 2.2.2 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 All 108 releases
← All changes | classes/autoptimizeScripts.php +120 -26 2.7.1 → 3.1.16 View file →
@@ -57,9 +57,14 @@
57 57 '_stq',
58 58 'nonce',
59 59 'post_id',
60 60 'data-noptimize',
61 + 'data-cfasync',
62 + 'data-pagespeed-no-defer',
61 63 'logHuman',
64 + 'amp-mobile-version-switcher',
65 + 'data-rocketlazyloadscript',
66 + 'rocket-browser-checker-js-after',
62 67 );
63 68
64 69 /**
65 70 * List of to be moved JS.
@@ -108,8 +113,22 @@
108 113 */
109 114 private $aggregate = true;
110 115
111 116 /**
117 + * Setting; if not aggregated, should we defer?
118 + *
119 + * @var bool
120 + */
121 + private $defer_not_aggregate = false;
122 +
123 + /**
124 + * Setting; defer inline JS?
125 + *
126 + * @var bool
127 + */
128 + private $defer_inline = false;
129 +
130 + /**
112 131 * Setting; try/catch wrapping or not.
113 132 *
114 133 * @var bool
115 134 */
@@ -164,13 +183,13 @@
164 183 */
165 184 private $md5hash = '';
166 185
167 186 /**
168 - * Setting (filter); whitelist of to be aggregated JS.
187 + * Setting (filter); allowlist of to be aggregated JS.
169 188 *
170 189 * @var string
171 190 */
172 - private $whitelist = '';
191 + private $allowlist = '';
173 192
174 193 /**
175 194 * Setting (filter); holds JS that should be removed.
176 195 *
@@ -199,17 +218,27 @@
199 218 * @param array $options all options.
200 219 */
201 220 public function read( $options )
202 221 {
203 - $noptimize_js = apply_filters( 'autoptimize_filter_js_noptimize', false, $this->content );
222 + $noptimize_js = false;
223 +
224 + // If page/ post check post_meta to see if optimize is off.
225 + if ( false === autoptimizeConfig::get_post_meta_ao_settings( 'ao_post_js_optimize' ) ) {
226 + $noptimize_js = true;
227 + }
228 +
229 + // And a filter to enforce JS noptimize.
230 + $noptimize_js = apply_filters( 'autoptimize_filter_js_noptimize', $noptimize_js, $this->content );
231 +
232 + // And finally bail if noptimize_js is true.
204 233 if ( $noptimize_js ) {
205 234 return false;
206 235 }
207 236
208 237 // only optimize known good JS?
209 - $whitelist_js = apply_filters( 'autoptimize_filter_js_whitelist', '', $this->content );
210 - if ( ! empty( $whitelist_js ) ) {
211 - $this->whitelist = array_filter( array_map( 'trim', explode( ',', $whitelist_js ) ) );
238 + $allowlist_js = apply_filters( 'autoptimize_filter_js_allowlist', '', $this->content );
239 + if ( ! empty( $allowlist_js ) ) {
240 + $this->allowlist = array_filter( array_map( 'trim', explode( ',', $allowlist_js ) ) );
212 241 }
213 242
214 243 // is there JS we should simply remove?
215 244 $removable_js = apply_filters( 'autoptimize_filter_js_removables', '', $this->content );
@@ -231,9 +260,21 @@
231 260 // Returning true for "dontaggregate" turns off aggregation.
232 261 if ( $this->aggregate && apply_filters( 'autoptimize_filter_js_dontaggregate', false ) ) {
233 262 $this->aggregate = false;
234 263 }
264 + // and the filter that should have been there to begin with.
265 + $this->aggregate = apply_filters( 'autoptimize_filter_js_aggregate', $this->aggregate );
235 266
267 + // Defer when not aggregating.
268 + if ( false === $this->aggregate && apply_filters( 'autoptimize_filter_js_defer_not_aggregate', $options['defer_not_aggregate'] ) ) {
269 + $this->defer_not_aggregate = true;
270 + }
271 +
272 + // Defer inline JS?
273 + if ( ( true === $this->defer_not_aggregate && apply_filters( 'autoptimize_js_filter_defer_inline', $options['defer_inline'] ) ) || apply_filters( 'autoptimize_js_filter_force_defer_inline', false ) ) {
274 + $this->defer_inline = true;
275 + }
276 +
236 277 // include inline?
237 278 if ( apply_filters( 'autoptimize_js_include_inline', $options['include_inline'] ) ) {
238 279 $this->include_inline = true;
239 280 }
@@ -300,9 +341,9 @@
300 341
301 342 // Get script files.
302 343 if ( preg_match_all( '#<script.*</script>#Usmi', $this->content, $matches ) ) {
303 344 foreach ( $matches[0] as $tag ) {
304 - // only consider script aggregation for types whitelisted in should_aggregate-function.
345 + // only consider script aggregation for types allowlisted in should_aggregate-function.
305 346 $should_aggregate = $this->should_aggregate( $tag );
306 347 if ( ! $should_aggregate ) {
307 348 $tag = '';
308 349 continue;
@@ -334,8 +375,25 @@
334 375 }
335 376 }
336 377 }
337 378
379 + // not aggregating but deferring?
380 + if ( $this->defer_not_aggregate && false === $this->aggregate && ( str_replace( $this->dontmove, '', $path ) === $path || ( apply_filters( 'autoptimize_filter_js_defer_external', true ) && str_replace( $this->dontmove, '', $orig_tag ) === $orig_tag ) ) && strpos( $new_tag, ' defer' ) === false ) {
381 + if ( false !== strpos( $new_tag, ' async' ) && true === apply_filters( 'autoptimize_filter_js_defer_trumps_async', true ) ) {
382 + // remove async flag to ensure JS is properly deferred, otherwise the asynced JS might fire
383 + // before deferred inlined JS is executed, off course except filter is set to false which
384 + // re-institutes previous behavior.
385 + $new_tag = str_replace( array( " async='async'", ' async="async"', ' async=async', ' async' ), '', $new_tag );
386 + }
387 +
388 + if ( false === strpos( $new_tag, ' async' ) ) {
389 + // either async wasn't there to begin with or it was removed.
390 + // if async is there, the autoptimize_filter_js_defer_trumps_async
391 + // filter was set to false and in that case defer should not be added.
392 + $new_tag = str_replace( '<script ', '<script defer ', $new_tag );
393 + }
394 + }
395 +
338 396 // Should we minify the non-aggregated script?
339 397 // -> if aggregate is on and exclude minify is on
340 398 // -> if aggregate is off and the file is not in dontmove.
341 399 if ( $path && $this->minify_excluded ) {
@@ -344,15 +402,21 @@
344 402 $minified_url = $this->minify_single( $path );
345 403 if ( ! empty( $minified_url ) ) {
346 404 // Replace original URL with minified URL from cache.
347 405 $new_tag = str_replace( $url, $minified_url, $new_tag );
348 - } else {
349 - // Remove the original script tag, because cache content is empty.
406 + } elseif ( apply_filters( 'autoptimize_filter_ccsjs_remove_empty_minified_url', false ) ) {
407 + // Remove the original script tag, because cache content is empty but only if filter
408 + // is trued because $minified_url is also false if original JS is minified already.
350 409 $new_tag = '';
351 410 }
352 411 }
353 412 }
354 413
414 + // Check if we still need to CDN (esp. for already minified resources).
415 + if ( ! empty( $this->cdn_url ) || has_filter( 'autoptimize_filter_base_replace_cdn' ) ) {
416 + $new_tag = str_replace( $url, $this->url_replace_cdn( $url ), $new_tag );
417 + }
418 +
355 419 if ( $this->ismovable( $new_tag ) ) {
356 420 // can be moved, flags and all.
357 421 if ( $this->movetolast( $new_tag ) ) {
358 422 $this->move['last'][] = $new_tag;
@@ -383,18 +447,46 @@
383 447 $code = preg_replace( '#.*<!\[CDATA\[(?:\s*\*/)?(.*)(?://|/\*)\s*?\]\]>.*#sm', '$1', $code[1] );
384 448 $code = preg_replace( '/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $code );
385 449 $this->scripts[] = 'INLINE;' . $code;
386 450 } else {
387 - // Can we move this?
388 - $autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag );
389 - if ( $this->ismovable( $tag ) || '' !== $autoptimize_js_moveable ) {
390 - if ( $this->movetolast( $tag ) || 'last' === $autoptimize_js_moveable ) {
391 - $this->move['last'][] = $tag;
451 + $_inline_deferable = apply_filters( 'autoptimize_filters_js_inline_deferable', array( 'nonce', 'post_id', 'syntaxhighlighter' ) );
452 + $_inline_dontmove = array_values( array_diff( $this->dontmove, $_inline_deferable ) );
453 + if ( false === $this->defer_inline ) {
454 + // Can we move this?
455 + $autoptimize_js_moveable = apply_filters( 'autoptimize_js_moveable', '', $tag );
456 + if ( $this->ismovable( $tag ) || '' !== $autoptimize_js_moveable ) {
457 + if ( $this->movetolast( $tag ) || 'last' === $autoptimize_js_moveable ) {
458 + $this->move['last'][] = $tag;
459 + } else {
460 + $this->move['first'][] = $tag;
461 + }
392 462 } else {
393 - $this->move['first'][] = $tag;
463 + $tag = '';
394 464 }
465 + } else if ( str_replace( $_inline_dontmove, '', $tag ) === $tag && strlen( $tag ) < apply_filters( 'autoptimize_filter_script_defer_inline_maxsize', 200000 ) ) {
466 + // defer inline JS by base64 encoding it but only if string is not ridiculously huge (to avoid issues with below regex mainly).
467 + preg_match( '#<script(?:[^>](?!id=))*\s*(?:id=(["\'])([^"\']+)\1)*+[^>]*+>(.*?)<\/script>#is', $tag, $match );
468 + if ( $match[2] ) {
469 + $_id = 'id="' . $match[2] . '" ';
470 + } else {
471 + $_id = '';
472 + }
473 +
474 + // if "minify inline" is on and if more then 9 spaces or 4 line breaks are found
475 + // in the inline JS then it is likely not minified, so minify before base64-encoding.
476 + $_script_contents = $match[3];
477 + if ( 'on' === autoptimizeOptionWrapper::get_option( 'autoptimize_html_minify_inline', 'off' ) && substr_count( $_script_contents, ' ' ) > 9 && substr_count( $_script_contents, "\n" ) > 4 && true === apply_filters( 'autoptimize_filter_script_defer_inline_minify', true ) ) {
478 + $_tmp_script_contents = trim( JSMin::minify( $_script_contents ) );
479 + if ( ! empty( $_tmp_script_contents ) ) {
480 + $_script_contents = $_tmp_script_contents;
481 + }
482 + }
483 +
484 + // base64 and defer the lot already.
485 + $new_tag = '<script defer ' . $_id . 'src="data:text/javascript;base64,' . base64_encode( $_script_contents ) . '"></script>';
486 + $this->content = str_replace( $this->hide_comments( $tag ), $new_tag, $this->content );
487 + $tag = '';
395 488 } else {
396 - // We shouldn't touch this.
397 489 $tag = '';
398 490 }
399 491 }
400 492 // Re-hide comments to be able to do the removal based on tag from $this->content.
@@ -426,9 +518,9 @@
426 518 *
427 519 * @param string $tag Script node & child(ren).
428 520 * @return bool
429 521 */
430 - public function should_aggregate( $tag )
522 + public static function should_aggregate( $tag )
431 523 {
432 524 if ( empty( $tag ) ) {
433 525 return false;
434 526 }
@@ -441,9 +533,9 @@
441 533 if ( ! empty( $tag_parts[1] ) ) {
442 534 $tag_without_contents = $tag_parts[1];
443 535 }
444 536
445 - $has_type = ( strpos( $tag_without_contents, 'type' ) !== false );
537 + $has_type = ( strpos( $tag_without_contents, ' type' ) !== false );
446 538
447 539 $type_valid = false;
448 540 if ( $has_type ) {
449 541 $type_valid = (bool) preg_match( '/type\s*=\s*[\'"]?(?:text|application)\/(?:javascript|ecmascript)[\'"]?/i', $tag_without_contents );
@@ -600,9 +692,9 @@
600 692 return $this->content;
601 693 }
602 694
603 695 /**
604 - * Checks against the white- and blacklists.
696 + * Checks against the allow- and blocklists.
605 697 *
606 698 * @param string $tag JS tag.
607 699 */
608 700 private function ismergeable( $tag )
@@ -610,15 +702,15 @@
610 702 if ( empty( $tag ) || ! $this->aggregate ) {
611 703 return false;
612 704 }
613 705
614 - if ( ! empty( $this->whitelist ) ) {
615 - foreach ( $this->whitelist as $match ) {
706 + if ( ! empty( $this->allowlist ) ) {
707 + foreach ( $this->allowlist as $match ) {
616 708 if ( false !== strpos( $tag, $match ) ) {
617 709 return true;
618 710 }
619 711 }
620 - // No match with whitelist.
712 + // No match with allowlist.
621 713 return false;
622 714 } else {
623 715 foreach ( $this->domove as $match ) {
624 716 if ( false !== strpos( $tag, $match ) ) {
@@ -643,11 +735,11 @@
643 735 }
644 736 }
645 737
646 738 /**
647 - * Checks agains the blacklist.
739 + * Checks agains the blocklist.
648 740 *
649 - * @param string $tag tag to check for blacklist (exclusions).
741 + * @param string $tag tag to check for blocklist (exclusions).
650 742 */
651 743 private function ismovable( $tag )
652 744 {
653 745 if ( empty( $tag ) || true !== $this->include_inline || apply_filters( 'autoptimize_filter_js_unmovable', true ) ) {
@@ -696,9 +788,8 @@
696 788 /**
697 789 * Determines wheter a <script> $tag can be excluded from minification (as already minified) based on:
698 790 * - inject_min_late being active
699 791 * - filename ending in `min.js`
700 - * - filename matching `js/jquery/jquery.js` (WordPress core jquery, is minified)
701 792 * - filename matching one passed in the consider minified filter
702 793 *
703 794 * @param string $js_path Path to JS file.
704 795 * @return bool
@@ -707,9 +798,9 @@
707 798 $consider_minified_array = apply_filters( 'autoptimize_filter_js_consider_minified', false );
708 799 if ( true !== $this->inject_min_late ) {
709 800 // late-inject turned off.
710 801 return false;
711 - } elseif ( ( false === strpos( $js_path, 'min.js' ) ) && ( false === strpos( $js_path, 'wp-includes/js/jquery/jquery.js' ) ) && ( str_replace( $consider_minified_array, '', $js_path ) === $js_path ) ) {
802 + } elseif ( ( false === strpos( $js_path, 'min.js' ) ) && ( str_replace( $consider_minified_array, '', $js_path ) === $js_path ) ) {
712 803 // file not minified based on filename & filter.
713 804 return false;
714 805 } else {
715 806 // phew, all is safe, we can late-inject.
@@ -754,8 +845,11 @@
754 845 // Check if minified cache content is empty.
755 846 if ( empty( $contents ) ) {
756 847 return false;
757 848 }
849 +
850 + // Filter contents of excluded minified CSS.
851 + $contents = apply_filters( 'autoptimize_filter_js_single_after_minify', $contents );
758 852
759 853 // Store in cache.
760 854 $cache->cache( $contents, 'text/javascript' );
761 855 }