add wp-rocket
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
namespace WP_Rocket\Buffer;
|
||||
|
||||
use WP_Rocket\Logger\Logger;
|
||||
|
||||
/**
|
||||
* Handle page cache and optimizations.
|
||||
*
|
||||
* @since 3.3
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
abstract class Abstract_Buffer {
|
||||
|
||||
/**
|
||||
* Process identifier used by the logger.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.3
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $process_id;
|
||||
|
||||
/**
|
||||
* Instance of the Tests class.
|
||||
*
|
||||
* @var Tests
|
||||
* @since 3.3
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $tests;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param Tests $tests Tests instance.
|
||||
*/
|
||||
public function __construct( Tests $tests ) {
|
||||
$this->tests = $tests;
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** PROCESS ================================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Launch the process if the tests succeed.
|
||||
* This should be the first thing to use after initializing the class.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @see $this->tests->can_init_process()
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
abstract public function maybe_init_process();
|
||||
|
||||
/**
|
||||
* Process the page buffer if the 2nd set of tests succeed.
|
||||
* It should be used like this:
|
||||
* ob_start( [ $this, 'maybe_process_buffer' ] );
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @see $this->tests->can_process_buffer()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $buffer The buffer content.
|
||||
* @return string The buffered content
|
||||
*/
|
||||
abstract public function maybe_process_buffer( $buffer );
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** LOG ===================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Log the last test "error".
|
||||
*
|
||||
* @since 3.3
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected function log_last_test_error() {
|
||||
$error = $this->tests->get_last_error();
|
||||
|
||||
$this->log( $error['message'], $error['data'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Log events.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $message A message to log.
|
||||
* @param array $data Related data.
|
||||
* @param string $type Event type to log. Possible values are 'info', 'error', and 'debug' (default).
|
||||
*/
|
||||
protected function log( $message, $data = [], $type = 'debug' ) {
|
||||
$data = array_merge(
|
||||
[
|
||||
$this->get_process_id(),
|
||||
'request_uri' => $this->tests->get_raw_request_uri(),
|
||||
],
|
||||
$data
|
||||
);
|
||||
|
||||
if ( isset( $data['cookies'] ) ) {
|
||||
$data['cookies'] = Logger::remove_auth_cookies( $data['cookies'] );
|
||||
}
|
||||
|
||||
switch ( $type ) {
|
||||
case 'info':
|
||||
Logger::info( $message, $data );
|
||||
break;
|
||||
case 'error':
|
||||
Logger::error( $message, $data );
|
||||
break;
|
||||
default:
|
||||
Logger::debug( $message, $data );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the process identifier.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_process_id() {
|
||||
return $this->process_id . ' - Thread #' . Logger::get_thread_id();
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** VARIOUS TOOLS =========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if the page content is HTML.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $buffer The buffer content.
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_html( $buffer ) {
|
||||
return preg_match( '/<\/html>/i', $buffer );
|
||||
}
|
||||
}
|
704
wp-content/plugins/wp-rocket/inc/classes/Buffer/class-cache.php
Normal file
704
wp-content/plugins/wp-rocket/inc/classes/Buffer/class-cache.php
Normal file
@@ -0,0 +1,704 @@
|
||||
<?php
|
||||
namespace WP_Rocket\Buffer;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Handle page cache.
|
||||
*
|
||||
* @since 3.3
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Cache extends Abstract_Buffer {
|
||||
|
||||
/**
|
||||
* Process identifier used by the logger.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.3
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $process_id = 'caching process';
|
||||
|
||||
/**
|
||||
* Tests instance
|
||||
*
|
||||
* @var Tests
|
||||
*/
|
||||
protected $tests;
|
||||
|
||||
/**
|
||||
* Config instance
|
||||
*
|
||||
* @var Config
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* Path to the directory containing the cache files.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.3
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
private $cache_dir_path;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param Tests $tests Tests instance.
|
||||
* @param Config $config Config instance.
|
||||
* @param array $args {
|
||||
* An array of arguments.
|
||||
*
|
||||
* @type string $cache_dir_path Path to the directory containing the cache files.
|
||||
* }
|
||||
*/
|
||||
public function __construct( Tests $tests, Config $config, array $args ) {
|
||||
$this->config = $config;
|
||||
$this->cache_dir_path = rtrim( $args['cache_dir_path'], '/' ) . '/';
|
||||
|
||||
parent::__construct( $tests );
|
||||
|
||||
$this->log( 'CACHING PROCESS STARTED.', [], 'info' );
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** CACHE =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Serve the cache file if it exists. If not, init the buffer.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function maybe_init_process() {
|
||||
if ( ! $this->tests->can_init_process() ) {
|
||||
$this->define_donotoptimize_true();
|
||||
$this->log_last_test_error();
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve the cache file if it exists.
|
||||
*/
|
||||
$cache_filepath = $this->get_cache_path();
|
||||
|
||||
$this->log(
|
||||
'Looking for cache file.',
|
||||
[
|
||||
'path' => $cache_filepath,
|
||||
]
|
||||
);
|
||||
|
||||
$cache_filepath_gzip = $cache_filepath . '_gzip';
|
||||
$accept_encoding = $this->config->get_server_input( 'HTTP_ACCEPT_ENCODING' );
|
||||
$accept_gzip = $accept_encoding && false !== strpos( $accept_encoding, 'gzip' );
|
||||
|
||||
// Check if cache file exist.
|
||||
if ( $accept_gzip && is_readable( $cache_filepath_gzip ) ) {
|
||||
$this->serve_gzip_cache_file( $cache_filepath_gzip );
|
||||
}
|
||||
|
||||
if ( is_readable( $cache_filepath ) ) {
|
||||
$this->serve_cache_file( $cache_filepath );
|
||||
}
|
||||
|
||||
// Maybe we're looking for a webp file.
|
||||
$cache_filename = basename( $cache_filepath );
|
||||
|
||||
if ( strpos( $cache_filename, '-webp' ) !== false ) {
|
||||
// We're looking for a webp file that doesn't exist: try to locate any `.no-webp` file.
|
||||
$cache_dir_path = rtrim( dirname( $cache_filepath ), '/\\' ) . DIRECTORY_SEPARATOR;
|
||||
|
||||
if ( file_exists( $cache_dir_path . '.no-webp' ) ) {
|
||||
// We have a `.no-webp` file: try to deliver a non-webp cache file.
|
||||
$cache_filepath = $cache_dir_path . str_replace( '-webp', '', $cache_filename );
|
||||
$cache_filepath_gzip = $cache_filepath . '_gzip';
|
||||
|
||||
$this->log(
|
||||
'Looking for non-webp cache file.',
|
||||
[
|
||||
'path' => $cache_filepath,
|
||||
]
|
||||
);
|
||||
|
||||
// Try to deliver the non-webp version instead.
|
||||
if ( $accept_gzip && is_readable( $cache_filepath_gzip ) ) {
|
||||
$this->serve_gzip_cache_file( $cache_filepath_gzip );
|
||||
}
|
||||
|
||||
if ( is_readable( $cache_filepath ) ) {
|
||||
$this->serve_cache_file( $cache_filepath );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* No cache file yet: launch caching process.
|
||||
*/
|
||||
$this->log(
|
||||
'Start buffer.',
|
||||
[
|
||||
'path' => $cache_filepath,
|
||||
]
|
||||
);
|
||||
|
||||
ob_start( [ $this, 'maybe_process_buffer' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve a cache file.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $cache_filepath Path to the cache file.
|
||||
*/
|
||||
private function serve_cache_file( $cache_filepath ) {
|
||||
header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', filemtime( $cache_filepath ) ) . ' GMT' );
|
||||
|
||||
$if_modified_since = $this->get_if_modified_since();
|
||||
|
||||
// Checking if the client is validating his cache and if it is current.
|
||||
if ( $if_modified_since && ( strtotime( $if_modified_since ) === @filemtime( $cache_filepath ) ) ) {
|
||||
// Client's cache is current, so we just respond '304 Not Modified'.
|
||||
header( $this->config->get_server_input( 'SERVER_PROTOCOL', '' ) . ' 304 Not Modified', true, 304 );
|
||||
header( 'Expires: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
|
||||
header( 'Cache-Control: no-cache, must-revalidate' );
|
||||
|
||||
$this->log(
|
||||
'Serving `304` cache file.',
|
||||
[
|
||||
'path' => $cache_filepath,
|
||||
'modified' => $if_modified_since,
|
||||
],
|
||||
'info'
|
||||
);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Serve the cache if file isn't store in the client browser cache.
|
||||
readfile( $cache_filepath );
|
||||
|
||||
$this->log(
|
||||
'Serving cache file.',
|
||||
[
|
||||
'path' => $cache_filepath,
|
||||
'modified' => $if_modified_since,
|
||||
],
|
||||
'info'
|
||||
);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve a gzipped cache file.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $cache_filepath Path to the gzip cache file.
|
||||
*/
|
||||
private function serve_gzip_cache_file( $cache_filepath ) {
|
||||
header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', filemtime( $cache_filepath ) ) . ' GMT' );
|
||||
|
||||
$if_modified_since = $this->get_if_modified_since();
|
||||
|
||||
// Checking if the client is validating his cache and if it is current.
|
||||
if ( $if_modified_since && ( strtotime( $if_modified_since ) === @filemtime( $cache_filepath ) ) ) {
|
||||
// Client's cache is current, so we just respond '304 Not Modified'.
|
||||
header( $this->config->get_server_input( 'SERVER_PROTOCOL', '' ) . ' 304 Not Modified', true, 304 );
|
||||
header( 'Expires: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
|
||||
header( 'Cache-Control: no-cache, must-revalidate' );
|
||||
|
||||
$this->log(
|
||||
'Serving `304` gzip cache file.',
|
||||
[
|
||||
'path' => $cache_filepath,
|
||||
'modified' => $if_modified_since,
|
||||
],
|
||||
'info'
|
||||
);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Serve the cache if file isn't store in the client browser cache.
|
||||
readgzfile( $cache_filepath );
|
||||
|
||||
$this->log(
|
||||
'Serving gzip cache file.',
|
||||
[
|
||||
'path' => $cache_filepath,
|
||||
'modified' => $if_modified_since,
|
||||
],
|
||||
'info'
|
||||
);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe cache the page content.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $buffer The buffer content.
|
||||
* @return string The buffered content.
|
||||
*/
|
||||
public function maybe_process_buffer( $buffer ) {
|
||||
if ( ! $this->tests->can_process_buffer( $buffer ) ) {
|
||||
$this->log_last_test_error();
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
$footprint = '';
|
||||
$is_html = $this->is_html( $buffer );
|
||||
|
||||
if ( ! static::can_generate_caching_files() ) {
|
||||
// Not allowed to generate cache files.
|
||||
if ( $is_html ) {
|
||||
$footprint = $this->get_rocket_footprint();
|
||||
}
|
||||
|
||||
$this->log(
|
||||
'Page not cached by filter.',
|
||||
[
|
||||
'filter' => 'do_rocket_generate_caching_files',
|
||||
]
|
||||
);
|
||||
return $buffer . $footprint;
|
||||
}
|
||||
|
||||
$webp_enabled = preg_match( '@<!-- Rocket (has|no) webp -->@', $buffer, $webp_tag );
|
||||
$has_webp = ! empty( $webp_tag ) ? 'has' === $webp_tag[1] : false;
|
||||
$cache_filepath = $this->get_cache_path( [ 'webp' => $has_webp ] );
|
||||
$cache_dir_path = dirname( $cache_filepath );
|
||||
|
||||
// Create cache folders.
|
||||
rocket_mkdir_p( $cache_dir_path );
|
||||
|
||||
if ( $is_html ) {
|
||||
$footprint = $this->get_rocket_footprint( time() );
|
||||
}
|
||||
|
||||
// Webp request.
|
||||
if ( $webp_enabled ) {
|
||||
$buffer = str_replace( $webp_tag[0], '', $buffer );
|
||||
|
||||
if ( ! $has_webp ) {
|
||||
// The buffer doesn’t contain webp files.
|
||||
$cache_dir_path = rtrim( dirname( $cache_filepath ), '/\\' );
|
||||
|
||||
$this->maybe_create_nowebp_file( $cache_dir_path );
|
||||
}
|
||||
}
|
||||
|
||||
$this->write_cache_file( $cache_filepath, $buffer . $footprint );
|
||||
$this->maybe_create_nginx_mobile_file( $cache_dir_path );
|
||||
|
||||
// Send headers with the last modified time of the cache file.
|
||||
if ( file_exists( $cache_filepath ) ) {
|
||||
header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', filemtime( $cache_filepath ) ) . ' GMT' );
|
||||
}
|
||||
|
||||
if ( $is_html ) {
|
||||
$footprint = $this->get_rocket_footprint();
|
||||
}
|
||||
|
||||
$this->log(
|
||||
'Page cached.',
|
||||
[
|
||||
'path' => $cache_filepath,
|
||||
],
|
||||
'info'
|
||||
);
|
||||
|
||||
return $buffer . $footprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the cache file(s)
|
||||
*
|
||||
* @since 3.5
|
||||
* @author Remy Perona
|
||||
*
|
||||
* @param string $cache_filepath Absolute path to the cache file.
|
||||
* @param string $content Content to write in the cache file.
|
||||
* @return void
|
||||
*/
|
||||
private function write_cache_file( $cache_filepath, $content ) {
|
||||
$gzip_filepath = $cache_filepath . '_gzip';
|
||||
$temp_filepath = $cache_filepath . '_temp';
|
||||
$temp_gzip_filepath = $gzip_filepath . '_temp';
|
||||
|
||||
if ( rocket_direct_filesystem()->exists( $temp_filepath ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the cache file.
|
||||
rocket_put_content( $temp_filepath, $content );
|
||||
rocket_direct_filesystem()->move( $temp_filepath, $cache_filepath, true );
|
||||
|
||||
if ( function_exists( 'gzencode' ) ) {
|
||||
/**
|
||||
* Filters the Gzip compression level to use for the cache file
|
||||
*
|
||||
* @param int $compression_level Compression level between 0 and 9.
|
||||
*/
|
||||
$compression_level = apply_filters( 'rocket_gzencode_level_compression', 6 );
|
||||
|
||||
rocket_put_content( $temp_gzip_filepath, gzencode( $content, $compression_level ) );
|
||||
rocket_direct_filesystem()->move( $temp_gzip_filepath, $gzip_filepath, true );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the cache file.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $args {
|
||||
* A list of arguments.
|
||||
*
|
||||
* @type bool $webp Set to false to prevent adding the part related to webp.
|
||||
* }
|
||||
* @return string
|
||||
*/
|
||||
public function get_cache_path( $args = [] ) {
|
||||
$args = array_merge(
|
||||
[
|
||||
'webp' => true,
|
||||
],
|
||||
$args
|
||||
);
|
||||
$cookies = $this->tests->get_cookies();
|
||||
$request_uri_path = $this->get_request_cache_path( $cookies );
|
||||
$filename = 'index';
|
||||
|
||||
$filename = $this->maybe_mobile_filename( $filename );
|
||||
|
||||
// Rename the caching filename for SSL URLs.
|
||||
if ( is_ssl() && $this->config->get_config( 'cache_ssl' ) ) {
|
||||
$filename .= '-https';
|
||||
}
|
||||
|
||||
if ( $args['webp'] ) {
|
||||
$filename = $this->maybe_webp_filename( $filename );
|
||||
}
|
||||
|
||||
$filename = $this->maybe_dynamic_cookies_filename( $filename, $cookies );
|
||||
|
||||
// Ensure proper formatting of the path.
|
||||
$request_uri_path = preg_replace_callback( '/%[0-9A-F]{2}/', [ $this, 'reset_lowercase' ], $request_uri_path );
|
||||
// Directories in Windows can't contain question marks.
|
||||
$request_uri_path = str_replace( '?', '#', $request_uri_path );
|
||||
// Limit filename max length to 255 characters.
|
||||
$request_uri_path .= '/' . substr( $filename, 0, 250 ) . '.html';
|
||||
|
||||
return $request_uri_path;
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** VARIOUS TOOLS =========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/**
|
||||
* Declares and sets value of constant preventing Optimizations.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
final private function define_donotoptimize_true() {
|
||||
if ( ! defined( 'DONOTROCKETOPTIMIZE' ) ) {
|
||||
define( 'DONOTROCKETOPTIMIZE', true ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets If-modified-since header value
|
||||
*
|
||||
* @since 3.3
|
||||
* @access private
|
||||
* @author Remy Perona
|
||||
* @return string
|
||||
*/
|
||||
private function get_if_modified_since() {
|
||||
if ( function_exists( 'apache_request_headers' ) ) {
|
||||
$headers = apache_request_headers();
|
||||
|
||||
return isset( $headers['If-Modified-Since'] ) ? $headers['If-Modified-Since'] : '';
|
||||
}
|
||||
|
||||
return $this->config->get_server_input( 'HTTP_IF_MODIFIED_SINCE', '' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get WP Rocket footprint
|
||||
*
|
||||
* @since 3.0.5 White label footprint if WP_ROCKET_WHITE_LABEL_FOOTPRINT is defined.
|
||||
* @since 2.0
|
||||
*
|
||||
* @param int $time UNIX timestamp when the cache file was saved.
|
||||
* @return string The footprint that will be printed
|
||||
*/
|
||||
private function get_rocket_footprint( $time = '' ) {
|
||||
$footprint = defined( 'WP_ROCKET_WHITE_LABEL_FOOTPRINT' ) ?
|
||||
"\n" . '<!-- Cached for great performance' :
|
||||
"\n" . '<!-- This website is like a Rocket, isn\'t it? Performance optimized by ' . WP_ROCKET_PLUGIN_NAME . '. Learn more: https://wp-rocket.me';
|
||||
if ( ! empty( $time ) ) {
|
||||
$footprint .= ' - Debug: cached@' . $time;
|
||||
}
|
||||
$footprint .= ' -->';
|
||||
return $footprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hidden empty file for mobile detection on NGINX with the Rocket NGINX configuration.
|
||||
*
|
||||
* @param string $cache_dir_path Path to the current cache directory.
|
||||
* @return void
|
||||
*/
|
||||
private function maybe_create_nginx_mobile_file( $cache_dir_path ) {
|
||||
global $is_nginx;
|
||||
|
||||
if ( ! $this->config->get_config( 'do_caching_mobile_files' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $is_nginx ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$nginx_mobile_detect = $cache_dir_path . '/.mobile-active';
|
||||
|
||||
if ( rocket_direct_filesystem()->exists( $nginx_mobile_detect ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
rocket_direct_filesystem()->touch( $nginx_mobile_detect );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hidden empty file when webp is enabled but the buffer doesn’t contain webp files.
|
||||
*
|
||||
* @since 3.4
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $cache_dir_path Path to the current cache directory (without trailing slah).
|
||||
*/
|
||||
private function maybe_create_nowebp_file( $cache_dir_path ) {
|
||||
$nowebp_filepath = $cache_dir_path . DIRECTORY_SEPARATOR . '.no-webp';
|
||||
|
||||
if ( rocket_direct_filesystem()->exists( $nowebp_filepath ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
rocket_direct_filesystem()->touch( $nowebp_filepath );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if generating cache files is allowed.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function can_generate_caching_files() {
|
||||
/**
|
||||
* Allow to the generate the caching file.
|
||||
*
|
||||
* @since 2.5
|
||||
*
|
||||
* @param bool True will force the cache file generation.
|
||||
*/
|
||||
return (bool) apply_filters( 'do_rocket_generate_caching_files', true ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base cache path for the current request
|
||||
*
|
||||
* @since 3.3
|
||||
* @author Remy Perona
|
||||
*
|
||||
* @param array $cookies Cookies for the current request.
|
||||
* @return string
|
||||
*/
|
||||
private function get_request_cache_path( $cookies ) {
|
||||
$host = $this->config->get_host();
|
||||
|
||||
if ( $this->config->get_config( 'url_no_dots' ) ) {
|
||||
$host = str_replace( '.', '_', $host );
|
||||
}
|
||||
|
||||
$request_uri = $this->tests->get_clean_request_uri();
|
||||
$cookie_hash = $this->config->get_config( 'cookie_hash' );
|
||||
$logged_in_cookie = $this->config->get_config( 'logged_in_cookie' );
|
||||
$logged_in_cookie_no_hash = str_replace( $cookie_hash, '', $logged_in_cookie );
|
||||
|
||||
// Get cache folder of host name.
|
||||
if ( $logged_in_cookie && isset( $cookies[ $logged_in_cookie ] ) && ! $this->tests->has_rejected_cookie( $logged_in_cookie_no_hash ) ) {
|
||||
if ( $this->config->get_config( 'common_cache_logged_users' ) ) {
|
||||
return $this->cache_dir_path . $host . '-loggedin' . rtrim( $request_uri, '/' );
|
||||
}
|
||||
|
||||
$user_key = explode( '|', $cookies[ $logged_in_cookie ] );
|
||||
$user_key = reset( $user_key );
|
||||
$user_key = $user_key . '-' . $this->config->get_config( 'secret_cache_key' );
|
||||
|
||||
// Get cache folder of host name.
|
||||
return $this->cache_dir_path . $host . '-' . $user_key . rtrim( $request_uri, '/' );
|
||||
}
|
||||
|
||||
return $this->cache_dir_path . $host . rtrim( $request_uri, '/' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the filename if the request is from a mobile device.
|
||||
*
|
||||
* @since 3.3
|
||||
* @author Remy Perona
|
||||
*
|
||||
* @param string $filename Cache filename.
|
||||
* @return string
|
||||
*/
|
||||
private function maybe_mobile_filename( $filename ) {
|
||||
$cache_mobile_files_tablet = $this->config->get_config( 'cache_mobile_files_tablet' );
|
||||
|
||||
if ( ! ( $this->config->get_config( 'cache_mobile' ) && $this->config->get_config( 'do_caching_mobile_files' ) ) ) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
if ( ! $cache_mobile_files_tablet ) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'WP_Rocket_Mobile_Detect' ) ) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
$detect = new \WP_Rocket_Mobile_Detect();
|
||||
|
||||
if ( $detect->isMobile() && ! $detect->isTablet() && 'desktop' === $cache_mobile_files_tablet || ( $detect->isMobile() || $detect->isTablet() ) && 'mobile' === $cache_mobile_files_tablet ) {
|
||||
return $filename .= '-mobile';
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the filename if the request is WebP compatible
|
||||
*
|
||||
* @since 3.4
|
||||
* @author Remy Perona
|
||||
*
|
||||
* @param string $filename Cache filename.
|
||||
* @return string
|
||||
*/
|
||||
private function maybe_webp_filename( $filename ) {
|
||||
if ( ! $this->config->get_config( 'cache_webp' ) ) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force WP Rocket to disable its webp cache.
|
||||
*
|
||||
* @since 3.4
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param bool $disable_webp_cache Set to true to disable the webp cache.
|
||||
*/
|
||||
$disable_webp_cache = apply_filters( 'rocket_disable_webp_cache', false );
|
||||
|
||||
if ( $disable_webp_cache ) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
$http_accept = $this->config->get_server_input( 'HTTP_ACCEPT', '' );
|
||||
|
||||
if ( ! $http_accept && function_exists( 'apache_request_headers' ) ) {
|
||||
$headers = apache_request_headers();
|
||||
$http_accept = isset( $headers['Accept'] ) ? $headers['Accept'] : '';
|
||||
}
|
||||
|
||||
if ( ! $http_accept || false === strpos( $http_accept, 'webp' ) ) {
|
||||
if ( preg_match( '#Firefox/(?<version>[0-9]{2})#i', $this->config->get_server_input( 'HTTP_USER_AGENT' ), $matches ) ) {
|
||||
if ( 66 <= (int) $matches['version'] ) {
|
||||
return $filename . '-webp';
|
||||
}
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
return $filename . '-webp';
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the filename if dynamic cookies are set
|
||||
*
|
||||
* @param string $filename Cache filename.
|
||||
* @param array $cookies Cookies for the request.
|
||||
* @return string
|
||||
*/
|
||||
private function maybe_dynamic_cookies_filename( $filename, $cookies ) {
|
||||
$cache_dynamic_cookies = $this->config->get_config( 'cache_dynamic_cookies' );
|
||||
|
||||
if ( ! $cache_dynamic_cookies ) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
foreach ( $cache_dynamic_cookies as $key => $cookie_name ) {
|
||||
if ( is_array( $cookie_name ) ) {
|
||||
if ( isset( $_COOKIE[ $key ] ) ) {
|
||||
foreach ( $cookie_name as $cookie_key ) {
|
||||
if ( '' !== $cookies[ $key ][ $cookie_key ] ) {
|
||||
$cache_key = $cookies[ $key ][ $cookie_key ];
|
||||
$cache_key = preg_replace( '/[^a-z0-9_\-]/i', '-', $cache_key );
|
||||
$filename .= '-' . $cache_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isset( $cookies[ $cookie_name ] ) && '' !== $cookies[ $cookie_name ] ) {
|
||||
$cache_key = $cookies[ $cookie_name ];
|
||||
$cache_key = preg_replace( '/[^a-z0-9_\-]/i', '-', $cache_key );
|
||||
$filename .= '-' . $cache_key;
|
||||
}
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force lowercase on encoded url strings from different alphabets to prevent issues on some hostings.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $matches Cache path.
|
||||
* @return string Cache path in lowercase.
|
||||
*/
|
||||
protected function reset_lowercase( $matches ) {
|
||||
return strtolower( $matches[0] );
|
||||
}
|
||||
}
|
296
wp-content/plugins/wp-rocket/inc/classes/Buffer/class-config.php
Normal file
296
wp-content/plugins/wp-rocket/inc/classes/Buffer/class-config.php
Normal file
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
namespace WP_Rocket\Buffer;
|
||||
|
||||
/**
|
||||
* Configuration class for WP Rocket cache
|
||||
*
|
||||
* @since 3.3
|
||||
* @author Remy Perona
|
||||
*/
|
||||
class Config {
|
||||
use \WP_Rocket\Traits\Memoize;
|
||||
|
||||
/**
|
||||
* Path to the directory containing the config files.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.3
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
private static $config_dir_path;
|
||||
|
||||
/**
|
||||
* Values of $_SERVER to use for some tests.
|
||||
*
|
||||
* @var array
|
||||
* @since 3.3
|
||||
* @access private
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
private static $server;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $args {
|
||||
* An array of arguments.
|
||||
*
|
||||
* @type string $config_dir_path WP Rocket config directory path.
|
||||
* @type array $server Values of $_SERVER to use for the tests. Default is $_SERVER.
|
||||
* }
|
||||
*/
|
||||
public function __construct( $args ) {
|
||||
if ( isset( self::$config_dir_path ) ) {
|
||||
// Make sure to keep the same values all along.
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isset( $args['server'] ) && ! empty( $_SERVER ) && is_array( $_SERVER ) ) {
|
||||
$args['server'] = $_SERVER;
|
||||
}
|
||||
|
||||
self::$config_dir_path = rtrim( $args['config_dir_path'], '/' ) . '/';
|
||||
self::$server = ! empty( $args['server'] ) && is_array( $args['server'] ) ? $args['server'] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a $_SERVER entry.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $entry_name Name of the entry.
|
||||
* @param mixed $default Value to return if the entry is not set.
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_server_input( $entry_name, $default = null ) {
|
||||
if ( ! isset( self::$server[ $entry_name ] ) ) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
return self::$server[ $entry_name ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `server` property.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_server() {
|
||||
return self::$server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific config/option value.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $config_name Name of a specific config/option.
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_config( $config_name ) {
|
||||
$config = $this->get_configs();
|
||||
return isset( $config[ $config_name ] ) ? $config[ $config_name ] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the whole current configuration.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return array|bool An array containing the configuration. False on failure.
|
||||
*/
|
||||
public function get_configs() {
|
||||
if ( self::is_memoized( __FUNCTION__ ) ) {
|
||||
return self::get_memoized( __FUNCTION__ );
|
||||
}
|
||||
|
||||
$config_file_path = $this->get_config_file_path();
|
||||
|
||||
if ( ! $config_file_path['success'] ) {
|
||||
return self::memoize( __FUNCTION__, [], false );
|
||||
}
|
||||
|
||||
include $config_file_path['path'];
|
||||
|
||||
$config = [
|
||||
'cookie_hash' => '',
|
||||
'logged_in_cookie' => '',
|
||||
'common_cache_logged_users' => 0,
|
||||
'cache_mobile_files_tablet' => 'desktop',
|
||||
'cache_ssl' => 0,
|
||||
'cache_webp' => 0,
|
||||
'cache_mobile' => 0,
|
||||
'do_caching_mobile_files' => 0,
|
||||
'secret_cache_key' => '',
|
||||
'cache_reject_uri' => '',
|
||||
'cache_query_strings' => [],
|
||||
'cache_ignored_parameters' => [],
|
||||
'cache_reject_cookies' => '',
|
||||
'cache_reject_ua' => '',
|
||||
'cache_mandatory_cookies' => '',
|
||||
'cache_dynamic_cookies' => [],
|
||||
'url_no_dots' => 0,
|
||||
];
|
||||
|
||||
foreach ( $config as $entry_name => $entry_value ) {
|
||||
$var_name = 'rocket_' . $entry_name;
|
||||
|
||||
if ( isset( $$var_name ) ) {
|
||||
$config[ $entry_name ] = $$var_name;
|
||||
}
|
||||
}
|
||||
|
||||
return self::memoize( __FUNCTION__, [], $config );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the host, to use for config and cache file path.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_host() {
|
||||
if ( self::is_memoized( __FUNCTION__ ) ) {
|
||||
return self::get_memoized( __FUNCTION__ );
|
||||
}
|
||||
|
||||
$host = $this->get_server_input( 'HTTP_HOST', (string) time() );
|
||||
$host = preg_replace( '/:\d+$/', '', $host );
|
||||
$host = trim( strtolower( $host ), '.' );
|
||||
|
||||
return self::memoize( __FUNCTION__, [], rawurlencode( $host ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to an existing config file.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string|bool The path to the file. False if no file is found.
|
||||
*/
|
||||
public function get_config_file_path() {
|
||||
if ( self::is_memoized( __FUNCTION__ ) ) {
|
||||
return self::get_memoized( __FUNCTION__ );
|
||||
}
|
||||
|
||||
$config_dir_real_path = realpath( self::$config_dir_path ) . DIRECTORY_SEPARATOR;
|
||||
|
||||
$host = $this->get_host();
|
||||
|
||||
if ( realpath( self::$config_dir_path . $host . '.php' ) && 0 === stripos( realpath( self::$config_dir_path . $host . '.php' ), $config_dir_real_path ) ) {
|
||||
$config_file_path = self::$config_dir_path . $host . '.php';
|
||||
return self::memoize(
|
||||
__FUNCTION__,
|
||||
[],
|
||||
[
|
||||
'success' => true,
|
||||
'path' => $config_file_path,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$path = str_replace( '\\', '/', strtok( $this->get_server_input( 'REQUEST_URI', '' ), '?' ) );
|
||||
$path = preg_replace( '|(?<=.)/+|', '/', $path );
|
||||
$path = explode( '%2F', preg_replace( '/^(?:%2F)*(.*?)(?:%2F)*$/', '$1', rawurlencode( $path ) ) );
|
||||
|
||||
foreach ( $path as $p ) {
|
||||
static $dir;
|
||||
|
||||
if ( realpath( self::$config_dir_path . $host . '.' . $p . '.php' ) && 0 === stripos( realpath( self::$config_dir_path . $host . '.' . $p . '.php' ), $config_dir_real_path ) ) {
|
||||
$config_file_path = self::$config_dir_path . $host . '.' . $p . '.php';
|
||||
return self::memoize(
|
||||
__FUNCTION__,
|
||||
[],
|
||||
[
|
||||
'success' => true,
|
||||
'path' => $config_file_path,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if ( realpath( self::$config_dir_path . $host . '.' . $dir . $p . '.php' ) && 0 === stripos( realpath( self::$config_dir_path . $host . '.' . $dir . $p . '.php' ), $config_dir_real_path ) ) {
|
||||
$config_file_path = self::$config_dir_path . $host . '.' . $dir . $p . '.php';
|
||||
return self::memoize(
|
||||
__FUNCTION__,
|
||||
[],
|
||||
[
|
||||
'success' => true,
|
||||
'path' => $config_file_path,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$dir .= $p . '.';
|
||||
}
|
||||
|
||||
return self::memoize(
|
||||
__FUNCTION__,
|
||||
[],
|
||||
[
|
||||
'success' => false,
|
||||
'path' => self::$config_dir_path . $host . implode( '/', $path ) . '.php',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** SPECIFIC CONFIG GETTERS ================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get rejected cookies as a regex pattern.
|
||||
* `#` is used as pattern delimiter.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_rejected_cookies() {
|
||||
$rejected_cookies = $this->get_config( 'cache_reject_cookies' );
|
||||
|
||||
if ( '' === $rejected_cookies ) {
|
||||
return $rejected_cookies;
|
||||
}
|
||||
|
||||
return '#' . $rejected_cookies . '#';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mandatory cookies as a regex pattern.
|
||||
* `#` is used as pattern delimiter.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_mandatory_cookies() {
|
||||
$mandatory_cookies = $this->get_config( 'cache_mandatory_cookies' );
|
||||
|
||||
if ( '' === $mandatory_cookies ) {
|
||||
return $mandatory_cookies;
|
||||
}
|
||||
|
||||
return '#' . $mandatory_cookies . '#';
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
namespace WP_Rocket\Buffer;
|
||||
|
||||
/**
|
||||
* Handle page optimizations.
|
||||
*
|
||||
* @since 3.3
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Optimization extends Abstract_Buffer {
|
||||
|
||||
/**
|
||||
* Process identifier used by the logger.
|
||||
*
|
||||
* @var string
|
||||
* @since 3.3
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
protected $process_id = 'optimization process';
|
||||
|
||||
/**
|
||||
* Tests instance
|
||||
*
|
||||
* @var Tests
|
||||
*/
|
||||
protected $tests;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param Tests $tests Tests instance.
|
||||
*/
|
||||
public function __construct( Tests $tests ) {
|
||||
parent::__construct( $tests );
|
||||
|
||||
$this->log( 'OPTIMIZATION PROCESS STARTED.', [], 'info' );
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** CACHE =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Do preliminary tests and maybe launch the buffer process.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
public function maybe_init_process() {
|
||||
if ( ! $this->tests->can_init_process() ) {
|
||||
$this->log_last_test_error();
|
||||
return;
|
||||
}
|
||||
|
||||
ob_start( [ $this, 'maybe_process_buffer' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe optimize the page content.
|
||||
*
|
||||
* @since 3.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $buffer The buffer content.
|
||||
* @return string The buffered content.
|
||||
*/
|
||||
public function maybe_process_buffer( $buffer ) {
|
||||
/**
|
||||
* Triggered before WP Rocket starts the optimization process.
|
||||
*
|
||||
* @since 3.4.2
|
||||
* @author Soponar Cristina
|
||||
*
|
||||
* @param string $buffer HTML content.
|
||||
*/
|
||||
do_action( 'rocket_before_maybe_process_buffer', $buffer );
|
||||
|
||||
if ( ! $this->is_html( $buffer ) ) {
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
if ( ! $this->tests->can_process_buffer( $buffer ) ) {
|
||||
$this->log_last_test_error();
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* This hook is used for:
|
||||
* - Async CSS files
|
||||
* - Defer JavaScript files
|
||||
* - Minify/Combine HTML/CSS/JavaScript
|
||||
* - CDN
|
||||
* - LazyLoad
|
||||
*
|
||||
* @param string $buffer The page content.
|
||||
*/
|
||||
$buffer = (string) apply_filters( 'rocket_buffer', $buffer );
|
||||
|
||||
$this->log( 'Page optimized.', [], 'info' );
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
}
|
1122
wp-content/plugins/wp-rocket/inc/classes/Buffer/class-tests.php
Normal file
1122
wp-content/plugins/wp-rocket/inc/classes/Buffer/class-tests.php
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user