PluginProbe
Autoptimize / trunk
Autoptimize vtrunk
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/autoptimizeCache.php +214 -20 2.4.1 → trunk 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
@@ -85,12 +90,25 @@
85 90 *
86 91 * @param string $data Data to cache.
87 92 * @param string $mime Mimetype.
88 93 *
89 - * @return void
94 + * @return void|bool
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,9 +381,21 @@
334 381 * @return bool
335 382 */
336 383 public static function clearall( $propagate = true )
337 384 {
338 - if ( ! self::cacheavail() ) {
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 ( 'et_core_clear_wp_cache' === $caller ) {
390 + if ( apply_filters( 'autoptimize_filter_cache_divi_wrong_complain', true ) ) {
391 + _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 [email protected] to discuss.', 'Autoptimize 2.9.6' );
392 + }
393 + return false;
394 + }
395 + }
396 +
397 + if ( ! self::cacheavail() || true === apply_filters( 'autoptimize_filter_cache_clearall_disabled', false ) ) {
339 398 return false;
340 399 }
341 400
342 401 // TODO/FIXME: If cache is big, switch to advanced/new cache clearing automatically?
@@ -345,8 +404,14 @@
345 404 } else {
346 405 self::clear_cache_classic();
347 406 }
348 407
408 + // Remove 404 handler if required.
409 + if ( self::do_fallback() ) {
410 + $_fallback_php = trailingslashit( WP_CONTENT_DIR ) . 'autoptimize_404_handler.php';
411 + @unlink( $_fallback_php ); // @codingStandardsIgnoreLine
412 + }
413 +
349 414 // Remove the transient so it gets regenerated...
350 415 delete_transient( 'autoptimize_stats' );
351 416
352 417 // Cache was just purged, clear page cache and allow others to hook into our purging...
@@ -360,10 +425,12 @@
360 425 add_action( 'autoptimize_action_cachepurged', array( 'autoptimizeCache', 'flushPageCache' ), 10, 0 );
361 426 }
362 427
363 428 // Warm cache (part of speedupper)!
364 - if ( apply_filters( 'autoptimize_filter_speedupper', true ) ) {
429 + if ( apply_filters( 'autoptimize_filter_speedupper', true ) && false == get_transient( 'autoptimize_cache_warmer_protector' ) ) {
430 + set_transient( 'autoptimize_cache_warmer_protector', 'I shall not warm cache for another 10 minutes.', 60 * 10 );
365 431 $url = site_url() . '/?ao_speedup_cachebuster=' . rand( 1, 100000 );
432 + $url = apply_filters( 'autoptimize_filter_cache_warmer_url', $url );
366 433 $cache = @wp_remote_get( $url ); // @codingStandardsIgnoreLine
367 434 unset( $cache );
368 435 }
369 436
@@ -478,17 +545,15 @@
478 545 * @return bool
479 546 */
480 547 public static function cacheavail()
481 548 {
482 - if ( ! defined( 'AUTOPTIMIZE_CACHE_DIR' ) ) {
483 - // We didn't set a cache.
484 - return false;
549 + // readonly FS explicitly OK'ed by dev, let's assume the cache dirs are there!
550 + if ( defined( 'AUTOPTIMIZE_CACHE_READONLY' ) ) {
551 + return true;
485 552 }
486 553
487 - foreach ( array( '', 'js', 'css' ) as $dir ) {
488 - if ( ! self::check_cache_dir( AUTOPTIMIZE_CACHE_DIR . $dir ) ) {
489 - return false;
490 - }
554 + if ( false === autoptimizeCache::check_and_create_dirs() ) {
555 + return false;
491 556 }
492 557
493 558 // Using .htaccess inside our cache folder to overrule wp-super-cache.
494 559 $htaccess = AUTOPTIMIZE_CACHE_DIR . '/.htaccess';
@@ -553,16 +618,124 @@
553 618 Deny from all
554 619 </Files>
555 620 </IfModule>';
556 621 }
622 +
623 + if ( self::do_fallback() === true ) {
624 + $content .= "\nErrorDocument 404 " . trailingslashit( parse_url( content_url(), PHP_URL_PATH ) ) . 'autoptimize_404_handler.php';
625 + }
557 626 @file_put_contents( $htaccess, $content ); // @codingStandardsIgnoreLine
558 627 }
559 628
629 + if ( self::do_fallback() ) {
630 + self::check_fallback_php();
631 + }
632 +
560 633 // All OK!
561 634 return true;
562 635 }
563 636
564 637 /**
638 + * Checks if fallback-php file exists and create it if not.
639 + *
640 + * Return bool
641 + */
642 + public static function check_fallback_php() {
643 + $_fallback_filename = 'autoptimize_404_handler.php';
644 + $_fallback_php = trailingslashit( WP_CONTENT_DIR ) . $_fallback_filename;
645 + $_fallback_status = true;
646 +
647 + if ( ! file_exists( $_fallback_php ) && is_writable( WP_CONTENT_DIR ) ) {
648 + $_fallback_php_contents = file_get_contents( AUTOPTIMIZE_PLUGIN_DIR . 'config/' . $_fallback_filename );
649 + $_fallback_php_contents = str_replace( '<?php exit;', '<?php', $_fallback_php_contents );
650 + $_fallback_php_contents = str_replace( '<!--ao-cache-dir-->', AUTOPTIMIZE_CACHE_DIR, $_fallback_php_contents );
651 + $_fallback_php_contents = str_replace( '<!--ao-cachefile-prefix-->', AUTOPTIMIZE_CACHEFILE_PREFIX, $_fallback_php_contents );
652 + if ( is_multisite() ) {
653 + $_fallback_php_contents = str_replace( '$multisite = false;', '$multisite = true;', $_fallback_php_contents );
654 + }
655 + if ( apply_filters( 'autoptimize_filter_cache_fallback_log_errors', false ) ) {
656 + $_fallback_php_contents = str_replace( '// error_log', 'error_log', $_fallback_php_contents );
657 + }
658 + $_fallback_status = file_put_contents( $_fallback_php, $_fallback_php_contents );
659 + }
660 +
661 + return $_fallback_status;
662 + }
663 +
664 + /**
665 + * Tells if AO should try to avoid 404's by creating fallback filesize
666 + * and create a php 404 handler and tell .htaccess to redirect to said handler
667 + * and hook into WordPress to redirect 404 to said handler as well. NGINX users
668 + * are smart enough to get this working, no? ;-)
669 + *
670 + * Return bool
671 + */
672 + public static function do_fallback() {
673 + static $_do_fallback = null;
674 +
675 + if ( null === $_do_fallback ) {
676 + $_do_fallback = (bool) apply_filters( 'autoptimize_filter_cache_do_fallback', autoptimizeOptionWrapper::get_option( 'autoptimize_cache_fallback', '1' ) );
677 + }
678 +
679 + return $_do_fallback;
680 + }
681 +
682 + /**
683 + * Hooks into template_redirect, will act on 404-ing requests for
684 + * Autoptimized files and redirects to the fallback CSS/ JS if available
685 + * and 410'ing ("Gone") if fallback not available.
686 + */
687 + public static function wordpress_notfound_fallback() {
688 + $original_request = strtok( $_SERVER['REQUEST_URI'], '?' );
689 + if ( strpos( $original_request, wp_basename( WP_CONTENT_DIR ) . AUTOPTIMIZE_CACHE_CHILD_DIR ) !== false && is_404() ) {
690 + // make sure this is not considered a 404.
691 + global $wp_query;
692 + $wp_query->is_404 = false;
693 +
694 + // set fallback path.
695 + $js_or_css = pathinfo( $original_request, PATHINFO_EXTENSION );
696 + $fallback_path = AUTOPTIMIZE_CACHE_DIR . $js_or_css . '/autoptimize_fallback.' . $js_or_css;
697 +
698 + // prepare for Shakeeb's Unused CSS files to be 404-handled as well.
699 + if ( strpos( $original_request, 'uucss/uucss-' ) !== false ) {
700 + $original_request = preg_replace( '/uucss\/uucss-[a-z0-9]{32}-/', 'css/', $original_request );
701 + }
702 +
703 + // set fallback URL.
704 + $fallback_target = preg_replace( '/(.*)_(?:[a-z0-9]{32})\.(js|css)$/', '${1}_fallback.${2}', $original_request );
705 +
706 + // redirect to fallback if possible.
707 + if ( $original_request !== $fallback_target && file_exists( $fallback_path ) ) {
708 + // redirect to fallback.
709 + wp_redirect( $fallback_target, 302 );
710 + } else {
711 + // return HTTP 410 (gone) reponse.
712 + status_header( 410 );
713 + }
714 + }
715 + }
716 +
717 + /**
718 + * Checks if cache dirs exist and create if not.
719 + * Returns false if not succesful.
720 + *
721 + * @return bool
722 + */
723 + public static function check_and_create_dirs() {
724 + if ( ! defined( 'AUTOPTIMIZE_CACHE_DIR' ) ) {
725 + // We didn't set a cache.
726 + return false;
727 + }
728 +
729 + foreach ( array( '', 'js', 'css' ) as $dir ) {
730 + if ( ! self::check_cache_dir( AUTOPTIMIZE_CACHE_DIR . $dir ) ) {
731 + return false;
732 + }
733 + }
734 + return true;
735 + }
736 +
737 + /**
565 738 * Ensures the specified `$dir` exists and is writeable.
566 739 * Returns false if that's not the case.
567 740 *
568 741 * @param string $dir Directory to check/create.
@@ -613,8 +786,12 @@
613 786 } elseif ( function_exists( 'w3tc_pgcache_flush' ) ) {
614 787 w3tc_pgcache_flush();
615 788 } elseif ( function_exists( 'wp_fast_cache_bulk_delete_all' ) ) {
616 789 wp_fast_cache_bulk_delete_all();
790 + } elseif ( function_exists( 'rapidcache_clear_cache' ) ) {
791 + rapidcache_clear_cache();
792 + } elseif ( class_exists( 'Swift_Performance_Cache' ) ) {
793 + Swift_Performance_Cache::clear_all_cache();
617 794 } elseif ( class_exists( 'WpFastestCache' ) ) {
618 795 $wpfc = new WpFastestCache();
619 796 $wpfc->deleteCache();
620 797 } elseif ( class_exists( 'c_ws_plugin__qcache_purging_routines' ) ) {
@@ -643,8 +820,22 @@
643 820 }
644 821 }
645 822 } elseif ( function_exists( 'sg_cachepress_purge_cache' ) ) {
646 823 sg_cachepress_purge_cache();
824 + } elseif ( array_key_exists( 'KINSTA_CACHE_ZONE', $_SERVER ) ) {
825 + $_kinsta_clear_cache_url = 'https://localhost/kinsta-clear-cache-all';
826 + $_kinsta_response = wp_remote_get(
827 + $_kinsta_clear_cache_url,
828 + array(
829 + 'sslverify' => false,
830 + 'timeout' => 5,
831 + )
832 + );
833 + } elseif ( class_exists( 'RaidboxesNginxCacheFunctions' ) ) {
834 + $rb_cache_helper = new RaidboxesNginxCacheFunctions();
835 + $rb_cache_helper->purge_cache();
836 + } elseif ( defined('NGINX_HELPER_BASENAME') ) {
837 + do_action( 'rt_nginx_helper_purge_all' );
647 838 } elseif ( file_exists( WP_CONTENT_DIR . '/wp-cache-config.php' ) && function_exists( 'prune_super_cache' ) ) {
648 839 // fallback for WP-Super-Cache
649 840 global $cache_path;
650 841 if ( is_multisite() ) {
@@ -654,8 +845,11 @@
654 845 } else {
655 846 prune_super_cache( $cache_path . 'supercache/', true );
656 847 prune_super_cache( $cache_path, true );
657 848 }
849 + } elseif ( class_exists( 'NginxCache' ) ) {
850 + $nginx_cache = new NginxCache();
851 + $nginx_cache->purge_zone_once();
658 852 }
659 853 }
660 854 // @codingStandardsIgnoreEnd
661 855 }