PluginProbe
Autoptimize / 3.0.3
Autoptimize v3.0.3
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 2.7.8 All 107 releases
← All changes | classes/autoptimizeCache.php +210 -18 2.4.03.0.3 View file →
@@ -39,17 +39,22 @@
39 39 * @param string $ext Extension.
40 40 */
41 41 public function __construct( $md5, $ext = 'php' )
42 42 {
43 + $_min_ext = '';
44 + if ( apply_filters( 'autoptimize_filter_cache_url_add_min_ext', false ) ) {
45 + $_min_ext = '.min';
46 + }
47 +
43 48 $this->cachedir = AUTOPTIMIZE_CACHE_DIR;
44 49 $this->nogzip = AUTOPTIMIZE_CACHE_NOGZIP;
45 50 if ( ! $this->nogzip ) {
46 - $this->filename = AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . '.php';
51 + $this->filename = AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . $_min_ext . '.php';
47 52 } else {
48 53 if ( in_array( $ext, array( 'js', 'css' ) ) ) {
49 - $this->filename = $ext . '/' . AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . '.' . $ext;
54 + $this->filename = $ext . '/' . AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . $_min_ext . '.' . $ext;
50 55 } else {
51 - $this->filename = AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . '.' . $ext;
56 + $this->filename = AUTOPTIMIZE_CACHEFILE_PREFIX . $md5 . $_min_ext . '.' . $ext;
52 57 }
53 58 }
54 59 }
55 60
@@ -89,8 +94,21 @@
89 94 * @return void
90 95 */
91 96 public function cache( $data, $mime )
92 97 {
98 + // readonly FS explicitly OK'ed by developer, so just pretend all is OK.
99 + if ( defined( 'AUTOPTIMIZE_CACHE_READONLY' ) ) {
100 + return true;
101 + }
102 +
103 + // off by default; check if cachedirs exist every time before caching
104 + //
105 + // to be activated for users that experience these ugly errors;
106 + // PHP Warning: file_put_contents failed to open stream: No such file or directory.
107 + if ( apply_filters( 'autoptimize_filter_cache_checkdirs_on_write', false ) ) {
108 + $this->check_and_create_dirs();
109 + }
110 +
93 111 if ( false === $this->nogzip ) {
94 112 // We handle gzipping ourselves.
95 113 $file = 'default.php';
96 114 $phpcode = file_get_contents( AUTOPTIMIZE_PLUGIN_DIR . 'config/' . $file );
@@ -100,13 +118,31 @@
100 118 file_put_contents( $this->cachedir . $this->filename . '.none', $data );
101 119 } else {
102 120 // Write code to cache without doing anything else.
103 121 file_put_contents( $this->cachedir . $this->filename, $data );
122 +
123 + // save fallback .js or .css file if filter true (to be false by default) but not if snippet or single.
124 + if ( self::do_fallback() && strpos( $this->filename, '_snippet_' ) === false && strpos( $this->filename, '_single_' ) === false ) {
125 + $_extension = pathinfo( $this->filename, PATHINFO_EXTENSION );
126 + $_fallback_file = AUTOPTIMIZE_CACHEFILE_PREFIX . 'fallback.' . $_extension;
127 + if ( ( 'css' === $_extension || 'js' === $_extension ) && ! file_exists( $this->cachedir . $_extension . '/' . $_fallback_file ) ) {
128 + file_put_contents( $this->cachedir . $_extension . '/' . $_fallback_file, $data );
129 + }
130 + }
131 +
104 132 if ( apply_filters( 'autoptimize_filter_cache_create_static_gzip', false ) ) {
105 133 // Create an additional cached gzip file.
106 134 file_put_contents( $this->cachedir . $this->filename . '.gz', gzencode( $data, 9, FORCE_GZIP ) );
135 + // If PHP Brotli extension is installed, create an additional cached Brotli file.
136 + if ( function_exists( 'brotli_compress' ) ) {
137 + file_put_contents( $this->cachedir . $this->filename . '.br', brotli_compress( $data, 11, BROTLI_GENERIC ) );
138 + }
107 139 }
108 140 }
141 +
142 + // Provide 3rd party action hook for every cache file that is created.
143 + // This hook can for example be used to inject a copy of the created cache file to a other domain.
144 + do_action( 'autoptimize_action_cache_file_created', $this->cachedir . $this->filename );
109 145 }
110 146
111 147 /**
112 148 * Get cache filename.
@@ -194,8 +230,12 @@
194 230 * Clears contents of AUTOPTIMIZE_CACHE_DIR by renaming the current
195 231 * cache directory into a new one with a unique name and then
196 232 * re-creating the default (empty) cache directory.
197 233 *
234 + * Important/ Fixme: this does not take multisite into account, so
235 + * if advanced_cache_clear_enabled is true (it is not by default)
236 + * then the content for all subsites is zapped!
237 + *
198 238 * @return bool Returns true when everything is done successfully, false otherwise.
199 239 */
200 240 protected static function clear_cache_via_rename()
201 241 {
@@ -247,9 +287,9 @@
247 287 protected static function get_advanced_cache_clear_prefix()
248 288 {
249 289 $pathname = self::get_pathname_base();
250 290 $basename = basename( $pathname );
251 - $prefix = $basename . '-';
291 + $prefix = $basename . '-artifact-';
252 292
253 293 return $prefix;
254 294 }
255 295
@@ -274,8 +314,13 @@
274 314 * @return bool
275 315 */
276 316 public static function delete_advanced_cache_clear_artifacts()
277 317 {
318 + // Don't go through these motions (called from the cachechecker) if advanced cache clear isn't even active.
319 + if ( ! self::advanced_cache_clear_enabled() ) {
320 + return false;
321 + }
322 +
278 323 $dir = self::get_pathname_base();
279 324 $prefix = self::get_advanced_cache_clear_prefix();
280 325 $parent = dirname( $dir );
281 326 $ok = false;
@@ -281,14 +326,16 @@
281 326 $ok = false;
282 327
283 328 // Returns the list of files without '.' and '..' elements.
284 329 $files = self::get_dir_contents( $parent );
285 - foreach ( $files as $file ) {
286 - $path = $parent . '/' . $file;
287 - $prefixed = ( false !== strpos( $path, $prefix ) );
288 - // Removing only our own (prefixed) directories...
289 - if ( is_dir( $path ) && $prefixed ) {
290 - $ok = self::rmdir( $path );
330 + if ( is_array( $files ) && ! empty( $files ) ) {
331 + foreach ( $files as $file ) {
332 + $path = $parent . '/' . $file;
333 + $prefixed = ( false !== strpos( $path, $prefix ) );
334 + // Removing only our own (prefixed) directories...
335 + if ( is_dir( $path ) && $prefixed ) {
336 + $ok = self::rmdir( $path );
337 + }
291 338 }
292 339 }
293 340
294 341 return $ok;
@@ -334,8 +381,18 @@
334 381 * @return bool
335 382 */
336 383 public static function clearall( $propagate = true )
337 384 {
385 + if ( defined( 'ET_CORE_VERSION' ) && 'Divi' === get_template() ) {
386 + // see https://blog.futtta.be/2018/11/17/warning-divi-purging-autoptimizes-cache/ .
387 + $dbt = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
388 + $caller = isset( $dbt[1]['function'] ) ? $dbt[1]['function'] : null;
389 + if ( $caller === 'et_core_clear_wp_cache' ) {
390 + _doing_it_wrong( 'autoptimizeCache::clearall', 'Divi devs: please don\'t clear Autoptimize\'s cache, it is unneeded and can break sites. You can contact me at futtta@gmail.com to discuss.', 'Autoptimize 2.9.6' );
391 + return false;
392 + }
393 + }
394 +
338 395 if ( ! self::cacheavail() ) {
339 396 return false;
340 397 }
341 398
@@ -345,8 +402,14 @@
345 402 } else {
346 403 self::clear_cache_classic();
347 404 }
348 405
406 + // Remove 404 handler if required.
407 + if ( self::do_fallback() ) {
408 + $_fallback_php = trailingslashit( WP_CONTENT_DIR ) . 'autoptimize_404_handler.php';
409 + @unlink( $_fallback_php ); // @codingStandardsIgnoreLine
410 + }
411 +
349 412 // Remove the transient so it gets regenerated...
350 413 delete_transient( 'autoptimize_stats' );
351 414
352 415 // Cache was just purged, clear page cache and allow others to hook into our purging...
@@ -360,10 +423,12 @@
360 423 add_action( 'autoptimize_action_cachepurged', array( 'autoptimizeCache', 'flushPageCache' ), 10, 0 );
361 424 }
362 425
363 426 // Warm cache (part of speedupper)!
364 - if ( apply_filters( 'autoptimize_filter_speedupper', true ) ) {
427 + if ( apply_filters( 'autoptimize_filter_speedupper', true ) && false == get_transient( 'autoptimize_cache_warmer_protector' ) ) {
428 + set_transient( 'autoptimize_cache_warmer_protector', 'I shall not warm cache for another 10 minutes.', 60 * 10 );
365 429 $url = site_url() . '/?ao_speedup_cachebuster=' . rand( 1, 100000 );
430 + $url = apply_filters( 'autoptimize_filter_cache_warmer_url', $url );
366 431 $cache = @wp_remote_get( $url ); // @codingStandardsIgnoreLine
367 432 unset( $cache );
368 433 }
369 434
@@ -478,17 +543,15 @@
478 543 * @return bool
479 544 */
480 545 public static function cacheavail()
481 546 {
482 - if ( ! defined( 'AUTOPTIMIZE_CACHE_DIR' ) ) {
483 - // We didn't set a cache.
484 - return false;
547 + // readonly FS explicitly OK'ed by dev, let's assume the cache dirs are there!
548 + if ( defined( 'AUTOPTIMIZE_CACHE_READONLY' ) ) {
549 + return true;
485 550 }
486 551
487 - foreach ( array( '', 'js', 'css' ) as $dir ) {
488 - if ( ! self::check_cache_dir( AUTOPTIMIZE_CACHE_DIR . $dir ) ) {
489 - return false;
490 - }
552 + if ( false === autoptimizeCache::check_and_create_dirs() ) {
553 + return false;
491 554 }
492 555
493 556 // Using .htaccess inside our cache folder to overrule wp-super-cache.
494 557 $htaccess = AUTOPTIMIZE_CACHE_DIR . '/.htaccess';
@@ -553,16 +616,124 @@
553 616 Deny from all
554 617 </Files>
555 618 </IfModule>';
556 619 }
620 +
621 + if ( self::do_fallback() === true ) {
622 + $content .= "\nErrorDocument 404 " . trailingslashit( parse_url( content_url(), PHP_URL_PATH ) ) . 'autoptimize_404_handler.php';
623 + }
557 624 @file_put_contents( $htaccess, $content ); // @codingStandardsIgnoreLine
558 625 }
559 626
627 + if ( self::do_fallback() ) {
628 + self::check_fallback_php();
629 + }
630 +
560 631 // All OK!
561 632 return true;
562 633 }
563 634
564 635 /**
636 + * Checks if fallback-php file exists and create it if not.
637 + *
638 + * Return bool
639 + */
640 + public static function check_fallback_php() {
641 + $_fallback_filename = 'autoptimize_404_handler.php';
642 + $_fallback_php = trailingslashit( WP_CONTENT_DIR ) . $_fallback_filename;
643 + $_fallback_status = true;
644 +
645 + if ( ! file_exists( $_fallback_php ) && is_writable( WP_CONTENT_DIR ) ) {
646 + $_fallback_php_contents = file_get_contents( AUTOPTIMIZE_PLUGIN_DIR . 'config/' . $_fallback_filename );
647 + $_fallback_php_contents = str_replace( '<?php exit;', '<?php', $_fallback_php_contents );
648 + $_fallback_php_contents = str_replace( '<!--ao-cache-dir-->', AUTOPTIMIZE_CACHE_DIR, $_fallback_php_contents );
649 + $_fallback_php_contents = str_replace( '<!--ao-cachefile-prefix-->', AUTOPTIMIZE_CACHEFILE_PREFIX, $_fallback_php_contents );
650 + if ( is_multisite() ) {
651 + $_fallback_php_contents = str_replace( '$multisite = false;', '$multisite = true;', $_fallback_php_contents );
652 + }
653 + if ( apply_filters( 'autoptimize_filter_cache_fallback_log_errors', false ) ) {
654 + $_fallback_php_contents = str_replace( '// error_log', 'error_log', $_fallback_php_contents );
655 + }
656 + $_fallback_status = file_put_contents( $_fallback_php, $_fallback_php_contents );
657 + }
658 +
659 + return $_fallback_status;
660 + }
661 +
662 + /**
663 + * Tells if AO should try to avoid 404's by creating fallback filesize
664 + * and create a php 404 handler and tell .htaccess to redirect to said handler
665 + * and hook into WordPress to redirect 404 to said handler as well. NGINX users
666 + * are smart enough to get this working, no? ;-)
667 + *
668 + * Return bool
669 + */
670 + public static function do_fallback() {
671 + static $_do_fallback = null;
672 +
673 + if ( null === $_do_fallback ) {
674 + $_do_fallback = (bool) apply_filters( 'autoptimize_filter_cache_do_fallback', autoptimizeOptionWrapper::get_option( 'autoptimize_cache_fallback', '1' ) );
675 + }
676 +
677 + return $_do_fallback;
678 + }
679 +
680 + /**
681 + * Hooks into template_redirect, will act on 404-ing requests for
682 + * Autoptimized files and redirects to the fallback CSS/ JS if available
683 + * and 410'ing ("Gone") if fallback not available.
684 + */
685 + public static function wordpress_notfound_fallback() {
686 + $original_request = strtok( $_SERVER['REQUEST_URI'], '?' );
687 + if ( strpos( $original_request, wp_basename( WP_CONTENT_DIR ) . AUTOPTIMIZE_CACHE_CHILD_DIR ) !== false && is_404() ) {
688 + // make sure this is not considered a 404.
689 + global $wp_query;
690 + $wp_query->is_404 = false;
691 +
692 + // set fallback path.
693 + $js_or_css = pathinfo( $original_request, PATHINFO_EXTENSION );
694 + $fallback_path = AUTOPTIMIZE_CACHE_DIR . $js_or_css . '/autoptimize_fallback.' . $js_or_css;
695 +
696 + // prepare for Shakeeb's Unused CSS files to be 404-handled as well.
697 + if ( strpos( $original_request, 'uucss/uucss-' ) !== false ) {
698 + $original_request = preg_replace( '/uucss\/uucss-[a-z0-9]{32}-/', 'css/', $original_request );
699 + }
700 +
701 + // set fallback URL.
702 + $fallback_target = preg_replace( '/(.*)_(?:[a-z0-9]{32})\.(js|css)$/', '${1}_fallback.${2}', $original_request );
703 +
704 + // redirect to fallback if possible.
705 + if ( $original_request !== $fallback_target && file_exists( $fallback_path ) ) {
706 + // redirect to fallback.
707 + wp_redirect( $fallback_target, 302 );
708 + } else {
709 + // return HTTP 410 (gone) reponse.
710 + status_header( 410 );
711 + }
712 + }
713 + }
714 +
715 + /**
716 + * Checks if cache dirs exist and create if not.
717 + * Returns false if not succesful.
718 + *
719 + * @return bool
720 + */
721 + public static function check_and_create_dirs() {
722 + if ( ! defined( 'AUTOPTIMIZE_CACHE_DIR' ) ) {
723 + // We didn't set a cache.
724 + return false;
725 + }
726 +
727 + foreach ( array( '', 'js', 'css' ) as $dir ) {
728 + if ( ! self::check_cache_dir( AUTOPTIMIZE_CACHE_DIR . $dir ) ) {
729 + return false;
730 + }
731 + }
732 + return true;
733 + }
734 +
735 + /**
565 736 * Ensures the specified `$dir` exists and is writeable.
566 737 * Returns false if that's not the case.
567 738 *
568 739 * @param string $dir Directory to check/create.
@@ -613,8 +784,12 @@
613 784 } elseif ( function_exists( 'w3tc_pgcache_flush' ) ) {
614 785 w3tc_pgcache_flush();
615 786 } elseif ( function_exists( 'wp_fast_cache_bulk_delete_all' ) ) {
616 787 wp_fast_cache_bulk_delete_all();
788 + } elseif ( function_exists( 'rapidcache_clear_cache' ) ) {
789 + rapidcache_clear_cache();
790 + } elseif ( class_exists( 'Swift_Performance_Cache' ) ) {
791 + Swift_Performance_Cache::clear_all_cache();
617 792 } elseif ( class_exists( 'WpFastestCache' ) ) {
618 793 $wpfc = new WpFastestCache();
619 794 $wpfc->deleteCache();
620 795 } elseif ( class_exists( 'c_ws_plugin__qcache_purging_routines' ) ) {
@@ -643,8 +818,22 @@
643 818 }
644 819 }
645 820 } elseif ( function_exists( 'sg_cachepress_purge_cache' ) ) {
646 821 sg_cachepress_purge_cache();
822 + } elseif ( array_key_exists( 'KINSTA_CACHE_ZONE', $_SERVER ) ) {
823 + $_kinsta_clear_cache_url = 'https://localhost/kinsta-clear-cache-all';
824 + $_kinsta_response = wp_remote_get(
825 + $_kinsta_clear_cache_url,
826 + array(
827 + 'sslverify' => false,
828 + 'timeout' => 5,
829 + )
830 + );
831 + } elseif ( class_exists( 'RaidboxesNginxCacheFunctions' ) ) {
832 + $rb_cache_helper = new RaidboxesNginxCacheFunctions();
833 + $rb_cache_helper->purge_cache();
834 + } elseif ( defined('NGINX_HELPER_BASENAME') ) {
835 + do_action( 'rt_nginx_helper_purge_all' );
647 836 } elseif ( file_exists( WP_CONTENT_DIR . '/wp-cache-config.php' ) && function_exists( 'prune_super_cache' ) ) {
648 837 // fallback for WP-Super-Cache
649 838 global $cache_path;
650 839 if ( is_multisite() ) {
@@ -654,8 +843,11 @@
654 843 } else {
655 844 prune_super_cache( $cache_path . 'supercache/', true );
656 845 prune_super_cache( $cache_path, true );
657 846 }
847 + } elseif ( class_exists( 'NginxCache' ) ) {
848 + $nginx_cache = new NginxCache();
849 + $nginx_cache->purge_zone_once();
658 850 }
659 851 }
660 852 // @codingStandardsIgnoreEnd
661 853 }