swte

Untitled

Mar 14th, 2019
959
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 107.44 KB | None | 0 0
  1. <?php
  2. class Swift_Performance_Cache {
  3.  
  4.       // Current content
  5.       public $buffer;
  6.  
  7.       // Current cache path (relative to cache folder)
  8.       public $path;
  9.  
  10.       // Is dynamic or static page
  11.       public $is_dynamic_cache = false;
  12.  
  13.       // No cache mode
  14.       public $no_cache = false;
  15.  
  16.       // Run time disable caching
  17.       public $disabled_cache = false;
  18.  
  19.       public function __construct(){
  20.             do_action('swift_performance_cache_before_init');
  21.  
  22.             $device = (Swift_Performance_Lite::check_option('mobile-support', 1) && Swift_Performance_Lite::is_mobile() ? 'mobile' : 'desktop');
  23.  
  24.             // Force cache to check js errors
  25.             if (isset($_GET['force-cache'])){
  26.                   $_COOKIE = array();
  27.                   Swift_Performance_Lite::set_option('optimize-prebuild-only',0);
  28.                   Swift_Performance_Lite::set_option('merge-background-only',0);
  29.  
  30.                   // Check timeout and disable time consuming features if timeout is not extendable
  31.                   $timeout = get_option('swift_performance_timeout');
  32.                   if (empty($timeout)){
  33.                         Swift_Performance_Lite::update_option('critical-css', 0);
  34.                         Swift_Performance_Lite::update_option('merge-styles-exclude-3rd-party', 1);
  35.                         Swift_Performance_Lite::update_option('merge-scripts-exlude-3rd-party', 1);
  36.                   }
  37.                   else {
  38.                         Swift_Performance_Lite::set_option('critical-css',0);
  39.                   }
  40.  
  41.                   add_filter('swift_performance_is_cacheable', '__return_true');
  42.                   add_action('wp_head', function(){
  43.                         echo '<script data-dont-merge>window.addEventListener("error", function(){if(parent){parent.postMessage("report-js-error", "*");}});</script>';
  44.                   },6);
  45.             }
  46.  
  47.             // Logged in path
  48.             if (!isset($_GET['force-cached']) && isset($_COOKIE[LOGGED_IN_COOKIE]) && Swift_Performance_Lite::check_option('shared-logged-in-cache', '1', '!=')){
  49.                   @list($user_login,,,) = explode('|', $_COOKIE[LOGGED_IN_COOKIE]);
  50.                   $hash = md5($user_login . NONCE_SALT);
  51.                   $this->path = trailingslashit(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) . '/' . $device . '/authenticated/' . $hash . '/');
  52.             }
  53.             // Not logged in path
  54.             else {
  55.                   $this->path = trailingslashit(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) . '/' . $device . '/unauthenticated/');
  56.             }
  57.  
  58.             // Disable caching if shared logged in cache is set
  59.             add_action('init', function(){
  60.                   if (is_user_logged_in() && Swift_Performance_Lite::check_option('shared-logged-in-cache', '1') && !is_admin()){
  61.                         define('SWIFT_PERFORMANCE_DISABLE_CACHE', true);
  62.                   }
  63.             }, PHP_INT_MAX);
  64.  
  65.             // Clear cache on post save
  66.             add_action('save_post', array('Swift_Performance_Cache', 'clear_post_cache'));
  67.             add_action('delete_post', array('Swift_Performance_Cache', 'clear_post_cache'));
  68.             add_action('wp_trash_post', array('Swift_Performance_Cache', 'clear_post_cache'));
  69.             add_action('pre_post_update', array('Swift_Performance_Cache', 'clear_post_cache'));
  70.             add_action('delete_attachment', array('Swift_Performance_Cache', 'clear_post_cache'));
  71.             add_action('woocommerce_product_object_updated_props', array('Swift_Performance_Cache', 'clear_post_cache'));
  72.             add_action('woocommerce_product_set_stock', array('Swift_Performance_Cache', 'clear_post_cache'));
  73.             add_action('woocommerce_variation_set_stock', array('Swift_Performance_Cache', 'clear_post_cache'));
  74.             add_action('fl_builder_after_save_layout', array('Swift_Performance_Cache', 'clear_post_cache'));
  75.  
  76.             // Clear pagecache after publish/update post
  77.             add_action('save_post', array('Swift_Performance_Cache', 'clear_cache_after_post'));
  78.             add_action('delete_post', array('Swift_Performance_Cache', 'clear_cache_after_post'));
  79.             add_action('wp_trash_post', array('Swift_Performance_Cache', 'clear_cache_after_post'));
  80.             add_action('delete_attachment', array('Swift_Performance_Cache', 'clear_cache_after_post'));
  81.             add_action('woocommerce_product_object_updated_props', array('Swift_Performance_Cache', 'clear_cache_after_post'));
  82.             add_action('woocommerce_product_set_stock', array('Swift_Performance_Cache', 'clear_cache_after_post'));
  83.             add_action('woocommerce_variation_set_stock', array('Swift_Performance_Cache', 'clear_cache_after_post'));
  84.             add_action('fl_builder_after_save_layout', array('Swift_Performance_Cache', 'clear_cache_after_post'));
  85.  
  86.             // Elementor AJAX update
  87.             add_action('elementor/ajax/register_actions', function(){
  88.                   Swift_Performance_Cache::clear_post_cache($_REQUEST['editor_post_id']);
  89.             });
  90.  
  91.             // Scheduled posts
  92.             add_action('transition_post_status', function($new, $old = '', $post = NULL){
  93.                   if (!empty($post)){
  94.                         Swift_Performance_Cache::clear_post_cache($post->ID);
  95.                         Swift_Performance_Cache::clear_cache_after_post();
  96.                   }
  97.             });
  98.  
  99.             // Scheduled sales
  100.             add_action('wc_after_products_starting_sales', array('Swift_Performance_Cache', 'clear_post_cache_array'));
  101.             add_action('wc_after_products_ending_sales', array('Swift_Performance_Cache', 'clear_post_cache_array'));
  102.  
  103.             // Clear all cache actions
  104.             foreach (array('after_switch_theme','customize_save_after','update_option_permalink_structure','update_option_tag_base','update_option_category_base','wp_update_nav_menu', 'update_option_sidebars_widgets') as $action){
  105.                   add_action($action, array('Swift_Performance_Cache', 'clear_all_cache'));
  106.             }
  107.  
  108.             // Clear cache on plugin/update actions
  109.             add_action('activated_plugin', function($plugin){
  110.                   if ($plugin !== basename(SWIFT_PERFORMANCE_DIR) . '/performance.php'){
  111.                         $action     = esc_html__('A plugin has been activated. Page cache should be cleared.', 'swift-performance');
  112.                         ob_start();
  113.                         include SWIFT_PERFORMANCE_DIR . 'templates/clear-cache-notice.php';
  114.                         Swift_Performance_Lite::add_notice(ob_get_clean(), 'warning', 'plugin/update-action');
  115.                   }
  116.             });
  117.             add_action('deactivated_plugin', function($plugin){
  118.                   if ($plugin !== basename(SWIFT_PERFORMANCE_DIR) . '/performance.php'){
  119.                         $action     = esc_html__('A plugin has been activated. Page cache should be cleared.', 'swift-performance');
  120.                         ob_start();
  121.                         include SWIFT_PERFORMANCE_DIR . 'templates/clear-cache-notice.php';
  122.                         Swift_Performance_Lite::add_notice(ob_get_clean(), 'warning', 'plugin/update-action');
  123.                   }
  124.             });
  125.             add_action('upgrader_process_complete', function(){
  126.                   $action     = esc_html__('Updater process has been finished. Page cache probably should be cleared.', 'swift-performance');
  127.                   ob_start();
  128.                   include SWIFT_PERFORMANCE_DIR . 'templates/clear-cache-notice.php';
  129.                   Swift_Performance_Lite::add_notice(ob_get_clean(), 'warning', 'plugin/update-action');
  130.             });
  131.  
  132.             // Clear cache after comment approved
  133.             add_action('wp_set_comment_status', function($comment_id, $status){
  134.                   if (in_array($status, array('approve', 'hold'))){
  135.                        $comment = get_comment( $comment_id );
  136.                        Swift_Performance_Cache::clear_post_cache($comment->comment_post_ID);
  137.                   }
  138.             }, 10, 2);
  139.  
  140.             // Clear intelligent cache based on request (Legacy)
  141.             if (Swift_Performance_Lite::check_option('cache-expiry-mode', 'intelligent') && Swift_Performance_Lite::check_option('resource-saving-mode', 1)){
  142.                   add_action('shutdown', array($this, 'clear_intelligent_cache'));
  143.             }
  144.  
  145.             // Clear user cache on delete user
  146.             add_action( 'delete_user', array('Swift_Performance_Cache', 'clear_user_cache'));
  147.  
  148.             // Bypass Avatar
  149.             if (Swift_Performance_Lite::check_option('gravatar-cache', 1)){
  150.                   add_filter('get_avatar_url', array($this, 'bypass_gravatar'));
  151.             }
  152.  
  153.             // Cache WooCommerce empty minicart
  154.             if (Swift_Performance_Lite::check_option('cache-empty-minicart', 1) && isset($_GET['wc-ajax']) && $_GET['wc-ajax'] == 'get_refreshed_fragments' && (!isset($_COOKIE['woocommerce_cart_hash']) || empty($_COOKIE['woocommerce_cart_hash'])) && (!isset($_COOKIE['woocommerce_items_in_cart']) || empty($_COOKIE['woocommerce_items_in_cart']))){
  155.                   Swift_Performance_Lite::set_option('optimize-prebuild-only', 0);
  156.                   Swift_Performance_Lite::set_option('merge-background-only', 0);
  157.                   add_filter('swift_performance_is_cacheable_dynamic', '__return_true');
  158.  
  159.                   $timestamp = gmdate("D, d M Y H:i:s") . " GMT";
  160.                   header("Expires: $timestamp");
  161.                   header("Last-Modified: $timestamp");
  162.                   header("Pragma: no-cache");
  163.                   header("Cache-Control: no-cache, must-revalidate");
  164.             }
  165.  
  166.             // Clear post cache on user action
  167.             add_action('init', array('Swift_Performance_Cache', 'clear_on_user_action'));
  168.  
  169.             // Force no cache mode
  170.             if (isset($_GET['swift-no-cache'])){
  171.                   $this->no_cache = true;
  172.                   unset($_GET['swift-no-cache']);
  173.                   unset($_REQUEST['swift-no-cache']);
  174.                   $_SERVER['REQUEST_URI'] = preg_replace('~(&|\?)swift-no-cache=(\d+)~','',$_SERVER['REQUEST_URI']);
  175.             }
  176.  
  177.  
  178.             // Load cache
  179.             if (!$this->no_cache){
  180.                   $this->load_cache();
  181.             }
  182.  
  183.             // Set cache
  184.             if (self::is_cacheable() || self::is_cacheable_dynamic()){
  185.                   $this->is_dynamic_cache = (self::is_cacheable_dynamic() ? true : false);
  186.  
  187.                   ob_start(array($this, 'build_cache'));
  188.                   add_action('shutdown', array($this, 'set_cache'), apply_filters('swift_performance_set_cache_hook_priority',PHP_INT_MAX));
  189.             }
  190.             else if (self::is_cacheable_ajax()){
  191.                 ob_start(array($this, 'build_cache'));
  192.                 add_action('shutdown', array($this, 'set_ajax_cache'), apply_filters('swift_performance_set_cache_hook_priority',PHP_INT_MAX));
  193.             }
  194.  
  195.             // Print intelligent cache js in head
  196.             if (Swift_Performance_Lite::check_option('cache-expiry-mode', 'intelligent') && (self::is_cacheable() || self::is_cacheable_dynamic())){
  197.                   add_action('wp_head', array('Swift_Performance_Cache', 'intelligent_cache_xhr'), PHP_INT_MAX);
  198.             }
  199.  
  200.             do_action('swift_performance_cache_init');
  201.       }
  202.  
  203.       /**
  204.        * Catch the content
  205.        */
  206.       public function build_cache($buffer){
  207.             $this->buffer = $buffer;
  208.  
  209.             if (Swift_Performance_Lite::check_option('cache-expiry-mode', 'intelligent') && $this->no_cache){
  210.                   $this->check_integrity(self::avoid_mixed_content($this->buffer));
  211.             }
  212.  
  213.             return $buffer;
  214.       }
  215.  
  216.  
  217.       /**
  218.        * Save current page to cache
  219.        */
  220.       public function set_cache(){
  221.             global $wpdb;
  222.  
  223.             // Return on CLI
  224.             if (defined('WP_CLI') && WP_CLI){
  225.                   return;
  226.             }
  227.  
  228.             // Don't write cache if there isn't free thread for optimizing assets
  229.             if ($this->disabled_cache || (defined('SWIFT_PERFORMANCE_DISABLE_CACHE') && SWIFT_PERFORMANCE_DISABLE_CACHE)){
  230.                   return;
  231.             }
  232.  
  233.             // We have wp_query, so we re-check is it cacheable
  234.             if (!self::is_cacheable() && !self::is_cacheable_dynamic()){
  235.                   return;
  236.             }
  237.  
  238.             // Don't write cache for common request if background assets merging enabled
  239.             if (Swift_Performance_Lite::check_option('merge-background-only', 1) && !isset($_SERVER['HTTP_X_MERGE_ASSETS'])){
  240.                   return;
  241.             }
  242.  
  243.             // Remove background loading from cache
  244.             if (Swift_Performance_Lite::check_option('merge-background-only', 1) && Swift_Performance_Lite::check_option('cache-expiry-mode', array('timebased', 'actionbased'), 'IN') ){
  245.                   $this->buffer = str_replace("<script data-dont-merge>var xhr = new XMLHttpRequest();xhr.open('GET', document.location.href);xhr.setRequestHeader('X-merge-assets', 'true');xhr.send(null);</script>", '', $this->buffer);
  246.             }
  247.  
  248.             $this->buffer = self::avoid_mixed_content($this->buffer);
  249.  
  250.             // Fix output buffer conflict
  251.             if (strpos($this->buffer, '<!--SWIFT_PERFORMACE_OB_CONFLICT-->') !== false){
  252.                   $this->buffer = $GLOBALS['swift_performance']->modules['asset-manager']->asset_manager_callback($this->buffer);
  253.             }
  254.  
  255.             // Appcache
  256.             $device = Swift_Performance_Lite::is_mobile() ? '-mobile' : '-desktop';
  257.             if (Swift_Performance_Lite::check_option('appcache'.$device, 1)){
  258.                   $this->buffer = preg_replace('~<html~', '<html manifest="'.Swift_Performance_Lite::home_url().SWIFT_PERFORMANCE_SLUG.'.appcache"', $this->buffer);
  259.             }
  260.  
  261.  
  262.             // Set charset
  263.             if (!Swift_Performance_Lite::is_amp($this->buffer) && apply_filters('swift_performance_character_encoding_meta', true)){
  264.                   // Remove charset meta if exists
  265.                   $this->buffer = preg_replace('~<meta charset([^>]+)>~', '', $this->buffer);
  266.  
  267.                   // Append charset to the top
  268.                   $this->buffer = preg_replace('~<head([^>]*)?>~',"<head$1>\n<meta charset=\"".get_bloginfo('charset')."\">", $this->buffer, 1);
  269.             }
  270.  
  271.             $this->buffer = str_replace('</body>',"<!--Cached with ".SWIFT_PERFORMANCE_PLUGIN_NAME."-->\n</body>", $this->buffer);
  272.  
  273.             $this->buffer = apply_filters('swift_performance_buffer', $this->buffer);
  274.  
  275.             $content_type = '';
  276.             foreach (headers_list() as $header) {
  277.                   if (preg_match('~Content-type:\s?([a-z]*)/([a-z]*)~i', $header, $matches)){
  278.                         $content_type = $matches[1].'/'.$matches[2];
  279.                   }
  280.             }
  281.  
  282.             // Disk cache
  283.             if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN')){
  284.                   // Set cached file basename
  285.                   if (Swift_Performance_Lite::is_404()){
  286.                         $basename = '404.html';
  287.                         $type = '404';
  288.                   }
  289.                   else if ($content_type == 'text/xml' || Swift_Performance_Lite::is_feed()){
  290.                         $basename = 'index.xml';
  291.                         $type = 'xml';
  292.                   }
  293.                   else if ($content_type == 'application/json' || (defined('REST_REQUEST') && REST_REQUEST)){
  294.                         $basename = 'index.json';
  295.                         $type = 'json';
  296.                   }
  297.                   else {
  298.                         $basename = 'index.html';
  299.                         $type = 'html';
  300.                   }
  301.  
  302.                   // Dynamic cache
  303.                   if ($this->is_dynamic_cache){
  304.                         set_transient('swift_performance_dynamic_' . Swift_Performance_Lite::get_unique_id(), array('time' => time(), 'content' => $this->buffer), 3600);
  305.                         Swift_Performance_Lite::log('Dynamic cache (db) ' . $_SERVER['REQUEST_URI'] . serialize($_REQUEST), 9);
  306.                   }
  307.                   // General cache
  308.                   else {
  309.                         self::write_file($this->path . $basename, $this->buffer, true);
  310.                         if (Swift_Performance_Lite::check_option('enable-gzip', 1) && function_exists('gzencode')){
  311.                               self::write_file($this->path . $basename . '.gz', gzencode($this->buffer), true);
  312.                         }
  313.  
  314.                         // Update warmup table
  315.                         $id = Swift_Performance_Lite::get_warmup_id($_SERVER['HTTP_HOST'] . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
  316.                         $priority = Swift_Performance_Lite::get_default_warmup_priority();
  317.                         $url = (is_ssl() ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
  318.                         $timestamp = time();
  319.  
  320.                         Swift_Performance_Lite::mysql_query("INSERT IGNORE INTO " . SWIFT_PERFORMANCE_TABLE_PREFIX . "warmup (id, url, timestamp, type, priority) VALUES ('".Swift_Performance_Lite::get_warmup_id($url)."', '".esc_url($url)."', '".$timestamp."', '".$type."', ".(int)$priority.") ON DUPLICATE KEY UPDATE timestamp = '{$timestamp}', type = '{$type}'");
  321.  
  322.  
  323.                         Swift_Performance_Lite::log('General cache (file) ' . $this->path . $basename, 9);
  324.                   }
  325.             }
  326.             // Memcached
  327.             else if(Swift_Performance_Lite::check_option('caching-mode', 'memcached_php')){
  328.                   // Set cached file basename
  329.                   if (Swift_Performance_Lite::is_404()){
  330.                         $basename = '404.html';
  331.                         $type = '404';
  332.                   }
  333.                   else if ($content_type == 'text/xml' || Swift_Performance_Lite::is_feed()){
  334.                         $basename = 'index.xml';
  335.                         $type = 'xml';
  336.                   }
  337.                   else if ($content_type == 'application/json' || (defined('REST_REQUEST') && REST_REQUEST)){
  338.                         $basename = 'index.json';
  339.                         $type = 'json';
  340.                   }
  341.                   else {
  342.                         $basename = 'index.html';
  343.                         $type = 'html';
  344.                   }
  345.  
  346.                   // Dynamic cache
  347.                   if ($this->is_dynamic_cache){
  348.                         Swift_Performance_Cache::memcached_set(Swift_Performance_Lite::get_unique_id(), array('time' => time(), 'content' => $this->buffer));
  349.                         Swift_Performance_Lite::log('Dynamic cache (memcached) ' . $_SERVER['REQUEST_URI'] . serialize($_REQUEST), 9);
  350.                   }
  351.                   // General cache
  352.                   else {
  353.                         Swift_Performance_Cache::memcached_set($this->path . $basename, array('time' => time(), 'content' => $this->buffer));
  354.                         if (Swift_Performance_Lite::check_option('enable-gzip', 1) && function_exists('gzencode')){
  355.                               Swift_Performance_Cache::memcached_set($this->path . $basename . '.gz', array('time' => time(), 'content' => gzencode($this->buffer)));
  356.                         }
  357.  
  358.                         // Update warmup table
  359.                         $id = Swift_Performance_Lite::get_warmup_id($_SERVER['HTTP_HOST'] . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
  360.                         $priority = Swift_Performance_Lite::get_default_warmup_priority();
  361.                         $url = (is_ssl() ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
  362.                         $timestamp = time();
  363.  
  364.                         Swift_Performance_Lite::mysql_query("INSERT IGNORE INTO " . SWIFT_PERFORMANCE_TABLE_PREFIX . "warmup (id, url, timestamp, type, priority) VALUES ('".Swift_Performance_Lite::get_warmup_id($url)."', '".esc_url($url)."', '".$timestamp."', '".$type."', ".(int)$priority.") ON DUPLICATE KEY UPDATE timestamp = '{$timestamp}', type = '{$type}'");
  365.  
  366.  
  367.  
  368.                         Swift_Performance_Lite::log('General cache (memcached) ' . $this->path . $basename, 9);
  369.                   }
  370.             }
  371.       }
  372.  
  373.       /**
  374.        * Set AJAX object transient
  375.        */
  376.       public function set_ajax_cache(){
  377.             // Set AJAX object expiry
  378.             $expiry = Swift_Performance_Lite::get_option('ajax-cache-expiry-time');
  379.  
  380.             // Store AJAX object in db if we are using disk cache
  381.             if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN')){
  382.                   set_transient('swift_performance_ajax_' . Swift_Performance_Lite::get_unique_id(), array('time' => time(), 'content' => $this->buffer), $expiry);
  383.                   Swift_Performance_Lite::log('Ajax cache (db) ' . $_SERVER['REQUEST_URI'] . serialize($_REQUEST), 9);
  384.             }
  385.             // Memcached
  386.             else {
  387.                   Swift_Performance_Cache::memcached_set(Swift_Performance_Lite::get_unique_id(), array('time' => time(), 'content' => $this->buffer));
  388.                   Swift_Performance_Lite::log('Ajax cache (memcached) ' . $_SERVER['REQUEST_URI'] . serialize($_REQUEST), 9);
  389.             }
  390.  
  391.         return $this->buffer;
  392.       }
  393.  
  394.  
  395.  
  396.       /**
  397.        * Load cached file and stop if file exists
  398.        */
  399.       public function load_cache(){
  400.             // Return on CLI
  401.             if (defined('WP_CLI') && WP_CLI){
  402.                   return;
  403.             }
  404.  
  405.             // Set default content type
  406.             $content_type  = 'text/html';
  407.  
  408.             // Add Swift Performance header
  409.             if (!Swift_Performance_Lite::is_admin()){
  410.                   header(SWIFT_PERFORMANCE_SLUG . ': MISS');
  411.             }
  412.  
  413.             // Serve cached AJAX requests
  414.             if (self::is_cacheable_dynamic() || self::is_cacheable_ajax()){
  415.                   if (Swift_Performance_Lite::check_option('caching-mode', 'memcached_php')){
  416.                         $cached_request = Swift_Performance_Cache::memcached_get(Swift_Performance_Lite::get_unique_id());
  417.                   }
  418.                   else {
  419.                         if (self::is_cacheable_dynamic()){
  420.                               $cached_request = get_transient('swift_performance_dynamic_' . Swift_Performance_Lite::get_unique_id());
  421.                         }
  422.                         else {
  423.                               $cached_request = get_transient('swift_performance_ajax_' . Swift_Performance_Lite::get_unique_id());
  424.                         }
  425.                   }
  426.  
  427.                   if ($cached_request !== false){
  428.                         header(SWIFT_PERFORMANCE_SLUG . ': HIT');
  429.  
  430.                         if (isset($cached_request['basename'])){
  431.                               switch ($cached_request['basename']) {
  432.                                     case 'index.xml':
  433.                                           $content_type = 'text/xml';
  434.                                           break;
  435.                                     case 'index.json':
  436.                                           $content_type = 'text/json';
  437.                                           break;
  438.                               }
  439.                         }
  440.  
  441.                         $content                = $cached_request['content'];
  442.                         $last_modified_time     = $cached_request['time'];
  443.                         $etag                   = md5($content);
  444.  
  445.                         // Send headers
  446.                         if (Swift_Performance_Lite::check_option('304-header', 1) && (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified_time || @trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag)){
  447.                               header("HTTP/1.1 304 Not Modified");
  448.                         }
  449.  
  450.                         header("Last-Modified: ".gmdate("D, d M Y H:i:s", $last_modified_time)." GMT");
  451.                         header("Etag: $etag");
  452.                         header("Content-type: $content_type");
  453.                         echo $content;
  454.  
  455.                         die;
  456.                   }
  457.             }
  458.  
  459.             // Skip if requested page isn't cacheable
  460.             if (!self::is_cacheable()){
  461.                   return;
  462.             }
  463.  
  464.             // Disk cache
  465.             $is_disk_cache    = (strpos(Swift_Performance_Lite::get_option('caching-mode'), 'disk_cache') !== false);
  466.             $is_cached        = false;
  467.             $is_404           = false;
  468.             if (file_exists(SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.html')){
  469.                   $path       = SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.html';
  470.                   $is_cached  = true;
  471.             }
  472.             else if (file_exists(SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.xml')){
  473.                   $path       = SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.xml';
  474.                   $is_cached  = true;
  475.                   $content_type  = 'text/xml';
  476.             }
  477.             else if (file_exists(SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.json')){
  478.                   $path       = SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.json';
  479.                   $is_cached  = true;
  480.                   $content_type  = 'application/json';
  481.             }
  482.             else if (file_exists(SWIFT_PERFORMANCE_CACHE_DIR . $this->path . '404.html')){
  483.                   $path       = SWIFT_PERFORMANCE_CACHE_DIR . $this->path . '404.html';
  484.                   $is_cached  = true;
  485.                   $is_404     = true;
  486.             }
  487.             if ($is_disk_cache && $is_cached && filesize($path) > 0){
  488.                   header(SWIFT_PERFORMANCE_SLUG . ': HIT');
  489.  
  490.                   $last_modified_time = filemtime($path);
  491.                   $etag = md5_file($path);
  492.  
  493.                   if (!$is_404 && Swift_Performance_Lite::check_option('304-header', 1) && (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified_time || @trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag)){
  494.                         status_header(304);
  495.                   }
  496.                   else if ($is_404){
  497.                         status_header(404);
  498.                   }
  499.  
  500.                   header("Last-Modified: ".gmdate("D, d M Y H:i:s", $last_modified_time)." GMT");
  501.                   header("Etag: $etag");
  502.                   header("Content-type: $content_type");
  503.  
  504.                   if ( file_exists($path . '.gz') && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' ) !== false ) {
  505.                         header('Content-Encoding: gzip');
  506.                         readfile($path . '.gz');
  507.                   }
  508.                   else{
  509.                         readfile($path);
  510.                   }
  511.                   exit;
  512.             }
  513.             // Memcache
  514.             else if (Swift_Performance_Lite::check_option('caching-mode', 'memcached_php')){
  515.                   if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' ) !== false ) {
  516.                         $cached_request = Swift_Performance_Cache::memcached_get($this->path . 'index.html.gz');
  517.                         if (empty($cached_request)) {
  518.                               $cached_request = Swift_Performance_Cache::memcached_get($this->path . 'index.xml.gz');
  519.                               $content_type  = 'text/xml';
  520.                         }
  521.                         if (empty($cached_request)) {
  522.                               $cached_request = Swift_Performance_Cache::memcached_get($this->path . 'index.json.gz');
  523.                               $content_type  = 'application/json';
  524.                         }
  525.                         if (empty($cached_request)) {
  526.                               $cached_request = Swift_Performance_Cache::memcached_get($this->path . '404.html.gz');
  527.                         }
  528.                   }
  529.                   else {
  530.                         $cached_request = Swift_Performance_Cache::memcached_get($this->path . 'index.html');
  531.                         if (empty($cached_request)) {
  532.                               $cached_request = Swift_Performance_Cache::memcached_get($this->path . 'index.xml');
  533.                               $content_type  = 'text/xml';
  534.                         }
  535.                         if (empty($cached_request)) {
  536.                               $cached_request = Swift_Performance_Cache::memcached_get($this->path . 'index.json');
  537.                               $content_type  = 'application/json';
  538.                         }
  539.                         if (empty($cached_request)) {
  540.                               $cached_request = Swift_Performance_Cache::memcached_get($this->path . '404.html');
  541.                         }
  542.                   }
  543.  
  544.                   if ($cached_request !== false){
  545.                         header(SWIFT_PERFORMANCE_SLUG . ': HIT');
  546.  
  547.                         $content = $cached_request['content'];
  548.                         if (!empty($content)) {
  549.                               $last_modified_time     = $cached_request['time'];
  550.                               $etag                   = md5($content);
  551.  
  552.                               // Send headers
  553.                               if (Swift_Performance_Lite::check_option('304-header', 1) && (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified_time || @trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag)){
  554.                                     header("HTTP/1.1 304 Not Modified");
  555.                               }
  556.  
  557.                               header("Last-Modified: ".gmdate("D, d M Y H:i:s", $last_modified_time)." GMT");
  558.                               header("Etag: $etag");
  559.                               header("Content-type: $content_type");
  560.                               header("Content-Encoding: none");
  561.                               header('Connection: close');
  562.  
  563.                               if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' ) !== false ) {
  564.                                     header('Content-Encoding: gzip');
  565.                               }
  566.  
  567.                               echo $content;
  568.  
  569.                               exit;
  570.                         }
  571.                   }
  572.             }
  573.       }
  574.  
  575.       /**
  576.        * Check is dynamic content and cache identical, return dynamic content if not.
  577.        * @return string
  578.        */
  579.       public function check_integrity($buffer){
  580.             // Cache is identical
  581.             if (self::is_identical()){
  582.                   header('X-Cache-Status: identical');
  583.                   die;
  584.             }
  585.  
  586.             $buffer = str_replace('</body>', "<!--Cached with ".SWIFT_PERFORMANCE_PLUGIN_NAME."-->\n</body>", $buffer);
  587.  
  588.             $checksum = false;
  589.             // Disk cache
  590.             if(strpos(Swift_Performance_Lite::get_option('caching-mode'), 'disk_cache') !== false){
  591.                   if (self::is_cacheable()){
  592.                         if (!file_exists(SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.html')){
  593.                               header('X-Cache-Status: miss');
  594.                               return;
  595.                         }
  596.  
  597.                         $clean_cached = self::clean_buffer(file_get_contents(SWIFT_PERFORMANCE_CACHE_DIR . $this->path . 'index.html'));
  598.                         $clean_buffer = self::clean_buffer($buffer);
  599.  
  600.                         $checksum = (md5($clean_buffer) != md5($clean_cached));
  601.                   }
  602.                   else if (self::is_cacheable_dynamic()){
  603.                         $cached     = get_transient('swift_performance_dynamic_' . Swift_Performance_Lite::get_unique_id());
  604.                         $clean_cached = preg_replace_callback('~([0-9abcdef]{10})~',array('Swift_Performance_Cache', 'clean_nonce_buffer'), $cached['content']);
  605.                         $clean_buffer = preg_replace_callback('~([0-9abcdef]{10})~',array('Swift_Performance_Cache', 'clean_nonce_buffer'), $buffer);
  606.                         $checksum = (md5($clean_buffer) != md5($clean_cached));
  607.                   }
  608.  
  609.                   if ($checksum){
  610.                         header("Last-Modified: ".gmdate("D, d M Y H:i:s", time())." GMT");
  611.                         header("Etag: " . md5($buffer));
  612.                         header('X-Cache-Status: changed');
  613.                         return;
  614.                   }
  615.             }
  616.             // Memcached
  617.             else {
  618.                   if (self::is_cacheable()){
  619.                         $cached_request = Swift_Performance_Cache::memcached_get($this->path . 'index.html');
  620.                         if (empty($cached_request)){
  621.                               header('X-Cache-Status: miss');
  622.                               return;
  623.                         }
  624.  
  625.                         $clean_cached = self::clean_buffer($cached_request['content']);
  626.                         $clean_buffer = self::clean_buffer($buffer);
  627.  
  628.                         $checksum = (md5($clean_buffer) != md5($clean_cached));
  629.                   }
  630.                   else if (self::is_cacheable_dynamic()){
  631.                         $cached     = Swift_Performance_Cache::memcached_get(Swift_Performance_Lite::get_unique_id());
  632.                         $clean_cached = preg_replace_callback('~([0-9abcdef]{10})~',array('Swift_Performance_Cache', 'clean_nonce_buffer'), $cached['content']);
  633.                         $clean_buffer = preg_replace_callback('~([0-9abcdef]{10})~',array('Swift_Performance_Cache', 'clean_nonce_buffer'), $buffer);
  634.                         $checksum = (md5($clean_buffer) != md5($clean_cached));
  635.                   }
  636.  
  637.                   if ($checksum){
  638.                         header("Last-Modified: ".gmdate("D, d M Y H:i:s", time())." GMT");
  639.                         header("Etag: " . md5($buffer));
  640.                         header('X-Cache-Status: changed');
  641.                         return;
  642.                   }
  643.             }
  644.  
  645.             header('X-Cache-Status: not-modified');
  646.  
  647.       }
  648.  
  649.       /**
  650.        * Clear intelligent cache if request is POST or query string isn't empty
  651.        */
  652.       public function clear_intelligent_cache(){
  653.             // Exceptions
  654.  
  655.             // Swift check cache
  656.             if ($this->no_cache){
  657.                   return;
  658.             }
  659.  
  660.             // Don't clear on AJAX request
  661.             if (defined('DOING_AJAX')){
  662.                   return;
  663.             }
  664.  
  665.             // Don't clear cache on login/register
  666.             if (@in_array( $GLOBALS['pagenow'], array( 'wp-login.php', 'wp-register.php' ))){
  667.                   return;
  668.             }
  669.  
  670.             // Remove empty elements
  671.             $_GET = array_filter($_GET);
  672.             $_POST = array_filter($_POST);
  673.  
  674.             // Clear transients if necessary
  675.             if (!empty($_POST) || !empty($_GET)){
  676.                   self::clear_transients('identical');
  677.             }
  678.             Swift_Performance_Lite::log('Clear Intelligent Cache', 9);
  679.       }
  680.  
  681.       /**
  682.        * Bypass Gravatar
  683.        * @param string url
  684.        * @return string
  685.        */
  686.       public function bypass_gravatar($url){
  687.             // Bypass gravatar only
  688.             if (!preg_match('~gravatar.com$~', parse_url($url, PHP_URL_HOST))){
  689.                   return $url;
  690.             }
  691.  
  692.             // Serve gravatar if it is exists and not expired
  693.             $filename = apply_filters('swift_gravatar_cache_filename', md5($url));
  694.             $gravatar = SWIFT_PERFORMANCE_CACHE_DIR . 'garvatar-cache/' . $filename;
  695.  
  696.             if (file_exists($gravatar) && filemtime($gravatar) + (int)Swift_Performance_Lite::get_option('gravatar-cache-expiry') > time()){
  697.                   return SWIFT_PERFORMANCE_CACHE_URL . 'garvatar-cache/' . $filename;
  698.             }
  699.  
  700.             // We are here, so gravatar is not exists, or expired. Check is the gravatar-cache folder exists at all
  701.             if (!file_exists(SWIFT_PERFORMANCE_CACHE_DIR . 'garvatar-cache')){
  702.                   // No it isn't exists, so we try to create it
  703.                   if (!is_writable(SWIFT_PERFORMANCE_CACHE_DIR . 'garvatar-cache')){
  704.                         @mkdir(SWIFT_PERFORMANCE_CACHE_DIR . 'garvatar-cache', 0777, true);
  705.                   }
  706.                   else {
  707.                         // No luck, no cache
  708.                         return $url;
  709.                   }
  710.             }
  711.  
  712.             // Gravatar isn't exists, or expired, and gravatar-cache folder is exists
  713.             if (!file_exists($gravatar) || is_writable($gravatar)){
  714.                   // Download the image
  715.                   $response = wp_safe_remote_get( $url, array( 'timeout' => 300, 'stream' => true, 'filename' => $gravatar ) );
  716.  
  717.                   // Something went wrong :-/
  718.                   if (is_wp_error($response)) {
  719.                         @unlink($gravatar);
  720.                         Swift_Performance_Lite::log('Gravatar bypass failed: ' . $response->get_error_message(), 1);
  721.                         return $url;
  722.                   }
  723.                   else {
  724.                         list($width, $height) = getimagesize($gravatar);
  725.                         if (empty($width)){
  726.                               @file_put_contents($gravatar, base64_decode('/9j/4AAQSkZJRgABAQAAAQABAAD//gA7Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcgSlBFRyB2NjIpLCBxdWFsaXR5ID0gOTAK/9sAQwADAgIDAgIDAwMDBAMDBAUIBQUEBAUKBwcGCAwKDAwLCgsLDQ4SEA0OEQ4LCxAWEBETFBUVFQwPFxgWFBgSFBUU/9sAQwEDBAQFBAUJBQUJFA0LDRQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU/8AAEQgAIAAgAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/aAAwDAQACEQMRAD8A+uK0tH8O6lr7sthaPcbfvMMBR9ScCs2voDwXaQWfhbTFgACvAsjEd2YZY/maAPEdY8OaloDKL+0eAN91shlP0IyKza+gfGVpBeeF9TS4AKLA8gJ7MoyD+Yr5+oAfFE88qRxoXkchVVRkknoBXufgPQ9R0HRlt7+4WQH5kgAz5Oeo3d/881wnwj0lL3XJ7yRQwtIxsB7M2QD+QavYKAOa8d6JqOvaM1vYXCx/xPCRgy45A3dv6+teGSxPBK8ciFJEJVlYYII6g19MV4/8XNJSy1yC8jUKLuM7wO7LgE/kVoA//9k='));
  727.                         }
  728.                         // Yay!
  729.                         return SWIFT_PERFORMANCE_CACHE_URL . 'garvatar-cache/' . apply_filters('swift_gravatar_cache_filename', md5($url));
  730.                   }
  731.             }
  732.  
  733.             // No cache
  734.             return $url;
  735.       }
  736.  
  737.       /**
  738.        * Clean buffer to be able to compare integrity for logged in users
  739.        * @param string buffer
  740.        * @return string
  741.        */
  742.       public static function clean_buffer($buffer){
  743.             $buffer = preg_replace('~swift-no-cache(=|%3D)(\d*)~', '', $buffer);
  744.             $buffer = preg_replace_callback('~([0-9abcdef]{10})~',array('Swift_Performance_Cache', 'clean_nonce_buffer'), $buffer);
  745.             return $buffer;
  746.       }
  747.  
  748.       /**
  749.        * Remove valid nonces from the cache/buffer to be able to compare integrity for logged in users
  750.        * @param array $matches
  751.        * @return string
  752.        */
  753.       public static function clean_nonce_buffer($matches){
  754.             if (wp_verify_nonce($matches[0])){
  755.                   return '';
  756.             }
  757.             return $matches[0];
  758.       }
  759.  
  760.       /**
  761.        * Print cache check XHR request in head
  762.        */
  763.       public static function intelligent_cache_xhr(){
  764.             echo "\n<script data-dont-merge='1'>setTimeout(function(){ function fire(){ window.removeEventListener('touchstart',fire); window.removeEventListener('scroll',fire); document.removeEventListener('mousemove',fire); var request = new XMLHttpRequest(); request.open('GET', document.location.href + (document.location.href.match(/\?/) ? '&' : '?') + 'swift-no-cache=' + parseInt(Math.random() * 100000000), true); request.onreadystatechange = function() { if (request.readyState === 4 && request.getResponseHeader('X-Cache-Status') == 'changed' && '1' != '".Swift_Performance_Lite::get_option(' disable - instant - reload ')."') { document.open(); document.write(request.responseText.replace('<html', '<html data-swift-performance-refreshed')); document.close(); } }; if (document.querySelector('[data-swift-performance-refreshed]') == null) { request.send(); }; document.getElementsByTagName('html')[0].addEventListener('click', function() { request.abort() }, false); document.getElementsByTagName('html')[0].addEventListener('keypress', function() { request.abort() }, false); } window.addEventListener('load', function() { window.addEventListener('touchstart',fire); window.addEventListener('scroll',fire); document.addEventListener('mousemove',fire); }); },10); </script>";
  765.       }
  766.  
  767.       /**
  768.        * Check known user actions when we should clear post cache
  769.        */
  770.       public static function clear_on_user_action(){
  771.             // Comment
  772.             if (isset($_POST['comment_post_ID']) && !empty($_POST['comment_post_ID'])){
  773.                   self::clear_post_cache($_POST['comment_post_ID']);
  774.                   Swift_Performance_Lite::log('Clear post cache triggered (new comment) ', 9);
  775.             }
  776.  
  777.             // bbPress comment
  778.             else if (isset($_POST['bbp_reply_content']) && isset($_POST['bbp_topic_id']) && !empty($_POST['bbp_topic_id'])){
  779.                   self::clear_post_cache($_POST['bbp_topic_id']);
  780.                   Swift_Performance_Lite::log('Clear post cache triggered (new bbPress comment) ', 9);
  781.             }
  782.  
  783.             // Buddypress
  784.             else if (function_exists('bp_get_root_domain') && isset($_POST['action']) && $_POST['action'] == 'post_update'){
  785.                   $bb_permalink = '';
  786.                   if (isset($_POST['object']) && $_POST['object'] == 'group'){
  787.                         if (isset($_POST['item_id']) && !empty($_POST['item_id'])){
  788.                               $group = groups_get_group( array( 'group_id' => $_POST['item_id'] ) );
  789.                               $bb_permalink = trailingslashit( bp_get_root_domain() . '/' . bp_get_groups_root_slug() . '/' . $group->slug . '/' );
  790.                         }
  791.                   }
  792.                   else if ((!isset($_POST['object']) || empty($_POST['object'])) && (!isset($_POST['item_id']) || empty($_POST['item_id']))){
  793.                         $bb_permalink = str_replace(home_url(), '', bp_get_root_domain() .'/'. bp_get_root_slug());
  794.                   }
  795.  
  796.                   if (!empty($bb_permalink)){
  797.                         if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN')){
  798.                               // Disk cache
  799.                               self::recursive_rmdir($bb_permalink);
  800.                               Swift_Performance_Lite::log('Clear permalink cache (disk) triggered (buddypress activity) ', 9);
  801.                         }
  802.                         else {
  803.                               // Memcached
  804.                               $memcached = self::get_memcache_instance();
  805.                               $keys = $memcached->getAllKeys();
  806.                               foreach($keys as $item) {
  807.                                   if(preg_match('~^swift-performance' . str_replace(home_url(), '', $bb_permalink) .'~', $item)) {
  808.                                       $memcached->delete($item);
  809.                                   }
  810.                               }
  811.                               Swift_Performance_Lite::log('Clear permalink cache (memcached) triggered (buddypress activity) ', 9);
  812.                         }
  813.  
  814.                         // Update warmup table
  815.                         $id = Swift_Performance_Lite::get_warmup_id($bb_permalink);
  816.                         Swift_Performance_Lite::mysql_query("UPDATE " . SWIFT_PERFORMANCE_TABLE_PREFIX . "warmup SET timestamp = 0, type = '' WHERE id = '{$id}' LIMIT 1");
  817.  
  818.                         // Cloudflare
  819.                         if(Swift_Performance_Lite::check_option('cloudflare-auto-purge',1) && Swift_Performance_Lite::check_option('cloudflare-email', '', '!=') && Swift_Performance_Lite::check_option('cloudflare-api-key', '', '!=')){
  820.                               self::purge_cloudflare_zones($bb_permalink);
  821.                         }
  822.  
  823.                         // Varnish
  824.                         if(Swift_Performance_Lite::check_option('varnish-auto-purge',1)){
  825.                               self::purge_varnish_url($bb_permalink);
  826.                         }
  827.  
  828.                         // Prebuild cache
  829.                         if (Swift_Performance_Lite::check_option('automated_prebuild_cache',1)){
  830.                               wp_remote_get($bb_permalink);
  831.                               Swift_Performance_Lite::log('Prebuild cache for buddypress activity: ' . $bb_permalink, 9);
  832.                         }
  833.                   }
  834.             }
  835.       }
  836.  
  837.       /**
  838.        * Clear all cache
  839.        */
  840.       public static function clear_all_cache(){
  841.             do_action('swift_performance_before_clear_all_cache');
  842.  
  843.             // Delete prebuild booster
  844.             delete_option('swift_performance_prebuild_booster');
  845.  
  846.             // Clear cache
  847.             if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN')){
  848.                   // Disk cache
  849.                   self::recursive_rmdir();
  850.                   Swift_Performance_Lite::log('Clear all cache (disk)', 9);
  851.             }
  852.             else {
  853.                   // Memcached
  854.                   $memcached = self::get_memcache_instance();
  855.                   $memcached->flush();
  856.                   Swift_Performance_Lite::log('Clear all cache (memcached)', 9);
  857.             }
  858.  
  859.             // Cloudflare
  860.             if(Swift_Performance_Lite::check_option('cloudflare-auto-purge',1) && Swift_Performance_Lite::check_option('cloudflare-email', '', '!=') && Swift_Performance_Lite::check_option('cloudflare-api-key', '', '!=')){
  861.                   self::purge_cloudflare_zones();
  862.             }
  863.  
  864.             // Varnish
  865.             if(Swift_Performance_Lite::check_option('varnish-auto-purge',1)){
  866.                   self::purge_varnish_url(Swift_Performance_Lite::home_url(), true);
  867.             }
  868.  
  869.             Swift_Performance_Third_Party::clear_cache();
  870.  
  871.             // Prebuild cache
  872.             Swift_Performance_Lite::stop_prebuild();
  873.             if (Swift_Performance_Lite::check_option('automated_prebuild_cache',1)){
  874.                   wp_schedule_single_event(time(), 'swift_performance_prebuild_cache');
  875.                   Swift_Performance_Lite::log('Prebuild cache scheduled', 9);
  876.             }
  877.  
  878.             // Clear object cache
  879.             self::clear_transients();
  880.  
  881.             // MaxCDN
  882.             if (Swift_Performance_Lite::check_option('enable-cdn', 1) && Swift_Performance_Lite::check_option('maxcdn-alias', '','!=') && Swift_Performance_Lite::check_option('maxcdn-key', '','!=') && Swift_Performance_Lite::check_option('maxcdn-secret', '','!=')){
  883.                   Swift_Performance_CDN_Manager::purge_cdn();
  884.             }
  885.  
  886.             // Update warmup table
  887.             Swift_Performance_Lite::mysql_query("UPDATE " . SWIFT_PERFORMANCE_TABLE_PREFIX . "warmup SET timestamp = 0, type = ''");
  888.  
  889.             do_action('swift_performance_after_clear_all_cache');
  890.       }
  891.  
  892.       /**
  893.        * Clear expired cache
  894.        */
  895.       public static function clear_expired(){
  896.             global $wpdb;
  897.             do_action('swift_performance_before_clear_expired_cache');
  898.  
  899.             $timestamp  = time() - Swift_Performance_Lite::get_option('cache-expiry-time');
  900.             $expired    = $wpdb->get_col("SELECT url FROM " . SWIFT_PERFORMANCE_TABLE_PREFIX . "warmup WHERE timestamp <= '{$timestamp}'");
  901.             foreach ($expired as $permalink) {
  902.                   self::clear_permalink_cache($permalink);
  903.             }
  904.  
  905.             do_action('swift_performance_after_clear_expired_cache');
  906.       }
  907.  
  908.       /**
  909.        * Clear cache based on permalink
  910.        * @param string $permalink
  911.        */
  912.       public static function clear_permalink_cache($permalink){
  913.             do_action('swift_performance_before_clear_permalink_cache', $permalink);
  914.  
  915.             // Try to get the page/post id
  916.             $maybe_id = url_to_postid($permalink);
  917.             if (!empty($maybe_id)){
  918.                   self::clear_post_cache($maybe_id);
  919.                   return;
  920.             }
  921.  
  922.             // Cloudflare
  923.             if(Swift_Performance_Lite::check_option('cloudflare-auto-purge',1) && Swift_Performance_Lite::check_option('cloudflare-email', '', '!=') && Swift_Performance_Lite::check_option('cloudflare-api-key', '', '!=')){
  924.                   self::purge_cloudflare_zones($permalink);
  925.             }
  926.  
  927.             // Clear permalink
  928.             self::clear_single_cached_item($permalink);
  929.  
  930.             // Prebuild cache
  931.             if (Swift_Performance_Lite::check_option('automated_prebuild_cache',1)){
  932.                   wp_schedule_single_event(time(), 'swift_performance_prebuild_page_cache', array($permalink));
  933.             }
  934.  
  935.             do_action('swift_performance_after_clear_permalink_cache', $permalink);
  936.       }
  937.  
  938.       /**
  939.       * Delete cache multiple posts
  940.       * @param array $post_ids
  941.       */
  942.       public static function clear_post_cache_array($post_ids){
  943.             foreach ((array)$post_ids as $post_id) {
  944.                   clear_post_cache($post_id);
  945.             }
  946.       }
  947.  
  948.       /**
  949.        * Delete cached post on save
  950.        * @param int $post_id
  951.        */
  952.       public static function clear_post_cache($post_id){
  953.             do_action('swift_performance_before_clear_post_cache', $post_id);
  954.  
  955.             // Don't clear cache on autosave
  956.             if (defined('DOING_AUTOSAVE')){
  957.                   return;
  958.             }
  959.  
  960.             // WooCommerce product variation
  961.             if (get_post_type($post_id) == 'product_variation'){
  962.                   $parent_id  = apply_filters('swift_peformance_get_product_variation_parent', wp_get_post_parent_id($post_id), $post_id);
  963.                   $post_id    = (!empty($parent_id) ? $parent_id : $post_id);
  964.                   $maybe_comments_feed = false;
  965.             }
  966.  
  967.             $permalink = get_permalink($post_id);
  968.  
  969.             if (!Swift_Performance_Cache::is_object_cacheable($permalink, $post_id)){
  970.                   return;
  971.             }
  972.  
  973.             $permalinks = array();
  974.  
  975.             // Permalink
  976.             $permalinks[] = $permalink;
  977.  
  978.             // Comments feed
  979.             if (Swift_Performance_Lite::check_option('cache-feed',1)){
  980.                   @$permalinks[] = get_post_comments_feed_link($post_id);
  981.             }
  982.  
  983.             // AMP
  984.             if (function_exists('amp_get_permalink')) {
  985.                   $amp_url = amp_get_permalink($post_id);
  986.                   if (!empty($amp_url)){
  987.                         $permalinks[] = $amp_url;
  988.                   }
  989.         }
  990.  
  991.             // REST API
  992.             if (Swift_Performance_Lite::check_option('cache-rest',1)){
  993.                   $rest_url = self::get_rest_url($post_id);
  994.                   if (!empty($rest_url)){
  995.                         $permalinks[] = $rest_url;
  996.                   }
  997.             }
  998.  
  999.             // Archive URLs
  1000.             if (Swift_Performance_Lite::check_option('cache-archive',1)){
  1001.                   @$archive_urls = self::get_archive_urls($post_id);
  1002.                   if (!empty($archive_urls)){
  1003.                         $permalinks = array_merge($permalinks, $archive_urls);
  1004.                   }
  1005.             }
  1006.  
  1007.             // Author
  1008.             if (Swift_Performance_Lite::check_option('cache-author',1)){
  1009.                   @$author_urls = self::get_author_urls($post_id);
  1010.                   if (!empty($author_urls)){
  1011.                         $permalinks = array_merge($permalinks, $author_urls);
  1012.                   }
  1013.             }
  1014.  
  1015.             // Feeds
  1016.             if (Swift_Performance_Lite::check_option('cache-feed',1)){
  1017.                   $permalinks[] = get_bloginfo_rss('rdf_url');
  1018.                   $permalinks[] = get_bloginfo_rss('rss_url');
  1019.                   $permalinks[] = get_bloginfo_rss('rss2_url');
  1020.                   $permalinks[] = get_bloginfo_rss('atom_url');
  1021.                   $permalinks[] = get_bloginfo_rss('comments_rss2_url');
  1022.             }
  1023.  
  1024.  
  1025.             // Cloudflare
  1026.             if(Swift_Performance_Lite::check_option('cloudflare-auto-purge',1) && Swift_Performance_Lite::check_option('cloudflare-email', '', '!=') && Swift_Performance_Lite::check_option('cloudflare-api-key', '', '!=')){
  1027.                   self::purge_cloudflare_zones($permalinks);
  1028.             }
  1029.  
  1030.             // WP Engine
  1031.             if (method_exists('WpeCommon', 'purge_varnish_cache')){
  1032.                   WpeCommon::purge_varnish_cache($post_id);
  1033.             }
  1034.  
  1035.             // Clear cached permalinks
  1036.             foreach ($permalinks as $permalink) {
  1037.                   self::clear_single_cached_item($permalink);
  1038.             }
  1039.  
  1040.             // Prebuild cache
  1041.             if (Swift_Performance_Lite::check_option('automated_prebuild_cache',1)){
  1042.                   wp_schedule_single_event(time(), 'swift_performance_prebuild_page_cache', array($permalinks));
  1043.             }
  1044.  
  1045.             do_action('swift_performance_after_clear_post_cache', $post_id);
  1046.       }
  1047.  
  1048.       /**
  1049.        * Clear pagecache after publish/update post
  1050.        */
  1051.       public static function clear_cache_after_post(){
  1052.             $pages = Swift_Performance_Lite::get_option('clear-page-cache-after-post');
  1053.             if (!empty($pages)){
  1054.                   foreach ((array)$pages as $page){
  1055.                         self::clear_post_cache($page);
  1056.                   }
  1057.             }
  1058.       }
  1059.  
  1060.       /**
  1061.        * Clear single item from cache
  1062.        * @param string $permalink
  1063.        * @param int $post_id
  1064.        */
  1065.       public static function clear_single_cached_item($permalink, $post_id = 0){
  1066.             do_action('swift_performance_before_clear_single_cached_item', $permalink, $post_id);
  1067.             global $wp_rewrite, $wpdb;
  1068.  
  1069.             if (empty($permalink)){
  1070.                   return;
  1071.             }
  1072.  
  1073.             if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN')){
  1074.                   $base_path = trailingslashit(parse_url($permalink, PHP_URL_PATH));
  1075.                   $css_dir = $js_dir = '';
  1076.                   $desktop_cache_path     = $base_path . 'desktop/';
  1077.                   $mobile_cache_path      = $base_path . 'mobile/';
  1078.                   $paginate_cache_path    = $base_path . trailingslashit($wp_rewrite->pagination_base);
  1079.  
  1080.                   if (Swift_Performance_Lite::check_option('separate-css', 1)){
  1081.                         $css_dir = apply_filters('swift_performance_css_dir', trailingslashit(trim(parse_url($permalink, PHP_URL_PATH),'/')) . 'css');
  1082.                   }
  1083.                   if (Swift_Performance_Lite::check_option('separate-js', 1)){
  1084.                         $js_dir = apply_filters('swift_performance_css_dir', trailingslashit(trim(parse_url($permalink, PHP_URL_PATH),'/')) . 'js');
  1085.                   }
  1086.  
  1087.                   self::recursive_rmdir($desktop_cache_path);
  1088.                   self::recursive_rmdir($mobile_cache_path);
  1089.                   self::recursive_rmdir($paginate_cache_path);
  1090.  
  1091.                   if (!empty($css_dir)){
  1092.                         self::recursive_rmdir($css_dir);
  1093.                   }
  1094.  
  1095.                   if (!empty($js_dir)){
  1096.                         self::recursive_rmdir($js_dir);
  1097.                   }
  1098.  
  1099.                   Swift_Performance_Lite::log('Clear post cache (disk) ID: ' . $post_id . ', permalink: ' . $permalink, 9);
  1100.             }
  1101.             else {
  1102.                   // Memcached
  1103.                   $path = trailingslashit(parse_url($permalink, PHP_URL_PATH) . '/(desktop|mobile|'.preg_quote($wp_rewrite->pagination_base).')/');
  1104.                   $memcached = self::get_memcache_instance();
  1105.                   $keys = $memcached->getAllKeys();
  1106.                   foreach($keys as $item) {
  1107.                       if(preg_match('~^swift-performance_' . $path .'~', $item)) {
  1108.                           $memcached->delete($item);
  1109.                           Swift_Performance_Lite::log('Clear post cache (memcached) ID: ' . $post_id . ', permalink: ' . $permalink, 9);
  1110.                       }
  1111.                   }
  1112.             }
  1113.  
  1114.             // Varnish
  1115.             if(Swift_Performance_Lite::check_option('varnish-auto-purge',1)){
  1116.                   self::purge_varnish_url($permalink);
  1117.             }
  1118.  
  1119.             // Dynamic cache
  1120.             $url_path = hash('crc32', parse_url($permalink, PHP_URL_PATH));
  1121.             $transients = $wpdb->get_col("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '_transient_swift_performance_dynamic_{$url_path}_%'");
  1122.             foreach ($transients as $transient) {
  1123.                   delete_transient(str_replace('_transient_','',$transient));
  1124.             }
  1125.  
  1126.             // Update Warmup Table
  1127.             $id = Swift_Performance_Lite::get_warmup_id($permalink);
  1128.             Swift_Performance_Lite::mysql_query("UPDATE " . SWIFT_PERFORMANCE_TABLE_PREFIX . "warmup SET timestamp = 0, type = '' WHERE id = '{$id}'");
  1129.  
  1130.             do_action('swift_performance_after_clear_single_cached_item', $permalink, $post_id);
  1131.       }
  1132.  
  1133.       /**
  1134.        * Clear user cache
  1135.        * @param int $user_id
  1136.        */
  1137.       public static function clear_user_cache($user_id){
  1138.             $user = get_userdata($user_id);
  1139.             $hash = md5($user->user_login . NONCE_SALT);
  1140.  
  1141.             do_action('swift_performance_before_clear_user_cache', $user_id);
  1142.             if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN') && file_exists(SWIFT_PERFORMANCE_CACHE_DIR)){
  1143.  
  1144.                   $Directory = new RecursiveDirectoryIterator(SWIFT_PERFORMANCE_CACHE_DIR);
  1145.                   $Iterator = new RecursiveIteratorIterator($Directory);
  1146.                   $Regex = new RegexIterator($Iterator, '#'.$hash.'#i', RecursiveRegexIterator::GET_MATCH);
  1147.  
  1148.                   foreach ($Regex as $filename => $file){
  1149.                         $filename = rtrim($filename, '.');
  1150.                         if (file_exists($filename) && is_dir($filename)){
  1151.                               self::recursive_rmdir(str_replace(SWIFT_PERFORMANCE_CACHE_DIR, '', $filename));
  1152.                         }
  1153.                   }
  1154.  
  1155.                   Swift_Performance_Lite::log('Clear user cache (disk) ID: ' . $user_id, 9);
  1156.             }
  1157.             else {
  1158.                   // Memcached
  1159.                   $memcached = self::get_memcache_instance();
  1160.                   $keys = $memcached->getAllKeys();
  1161.                   foreach($keys as $item) {
  1162.                       if(preg_match('~'.$user->user_login.'~', $item)) {
  1163.                           $memcached->delete($item);
  1164.                           Swift_Performance_Lite::log('Clear user cache (memcached) ID: ' . $user_id, 9);
  1165.                       }
  1166.                   }
  1167.             }
  1168.  
  1169.             do_action('swift_performance_after_clear_user_cache', $user_id);
  1170.       }
  1171.  
  1172.       /**
  1173.        * Clear object cache transients
  1174.        * @param $type string
  1175.        */
  1176.       public static function clear_transients($type = 'all'){
  1177.             do_action('swift_performance_before_clear_transients', $type);
  1178.             global $wpdb;
  1179.             switch ($type) {
  1180.                   case 'ajax':
  1181.                         $wpdb->query('DELETE FROM ' . $wpdb->options . ' WHERE option_name LIKE "%swift_performance_ajax_%"');
  1182.                         Swift_Performance_Lite::log('Clear all transients', 9);
  1183.                         break;
  1184.                   case 'dynamic':
  1185.                         $wpdb->query('DELETE FROM ' . $wpdb->options . ' WHERE option_name LIKE "%swift_performance_dynamic_%"');
  1186.                         Swift_Performance_Lite::log('Clear all transients', 9);
  1187.                         break;
  1188.                   case 'identical':
  1189.                         $wpdb->query('DELETE FROM ' . $wpdb->options . ' WHERE option_name LIKE "%swift_performance_is_identical_%"');
  1190.                         Swift_Performance_Lite::log('Clear all transients', 9);
  1191.                         break;
  1192.                   case 'all':
  1193.                   default:
  1194.                         $wpdb->query('DELETE FROM ' . $wpdb->options . ' WHERE option_name LIKE "%swift_performance_is_identical_%" OR option_name LIKE "%swift_performance_ajax_%" OR option_name LIKE "%swift_performance_dynamic_%"');
  1195.                         Swift_Performance_Lite::log('Clear all transients', 9);
  1196.                         break;
  1197.             }
  1198.             do_action('swift_performance_after_clear_transients', $type);
  1199.       }
  1200.  
  1201.       /**
  1202.        * Is current page cacheable
  1203.        * @return boolean
  1204.        */
  1205.       public static function is_cacheable(){
  1206.             $ajax_cache       = !defined('DOING_AJAX');
  1207.             $xmlrpc           = !defined('XMLRPC_REQUEST');
  1208.             $logged_in        = (!Swift_Performance_Lite::is_user_logged_in() || Swift_Performance_Lite::check_option('enable-caching-logged-in-users', 1));
  1209.             $is_404           = (Swift_Performance_Lite::check_option('cache-404',1) || !Swift_Performance_Lite::is_404());
  1210.             $query_string     = (isset($_SERVER['HTTP_X_PREBUILD']) || Swift_Performance_Lite::check_option('ignore-query-string', 1) || empty($_GET));
  1211.             $post             = (isset($_SERVER['HTTP_X_PREBUILD']) || empty($_POST));
  1212.             $archive          = (Swift_Performance_Lite::check_option('exclude-archive',1) && Swift_Performance_Lite::is_archive() ? false : true);
  1213.             $author           = (Swift_Performance_Lite::check_option('exclude-author',1) && Swift_Performance_Lite::is_author() ? false : true);
  1214.             $rest             = (Swift_Performance_Lite::check_option('exclude-rest',1) && Swift_Performance_Lite::is_rest() ? false : true);
  1215.             $feed             = (Swift_Performance_Lite::check_option('exclude-feed',1) && Swift_Performance_Lite::is_feed() ? false : true);
  1216.             $maintenance      = !self::is_maintenance();
  1217.             return apply_filters('swift_performance_is_cacheable', !self::is_exluded() && $archive && $author && $rest && $feed && $is_404 && !Swift_Performance_Lite::is_admin() && $post && $query_string && $logged_in && $ajax_cache && $xmlrpc && $maintenance && !self::is_crawler() && !Swift_Performance_Lite::is_password_protected());
  1218.       }
  1219.  
  1220.       /**
  1221.        * Is current AJAX request cacheable
  1222.        * @return boolean
  1223.        */
  1224.       public static function is_cacheable_dynamic(){
  1225.             // Is there any dynamic parameter?
  1226.             if (empty($_GET) && empty($_POST)){
  1227.                   return apply_filters('swift_performance_is_cacheable_dynamic', false);
  1228.             }
  1229.  
  1230.             if (Swift_Performance_Lite::check_option('dynamic-caching', 1)){
  1231.                   $cacheable = false;
  1232.                   foreach (array_filter((array)Swift_Performance_Lite::get_option('cacheable-dynamic-requests')) as $key){
  1233.                         if (isset($_REQUEST[$key])){
  1234.                               $cacheable = true;
  1235.                               break;
  1236.                         }
  1237.                   }
  1238.                   $xmlrpc     = !defined('XMLRPC_REQUEST');
  1239.                   $logged_in  = (!Swift_Performance_Lite::is_user_logged_in() || Swift_Performance_Lite::check_option('enable-caching-logged-in-users', 1));
  1240.                   $is_404     = (Swift_Performance_Lite::check_option('cache-404',1) || !Swift_Performance_Lite::is_404());
  1241.                   $archive          = (Swift_Performance_Lite::check_option('exclude-archive',1) && Swift_Performance_Lite::is_archive() ? false : true);
  1242.                   $author           = (Swift_Performance_Lite::check_option('exclude-author',1) && Swift_Performance_Lite::is_author() ? false : true);
  1243.                   $rest             = (Swift_Performance_Lite::check_option('exclude-rest',1) && Swift_Performance_Lite::is_rest() ? false : true);
  1244.                   $feed             = (Swift_Performance_Lite::check_option('exclude-feed',1) && Swift_Performance_Lite::is_feed() ? false : true);
  1245.                   $maintenance      = !self::is_maintenance();
  1246.                   return apply_filters('swift_performance_is_cacheable_dynamic', !self::is_exluded() && $is_404 && $cacheable && $logged_in && $archive && $author && $rest && $feed && $maintenance && !Swift_Performance_Lite::is_admin() && !self::is_crawler() && !Swift_Performance_Lite::is_password_protected());
  1247.             }
  1248.             return apply_filters('swift_performance_is_cacheable_dynamic', false);
  1249.       }
  1250.  
  1251.       /**
  1252.        * Is current AJAX request cacheable
  1253.        * @return boolean
  1254.        */
  1255.       public static function is_cacheable_ajax(){
  1256.             return apply_filters('swift_performance_is_cacheable_ajax', ((!Swift_Performance_Lite::is_user_logged_in() || Swift_Performance_Lite::check_option('enable-caching-logged-in-users', 1)) && defined('DOING_AJAX') && DOING_AJAX && isset($_REQUEST['action']) && in_array($_REQUEST['action'], array_filter((array)Swift_Performance_Lite::get_option('cacheable-ajax-actions')))));
  1257.       }
  1258.  
  1259.       /**
  1260.       * Is given URL and/or id for prebuild
  1261.       * @param string $url
  1262.       * @param int $id
  1263.       * @return boolean
  1264.       */
  1265.       public static function is_object_cacheable($url, $id = 0){
  1266.             global $wpdb;
  1267.  
  1268.             $cacheable = true;
  1269.             $excluded = false;
  1270.  
  1271.             // Check dynamic parameters
  1272.             $query_string = parse_url($url, PHP_URL_QUERY);
  1273.             parse_str($query_string, $query);
  1274.             if (!empty($query)){
  1275.                   if (Swift_Performance_Lite::check_option('dynamic-caching', 1)){
  1276.                         $cacheable_dynamic = false;
  1277.                         foreach (array_filter((array)Swift_Performance_Lite::get_option('cacheable-dynamic-requests')) as $key){
  1278.                               if (isset($query[$key])){
  1279.                                     $cacheable_dynamic = true;
  1280.                                     break;
  1281.                               }
  1282.                         }
  1283.                         if (!$cacheable_dynamic){
  1284.                               return false;
  1285.                         }
  1286.                   }
  1287.                   else {
  1288.                         return false;
  1289.                   }
  1290.             }
  1291.  
  1292.             // Excluded strings
  1293.             foreach (array_filter((array)Swift_Performance_Lite::get_option('exclude-strings')) as $exclude_string){
  1294.                   if (!empty($exclude_string)){
  1295.                         if (substr($exclude_string,0,1) == '#' && substr($exclude_string,strlen($exclude_string)-1) == '#'){
  1296.                               if (preg_match($exclude_string, parse_url($url, PHP_URL_PATH))){
  1297.                                     return false;
  1298.                               }
  1299.                         }
  1300.                         else {
  1301.                               if (strpos(parse_url($url, PHP_URL_PATH), $exclude_string) !== false){
  1302.                                     return false;
  1303.                               }
  1304.                         }
  1305.                   }
  1306.             }
  1307.  
  1308.             // Excluded pages
  1309.  
  1310.             // First get the id if we don't have it yet
  1311.             if (empty($id)){
  1312.                   $id = url_to_postid($url);
  1313.             }
  1314.  
  1315.             // If id is still empty is not a post, walk away
  1316.             if (empty($id)){
  1317.                   return true;
  1318.             }
  1319.  
  1320.             // Manually excluded pages
  1321.             if(in_array($id, (array)Swift_Performance_Lite::get_option('exclude-pages'))){
  1322.                   return false;
  1323.             }
  1324.  
  1325.             // Excluded post types
  1326.             if (in_array(get_post_type($id), (array)Swift_Performance_Lite::get_option('exclude-post-types'))){
  1327.                   return false;
  1328.             }
  1329.  
  1330.             // Password protected
  1331.             if (Swift_Performance_Lite::is_password_protected($id)){
  1332.                   return false;
  1333.             }
  1334.  
  1335.             // WooCommerce
  1336.             if(class_exists('woocommerce')){
  1337.                   $results = $wpdb->get_results("SELECT option_value FROM {$wpdb->options} WHERE option_name LIKE 'woocommerce_%_page_id' AND option_name NOT IN ('woocommerce_shop_page_id', 'woocommerce_terms_page_id')", ARRAY_A);
  1338.                   foreach((array)$results as $result){
  1339.                         if (!empty($result['option_value']) && $result['option_value'] == $id){
  1340.                               return false;
  1341.                         }
  1342.                   }
  1343.             }
  1344.  
  1345.             return true;
  1346.       }
  1347.  
  1348.       /**
  1349.        * Is current request excluded from cache
  1350.        * @return boolean
  1351.        */
  1352.       public static function is_exluded(){
  1353.             $excluded = false;
  1354.  
  1355.             // Excluded strings
  1356.             foreach (array_filter((array)Swift_Performance_Lite::get_option('exclude-strings')) as $exclude_string){
  1357.                   if (!empty($exclude_string)){
  1358.                         if(substr($exclude_string,0,1) == '#' && substr($exclude_string,strlen($exclude_string)-1) == '#'){
  1359.                               $excluded = preg_match($exclude_string, parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
  1360.                         }
  1361.                         else {
  1362.                               $excluded = strpos(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), $exclude_string) !== false;
  1363.                         }
  1364.                   }
  1365.             }
  1366.  
  1367.             // Excluded content parts
  1368.             if (!$excluded){
  1369.                   foreach (array_filter((array)Swift_Performance_Lite::get_option('exclude-content-parts')) as $exclude_content_part){
  1370.                         if (!empty($exclude_content_part)){
  1371.                               if(substr($exclude_content_part,0,1) == '#' && substr($exclude_content_part,strlen($exclude_content_part)-1) == '#'){
  1372.                                     $excluded = preg_match($exclude_content_part, $GLOBALS['swift_performance']->modules['cache']->buffer);
  1373.                               }
  1374.                               else {
  1375.                                     $excluded = strpos($GLOBALS['swift_performance']->modules['cache']->buffer, $exclude_content_part) !== false;
  1376.                               }
  1377.                         }
  1378.                   }
  1379.             }
  1380.  
  1381.             // Excluded user agents
  1382.             if (!$excluded){
  1383.                   foreach (array_filter((array)Swift_Performance_Lite::get_option('exclude-useragents')) as $exclude_ua){
  1384.                         if (!empty($exclude_ua)){
  1385.                               if(substr($exclude_ua,0,1) == '#' && substr($exclude_ua,strlen($exclude_ua)-1) == '#'){
  1386.                                     $excluded = preg_match($exclude_ua, $_SERVER['HTTP_USER_AGENT']);
  1387.                               }
  1388.                               else {
  1389.                                     $excluded = strpos($_SERVER['HTTP_USER_AGENT'], $exclude_ua) !== false;
  1390.                               }
  1391.                         }
  1392.                   }
  1393.             }
  1394.  
  1395.             // Excluded pages
  1396.             if (!$excluded){
  1397.                   // Manually excluded pages
  1398.                   $excluded = (get_the_ID() != 0 && in_array(get_the_ID(), (array)Swift_Performance_Lite::get_option('exclude-pages')) );
  1399.  
  1400.                   // WooCommerce
  1401.                   if(class_exists('woocommerce')){
  1402.                         global $wpdb;
  1403.                         $results = $wpdb->get_results("SELECT option_value FROM {$wpdb->options} WHERE option_name LIKE 'woocommerce_%_page_id' AND option_name NOT IN ('woocommerce_shop_page_id', 'woocommerce_terms_page_id')", ARRAY_A);
  1404.                         foreach((array)$results as $result){
  1405.                               if (!empty($result['option_value']) && $result['option_value'] == get_the_ID()){
  1406.                                     $excluded = true;
  1407.                               }
  1408.                         }
  1409.                   }
  1410.  
  1411.             }
  1412.  
  1413.             // Excluded post types
  1414.             if (!$excluded){
  1415.                   $post_type = get_post_type();
  1416.                   if (!empty($post_type) && in_array($post_type, (array)Swift_Performance_Lite::get_option('exclude-post-types'))){
  1417.                         $excluded = true;
  1418.                   }
  1419.             }
  1420.  
  1421.             return apply_filters('swift_performance_is_excluded', $excluded);
  1422.  
  1423.       }
  1424.  
  1425.       /**
  1426.        * Detect crawlers
  1427.        */
  1428.       public static function is_crawler(){
  1429.             $crawlers = array( '.*Java.*outbrain', '008\/', '192\.comAgent', '2ip\.ru', '404checker', '^bluefish ', '^Calypso v\/', '^COMODO DCV', '^DangDang', '^DavClnt', '^FDM ', '^git\/', '^Goose\/', '^HTTPClient\/', '^Java\/', '^Jeode\/', '^Jetty\/', '^Mget', '^Microsoft URL Control', '^NG\/[0-9\.]', '^NING\/', '^PHP\/[0-9]', '^RMA\/', '^Ruby|Ruby\/[0-9]', '^scrutiny\/', '^VSE\/[0-9]', '^WordPress\.com', '^XRL\/[0-9]', '^ZmEu', 'a3logics\.in', 'A6-Indexer', 'a\.pr-cy\.ru', 'Aboundex', 'aboutthedomain', 'Accoona-AI-Agent', 'acoon', 'acrylicapps\.com\/pulp', 'adbeat', 'AddThis', 'ADmantX', 'adressendeutschland', 'Advanced Email Extractor v', 'agentslug', 'AHC', 'aihit', 'aiohttp\/', 'Airmail', 'akula\/', 'alertra', 'alexa site audit', 'Alibaba\.Security\.Heimdall', 'alyze\.info', 'amagit', 'AndroidDownloadManager', 'Anemone', 'Ant\.com', 'Anturis Agent', 'AnyEvent-HTTP\/', 'Apache-HttpClient\/', 'AportWorm\/[0-9]', 'AppEngine-Google', 'Arachmo', 'arachnode', 'Arachnophilia', 'aria2', 'asafaweb.com', 'AskQuickly', 'Astute', 'asynchttp', 'autocite', 'Autonomy', 'B-l-i-t-z-B-O-T', 'Backlink-Ceck\.de', 'Bad-Neighborhood', 'baidu\.com', 'baypup\/[0-9]', 'baypup\/colbert', 'BazQux', 'BCKLINKS', 'BDFetch', 'BegunAdvertising\/', 'BigBozz', 'biglotron', 'BingLocalSearch', 'BingPreview', 'binlar', 'biNu image cacher', 'biz_Directory', 'Blackboard Safeassign', 'Bloglovin', 'BlogPulseLive', 'BlogSearch', 'Blogtrottr', 'boitho\.com-dc', 'BPImageWalker', 'Braintree-Webhooks', 'Branch Metrics API', 'Branch-Passthrough', 'Browsershots', 'BUbiNG', 'Butterfly\/', 'BuzzSumo', 'CAAM\/[0-9]', 'CakePHP', 'CapsuleChecker', 'CaretNail', 'catexplorador', 'cb crawl', 'CC Metadata Scaper', 'Cerberian Drtrs', 'CERT\.at-Statistics-Survey', 'cg-eye', 'changedetection', 'Charlotte', 'CheckHost', 'checkprivacy', 'chkme\.com', 'CirrusExplorer\/', 'CISPA Vulnerability Notification', 'CJNetworkQuality', 'clips\.ua\.ac\.be', 'Cloud mapping experiment', 'CloudFlare-AlwaysOnline', 'Cloudinary\/[0-9]', 'cmcm\.com', 'coccoc', 'CommaFeed', 'Commons-HttpClient', 'Comodo SSL Checker', 'contactbigdatafr', 'convera', 'copyright sheriff', 'Covario-IDS', 'CrawlForMe\/[0-9]', 'cron-job\.org', 'Crowsnest', 'curb', 'Curious George', 'curl', 'cuwhois\/[0-9]', 'cybo\.com', 'DareBoost', 'DataparkSearch', 'dataprovider', 'Daum(oa)?[ \/][0-9]', 'DeuSu', 'developers\.google\.com\/\+\/web\/snippet\/', 'Digg', 'Dispatch\/', 'dlvr', 'DMBrowser-UV', 'DNS-Tools Header-Analyzer', 'DNSPod-reporting', 'docoloc', 'Dolphin http client\/', 'DomainAppender', 'dotSemantic', 'downforeveryoneorjustme', 'downnotifier\.com', 'DowntimeDetector', 'Dragonfly File Reader', 'drupact', 'Drupal \(\+http:\/\/drupal\.org\/\)', 'dubaiindex', 'EARTHCOM', 'Easy-Thumb', 'ec2linkfinder', 'eCairn-Grabber', 'ECCP', 'ElectricMonk', 'elefent', 'EMail Exractor', 'EmailWolf', 'Embed PHP Library', 'Embedly', 'europarchive\.org', 'evc-batch\/[0-9]', 'EventMachine HttpClient', 'Evidon', 'Evrinid', 'ExactSearch', 'ExaleadCloudview', 'Excel\/', 'Exif Viewer', 'Exploratodo', 'ezooms', 'facebookexternalhit', 'facebookplatform', 'fairshare', 'Faraday v', 'Faveeo', 'Favicon downloader', 'FavOrg', 'Feed Wrangler', 'Feedbin', 'FeedBooster', 'FeedBucket', 'FeedBunch\/[0-9]', 'FeedBurner', 'FeedChecker', 'Feedly', 'Feedspot', 'Feedwind\/[0-9]', 'feeltiptop', 'Fetch API', 'Fetch\/[0-9]', 'Fever\/[0-9]', 'findlink', 'findthatfile', 'FlipboardBrowserProxy', 'FlipboardProxy', 'FlipboardRSS', 'fluffy', 'flynxapp', 'forensiq', 'FoundSeoTool\/[0-9]', 'free thumbnails', 'FreeWebMonitoring SiteChecker', 'Funnelback', 'g00g1e\.net', 'GAChecker', 'ganarvisitas\/[0-9]', 'geek-tools', 'Genderanalyzer', 'Genieo', 'GentleSource', 'GetLinkInfo', 'getprismatic\.com', 'GetURLInfo\/[0-9]', 'GigablastOpenSource', 'github\.com\/', 'Go [\d\.]* package http', 'Go-http-client', 'gofetch', 'GomezAgent', 'gooblog', 'Goodzer\/[0-9]', 'Google favicon', 'Google Keyword Suggestion', 'Google Keyword Tool', 'Google PP Default', 'Google Search Console', 'Google Web Preview', 'Google-Adwords', 'Google-Apps-Script', 'Google-Calendar-Importer', 'Google-HTTP-Java-Client', 'Google-Publisher-Plugin', 'Google-SearchByImage', 'Google-Site-Verification', 'Google-Structured-Data-Testing-Tool', 'Google-Youtube-Links', 'google_partner_monitoring', 'GoogleDocs', 'GoogleHC\/', 'GoogleProducer', 'GoScraper', 'GoSpotCheck', 'GoSquared-Status-Checker', 'gosquared-thumbnailer', 'GotSiteMonitor', 'grabify', 'Grammarly', 'grouphigh', 'grub-client', 'GTmetrix', 'gvfs\/', 'HAA(A)?RTLAND http client', 'Hatena', 'hawkReader', 'HEADMasterSEO', 'HeartRails_Capture', 'heritrix', 'hledejLevne\.cz\/[0-9]', 'Holmes', 'HootSuite Image proxy', 'Hootsuite-WebFeed\/[0-9]', 'HostTracker', 'ht:\/\/check', 'htdig', 'HTMLParser\/', 'HTTP-Header-Abfrage', 'http-kit', 'HTTP-Tiny', 'HTTP_Compression_Test', 'http_request2', 'http_requester', 'HttpComponents', 'httphr', 'HTTPMon', 'PEAR HTTPRequest', 'httpscheck', 'httpssites_power', 'httpunit', 'HttpUrlConnection', 'httrack', 'hosterstats', 'huaweisymantec', 'HubPages.*crawlingpolicy', 'HubSpot Connect', 'HubSpot Marketing Grader', 'HyperZbozi.cz Feeder', 'i2kconnect\/', 'ichiro', 'IdeelaborPlagiaat', 'IDG Twitter Links Resolver', 'IDwhois\/[0-9]', 'Iframely', 'igdeSpyder', 'IlTrovatore', 'ImageEngine\/', 'Imagga', 'InAGist', 'inbound\.li parser', 'InDesign%20CC', 'infegy', 'infohelfer', 'InfoWizards Reciprocal Link System PRO', 'Instapaper', 'inpwrd\.com', 'Integrity', 'integromedb', 'internet_archive', 'InternetSeer', 'internetVista monitor', 'IODC', 'IOI', 'iplabel', 'IPS\/[0-9]', 'ips-agent', 'IPWorks HTTP\/S Component', 'iqdb\/', 'Irokez', 'isitup\.org', 'iskanie', 'iZSearch', 'janforman', 'Jigsaw', 'Jobboerse', 'jobo', 'Jobrapido', 'JS-Kit', 'KeepRight OpenStreetMap Checker', 'KeyCDN Perf Test', 'Keywords Research', 'KickFire', 'KimonoLabs\/', 'Kml-Google', 'knows\.is', 'kouio', 'KrOWLer', 'kulturarw3', 'KumKie', 'L\.webis', 'Larbin', 'LayeredExtractor', 'LibVLC', 'libwww', 'Licorne Image Snapshot', 'Liferea\/', 'link checker', 'Link Valet', 'link_thumbnailer', 'LinkAlarm\/', 'linkCheck', 'linkdex', 'LinkExaminer', 'linkfluence', 'linkpeek', 'LinkTiger', 'LinkWalker', 'Lipperhey', 'livedoor ScreenShot', 'LoadImpactPageAnalyzer', 'LoadImpactRload', 'LongURL API', 'looksystems\.net', 'ltx71', 'lwp-trivial', 'lycos', 'LYT\.SR', 'mabontland', 'MagpieRSS', 'Mail.Ru', 'MailChimp\.com', 'Mandrill', 'MapperCmd', 'marketinggrader', 'Mediapartners-Google', 'MegaIndex\.ru', 'Melvil Rawi\/', 'MergeFlow-PageReader', 'Metaspinner', 'MetaURI', 'Microsearch', 'Microsoft-WebDAV-MiniRedir', 'Microsoft Data Access Internet Publishing Provider Protocol', 'Microsoft Office ', 'Microsoft Windows Network Diagnostics', 'Mindjet', 'Miniflux', 'mixdata dot com', 'mixed-content-scan', 'Mnogosearch', 'mogimogi', 'Mojolicious \(Perl\)', 'monitis', 'Monitority\/[0-9]', 'montastic', 'MonTools', 'Moreover', 'Morning Paper', 'mowser', 'Mrcgiguy', 'mShots', 'MVAClient', 'nagios', 'Najdi\.si\/', 'Needle\/', 'NETCRAFT', 'NetLyzer FastProbe', 'netresearch', 'NetShelter ContentScan', 'NetTrack', 'Netvibes', 'Neustar WPM', 'NeutrinoAPI', 'NewsBlur .*Finder', 'NewsGator', 'newsme', 'newspaper\/', 'NG-Search', 'nineconnections\.com', 'NLNZ_IAHarvester', 'Nmap Scripting Engine', 'node-superagent', 'node\.io', 'nominet\.org\.uk', 'Norton-Safeweb', 'Notifixious', 'notifyninja', 'nuhk', 'nutch', 'Nuzzel', 'nWormFeedFinder', 'Nymesis', 'Ocelli\/[0-9]', 'oegp', 'okhttp', 'Omea Reader', 'omgili', 'Online Domain Tools', 'OpenCalaisSemanticProxy', 'Openstat\/', 'OpenVAS', 'Optimizer', 'Orbiter', 'OrgProbe\/[0-9]', 'ow\.ly', 'ownCloud News', 'OxfordCloudService\/[0-9]', 'Page Analyzer', 'Page Valet', 'page2rss', 'page_verifier', 'PagePeeker', 'Pagespeed\/[0-9]', 'Panopta', 'panscient', 'parsijoo', 'PayPal IPN', 'Pcore-HTTP', 'Pearltrees', 'peerindex', 'Peew', 'PhantomJS\/', 'Photon\/', 'phpcrawl', 'phpservermon', 'Pi-Monster', 'ping\.blo\.gs\/', 'Pingdom', 'Pingoscope', 'PingSpot', 'pinterest\.com', 'Pizilla', 'Ploetz \+ Zeller', 'Plukkie', 'PocketParser', 'Pompos', 'Porkbun', 'Port Monitor', 'postano', 'PostPost', 'postrank', 'PowerPoint\/', 'Priceonomics Analysis Engine', 'PritTorrent\/[0-9]', 'Prlog', 'probethenet', 'Project 25499', 'Promotion_Tools_www.searchenginepromotionhelp.com', 'prospectb2b', 'Protopage', 'proximic', 'pshtt, https scanning', 'PTST ', 'PTST\/[0-9]+', 'Pulsepoint XT3 web scraper', 'Python-httplib2', 'python-requests', 'Python-urllib', 'Qirina Hurdler', 'QQDownload', 'Qseero', 'Qualidator.com SiteAnalyzer', 'Quora Link Preview', 'Qwantify', 'Radian6', 'RankSonicSiteAuditor', 'Readability', 'RealPlayer%20Downloader', 'RebelMouse', 'redback\/', 'Redirect Checker Tool', 'ReederForMac', 'request\.js', 'ResponseCodeTest\/[0-9]', 'RestSharp', 'RetrevoPageAnalyzer', 'Riddler', 'Rival IQ', 'Robosourcer', 'Robozilla\/[0-9]', 'ROI Hunter', 'RPT-HTTPClient', 'RSSOwl', 'safe-agent-scanner', 'SalesIntelligent', 'SauceNAO', 'SBIder', 'Scoop', 'scooter', 'ScoutJet', 'ScoutURLMonitor', 'Scrapy', 'ScreenShotService\/[0-9]', 'Scrubby', 'search\.thunderstone', 'SearchSight', 'Seeker', 'semanticdiscovery', 'semanticjuice', 'Semiocast HTTP client', 'SEO Browser', 'Seo Servis', 'seo-nastroj.cz', 'Seobility', 'SEOCentro', 'SeoCheck', 'SeopultContentAnalyzer', 'Server Density Service Monitoring', 'servernfo\.com', 'Seznam screenshot-generator', 'Shelob', 'Shoppimon Analyzer', 'ShoppimonAgent\/[0-9]', 'ShopWiki', 'ShortLinkTranslate', 'shrinktheweb', 'SilverReader', 'SimplePie', 'SimplyFast', 'Site-Shot\/', 'Site24x7', 'SiteBar', 'SiteCondor', 'siteexplorer\.info', 'SiteGuardian', 'Siteimprove\.com', 'Sitemap(s)? Generator', 'Siteshooter B0t', 'SiteTruth', 'sitexy\.com', 'SkypeUriPreview', 'slider\.com', 'slurp', 'SMRF URL Expander', 'SMUrlExpander', 'Snappy', 'SniffRSS', 'sniptracker', 'Snoopy', 'sogou web', 'SortSite', 'spaziodati', 'Specificfeeds', 'speedy', 'SPEng', 'Spinn3r', 'spray-can', 'Sprinklr ', 'spyonweb', 'Sqworm', 'SSL Labs', 'StackRambler', 'Statastico\/', 'StatusCake', 'Stratagems Kumo', 'Stroke.cz', 'StudioFACA', 'suchen', 'summify', 'Super Monitoring', 'Surphace Scout', 'SwiteScraper', 'Symfony2 BrowserKit', 'SynHttpClient-Built', 'Sysomos', 'T0PHackTeam', 'Tarantula\/', 'Taringa UGC', 'teoma', 'terrainformatica\.com', 'Test Certificate Info', 'Tetrahedron\/[0-9]', 'The Drop Reaper', 'The Expert HTML Source Viewer', 'theinternetrules', 'theoldreader\.com', 'Thumbshots', 'ThumbSniper', 'TinEye', 'Tiny Tiny RSS', 'topster', 'touche.com', 'Traackr.com', 'truwoGPS', 'tweetedtimes\.com', 'Tweetminster', 'Tweezler\/', 'Twikle', 'Twingly', 'ubermetrics-technologies', 'uclassify', 'UdmSearch', 'Untiny', 'UnwindFetchor', 'updated', 'Upflow', 'URLChecker', 'URLitor.com', 'urlresolver', 'Urlstat', 'UrlTrends Ranking Updater', 'Vagabondo', 'vBSEO', 'via ggpht\.com GoogleImageProxy', 'VidibleScraper\/', 'visionutils', 'vkShare', 'voltron', 'voyager\/', 'VSAgent\/[0-9]', 'VSB-TUO\/[0-9]', 'VYU2', 'w3af\.org', 'W3C-checklink', 'W3C-mobileOK', 'W3C_I18n-Checker', 'W3C_Unicorn', 'wangling', 'WatchMouse', 'WbSrch\/', 'web-capture\.net', 'Web-Monitoring', 'Web-sniffer', 'Webauskunft', 'WebCapture', 'WebClient\/', 'webcollage', 'WebCookies', 'WebCorp', 'WebDoc', 'WebFetch', 'WebImages', 'WebIndex', 'webkit2png', 'webmastercoffee', 'webmon ', 'webscreenie', 'Webshot', 'Website Analyzer\/', 'websitepulse[+ ]checker', 'Websnapr\/', 'Webthumb\/[0-9]', 'WebThumbnail', 'WeCrawlForThePeace', 'WeLikeLinks', 'WEPA', 'WeSEE', 'wf84', 'wget', 'WhatsApp', 'WhatsMyIP', 'WhatWeb', 'WhereGoes\?', 'Whibse', 'Whynder Magnet', 'Windows-RSS-Platform', 'WinHttpRequest', 'wkhtmlto', 'wmtips', 'Woko', 'Word\/', 'WordPress\/', 'wotbox', 'WP Engine Install Performance API', 'wprecon\.com survey', 'WPScan', 'wscheck', 'WWW-Mechanize', 'www\.monitor\.us', 'XaxisSemanticsClassifier', 'Xenu Link Sleuth', 'XING-contenttabreceiver\/[0-9]', 'XmlSitemapGenerator', 'xpymep([0-9]?)\.exe', 'Y!J-(ASR|BSC)', 'Yaanb', 'yacy', 'Yahoo Ad monitoring', 'Yahoo Link Preview', 'YahooCacheSystem', 'YahooYSMcm', 'YandeG', 'yandex', 'yanga', 'yeti', ' YLT', 'Yo-yo', 'Yoleo Consumer', 'yoogliFetchAgent', 'YottaaMonitor', 'yourls\.org', 'Zao', 'Zemanta Aggregator', 'Zend\\\\Http\\\\Client', 'Zend_Http_Client', 'zgrab', 'ZnajdzFoto', 'ZyBorg', '[a-z0-9\-_]*((?<!cu)bot|crawler|archiver|transcoder|spider|uptime|validator|fetcher)');
  1430.             if (Swift_Performance_Lite::check_option('exclude-crawlers',1, '!=')){
  1431.                   return false;
  1432.             }
  1433.  
  1434.             return preg_match('~('.implode('|', $crawlers).')~', $_SERVER['HTTP_USER_AGENT']);
  1435.       }
  1436.  
  1437.       /**
  1438.        * Is cached version identical for the request
  1439.        * @return boolean
  1440.        */
  1441.       public static function is_identical($key = ''){
  1442.             if (Swift_Performance_Lite::check_option('resource-saving-mode', 1)){
  1443.                   $request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
  1444.                   $key = (empty($key) && (self::is_cacheable_ajax() || self::is_cacheable_dynamic()) ? Swift_Performance_Lite::get_unique_id() : (empty($key) ? $request_uri : $key));
  1445.                   $identical = get_transient('swift_performance_is_identical_' . md5($key));
  1446.                   if ($identical !== false){
  1447.                         return true;
  1448.                   }
  1449.                   else {
  1450.                         set_transient('swift_performance_is_identical_' . md5($key), true, 3600);
  1451.                   }
  1452.             }
  1453.  
  1454.             return false;
  1455.       }
  1456.  
  1457.       /**
  1458.        * Check is maintenance mode active
  1459.        * @return boolean
  1460.        */
  1461.       public static function is_maintenance(){
  1462.             if (!file_exists( ABSPATH . '.maintenance' )){
  1463.                   return false;
  1464.             }
  1465.  
  1466.             global $upgrading;
  1467.  
  1468.             include( ABSPATH . '.maintenance' );
  1469.             // If the $upgrading timestamp is older than 10 minutes, don't die.
  1470.             if ( ( time() - $upgrading ) >= 600 ){
  1471.                   return false;
  1472.             }
  1473.                   return true;
  1474.       }
  1475.  
  1476.       /**
  1477.        * Locate cache file and return the first match if exists
  1478.        * @return string|boolean
  1479.        */
  1480.       public static function locate_cache_file($permalink){
  1481.             $cache_file = false;
  1482.  
  1483.             $basedir = trailingslashit(Swift_Performance_Lite::get_option('cache-path')) . SWIFT_PERFORMANCE_CACHE_BASE_DIR;
  1484.             $basedir_regex = trailingslashit(Swift_Performance_Lite::get_option('cache-path')) . SWIFT_PERFORMANCE_CACHE_BASE_DIR . trailingslashit('('.implode('|', apply_filters('swift_performance_enabled_hosts', array(parse_url(Swift_Performance_Lite::home_url(), PHP_URL_HOST)))).')/' . trim(preg_quote(parse_url($permalink, PHP_URL_PATH)), '/'));
  1485.  
  1486.             if (@file_exists($basedir)){
  1487.                   $Directory = new RecursiveDirectoryIterator($basedir);
  1488.                   $Iterator = new RecursiveIteratorIterator($Directory);
  1489.                   $Regex = new RegexIterator($Iterator, '#'.$basedir_regex.'(@prefix/([_a-z0-9]+)/)?(desktop|mobile)/(unauthenticated|(authenticated/(a-z0-9+)))/((index|404)\.html|index\.xml|index\.json)$#i', RecursiveRegexIterator::GET_MATCH);
  1490.  
  1491.                   foreach ($Regex as $filename=>$file){
  1492.                           $cache_file = $filename;
  1493.                           break;
  1494.                   }
  1495.             }
  1496.  
  1497.             return $cache_file;
  1498.       }
  1499.  
  1500.       /**
  1501.        * Check if given URL is already in cache
  1502.        * @param string $permalink
  1503.        * @return boolean
  1504.        */
  1505.       public static function is_cached($permalink){
  1506.             return (self::get_cache_type($permalink) !== false);
  1507.       }
  1508.  
  1509.       /**
  1510.       * Get cached URL type
  1511.       * @param string $permalink
  1512.       * @return boolean|string (html|json|xml|404|false)
  1513.       */
  1514.       public static function get_cache_type($permalink){
  1515.             if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN')){
  1516.                   $cache_file = Swift_Performance_Cache::locate_cache_file($permalink);
  1517.                   if (empty($cache_file)){
  1518.                         return false;
  1519.                   }
  1520.  
  1521.                   $file = trailingslashit(dirname($cache_file));
  1522.  
  1523.                   if (!file_exists($file)){
  1524.                         return false;
  1525.                   }
  1526.                   if (file_exists($file . 'index.html')){
  1527.                         return 'html';
  1528.                   }
  1529.                   if (file_exists($file . 'index.json')){
  1530.                         return 'json';
  1531.                   }
  1532.                   if (file_exists($file . 'index.xml')){
  1533.                         return 'xml';
  1534.                   }
  1535.                   if (file_exists($file . '404.html')){
  1536.                         return '404';
  1537.                   }
  1538.             }
  1539.             else if (Swift_Performance_Lite::check_option('caching-mode', 'memcached_php')){
  1540.                   $path = dirname(Swift_Performance_Cache::locate_cache_file($permalink));
  1541.  
  1542.                   $cached_request = Swift_Performance_Cache::memcached_get($path . 'index.html');
  1543.                   if (!empty($cached_request)){
  1544.                         return 'html';
  1545.                   }
  1546.  
  1547.                   $cached_request   = Swift_Performance_Cache::memcached_get($path . 'index.xml');
  1548.                   if (!empty($cached_request)) {
  1549.                         return 'xml';
  1550.                   }
  1551.  
  1552.                   $cached_request   = Swift_Performance_Cache::memcached_get($path . 'index.json');
  1553.                   if (!empty($cached_request)) {
  1554.                         return 'json';
  1555.                   }
  1556.  
  1557.                   $cached_request   = Swift_Performance_Cache::memcached_get($path . '404.html');
  1558.                   if (!empty($cached_request)) {
  1559.                         return '404';
  1560.                   }
  1561.             }
  1562.             return false;
  1563.       }
  1564.  
  1565.       /**
  1566.       * Get cache creation time
  1567.       * @param string $permalink
  1568.       * @return int
  1569.       */
  1570.       public static function get_cache_time($permalink){
  1571.             $time = 0;
  1572.             $path = trailingslashit(dirname(Swift_Performance_Cache::locate_cache_file($permalink)));
  1573.             if (self::is_cached($permalink)){
  1574.                   if (Swift_Performance_Lite::check_option('caching-mode', array('disk_cache_rewrite', 'disk_cache_php'), 'IN')){
  1575.                         @$time = filectime($path);
  1576.                   }
  1577.                   else {
  1578.                         $cached_request = Swift_Performance_Cache::memcached_get($path . 'index.html');
  1579.                         if (empty($cached_request)) {
  1580.                               $cached_request = Swift_Performance_Cache::memcached_get($path . 'index.xml');
  1581.                         }
  1582.                         if (empty($cached_request)) {
  1583.                               $cached_request = Swift_Performance_Cache::memcached_get($path . 'index.json');
  1584.                         }
  1585.                         if (empty($cached_request)) {
  1586.                               $cached_request = Swift_Performance_Cache::memcached_get($path . '404.html');
  1587.                         }
  1588.                         if (!empty($cached_request) && isset($cached_request['time'])){
  1589.                               $time = $cached_request['time'];
  1590.                         }
  1591.                   }
  1592.             }
  1593.             return (int)$time;
  1594.       }
  1595.  
  1596.       /**
  1597.        * Write cached file
  1598.        * @param string $file file name
  1599.        * @param string $content file content (default empty)
  1600.        * @param boolean $force override existing (default false)
  1601.        * @return string public URL
  1602.        */
  1603.       public static function write_file($file, $content = '', $force = false){
  1604.             // Play in the sandbox only...
  1605.             if (strpos($file, './')){
  1606.                   return;
  1607.             }
  1608.  
  1609.             // Don't write empty content
  1610.             if (empty($content)){
  1611.                   return;
  1612.             }
  1613.  
  1614.             $dir = SWIFT_PERFORMANCE_CACHE_DIR . dirname($file);
  1615.             // Write cached file if file doesn't exists or we use force mode
  1616.             if ($force || !file_exists(SWIFT_PERFORMANCE_CACHE_DIR . $file)){
  1617.                   if (!file_exists($dir)){
  1618.                         @mkdir($dir, 0777, true);
  1619.                   }
  1620.                   @file_put_contents(SWIFT_PERFORMANCE_CACHE_DIR . $file, $content);
  1621.  
  1622.             }
  1623.  
  1624.             // Logged in hash
  1625.             $hash = '';
  1626.             if (isset($_COOKIE[LOGGED_IN_COOKIE])){
  1627.                   list(,,, $hash) = explode('|', $_COOKIE[LOGGED_IN_COOKIE]);
  1628.             }
  1629.  
  1630.             // Create empty index.html in every folder recursively to prevent directory index in cache
  1631.             $current = SWIFT_PERFORMANCE_CACHE_DIR;
  1632.             $folders = explode('/', dirname($file));
  1633.             for ($i=0;$i<count($folders);$i++){
  1634.                   $current .= $folders[$i] . '/';
  1635.                   if (($current == SWIFT_PERFORMANCE_CACHE_DIR . 'js/' || ($folders[$i] == 'authenticated' && isset($folders[$i+1]) && $folders[$i+1] == md5($hash))) && !file_exists($current . 'index.html')){
  1636.                         @touch($current . 'index.html');
  1637.                   }
  1638.             }
  1639.  
  1640.             Swift_Performance_Lite::log('Write file ' . SWIFT_PERFORMANCE_CACHE_URL . $file, 9);
  1641.  
  1642.             // Return with cached file URL
  1643.             return SWIFT_PERFORMANCE_CACHE_URL . $file;
  1644.       }
  1645.  
  1646.       /**
  1647.        * Clear cache folder recursively
  1648.        * @param string $dir
  1649.        * @return boolean
  1650.        */
  1651.        public static function recursive_rmdir($dir = '', $check_filetime = false){
  1652.              $basedir = trailingslashit(Swift_Performance_Lite::get_option('cache-path')) . SWIFT_PERFORMANCE_CACHE_BASE_DIR;
  1653.  
  1654.              foreach (apply_filters('swift_performance_enabled_hosts', array(parse_url(Swift_Performance_Lite::home_url(), PHP_URL_HOST))) as $host){
  1655.                    $cache_dir = trailingslashit($basedir . $host);
  1656.                    if (!file_exists($cache_dir . $dir)){
  1657.                     continue;
  1658.                 }
  1659.  
  1660.                    if (strpos(realpath($cache_dir . $dir), realpath($cache_dir)) === false){
  1661.                          continue;
  1662.                    }
  1663.  
  1664.                 $files = array_diff(scandir($cache_dir . $dir), array('.','..'));
  1665.                 foreach ((array)$files as $file) {
  1666.                          if (!$check_filetime || (time() - filectime($cache_dir . $dir . '/'. $file) >= Swift_Performance_Lite::get_option('cache-expiry-time') && !in_array($dir, array('/css','/js'))) ){
  1667.                           is_dir($cache_dir . $dir . '/'. $file) ? self::recursive_rmdir($dir . '/'. $file, $check_filetime) : @unlink($cache_dir . $dir . '/'. $file);
  1668.                          }
  1669.                 }
  1670.  
  1671.                 @rmdir($cache_dir . $dir);
  1672.              }
  1673.        }
  1674.  
  1675.       /**
  1676.        * Send PURGE request to varnish
  1677.        * @param string $url
  1678.        * @param boolean $wildcard (default false)
  1679.        */
  1680.       public static function purge_varnish_url($url, $wildcard = false){
  1681.             $varnish_host = Swift_Performance_Lite::get_option('custom-varnish-host');
  1682.             if (empty($varnish_host)){
  1683.                   $varnish_host = 'http://' . parse_url(Swift_Performance_Lite::home_url(), PHP_URL_HOST);
  1684.             }
  1685.  
  1686.             $parsed_url = parse_url($url);
  1687.             $purge_url  = $varnish_host . (isset($parsed_url['path']) ? $parsed_url['path'] : '') . ($wildcard ? '.*' : '');
  1688.  
  1689.         $response = wp_remote_request($purge_url, array( 'method' => 'PURGE', 'headers' => array( 'host' => parse_url(home_url(), PHP_URL_HOST), 'X-Purge-Method' => ($wildcard ? 'regex' : 'exact'))));
  1690.             Swift_Performance_Lite::log('Purge Varnish URL: ' . $purge_url, 9);
  1691.       }
  1692.  
  1693.       /**
  1694.        * Get first 20 CF zones
  1695.        * @return boolean|array
  1696.        */
  1697.       public static function get_cloudflare_zones(){
  1698.             if (Swift_Performance_Lite::check_option('cloudflare-email', '') || Swift_Performance_Lite::check_option('cloudflare-api-key', '')){
  1699.                   return false;
  1700.             }
  1701.  
  1702.             $host       = Swift_Performance_Lite::get_option('cloudflare-host');
  1703.             $host       = (empty($host) ? parse_url(Swift_Performance_Lite::home_url(), PHP_URL_HOST) : $host);
  1704.             $zones      = get_transient('swift_performance_'.$host.'_cf_zones');
  1705.             if (empty($zones)){
  1706.                   $response = wp_remote_get('https://api.cloudflare.com/client/v4/zones?name='.$host.'&status=active&page=1&per_page=20&order=status&direction=desc&match=all',
  1707.                         array(
  1708.                               'headers' => array(
  1709.                                 'X-Auth-Email' => Swift_Performance_Lite::get_option('cloudflare-email'),
  1710.                                 'X-Auth-Key' => Swift_Performance_Lite::get_option('cloudflare-api-key'),
  1711.                                 'Content-Type' => 'application/json'
  1712.                           )
  1713.                         )
  1714.                   );
  1715.                   if (is_wp_error($response)){
  1716.                         Swift_Performance_Lite::log('Cloudflare get zones failed: ' . $response->get_error_message(), 1);
  1717.                   }
  1718.                   else {
  1719.                         $decoded = json_decode($response['body']);
  1720.                         if (isset($decoded->errors) && !empty($decoded->errors)){
  1721.                               Swift_Performance_Lite::log('Cloudflare API error: ' . $decoded->errors[0]->message, 1);
  1722.                         }
  1723.                         else if (isset($decoded->result)){
  1724.                               $zones   = $decoded->result;
  1725.                               // Cache it for 60 sec
  1726.                               set_transient('swift_performance_'.$host.'_cf_zones', $zones, 60);
  1727.                         }
  1728.                         else {
  1729.                               Swift_Performance_Lite::log('Cloudflare zones are missing', 6);
  1730.                         }
  1731.                   }
  1732.  
  1733.             }
  1734.  
  1735.             return $zones;
  1736.       }
  1737.  
  1738.       /**
  1739.        * Get CF zones and cache it
  1740.        * @param string|array $files
  1741.        * @return boolean|array
  1742.        */
  1743.       public static function purge_cloudflare_zones($files = array()){
  1744.             if (empty($files)){
  1745.                   $body = '{"purge_everything":true}';
  1746.             }
  1747.             else {
  1748.                   $body = '{"files":' . json_encode((array)$files) . '}';
  1749.             }
  1750.  
  1751.             $zones = self::get_cloudflare_zones();
  1752.             if (!empty($zones) && is_array($zones)){
  1753.                   foreach ($zones as $zone) {
  1754.                         $response = wp_remote_request('https://api.cloudflare.com/client/v4/zones/'.$zone->id.'/purge_cache', array(
  1755.                         'method' => 'DELETE',
  1756.                         'body' => $body,
  1757.                               'headers' => array(
  1758.                                 'X-Auth-Email' => Swift_Performance_Lite::get_option('cloudflare-email'),
  1759.                                 'X-Auth-Key' => Swift_Performance_Lite::get_option('cloudflare-api-key'),
  1760.                                 'Content-Type' => 'application/json'
  1761.                           )
  1762.                     ));
  1763.                         if (is_wp_error($response)){
  1764.                               Swift_Performance_Lite::log('Cloudflare purge zone failed. Zonde ID: ' . $zone->id . ' Error:' . $response->get_error_message(), 1);
  1765.                         }
  1766.                         else{
  1767.                               $decoded = json_decode($response['body']);
  1768.  
  1769.                               if (isset($decoded->success) && $decoded->success == 'true'){
  1770.                                     if (empty($files)){
  1771.                                           Swift_Performance_Lite::log('Cloudflare zone ' . $zone->id . ' all files were purged.', 9);
  1772.                                     }
  1773.                                     else {
  1774.                                           Swift_Performance_Lite::log('Cloudflare zone ' . $zone->id . ' were purged. Files: ' . implode(', ', (array)$files), 9);
  1775.                                     }
  1776.                               }
  1777.                               else {
  1778.                                     Swift_Performance_Lite::log('Cloudflare purge zone failed. Zonde ID: ' . $zone->id . ' Error:' . json_encode($decoded->errors));
  1779.                               }
  1780.                         }
  1781.                   }
  1782.             }
  1783.       }
  1784.  
  1785.       /**
  1786.        * Get REST API url for specific post (if exists)
  1787.        * @return string
  1788.        */
  1789.       public static function get_rest_url($post_id){
  1790.             $post       = get_post_type_object($post_id);
  1791.             $post_type  = get_post_type($post_id);
  1792.             $namespace  = 'wp/v2/';
  1793.             $url        = '';
  1794.             if (function_exists('get_rest_url')){
  1795.                   if (isset($post->rest_base)){
  1796.                         $url = get_rest_url() . $namespace . $post->rest_base . '/' . $post_id . '/';
  1797.                   }
  1798.                   else if ($post_type == 'post') {
  1799.                         $url = get_rest_url() . $namespace . 'posts/' . $post_id . '/';
  1800.                   }
  1801.                   else if ($post_type == 'page') {
  1802.                         $url = get_rest_url() . $namespace . 'pages/' . $post_id . '/';
  1803.                   }
  1804.             }
  1805.             return $url;
  1806.       }
  1807.  
  1808.       /**
  1809.        * Get archive page urls for specific post
  1810.        * @return array
  1811.        */
  1812.       public static function get_archive_urls($post_id){
  1813.             $namespace  = 'wp/v2/';
  1814.             $urls = array();
  1815.  
  1816.             // Categories
  1817.             $categories = get_the_category($post_id);
  1818.             if (!empty($categories)){
  1819.                   foreach ($categories as $category){
  1820.                         $urls[] = get_category_link($category->term_id);
  1821.                 $urls[] = get_rest_url() . $namespace . 'categories/' . $category->term_id . '/';
  1822.                   }
  1823.             }
  1824.  
  1825.             // Tags
  1826.             $tags = get_the_category($post_id);
  1827.             if (!empty($tags)){
  1828.                   foreach ($tags as $tag){
  1829.                         $urls[] = get_tag_link($tag->term_id);
  1830.                 $urls[] = get_rest_url() . $namespace . 'tags/' . $tag->term_id . '/';
  1831.                   }
  1832.             }
  1833.  
  1834.             // Custom taxonomies
  1835.             $taxonomies = get_post_taxonomies($post_id);
  1836.             if (!empty($taxonomies)){
  1837.                   foreach ($taxonomies as $taxonomy){
  1838.                         $terms = wp_get_post_terms($post_id, $taxonomy);
  1839.                         foreach ($terms as $term) {
  1840.                               $urls[] = get_term_link($term);
  1841.                               $urls[] = get_rest_url() . $namespace. trailingslashit($term->taxonomy) . $term->slug . '/';
  1842.                         }
  1843.                   }
  1844.             }
  1845.  
  1846.             // WooCommerce product
  1847.             if (get_post_type($post_id) == 'product' || get_post_type($post_id) == 'product_variation'){
  1848.                   $urls[] = get_permalink(get_option('woocommerce_shop_page_id'));
  1849.             }
  1850.  
  1851.             return $urls;
  1852.       }
  1853.  
  1854.       /**
  1855.        * Get author page urls for specific post
  1856.        * @return array
  1857.        */
  1858.       public static function get_author_urls($post_id){
  1859.             $urls = array();
  1860.             $namespace  = 'wp/v2/';
  1861.             $author_id = get_post_field('post_author', $post_id);
  1862.             $urls[] = get_author_posts_url($author_id);
  1863.             if (Swift_Performance_Lite::check_option('cache-feed',1)){
  1864.                   $urls[] = get_author_feed_link($author_id);
  1865.             }
  1866.             if (function_exists('get_rest_url')){
  1867.                   $urls[] = get_rest_url() . $namespace . 'users/' . $author_id . '/';
  1868.             }
  1869.  
  1870.             return $urls;
  1871.       }
  1872.  
  1873.       /**
  1874.        * Get an instance of Memcached API
  1875.        * @return Memcached
  1876.        */
  1877.       public static function get_memcache_instance(){
  1878.             if (!isset($GLOBALS['swift_performance_memcached']) || !is_object($GLOBALS['swift_performance_memcached'])){
  1879.                   $result = false;
  1880.                   // Create Memcached instance
  1881.                   if (class_exists('Memcached')){
  1882.                         $GLOBALS['swift_performance_memcached'] = new Memcached();
  1883.                         $result = $GLOBALS['swift_performance_memcached']->addServer(Swift_Performance_Lite::get_option('memcached-host'), (int)Swift_Performance_Lite::get_option('memcached-port'));
  1884.                   }
  1885.                   // Memcached isn't exists
  1886.                   else {
  1887.                         $fail = true;
  1888.                   }
  1889.  
  1890.                   // Fallback if init was failed
  1891.                   if ($result === false){
  1892.                         Swift_Performance_Lite::set_option('caching-mode', 'disk_cache_php');
  1893.                         Swift_Performance_Lite::update_option('caching-mode', 'disk_cache_php');
  1894.                   }
  1895.             }
  1896.             return @$GLOBALS['swift_performance_memcached'];
  1897.       }
  1898.  
  1899.       /**
  1900.        * Set content in memcached
  1901.        * @param string $path
  1902.        * @param string $content
  1903.        */
  1904.       public static function memcached_set($path, $content){
  1905.             $memcached = Swift_Performance_Cache::get_memcache_instance();
  1906.  
  1907.             $memcached->set('swift-performance_' . $path, $content, Swift_Performance_Lite::get_option('cache-expiry-time'));
  1908.       }
  1909.  
  1910.       /**
  1911.       * Get content from memcached
  1912.       * @param string $path
  1913.       */
  1914.       public static function memcached_get($path){
  1915.             $memcached = Swift_Performance_Cache::get_memcache_instance();
  1916.  
  1917.             return $memcached->get('swift-performance_' . $path);
  1918.       }
  1919.  
  1920.       /**
  1921.        * Proxy the current request and return the results
  1922.        * @return string
  1923.        */
  1924.       public static function proxy_request(){
  1925.             // Headers
  1926.             $headers = array('X-Proxy' => 'Swift_Performance');
  1927.             foreach ($_SERVER as $key => $value) {
  1928.                   $headers[preg_replace('~^http_~i', '', $key)] = $value;
  1929.             }
  1930.  
  1931.             // Cookies
  1932.             $cookies = array();
  1933.             foreach ( $_COOKIE as $name => $value ) {
  1934.                   $cookies[] = new WP_Http_Cookie( array( 'name' => $name, 'value' => $value ) );
  1935.             }
  1936.  
  1937.             // Proxy the original request
  1938.             $response = wp_remote_post(home_url($_SERVER['REQUEST_URI']), array(
  1939.                   'method' => 'POST',
  1940.                   'timeout' => 45,
  1941.                   'blocking' => true,
  1942.                   'headers' => $headers,
  1943.                   'body' => $_POST,
  1944.                   'cookies' => $cookies
  1945.                 )
  1946.             );
  1947.  
  1948.             if (!is_wp_error($response)){
  1949.                   return $response['body'];
  1950.             }
  1951.  
  1952.             return false;
  1953.       }
  1954.  
  1955.       /**
  1956.        * Remove http to avoid mixed content on cached pages
  1957.        */
  1958.        public static function avoid_mixed_content($html){
  1959.              if (Swift_Performance_Lite::check_option('avoid-mixed-content',1)){
  1960.                    // Avoid mixed content issues
  1961.                    $html = preg_replace('~https?://([^"\'\s]*)\.(jpe?g|png|gif|swf|flv|mpeg|mpg|mpe|3gp|mov|avi|wav|flac|mp2|mp3|m4a|mp4|m4p|aac)~i', "//$1.$2", $html);
  1962.                    $html = preg_replace('~src=(\'|")https?:~', 'src=$1', $html);
  1963.                    $html = preg_replace('~<link rel=\'stylesheet\'((?!href=).)*href=(\'|")https?:~', '<link rel=\'stylesheet\'$1href=$2', $html);
  1964.              }
  1965.              return $html;
  1966.        }
  1967.  
  1968.  
  1969. }
  1970.  
  1971. // Create instance
  1972. if (Swift_Performance_Lite::check_option('enable-caching', 1)){
  1973.       return new Swift_Performance_Cache();
  1974. }
  1975. else {
  1976.       return false;
  1977. }
  1978.  
  1979. ?>
Add Comment
Please, Sign In to add comment