Created
January 30, 2016 14:01
-
-
Save timersys/839ea57c819e4811c651 to your computer and use it in GitHub Desktop.
W3 Total Cache Modification to work with Geotargeting Pro Plugin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * File to replace original W3 Total cache one located in /lib/W3/ | |
| * To make it work Page cache + Geotargeting Pro | |
| * https://timersys.com/geotargeting/ | |
| * */ | |
| // To support legacy updates with old add-ins | |
| if (class_exists('W3_PgCache')) | |
| return; | |
| /** | |
| * W3 PgCache | |
| */ | |
| /** | |
| * Class W3_PgCache | |
| */ | |
| class W3_PgCache { | |
| /** | |
| * Advanced cache config | |
| * | |
| * @var W3_Config | |
| */ | |
| var $_config = null; | |
| /** | |
| * Caching flag | |
| * | |
| * @var boolean | |
| */ | |
| var $_caching = false; | |
| /** | |
| * Time start | |
| * | |
| * @var double | |
| */ | |
| var $_time_start = 0; | |
| /** | |
| * Lifetime | |
| * @var integer | |
| */ | |
| var $_lifetime = 0; | |
| /** | |
| * Enhanced mode flag | |
| * | |
| * @var boolean | |
| */ | |
| var $_enhanced_mode = false; | |
| /** | |
| * Debug flag | |
| * | |
| * @var boolean | |
| */ | |
| var $_debug = false; | |
| /** | |
| * Request host | |
| * @var string | |
| */ | |
| var $_request_host = ''; | |
| /** | |
| * Request URI | |
| * @var string | |
| */ | |
| var $_request_uri = ''; | |
| /** | |
| * Page key | |
| * @var string | |
| */ | |
| var $_page_key = ''; | |
| /** | |
| * Shutdown buffer | |
| * @var string | |
| */ | |
| var $_shutdown_buffer = ''; | |
| /** | |
| * Shutdown compression | |
| * @var string | |
| */ | |
| var $_shutdown_compression = ''; | |
| /** | |
| * Mobile object | |
| * @var W3_Mobile | |
| */ | |
| var $_mobile = null; | |
| /** | |
| * Referrer object | |
| * @var W3_Referrer | |
| */ | |
| var $_referrer = null; | |
| /** | |
| * Cache reject reason | |
| * | |
| * @var string | |
| */ | |
| var $cache_reject_reason = ''; | |
| /** | |
| * @var If sitemap was matched | |
| */ | |
| var $_sitemap_matched; | |
| /** | |
| * @var bool If cached page should be displayed after init | |
| */ | |
| var $_late_init = false; | |
| var $_cached_data = null; | |
| var $_old_exists = false; | |
| /** | |
| * Returns instance. | |
| * For backward compatibility with 0.9.2.3 version of /wp-content files | |
| * | |
| * @return W3_PgCache | |
| */ | |
| function instance() { | |
| $i = w3_instance('W3_PgCache'); | |
| $i->_legacy = true; | |
| return $i; | |
| } | |
| /** | |
| * PHP5 Constructor | |
| */ | |
| function __construct() { | |
| $this->_config = w3_instance('W3_Config'); | |
| $this->_debug = $this->_config->get_boolean('pgcache.debug'); | |
| $request_host = w3_get_host(); | |
| $pos = strpos($request_host, ':'); | |
| if ($pos !== false) | |
| $request_host = substr($request_host, 0, $pos); | |
| $this->_request_host = $request_host; | |
| $this->_request_uri = $_SERVER['REQUEST_URI']; | |
| $this->_lifetime = $this->_config->get_integer('pgcache.lifetime'); | |
| $this->_late_init = $this->_config->get_boolean('pgcache.late_init'); | |
| $this->_enhanced_mode = ($this->_config->get_string('pgcache.engine') == 'file_generic'); | |
| if ($this->_config->get_boolean('mobile.enabled')) { | |
| $this->_mobile = w3_instance('W3_Mobile'); | |
| } | |
| if ($this->_config->get_boolean('referrer.enabled')) { | |
| $this->_referrer = w3_instance('W3_Referrer'); | |
| } | |
| } | |
| /** | |
| * Do cache logic | |
| */ | |
| function process() { | |
| /** | |
| * Skip caching for some pages | |
| */ | |
| switch (true) { | |
| case defined('DONOTCACHEPAGE'): | |
| case defined('DOING_AJAX'): | |
| case defined('DOING_CRON'): | |
| case defined('APP_REQUEST'): | |
| case defined('XMLRPC_REQUEST'): | |
| case defined('WP_ADMIN'): | |
| case (defined('SHORTINIT') && SHORTINIT): | |
| return; | |
| } | |
| /* | |
| * Legacy mode with 0.9.2.3 version of /wp-content add-ins | |
| */ | |
| if (isset($this->_legacy)) { | |
| $redirect = w3_instance('W3_Redirect'); | |
| $redirect->process(); | |
| } | |
| /** | |
| * Do page cache logic | |
| */ | |
| if ($this->_debug) { | |
| $this->_time_start = w3_microtime(); | |
| } | |
| $this->_caching = $this->_can_cache(); | |
| global $w3_late_init; | |
| /** | |
| * @var W3_Dispatcher $dispatcher | |
| */ | |
| $dispatcher = w3_instance('W3_Dispatcher'); | |
| $dispatcher->set_newrelic_appname($this->_config); | |
| if ($this->_caching) { | |
| $this->_cached_data = $this->_extract_cached_page(); | |
| if ($this->_cached_data) { | |
| if ($this->_late_init) { | |
| $w3_late_init = true; | |
| return; | |
| } else { | |
| $this->process_cached_page($this->_cached_data); | |
| exit; | |
| } | |
| } else | |
| $this->_late_init = false; | |
| } else { | |
| $this->_late_init = false; | |
| } | |
| $w3_late_init = $this->_late_init; | |
| /** | |
| * Start output buffering | |
| */ | |
| w3tc_add_ob_callback('pagecache', array($this,'ob_callback')); | |
| } | |
| /** | |
| * Extracts page from cache | |
| * | |
| * @return boolean | |
| */ | |
| function _extract_cached_page() { | |
| $cache = $this->_get_cache(); | |
| $mobile_group = $this->_get_mobile_group(); | |
| $referrer_group = $this->_get_referrer_group(); | |
| $encryption = $this->_get_encryption(); | |
| $compression = $this->_get_compression(); | |
| $raw = !$compression; | |
| $this->_page_key = $this->_get_page_key($mobile_group, $referrer_group, | |
| $encryption, $compression); | |
| $group = '0'; | |
| $sitemap_regex = $this->_config->get_string('pgcache.purge.sitemap_regex'); | |
| if ($sitemap_regex && preg_match('/' . $sitemap_regex . '/', basename($this->_request_uri))) { | |
| $group = 'sitemaps'; | |
| $this->_sitemap_matched = true; | |
| } else { | |
| $this->_sitemap_matched = false; | |
| } | |
| /** | |
| * Check if page is cached | |
| */ | |
| $data = $cache->get_with_old($this->_page_key, $group); | |
| list($data, $this->_old_exists) = $data; | |
| /** | |
| * Try to get uncompressed version of cache | |
| */ | |
| if ($compression && !$data) { | |
| $raw = true; | |
| $this->_page_key = $this->_get_page_key($mobile_group, | |
| $referrer_group, $encryption, false); | |
| $data = $cache->get_with_old($this->_page_key); | |
| list($data, $this->_old_exists) = $data; | |
| } | |
| if (!$data) | |
| return null; | |
| $data['raw'] = $raw; | |
| $data['compression'] = $compression; | |
| return $data; | |
| } | |
| /** | |
| * Process extracted cached pages | |
| * @param $data | |
| */ | |
| public function process_cached_page($data) { | |
| /** | |
| * Do Bad Behavior check | |
| */ | |
| $this->_bad_behavior(); | |
| $is_404 = $data['404']; | |
| $headers = $data['headers']; | |
| $time = $data['time']; | |
| $content = & $data['content']; | |
| $compression = $data['compression']; | |
| $raw = $data['raw']; | |
| /** | |
| * Calculate content etag | |
| */ | |
| $etag = md5($content); | |
| /** | |
| * Send headers | |
| */ | |
| $this->_send_headers($is_404, $time, $etag, $compression, $headers); | |
| if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'HEAD') | |
| return; | |
| /** | |
| * Do manual compression for uncompressed page | |
| */ | |
| if ($raw) { | |
| /** | |
| * Append debug info | |
| */ | |
| if ($this->_debug && w3_can_print_comment($content)) { | |
| $time_total = w3_microtime() - $this->_time_start; | |
| $debug_info = $this->_get_debug_info(true, '', true, $time_total); | |
| $content .= "\r\n\r\n" . $debug_info; | |
| } | |
| /** | |
| * Parse dynamic tags | |
| */ | |
| $this->_parse_dynamic($content); | |
| /** | |
| * Compress content | |
| */ | |
| $this->_compress($content, $compression); | |
| } | |
| echo $content; | |
| } | |
| /** | |
| * Output buffering callback | |
| * | |
| * @param string $buffer | |
| * @return string | |
| */ | |
| function ob_callback(&$buffer) { | |
| if ($buffer != '' && $this->_is_cacheable_content_type()) { | |
| $compression = false; | |
| $has_dynamic = $this->_has_dynamic($buffer); | |
| $can_cache = apply_filters('w3tc_can_cache', $this->_can_cache2($buffer), $this); | |
| if ($can_cache) { | |
| list($compression, $buffer) = $this->_can_cache_buffer($buffer, $has_dynamic); | |
| } else { | |
| if ($this->_debug && w3_can_print_comment($buffer)) | |
| $buffer = $this->_debugging_not_caching($buffer, $compression); | |
| if ($this->cache_reject_reason) { | |
| if ($this->_old_exists) { | |
| /** | |
| * @var W3_Cache_File_Generic $cache | |
| */ | |
| $cache = $this->_get_cache(); | |
| $cache->hard_delete($this->_page_key); | |
| } | |
| } | |
| } | |
| /** | |
| * We can't capture output in ob_callback | |
| * so we use shutdown function | |
| */ | |
| if ($has_dynamic) { | |
| $this->_shutdown_buffer = $buffer; | |
| $this->_shutdown_compression = $compression; | |
| $buffer = ''; | |
| register_shutdown_function(array( | |
| &$this, | |
| 'shutdown' | |
| )); | |
| } | |
| } | |
| return $buffer; | |
| } | |
| /** | |
| * Shutdown callback | |
| * @return void | |
| */ | |
| function shutdown() { | |
| /** | |
| * Parse dynamic content | |
| */ | |
| $this->_parse_dynamic($this->_shutdown_buffer); | |
| /** | |
| * Compress page | |
| */ | |
| $this->_compress($this->_shutdown_buffer, $this->_shutdown_compression); | |
| echo $this->_shutdown_buffer; | |
| } | |
| /** | |
| * Checks if can we do cache logic | |
| * | |
| * @return boolean | |
| */ | |
| function _can_cache() { | |
| /** | |
| * Don't cache in console mode | |
| */ | |
| if (PHP_SAPI === 'cli') { | |
| $this->cache_reject_reason = 'Console mode'; | |
| return false; | |
| } | |
| /** | |
| * Skip if session defined | |
| */ | |
| if (defined('SID') && SID != '') { | |
| $this->cache_reject_reason = 'Session started'; | |
| return false; | |
| } | |
| /** | |
| * Skip if posting | |
| */ | |
| if (isset($_SERVER['REQUEST_METHOD']) && in_array(strtoupper($_SERVER['REQUEST_METHOD']), array('DELETE', 'PUT','OPTIONS','TRACE', 'CONNECT','POST'))) { | |
| $this->cache_reject_reason = sprintf('Requested method is %s', $_SERVER['REQUEST_METHOD']); | |
| return false; | |
| } | |
| /** | |
| * Skip if HEAD request | |
| */ | |
| if (isset($_SERVER['REQUEST_METHOD']) && strtoupper($_SERVER['REQUEST_METHOD']) == 'HEAD' && | |
| ($this->_enhanced_mode || $this->_config->get_boolean('pgcache.reject.request_head'))) { | |
| $this->cache_reject_reason = 'Requested method is HEAD'; | |
| return false; | |
| } | |
| /** | |
| * Skip if there is query in the request uri | |
| */ | |
| if (!$this->_check_query_string() &&!$this->_config->get_boolean('pgcache.cache.query') && strstr($this->_request_uri, '?') !== false) { | |
| $this->cache_reject_reason = 'Requested URI contains query'; | |
| return false; | |
| } | |
| /** | |
| * Check request URI | |
| */ | |
| if (!in_array($_SERVER['PHP_SELF'], $this->_config->get_array('pgcache.accept.files')) && !$this->_check_request_uri()) { | |
| $this->cache_reject_reason = 'Requested URI is rejected'; | |
| return false; | |
| } | |
| /** | |
| * Check User Agent | |
| */ | |
| if (!$this->_check_ua()) { | |
| $this->cache_reject_reason = 'User agent is rejected'; | |
| return false; | |
| } | |
| /** | |
| * Check WordPress cookies | |
| */ | |
| if (!$this->_check_cookies()) { | |
| $this->cache_reject_reason = 'Cookie is rejected'; | |
| return false; | |
| } | |
| /** | |
| * Skip if user is logged in or user role is logged in | |
| */ | |
| if ($this->_config->get_boolean('pgcache.reject.logged') ) { | |
| if (!$this->_check_logged_in()) { | |
| $this->cache_reject_reason = 'User is logged in'; | |
| return false; | |
| } | |
| } else { | |
| if (!$this->_check_logged_in_role_allowed()) { | |
| $this->cache_reject_reason = 'An user role is logged in'; | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| /** | |
| * Checks if can we do cache logic | |
| * | |
| * @param string $buffer | |
| * @return boolean | |
| */ | |
| function _can_cache2(&$buffer) { | |
| /** | |
| * Skip if caching is disabled | |
| */ | |
| if (!$this->_caching) { | |
| return false; | |
| } | |
| /** | |
| * Check for database error | |
| */ | |
| if (w3_is_database_error($buffer)) { | |
| $this->cache_reject_reason = 'Database error occurred'; | |
| return false; | |
| } | |
| /** | |
| * Check for DONOTCACHEPAGE constant | |
| */ | |
| if (defined('DONOTCACHEPAGE') && DONOTCACHEPAGE) { | |
| $this->cache_reject_reason = 'DONOTCACHEPAGE constant is defined'; | |
| return false; | |
| } | |
| /** | |
| * Check hostname | |
| */ | |
| if ((!w3_is_multisite() || (w3_is_multisite() && !w3_force_master())) && | |
| $this->_config->get_boolean('pgcache.check.domain') && w3_get_host() != w3_get_home_domain()) { | |
| $this->cache_reject_reason = 'Hostname mismatch'; | |
| return false; | |
| } | |
| /** | |
| * Don't cache 404 pages | |
| */ | |
| if (!$this->_config->get_boolean('pgcache.cache.404') && function_exists('is_404') && is_404()) { | |
| $this->cache_reject_reason = 'Page is 404'; | |
| return false; | |
| } | |
| /** | |
| * Don't cache homepage | |
| */ | |
| if (!$this->_config->get_boolean('pgcache.cache.home') && function_exists('is_home') && is_home()) { | |
| $this->cache_reject_reason = is_front_page() && is_home() ? 'Page is front page' : 'Page is posts page'; | |
| return false; | |
| } | |
| /** | |
| * Don't cache front page | |
| */ | |
| if ($this->_config->get_boolean('pgcache.reject.front_page') && function_exists('is_front_page') && is_front_page() && !is_home()) { | |
| $this->cache_reject_reason = 'Page is front page'; | |
| return false; | |
| } | |
| /** | |
| * Don't cache feed | |
| */ | |
| if (!$this->_config->get_boolean('pgcache.cache.feed') && function_exists('is_feed') && is_feed()) { | |
| $this->cache_reject_reason = 'Page is feed'; | |
| return false; | |
| } | |
| /** | |
| * Check if page contains dynamic tags | |
| */ | |
| if ($this->_enhanced_mode && $this->_has_dynamic($buffer)) { | |
| $this->cache_reject_reason = 'Page contains dynamic tags (mfunc or mclude) can not be cached in enhanced mode'; | |
| return false; | |
| } | |
| return true; | |
| } | |
| /** | |
| * Returns cache object | |
| * | |
| * @return W3_Cache_Base | |
| */ | |
| function _get_cache() { | |
| static $cache = array(); | |
| if (!isset($cache[0])) { | |
| $engine = $this->_config->get_string('pgcache.engine'); | |
| switch ($engine) { | |
| case 'memcached': | |
| $engineConfig = array( | |
| 'servers' => $this->_config->get_array('pgcache.memcached.servers'), | |
| 'persistant' => $this->_config->get_boolean('pgcache.memcached.persistant') | |
| ); | |
| break; | |
| case 'file': | |
| $engineConfig = array( | |
| 'section' => 'page', | |
| 'flush_parent' => (w3_get_blog_id() == 0), | |
| 'locking' => $this->_config->get_boolean('pgcache.file.locking'), | |
| 'flush_timelimit' => $this->_config->get_integer('timelimit.cache_flush') | |
| ); | |
| break; | |
| case 'file_generic': | |
| if (w3_get_blog_id() == 0) | |
| $flush_dir = W3TC_CACHE_PAGE_ENHANCED_DIR; | |
| else | |
| $flush_dir = W3TC_CACHE_PAGE_ENHANCED_DIR . '/' . w3_get_domain(w3_get_host()); | |
| $engineConfig = array( | |
| 'exclude' => array( | |
| '.htaccess' | |
| ), | |
| 'expire' => $this->_lifetime, | |
| 'cache_dir' => W3TC_CACHE_PAGE_ENHANCED_DIR, | |
| 'locking' => $this->_config->get_boolean('pgcache.file.locking'), | |
| 'flush_timelimit' => $this->_config->get_integer('timelimit.cache_flush'), | |
| 'flush_dir' => $flush_dir, | |
| ); | |
| break; | |
| default: | |
| $engineConfig = array(); | |
| } | |
| $engineConfig['use_expired_data'] = true; | |
| $engineConfig['module'] = 'pgcache'; | |
| $engineConfig['host'] = w3_get_host(); | |
| $engineConfig['instance_id'] = w3_get_instance_id(); | |
| w3_require_once(W3TC_LIB_W3_DIR . '/Cache.php'); | |
| $cache[0] = W3_Cache::instance($engine, $engineConfig); | |
| } | |
| return $cache[0]; | |
| } | |
| /** | |
| * Checks request URI | |
| * | |
| * @return boolean | |
| */ | |
| function _check_request_uri() { | |
| $auto_reject_uri = array( | |
| 'wp-login', | |
| 'wp-register' | |
| ); | |
| foreach ($auto_reject_uri as $uri) { | |
| if (strstr($this->_request_uri, $uri) !== false) { | |
| return false; | |
| } | |
| } | |
| $reject_uri = $this->_config->get_array('pgcache.reject.uri'); | |
| $reject_uri = array_map('w3_parse_path', $reject_uri); | |
| foreach ($reject_uri as $expr) { | |
| $expr = trim($expr); | |
| if ($expr != '' && preg_match('~' . $expr . '~i', $this->_request_uri)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| /** | |
| * Checks User Agent | |
| * | |
| * @return boolean | |
| */ | |
| function _check_ua() { | |
| w3_require_once(W3TC_LIB_W3_DIR . '/Request.php'); | |
| $uas = $this->_config->get_array('pgcache.reject.ua'); | |
| $uas = array_merge($uas, array(W3TC_POWERED_BY)); | |
| foreach ($uas as $ua) { | |
| if (!empty($ua)) { | |
| if (isset($_SERVER['HTTP_USER_AGENT']) && stristr($_SERVER['HTTP_USER_AGENT'], $ua) !== false) | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| /** | |
| * Checks WordPress cookies | |
| * | |
| * @return boolean | |
| */ | |
| function _check_cookies() { | |
| foreach (array_keys($_COOKIE) as $cookie_name) { | |
| if ($cookie_name == 'wordpress_test_cookie') { | |
| continue; | |
| } | |
| if (preg_match('/^(wp-postpass|comment_author)/', $cookie_name)) { | |
| return false; | |
| } | |
| } | |
| foreach ($this->_config->get_array('pgcache.reject.cookie') as $reject_cookie) { | |
| if (!empty($reject_cookie)) { | |
| foreach (array_keys($_COOKIE) as $cookie_name) { | |
| if (strstr($cookie_name, $reject_cookie) !== false) { | |
| return false; | |
| } | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| /** | |
| * Check if user is logged in | |
| * | |
| * @return boolean | |
| */ | |
| function _check_logged_in() { | |
| foreach (array_keys($_COOKIE) as $cookie_name) { | |
| if (strpos($cookie_name, 'wordpress_logged_in') === 0) | |
| return false; | |
| } | |
| return true; | |
| } | |
| /** | |
| * Check if logged in user role is allwed to be cached | |
| * | |
| * @return boolean | |
| */ | |
| function _check_logged_in_role_allowed() { | |
| if (!$this->_config->get_boolean('pgcache.reject.logged_roles')) | |
| return true; | |
| $roles = $this->_config->get_array('pgcache.reject.roles'); | |
| if (empty($roles)) | |
| return true; | |
| foreach (array_keys($_COOKIE) as $cookie_name) { | |
| if (strpos($cookie_name, 'w3tc_logged_') === 0) { | |
| foreach($roles as $role) { | |
| if(strstr($cookie_name, md5(NONCE_KEY . $role))) | |
| return false; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| /** | |
| * Checks if rules file present and creates it if not | |
| */ | |
| function _check_rules_present() { | |
| if (w3_is_nginx()) | |
| return; // nginx store it in a single file | |
| w3_require_once(W3TC_INC_DIR . '/functions/rule_cut.php'); | |
| $filename = w3_get_pgcache_rules_cache_path(); | |
| if (file_exists($filename)) | |
| return; | |
| // we call it as little times as possible | |
| // its expensive, but have to restore lost .htaccess file | |
| $e = w3_instance('W3_PgCacheAdminEnvironment'); | |
| try { | |
| $e->fix_on_wpadmin_request($this->_config, true); | |
| } catch (Exception $ex) { | |
| } | |
| } | |
| /** | |
| * Compress data | |
| * | |
| * @param string $data | |
| * @param string $compression | |
| * @return string | |
| */ | |
| function _compress(&$data, $compression) { | |
| switch ($compression) { | |
| case 'gzip': | |
| $data = gzencode($data); | |
| break; | |
| case 'deflate': | |
| $data = gzdeflate($data); | |
| break; | |
| } | |
| } | |
| /** | |
| * Returns current mobile group | |
| * | |
| * @return string | |
| */ | |
| function _get_mobile_group() { | |
| if ($this->_mobile) { | |
| return $this->_mobile->get_group(); | |
| } | |
| return ''; | |
| } | |
| /** | |
| * Returns current referrer group | |
| * | |
| * @return string | |
| */ | |
| function _get_referrer_group() { | |
| if ($this->_referrer) { | |
| return $this->_referrer->get_group(); | |
| } | |
| return ''; | |
| } | |
| /** | |
| * Returns current encryption | |
| * | |
| * @return string | |
| */ | |
| function _get_encryption() { | |
| if (w3_is_https()) { | |
| return 'ssl'; | |
| } | |
| return ''; | |
| } | |
| /** | |
| * Returns current compression | |
| * | |
| * @return boolean | |
| */ | |
| function _get_compression() { | |
| if ($this->_debug) // cannt generate/use compressed files during debug mode | |
| return false; | |
| if (!w3_zlib_output_compression() && !headers_sent() && !$this->_is_buggy_ie()) { | |
| $compressions = $this->_get_compressions(); | |
| foreach ($compressions as $compression) { | |
| if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && stristr($_SERVER['HTTP_ACCEPT_ENCODING'], $compression) !== false) { | |
| return $compression; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| /** | |
| * Returns array of compressions | |
| * | |
| * @return array | |
| */ | |
| function _get_compressions() { | |
| $compressions = array( | |
| false | |
| ); | |
| if ($this->_config->get_boolean('browsercache.enabled') && $this->_config->get_boolean('browsercache.html.compression') && function_exists('gzencode')) { | |
| $compressions[] = 'gzip'; | |
| } | |
| return $compressions; | |
| } | |
| /** | |
| * Returns array of response headers | |
| * | |
| * @return array | |
| */ | |
| function _get_response_headers() { | |
| $headers = array(); | |
| if (function_exists('headers_list')) { | |
| $headers_list = headers_list(); | |
| if ($headers_list) { | |
| foreach ($headers_list as $header) { | |
| $pos = strpos($header, ':'); | |
| if ($pos) { | |
| $header_name = substr($header, 0, $pos); | |
| $header_value = substr($header, $pos+1); | |
| } else { | |
| $header_name = $header; | |
| $header_value = ''; | |
| } | |
| $headers[$header_name] = $header_value; | |
| } | |
| } | |
| } | |
| return $headers; | |
| } | |
| /** | |
| * Checks for buggy IE6 that doesn't support compression | |
| * | |
| * @return boolean | |
| */ | |
| function _is_buggy_ie() { | |
| if (isset($_SERVER['HTTP_USER_AGENT'])) { | |
| $ua = $_SERVER['HTTP_USER_AGENT']; | |
| if (strpos($ua, 'Mozilla/4.0 (compatible; MSIE ') === 0 && strpos($ua, 'Opera') === false) { | |
| $version = (float) substr($ua, 30); | |
| return ($version < 6 || ($version == 6 && strpos($ua, 'SV1') === false)); | |
| } | |
| } | |
| return false; | |
| } | |
| /** | |
| * Returns array of data headers | |
| * | |
| * @return array | |
| */ | |
| function _get_cached_headers() { | |
| $data_headers = array(); | |
| $cache_headers = $this->_config->get_array('pgcache.cache.headers'); | |
| $response_headers = $this->_get_response_headers(); | |
| foreach ($response_headers as $header_name => $header_value) { | |
| foreach ($cache_headers as $cache_header_name) { | |
| if (strcasecmp($header_name, $cache_header_name) == 0) { | |
| $data_headers[$header_name] = $header_value; | |
| } | |
| } | |
| } | |
| return $data_headers; | |
| } | |
| /** | |
| * Returns page key | |
| * | |
| * @param string $mobile_group | |
| * @param string $referrer_group | |
| * @param string $encryption | |
| * @param string $compression | |
| * @param string $content_type | |
| * @param string $request_uri | |
| * @return string | |
| */ | |
| function _get_page_key($mobile_group = '', $referrer_group = '', | |
| $encryption = '', $compression = '', $content_type = '', $request_uri = '') { | |
| if ($request_uri) | |
| $key = substr($request_uri, strtolower(substr($request_uri, 0, 8)) == 'https' ? 8: 7); | |
| else | |
| $key = $this->_request_host . $this->_request_uri; | |
| // replace fragment | |
| $key = preg_replace('~#.*$~', '', $key); | |
| if ($this->_enhanced_mode) { | |
| // URL decode | |
| $key = urldecode($key); | |
| // replace double slashes | |
| $key = preg_replace('~[/\\\]+~', '/', $key); | |
| // replace query string | |
| $key = preg_replace('~\?.*$~', '', $key); | |
| // replace index.php | |
| $key = str_replace('/index.php', '/', $key); | |
| // trim slash | |
| $key = ltrim($key, '/'); | |
| if ($key && substr($key, -1) != '/') { | |
| $key .= '/'; | |
| } | |
| $key .= '_index'; | |
| } else { | |
| if ($this->_check_query_string()) | |
| // replace query string | |
| $key = preg_replace('~\?.*$~', '', $key); | |
| $key = md5($key); | |
| } | |
| /** | |
| * Append mobile group | |
| */ | |
| if ($mobile_group) { | |
| $key .= '_' . $mobile_group; | |
| } | |
| /** | |
| * Append referrer group | |
| */ | |
| if ($referrer_group) { | |
| $key .= '_' . $referrer_group; | |
| } | |
| /** | |
| * Append encryption | |
| */ | |
| if ($encryption) { | |
| $key .= '_' . $encryption; | |
| } | |
| if (w3_is_preview_mode()) { | |
| $key .= '_preview'; | |
| } | |
| /** | |
| * Geotargeting | |
| */ | |
| $country = isset( $_SESSION['geot_country'] ) ? unserialize( $_SESSION['geot_country'] ) : ''; | |
| if( isset( $country->isoCode ) ) | |
| $key .= $country->isoCode; | |
| else | |
| $key .= $country; | |
| if ($this->_enhanced_mode) { | |
| /** | |
| * Append HTML extension. | |
| * For nginx - we create .xml cache entries and redirect to them | |
| */ | |
| if (w3_is_nginx() && substr($content_type, 0, 8) == 'text/xml' && | |
| $this->_config->get_boolean('pgcache.cache.nginx_handle_xml')) | |
| $key .= '.xml'; | |
| else | |
| $key .= '.html'; | |
| } | |
| /** | |
| * Append compression | |
| */ | |
| if ($compression) { | |
| $key .= '_' . $compression; | |
| } | |
| return $key; | |
| } | |
| /** | |
| * Returns debug info | |
| * | |
| * @param boolean $cache | |
| * @param string $reason | |
| * @param boolean $status | |
| * @param double $time | |
| * @return string | |
| */ | |
| function _get_debug_info($cache, $reason, $status, $time) { | |
| $engine = $this->_config->get_string('pgcache.engine'); | |
| $debug_info = "<!-- W3 Total Cache: Page cache debug info:\r\n"; | |
| $debug_info .= sprintf("%s%s\r\n", str_pad('Engine: ', 20), w3_get_engine_name($engine)); | |
| $debug_info .= sprintf("%s%s\r\n", str_pad('Cache key: ', 20), w3_escape_comment($this->_page_key)); | |
| $debug_info .= sprintf("%s%s\r\n", str_pad('Caching: ', 20), ($cache ? 'enabled' : 'disabled')); | |
| if (!$cache) { | |
| $debug_info .= sprintf("%s%s\r\n", str_pad('Reject reason: ', 20), $reason); | |
| } | |
| if ($engine != 'file_generic') | |
| $debug_info .= sprintf("%s%s\r\n", str_pad('Status: ', 20), ($status ? 'cached' : 'not cached')); | |
| $debug_info .= sprintf("%s%.3fs\r\n", str_pad('Creation Time: ', 20), $time); | |
| $headers = $this->_get_response_headers(); | |
| if (count($headers)) { | |
| $debug_info .= "Header info:\r\n"; | |
| foreach ($headers as $header_name => $header_value) { | |
| $debug_info .= sprintf("%s%s\r\n", str_pad(w3_escape_comment($header_name) . ': ', 20), w3_escape_comment($header_value)); | |
| } | |
| } | |
| $debug_info .= '-->'; | |
| return $debug_info; | |
| } | |
| /** | |
| * Sends headers | |
| * | |
| * @param array $headers | |
| * @return boolean | |
| */ | |
| function _headers($headers) { | |
| if (!headers_sent()) { | |
| foreach ($headers as $name => $value) { | |
| $header = ($name == 'Status' ? $value : $name . ': ' . $value); | |
| @header($header); | |
| } | |
| return true; | |
| } | |
| return false; | |
| } | |
| /** | |
| * Sends headers | |
| * @param boolean $is_404 | |
| * @param string $etag | |
| * @param integer $time | |
| * @param string $compression | |
| * @param array $custom_headers | |
| * @return boolean | |
| */ | |
| function _send_headers($is_404, $time, $etag, $compression, $custom_headers = array()) { | |
| $exit = false; | |
| $headers = array(); | |
| $curr_time = time(); | |
| $bc_lifetime = $this->_config->get_integer( | |
| 'browsercache.html.lifetime'); | |
| $expires = (is_null($time)? $curr_time: $time) + $bc_lifetime; | |
| $max_age = ($expires > $curr_time ? $expires - $curr_time : 0); | |
| if ($is_404) { | |
| /** | |
| * Add 404 header | |
| */ | |
| $headers = array_merge($headers, array( | |
| 'Status' => 'HTTP/1.1 404 Not Found' | |
| )); | |
| } elseif ((!is_null($time) && $this->_check_modified_since($time)) || $this->_check_match($etag)) { | |
| /** | |
| * Add 304 header | |
| */ | |
| $headers = array_merge($headers, array( | |
| 'Status' => 'HTTP/1.1 304 Not Modified' | |
| )); | |
| /** | |
| * Don't send content if it isn't modified | |
| */ | |
| $exit = true; | |
| } | |
| if ($this->_config->get_boolean('browsercache.enabled')) { | |
| if ($this->_config->get_boolean('browsercache.html.last_modified')) { | |
| $headers = array_merge($headers, array( | |
| 'Last-Modified' => w3_http_date($time) | |
| )); | |
| } | |
| if ($this->_config->get_boolean('browsercache.html.expires')) { | |
| $headers = array_merge($headers, array( | |
| 'Expires' => w3_http_date($expires) | |
| )); | |
| } | |
| if ($this->_config->get_boolean('browsercache.html.cache.control')) { | |
| switch ($this->_config->get_string('browsercache.html.cache.policy')) { | |
| case 'cache': | |
| $headers = array_merge($headers, array( | |
| 'Pragma' => 'public', | |
| 'Cache-Control' => 'public' | |
| )); | |
| break; | |
| case 'cache_public_maxage': | |
| $headers = array_merge($headers, array( | |
| 'Pragma' => 'public', | |
| 'Cache-Control' => sprintf('max-age=%d, public', $max_age) | |
| )); | |
| break; | |
| case 'cache_validation': | |
| $headers = array_merge($headers, array( | |
| 'Pragma' => 'public', | |
| 'Cache-Control' => 'public, must-revalidate, proxy-revalidate' | |
| )); | |
| break; | |
| case 'cache_noproxy': | |
| $headers = array_merge($headers, array( | |
| 'Pragma' => 'public', | |
| 'Cache-Control' => 'private, must-revalidate' | |
| )); | |
| break; | |
| case 'cache_maxage': | |
| $headers = array_merge($headers, array( | |
| 'Pragma' => 'public', | |
| 'Cache-Control' => sprintf('max-age=%d, public, must-revalidate, proxy-revalidate', $max_age) | |
| )); | |
| break; | |
| case 'no_cache': | |
| $headers = array_merge($headers, array( | |
| 'Pragma' => 'no-cache', | |
| 'Cache-Control' => 'max-age=0, private, no-store, no-cache, must-revalidate' | |
| )); | |
| break; | |
| } | |
| } | |
| if ($this->_config->get_boolean('browsercache.html.etag')) { | |
| $headers = array_merge($headers, array( | |
| 'Etag' => $etag | |
| )); | |
| } | |
| if ($this->_config->get_boolean('browsercache.html.w3tc')) { | |
| $headers = array_merge($headers, array( | |
| 'X-Powered-By' => W3TC_POWERED_BY | |
| )); | |
| } | |
| } | |
| $vary =''; | |
| //compressed && UAG | |
| if ($compression && $this->_get_mobile_group()) { | |
| $vary = 'Accept-Encoding,User-Agent,Cookie'; | |
| $headers = array_merge($headers, array( | |
| 'Content-Encoding' => $compression | |
| )); | |
| //compressed | |
| } elseif ($compression) { | |
| $vary = 'Accept-Encoding'; | |
| $headers = array_merge($headers, array( | |
| 'Content-Encoding' => $compression | |
| )); | |
| //uncompressed && UAG | |
| } elseif ($this->_get_mobile_group()) { | |
| $vary = 'User-Agent,Cookie'; | |
| } | |
| //Add Cookie to vary if user logged in and not previously set | |
| if (!$this->_check_logged_in() && strpos($vary, 'Cookie') === false) { | |
| if ($vary) | |
| $vary .= ',Cookie'; | |
| else | |
| $vary = 'Cookie'; | |
| } | |
| /** | |
| * Add vary header | |
| */ | |
| if ($vary) | |
| $headers = array_merge($headers, array( | |
| 'Vary' => $vary)); | |
| /** | |
| * Add custom headers | |
| */ | |
| $headers = array_merge($headers, $custom_headers); | |
| /** | |
| * Disable caching for preview mode | |
| */ | |
| if (w3_is_preview_mode()) { | |
| $headers = array_merge($headers, array( | |
| 'Pragma' => 'private', | |
| 'Cache-Control' => 'private' | |
| )); | |
| } | |
| /** | |
| * Send headers to client | |
| */ | |
| $result = $this->_headers($headers); | |
| if ($exit) { | |
| exit(); | |
| } | |
| return $result; | |
| } | |
| /** | |
| * Check if content was modified by time | |
| * @param integer $time | |
| * @return boolean | |
| */ | |
| function _check_modified_since($time) { | |
| if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { | |
| $if_modified_since = $_SERVER['HTTP_IF_MODIFIED_SINCE']; | |
| // IE has tacked on extra data to this header, strip it | |
| if (($semicolon = strrpos($if_modified_since, ';')) !== false) { | |
| $if_modified_since = substr($if_modified_since, 0, $semicolon); | |
| } | |
| return ($time == strtotime($if_modified_since)); | |
| } | |
| return false; | |
| } | |
| /** | |
| * Check if content was modified by etag | |
| * @param string $etag | |
| * @return boolean | |
| */ | |
| function _check_match($etag) { | |
| if (!empty($_SERVER['HTTP_IF_NONE_MATCH'])) { | |
| $if_none_match = (get_magic_quotes_gpc() ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : $_SERVER['HTTP_IF_NONE_MATCH']); | |
| $client_etags = explode(',', $if_none_match); | |
| foreach ($client_etags as $client_etag) { | |
| $client_etag = trim($client_etag); | |
| if ($etag == $client_etag) { | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| /** | |
| * Bad Behavior support | |
| * @return void | |
| */ | |
| function _bad_behavior() { | |
| $bb_file = $this->_config->get_cache_option('pgcache.bad_behavior_path'); | |
| if ($bb_file != '') | |
| require_once $bb_file; | |
| } | |
| /** | |
| * Parses dynamic tags | |
| */ | |
| function _parse_dynamic(&$buffer) { | |
| if (!defined('W3TC_DYNAMIC_SECURITY')) | |
| return; | |
| $buffer = preg_replace_callback('~<!--\s*mfunc\s*' . W3TC_DYNAMIC_SECURITY . '(.*)-->(.*)<!--\s*/mfunc\s*' . W3TC_DYNAMIC_SECURITY . '\s*-->~Uis', array( | |
| &$this, | |
| '_parse_dynamic_mfunc' | |
| ), $buffer); | |
| $buffer = preg_replace_callback('~<!--\s*mclude\s*' . W3TC_DYNAMIC_SECURITY . '(.*)-->(.*)<!--\s*/mclude\s*' . W3TC_DYNAMIC_SECURITY . '\s*-->~Uis', array( | |
| &$this, | |
| '_parse_dynamic_mclude' | |
| ), $buffer); | |
| } | |
| /** | |
| * Parse dynamic mfunc callback | |
| * @param array $matches | |
| * @return string | |
| */ | |
| function _parse_dynamic_mfunc($matches) { | |
| $code1 = trim($matches[1]); | |
| $code2 = trim($matches[2]); | |
| $code = ($code1 ? $code1 : $code2); | |
| if ($code) { | |
| $code = trim($code, ';') . ';'; | |
| ob_start(); | |
| $result = eval($code); | |
| $output = ob_get_contents(); | |
| ob_end_clean(); | |
| if ($result === false) { | |
| $output = sprintf('Unable to execute code: %s', htmlspecialchars($code)); | |
| } | |
| } else { | |
| $output = htmlspecialchars('Invalid mfunc tag syntax. The correct format is: <!-- W3TC_DYNAMIC_SECURITY mfunc PHP code --><!-- /mfunc W3TC_DYNAMIC_SECURITY --> or <!-- W3TC_DYNAMIC_SECURITY mfunc -->PHP code<!-- /mfunc W3TC_DYNAMIC_SECURITY -->.'); | |
| } | |
| return $output; | |
| } | |
| /** | |
| * Parse dynamic mclude callback | |
| * @param array $matches | |
| * @return string | |
| */ | |
| function _parse_dynamic_mclude($matches) { | |
| $file1 = trim($matches[1]); | |
| $file2 = trim($matches[2]); | |
| $file = ($file1 ? $file1 : $file2); | |
| if ($file) { | |
| $file = ABSPATH . $file; | |
| if (file_exists($file) && is_readable($file)) { | |
| ob_start(); | |
| include $file; | |
| $output = ob_get_contents(); | |
| ob_end_clean(); | |
| } else { | |
| $output = sprintf('Unable to open file: %s', htmlspecialchars($file)); | |
| } | |
| } else { | |
| $output = htmlspecialchars('Incorrect mclude tag syntax. The correct format is: <!-- mclude W3TC_DYNAMIC_SECURITY path/to/file.php --><!-- /mclude W3TC_DYNAMIC_SECURITY --> or <!-- mclude W3TC_DYNAMIC_SECURITY -->path/to/file.php<!-- /mclude W3TC_DYNAMIC_SECURITY -->.'); | |
| } | |
| return $output; | |
| } | |
| /** | |
| * Checks if buffer has dynamic tags | |
| * | |
| * @param string $buffer | |
| * @return boolean | |
| */ | |
| function _has_dynamic(&$buffer) { | |
| if (!defined('W3TC_DYNAMIC_SECURITY')) | |
| return false; | |
| return preg_match('~<!--\s*m(func|clude)\s*' . W3TC_DYNAMIC_SECURITY . '(.*)-->(.*)<!--\s*/m(func|clude)\s*' . W3TC_DYNAMIC_SECURITY . '\s*-->~Uis', $buffer); | |
| } | |
| /** | |
| * Check whether requested page has content type that can be cached | |
| * @return bool | |
| */ | |
| private function _is_cacheable_content_type() { | |
| $content_type = ''; | |
| $headers = headers_list(); | |
| foreach($headers as $header) { | |
| $header = strtolower($header); | |
| if (stripos($header, 'content-type') !== false) { | |
| $temp = explode(';', $header); | |
| $temp = array_shift($temp); | |
| $temp = explode(':', $temp); | |
| $content_type = trim($temp[1]); | |
| } | |
| } | |
| $cache_headers = apply_filters('w3tc_is_cacheable_content_type', | |
| array('application/json', 'text/html', 'text/xml', 'application/xhtml+xml')); | |
| return in_array($content_type, $cache_headers); | |
| } | |
| private function _check_query_string() { | |
| $accept_qs = $this->_config->get_array('pgcache.accept.qs'); | |
| foreach ($_GET as $key => $value) { | |
| if (!in_array(strtolower($key), $accept_qs)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| /** | |
| * | |
| */ | |
| public function delayed_cache_print() { | |
| if ($this->_late_init && $this->_caching) { | |
| $this->process_cached_page($this->_cached_data); | |
| exit; | |
| } | |
| } | |
| /** | |
| * @param $buffer | |
| * @param $has_dynamic | |
| * @return array | |
| */ | |
| private function _can_cache_buffer(&$buffer, $has_dynamic) { | |
| $mobile_group = $this->_get_mobile_group(); | |
| $referrer_group = $this->_get_referrer_group(); | |
| $encryption = $this->_get_encryption(); | |
| $compression = $this->_get_compression(); | |
| $compressions = $this->_get_compressions(); | |
| /** | |
| * Don't use compression for debug mode or dynamic tags | |
| * because we need to modify buffer before send it to client | |
| */ | |
| $raw = ($this->_debug || $has_dynamic); | |
| if ($raw) { | |
| $compression = false; | |
| $compressions = array(false); | |
| } | |
| $content_type = ''; | |
| $is_404 = (function_exists('is_404') ? is_404() : false); | |
| $headers = $this->_get_cached_headers(); | |
| if ($this->_enhanced_mode && !$this->_late_init) { | |
| $this->_check_rules_present(); | |
| if (isset($headers['Content-Type'])) | |
| $content_type = $headers['Content-Type']; | |
| } | |
| $time = time(); | |
| $cache = $this->_get_cache(); | |
| /** | |
| * Store different versions of cache | |
| */ | |
| $buffers = array(); | |
| $group = '0'; | |
| if (!isset($this->_sitemap_matched)) { | |
| $sitemap_regex = $this->_config->get_string('pgcache.purge.sitemap_regex'); | |
| if ($sitemap_regex && preg_match('/' . $sitemap_regex . '/', basename($this->_request_uri))) { | |
| $group = 'sitemaps'; | |
| $this->_sitemap_matched = true; | |
| } | |
| } elseif ($this->_sitemap_matched) | |
| $group = 'sitemaps'; | |
| foreach ($compressions as $_compression) { | |
| $_page_key = $this->_get_page_key($mobile_group, | |
| $referrer_group, $encryption, $_compression, | |
| $content_type); | |
| /** | |
| * Compress content | |
| */ | |
| $buffers[$_compression] = $buffer; | |
| $this->_compress($buffers[$_compression], $_compression); | |
| /** | |
| * Store cache data | |
| */ | |
| $_data = array( | |
| '404' => $is_404, | |
| 'headers' => $headers, | |
| 'time' => $time, | |
| 'content' => &$buffers[$_compression] | |
| ); | |
| $cache->set($_page_key, $_data, $this->_lifetime, $group); | |
| } | |
| /** | |
| * Change buffer if using compression | |
| */ | |
| if ($compression && isset($buffers[$compression])) { | |
| $buffer = & $buffers[$compression]; | |
| } | |
| /** | |
| * Calculate content etag | |
| */ | |
| $etag = md5($buffer); | |
| /** | |
| * Send headers | |
| */ | |
| $this->_send_headers($is_404, $time, $etag, $compression, $headers); | |
| if ($raw) { | |
| if ($this->_debug && w3_can_print_comment($buffer)) { | |
| $buffer = $this->_debugging_caching($buffer, $mobile_group, $referrer_group, $encryption, | |
| $compression, $content_type); | |
| } | |
| /** | |
| * Don't use shutdown function below | |
| */ | |
| if (!$has_dynamic) { | |
| $this->_compress($buffer, $compression); | |
| return array($compression, $buffer); | |
| } | |
| return array($compression, $buffer); | |
| } | |
| return array($compression, $buffer); | |
| } | |
| /** | |
| * @param $buffer | |
| * @param $compression | |
| * @return string | |
| */ | |
| private function _debugging_not_caching(&$buffer, $compression) { | |
| $mobile_group = $this->_get_mobile_group(); | |
| $referrer_group = $this->_get_referrer_group(); | |
| $encryption = $this->_get_encryption(); | |
| /** | |
| * Set page key for debug | |
| */ | |
| $this->_page_key = $this->_get_page_key($mobile_group, | |
| $referrer_group, $encryption, $compression); | |
| /** | |
| * Append debug info | |
| */ | |
| $time_total = w3_microtime() - $this->_time_start; | |
| $debug_info = $this->_get_debug_info(false, $this->cache_reject_reason, false, $time_total); | |
| $buffer .= "\r\n\r\n" . $debug_info; | |
| return $buffer; | |
| } | |
| /** | |
| * @param $buffer | |
| * @param $mobile_group | |
| * @param $referrer_group | |
| * @param $encryption | |
| * @param $compression | |
| * @param $content_type | |
| * @return string | |
| */ | |
| private function _debugging_caching(&$buffer, $mobile_group, $referrer_group, $encryption, | |
| $compression = '', $content_type = '') { | |
| /** | |
| * Set page key for debug | |
| */ | |
| $this->_page_key = $this->_get_page_key($mobile_group, | |
| $referrer_group, $encryption, $compression, | |
| $content_type); | |
| /** | |
| * Append debug info | |
| */ | |
| $time_total = w3_microtime() - $this->_time_start; | |
| $debug_info = $this->_get_debug_info(true, '', false, $time_total); | |
| $buffer .= "\r\n\r\n" . $debug_info; | |
| return $buffer; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment