View file File name : OptimizerWebPageCache.php Content :<?php namespace TenWebOptimizer\WebPageCache; use TenWebOptimizer\OptimizerSettings; /* * Base class other (more-specific) classes inherit from. */ if (!defined('ABSPATH')) { exit; } /** * The cache structure * wp-content/tw_optimize/page_cache/{host}/{request_uri}/{query_args} * e.g. wp-content/tw_optimize/page_cache/{wp.local}/sample-page/#lang=us * * */ class OptimizerWebPageCache { protected static $instance = null; protected static $config = null; protected $accept_gzip = false; protected $validator = null; protected $cache_dir = null; protected $cache_file = null; protected $cache_headers_file = null; protected $cache_file_gzip = null; public function __construct() { $two_settings = self::get_config('two_settings'); $two_settings = json_decode($two_settings, true); if (!isset($two_settings['two_connected']) || $two_settings['two_connected'] !== '1') { return; } // Additional check for the old users who have cache enabled with old configs. if (!defined('TWO_PLUGIN_DIR_CACHE')) { define('TWO_PLUGIN_DIR_CACHE', WP_CONTENT_DIR . '/plugins/tenweb-speed-optimizer/'); } require_once __DIR__ . '/OptimizerWebPageValidations.php'; $this->validator = OptimizerWebPageValidations::get_instance(); if ($this->can_process_page()) { $this->init(); $this->process_page(); } } protected function process_page() { $file_to_serve = $this->get_cached_file_to_serve(); if ($file_to_serve !== null && !$this->maybe_clear_cache($file_to_serve)) { $this->serve_cached_file($file_to_serve); } // cache content ob_start([$this, 'cache_content']); } protected function init() { global $TwoSettings; $accept_encoding_value = ''; if (isset($this->request_headers()['ACCEPT-ENCODING'])) { $accept_encoding = $this->request_headers()['ACCEPT-ENCODING']; $accept_encoding_value = $accept_encoding && strpos($accept_encoding, 'gzip') !== false; } $serve_gzip = $TwoSettings->get_settings('two_serve_gzip') == 'yes' ? true : false; $serve_gzip_in_any_case = $TwoSettings->get_settings('two_empty_encoding_serve_gzip') == 'yes' ? true : false; if ($serve_gzip && ($accept_encoding_value || $serve_gzip_in_any_case)) { $this->accept_gzip = true; } $this->cache_dir = $this->get_cache_file_dir(); // don't use index.html to be able to use delete_all_cache_file() function $this->cache_file = $this->cache_dir . '/source.html'; $this->cache_headers_file = $this->cache_dir . '/headers.json'; $this->cache_file_gzip = $this->cache_dir . '/source.html.gzip'; } protected function request_headers() { $arh = []; $rx_http = '/\AHTTP_/'; foreach ($_SERVER as $key => $val) { if (preg_match($rx_http, $key)) { $arh_key = preg_replace($rx_http, '', $key); // do some nasty string manipulations to restore the original letter case // this should work in most cases $rx_matches = explode('_', $arh_key); if (count($rx_matches) > 0 and strlen($arh_key) > 2) { foreach ($rx_matches as $ak_key => $ak_val) { $rx_matches[$ak_key] = ucfirst($ak_val); } $arh_key = implode('-', $rx_matches); } $arh[$arh_key] = $val; } } return $arh; } protected function get_cached_file_to_serve() { if ($this->accept_gzip && file_exists($this->cache_file_gzip) && is_readable($this->cache_file_gzip)) { return $this->cache_file_gzip; } if (file_exists($this->cache_file) && is_readable($this->cache_file)) { return $this->cache_file; } return null; } protected function serve_cached_file($file_to_serve) { $file_time = filemtime($file_to_serve); if (!isset($_COOKIE['wcml_client_currency'])) { // if this is wcml_client_currency cookie, browser should not use it's cache header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $file_time) . ' GMT'); } header('X-TWO-PAGE-CACHED: 1'); if (file_exists($this->cache_headers_file)) { $cache_headers = json_decode(file_get_contents($this->cache_headers_file)); // phpcs:ignore foreach ($cache_headers as $header) { header($header); } } // If the cache has not been modified since $_SERVER["HTTP_IF_MODIFIED_SINCE"], then send 304 not modified and // browser use cached html (cached in browser) if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && (strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $file_time)) { // phpcs:ignore header(strip_tags($_SERVER['SERVER_PROTOCOL']) . ' 304 Not Modified', true, 304); // phpcs:ignore header('Expires: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-cache, must-revalidate'); die; } if (substr($file_to_serve, -5) === '.gzip') { readgzfile($file_to_serve); } else { readfile($file_to_serve); } die; } protected function cache_content($buffer) { if (!is_dir($this->cache_dir)) { if (!mkdir($concurrentDirectory = $this->cache_dir, 0777, true) && !is_dir($concurrentDirectory)) { // phpcs:ignore return $buffer; } } if (!is_writable($this->cache_dir) || !is_readable($this->cache_dir)) { // phpcs:ignore return $buffer; } if (!$this->validator->valid_buffer_to_cache($buffer)) { return $buffer; } $buffer .= "\n<!-- =^..^= Cached =^..^= -->"; file_put_contents($this->cache_file, $buffer); // phpcs:ignore file_put_contents($this->cache_headers_file, json_encode(headers_list())); // phpcs:ignore file_put_contents($this->cache_file_gzip, gzencode($buffer)); // phpcs:ignore header('X-TWO-PAGE-CACHED: 0'); return $buffer; } protected function can_process_page() { if (!$this->validator->allowed_request_method()) { return false; } if (!$this->validator->allowed_wp_page()) { return false; } if ($this->validator->rejected_file()) { return false; } if ($this->validator->rejected_cookie()) { return false; } if ($this->validator->reject_uri()) { return false; } if ($this->validator->reject_ua()) { return false; } if (!$this->validator->allowed_query_string()) { return false; } if ($this->validator->has_donotcachepage()) { return false; } require_once TWO_PLUGIN_DIR_CACHE . '/includes/OptimizerUtils.php'; require_once TWO_PLUGIN_DIR_CACHE . '/includes/OptimizerCache.php'; require_once TWO_PLUGIN_DIR_CACHE . '/includes/OptimizerSettings.php'; require_once TWO_PLUGIN_DIR_CACHE . '/includes/OptimizerUrl.php'; global $TwoSettings; $TwoSettings = OptimizerSettings::get_instance(); if (!$this->validator->allowed_by_optimizer()) { return false; } return true; } protected function maybe_clear_cache($file_to_serve) { global $TwoSettings; $cache_life_time = intval($TwoSettings->get_settings('two_page_cache_life_time')); if (!$cache_life_time) { $cache_life_time = $TwoSettings->get_default_setting('two_page_cache_life_time'); } if (time() - filemtime($file_to_serve) < $cache_life_time) { return false; } $is_home = self::is_home(explode('?', strip_tags($_SERVER['REQUEST_URI']))[0]); // phpcs:ignore $cache_dir_without_get_args = explode('/#', $this->cache_dir)[0]; if (substr($cache_dir_without_get_args, -1) !== '/') { $cache_dir_without_get_args .= '/'; } self::delete_page_cache($cache_dir_without_get_args, $is_home); return true; } public function get_cache_file_dir() { $request_uri = explode('?', strip_tags($_SERVER['REQUEST_URI']))[0]; // phpcs:ignore $cache_file_dir = self::get_cache_dir_for_page(strip_tags($_SERVER['HTTP_HOST']), $request_uri); // phpcs:ignore $cookies = $this->validator->get_cookies(); if (!empty($cookies)) { $cookie_string = '@'; foreach ($cookies as $cookie_name => $cookie_value) { $cookie_string .= $cookie_name . '=' . $cookie_value . '&'; } $cookie_string = rtrim($cookie_string, '&'); $cache_file_dir .= $cookie_string . '/'; } $query_params = $this->validator->get_query_params(); if (empty($query_params)) { return $cache_file_dir; } $query_string = '#'; foreach ($query_params as $param_name => $param_value) { $query_string .= $param_name . '=' . $param_value . '&'; } $query_string = rtrim($query_string, '&'); return $cache_file_dir . $query_string . '/'; } public static function delete_cache_by_post_id($post_id) { self::delete_cache_by_url(get_permalink($post_id)); } public static function delete_cache_by_url($url) { $parsed_url = wp_parse_url($url); $is_home_url = self::is_home($parsed_url['path']); self::delete_page_cache(self::get_cache_dir_for_page_from_url($url), $is_home_url); } public static function delete_page_cache($dir, $is_home_url) { if (is_dir($dir)) { foreach (scandir($dir) as $file) { if ($file === '.' || $file === '..') { continue; } $path = $dir . $file; if (is_file($path)) { unlink($path); // phpcs:ignore } else { $not_allow_delete = []; if ($is_home_url) { if (substr($file, 0, 1) === '#') { \TenWebOptimizer\OptimizerUtils::delete_all_cache_file($path, $not_allow_delete); } } else { \TenWebOptimizer\OptimizerUtils::delete_all_cache_file($path, $not_allow_delete); } } } // if dir is empty if (is_dir($dir)) { $dir_arr = scandir($dir); if (is_array($dir_arr) && count($dir_arr) === 2) { rmdir($dir); // phpcs:ignore } } } } public static function delete_all_cached_pages() { \TenWebOptimizer\OptimizerUtils::delete_all_cache_file(TENWEB_SO_PAGE_CACHE_DIR); return !is_dir(TENWEB_SO_PAGE_CACHE_DIR); } public static function get_cache_dir_for_page($host, $request_uri) { $is_home_page = self::is_home($request_uri); $cache_dir_name = ''; if (!$is_home_page) { $cache_dir_name = rtrim($request_uri, '/'); $cache_dir_name = ltrim($cache_dir_name, '/'); } // add $host, to support multisite to $cache_dir = TENWEB_SO_PAGE_CACHE_DIR . $host . '/'; if ($cache_dir_name) { $cache_dir .= $cache_dir_name . '/'; } return $cache_dir; } public static function get_cache_dir_for_page_from_url($url) { $parsed_url = wp_parse_url($url); return self::get_cache_dir_for_page($parsed_url['host'], $parsed_url['path']); } public static function get_config($config_name) { if (self::$config === null) { self::$config = json_decode(file_get_contents(WP_CONTENT_DIR . '/10web-page-cache-config/config.json')); } return self::$config->$config_name; } public static function is_home($request_uri) { return empty(ltrim($request_uri, '/')); } public static function get_instance() { if (null == self::$instance) { self::$instance = new self(); } return self::$instance; } }