| @@ -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 | } |