309 lines
6.5 KiB
PHP
309 lines
6.5 KiB
PHP
<?php
|
|
|
|
namespace WP_Rocket\Engine\Optimization;
|
|
|
|
use WP_Rocket\Admin\Options_Data;
|
|
use WP_Rocket\Event_Management\Subscriber_Interface;
|
|
|
|
/**
|
|
* Create a static file for CSS/JS generated by a PHP file
|
|
*
|
|
* @since 3.1
|
|
*/
|
|
class CacheDynamicResource extends AbstractOptimization implements Subscriber_Interface {
|
|
use CSSTrait;
|
|
|
|
/**
|
|
* Plugin options instance.
|
|
*
|
|
* @since 3.1
|
|
*
|
|
* @var Options_Data
|
|
*/
|
|
protected $options;
|
|
|
|
/**
|
|
* Cache busting base path
|
|
*
|
|
* @since 3.1
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $busting_path;
|
|
|
|
/**
|
|
* Cache busting base URL
|
|
*
|
|
* @since 3.1
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $busting_url;
|
|
|
|
/**
|
|
* Excluded files from optimization
|
|
*
|
|
* @since 3.1
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $excluded_files;
|
|
|
|
/**
|
|
* Extension to use for the CDN zones selection
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $extension = '';
|
|
|
|
/**
|
|
* Creates an instance of CacheDynamicResource.
|
|
*
|
|
* @since 3.1
|
|
*
|
|
* @param Options_Data $options Plugin options instance.
|
|
* @param string $busting_path Base cache busting files path.
|
|
* @param string $busting_url Base cache busting files URL.
|
|
*/
|
|
public function __construct( Options_Data $options, $busting_path, $busting_url ) {
|
|
$site_id = get_current_blog_id();
|
|
$this->options = $options;
|
|
$this->busting_path = "{$busting_path}{$site_id}/";
|
|
$this->busting_url = "{$busting_url}{$site_id}/";
|
|
|
|
/**
|
|
* Filters files to exclude from static dynamic resources
|
|
*
|
|
* @since 2.9.3
|
|
* @author Remy Perona
|
|
*
|
|
* @param array $excluded_files An array of filepath to exclude.
|
|
*/
|
|
$this->excluded_files = (array) apply_filters( 'rocket_exclude_static_dynamic_resources', [] );
|
|
$this->excluded_files[] = '/wp-admin/admin-ajax.php';
|
|
|
|
foreach ( $this->excluded_files as $i => $excluded_file ) {
|
|
// Escape character for future use in regex pattern.
|
|
$this->excluded_files[ $i ] = str_replace( '#', '\#', $excluded_file );
|
|
}
|
|
|
|
$this->excluded_files = implode( '|', $this->excluded_files );
|
|
}
|
|
|
|
/**
|
|
* Return an array of events that this subscriber wants to listen to.
|
|
*
|
|
* @since 3.1
|
|
*
|
|
* @return array
|
|
*/
|
|
public static function get_subscribed_events() {
|
|
return [
|
|
'style_loader_src' => [ 'cache_dynamic_resource', 16 ],
|
|
'script_loader_src' => [ 'cache_dynamic_resource', 16 ],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Filters the source dynamic php file to replace it with a static file
|
|
*
|
|
* @since 3.1
|
|
*
|
|
* @param string $src source URL.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function cache_dynamic_resource( $src ) {
|
|
if ( ! $this->is_allowed() ) {
|
|
return $src;
|
|
}
|
|
|
|
switch ( current_filter() ) {
|
|
case 'script_loader_src':
|
|
$this->set_extension( 'js' );
|
|
break;
|
|
case 'style_loader_src':
|
|
$this->set_extension( 'css' );
|
|
break;
|
|
}
|
|
|
|
if ( $this->is_excluded_file( $src ) ) {
|
|
return $src;
|
|
}
|
|
|
|
return $this->replace_url( $src );
|
|
}
|
|
|
|
/**
|
|
* Replaces the dynamic URL by the static file URL
|
|
*
|
|
* @since 3.1
|
|
*
|
|
* @param string $src Source URL.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function replace_url( $src ) {
|
|
$path = ltrim( rocket_extract_url_component( $src, PHP_URL_PATH ), '/' );
|
|
|
|
/**
|
|
* Filters the dynamic resource cache filename
|
|
*
|
|
* @since 2.9
|
|
*
|
|
* @param string $filename filename for the cache file
|
|
*/
|
|
$filename = apply_filters( 'rocket_dynamic_resource_cache_filename', preg_replace( '/\.php$/', '.' . $this->extension, $path ) );
|
|
$filename = ltrim( rocket_realpath( rtrim( str_replace( [ ' ', '%20' ], '-', $filename ) ) ), '/' );
|
|
$filepath = $this->busting_path . $filename;
|
|
|
|
if ( ! rocket_direct_filesystem()->is_readable( $filepath ) ) {
|
|
$content = $this->get_url_content( $src );
|
|
|
|
if ( ! $content ) {
|
|
return $src;
|
|
}
|
|
|
|
if ( 'css' === $this->extension ) {
|
|
$content = $this->rewrite_paths( $this->get_file_path( $src ), $filepath, $content );
|
|
$content = $this->apply_font_display_swap( $content );
|
|
}
|
|
|
|
if ( ! $this->write_file( $content, $filepath ) ) {
|
|
return $src;
|
|
}
|
|
}
|
|
|
|
return $this->get_cache_url( $filename );
|
|
}
|
|
|
|
/**
|
|
* Determines if we can optimize
|
|
*
|
|
* @since 3.1
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function is_allowed() {
|
|
global $pagenow;
|
|
|
|
if ( rocket_bypass() ) {
|
|
return false;
|
|
}
|
|
|
|
if ( rocket_get_constant( 'DONOTROCKETOPTIMIZE' ) ) {
|
|
return false;
|
|
}
|
|
|
|
if ( is_user_logged_in() && ! $this->options->get( 'cache_logged_user' ) ) {
|
|
return false;
|
|
}
|
|
|
|
if ( 'wp-login.php' === $pagenow ) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Determines if the file is excluded from optimization
|
|
*
|
|
* @since 3.1
|
|
*
|
|
* @param string $src source URL.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function is_excluded_file( $src ) {
|
|
$file = get_rocket_parse_url( $src );
|
|
|
|
if ( isset( $file['path'] ) && ! preg_match( '#\.php$#', $file['path'] ) ) {
|
|
return true;
|
|
}
|
|
|
|
if ( $this->is_external_file( $src ) ) {
|
|
return true;
|
|
}
|
|
|
|
if ( preg_match( '#^' . $this->excluded_files . '$#', $file['path'] ) ) {
|
|
return true;
|
|
}
|
|
|
|
if ( ! isset( $file['query'] ) ) {
|
|
return false;
|
|
}
|
|
|
|
$file['query'] = remove_query_arg( 'ver', $file['query'] );
|
|
|
|
return (bool) $file['query'];
|
|
}
|
|
|
|
/**
|
|
* Sets the current file extension and minify key
|
|
*
|
|
* @since 3.1
|
|
*
|
|
* @param string $extension Current file extension.
|
|
*/
|
|
public function set_extension( $extension ) {
|
|
$this->extension = $extension;
|
|
$this->minify_key = $this->options->get( 'minify_' . $this->extension . '_key' );
|
|
}
|
|
|
|
/**
|
|
* Gets the CDN zones.
|
|
*
|
|
* @since 3.1
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_zones() {
|
|
return [ 'all', 'css_and_js', $this->extension ];
|
|
}
|
|
|
|
/**
|
|
* Gets the cache URL for the static file
|
|
*
|
|
* @since 3.1
|
|
*
|
|
* @param string $filename Filename for the static file.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function get_cache_url( $filename ) {
|
|
$cache_url = $this->busting_url . $filename;
|
|
|
|
switch ( $this->extension ) {
|
|
case 'css':
|
|
// This filter is documented in inc/classes/optimization/css/class-abstract-css-optimization.php.
|
|
$cache_url = apply_filters( 'rocket_css_url', $cache_url );
|
|
break;
|
|
case 'js':
|
|
// This filter is documented in inc/classes/optimization/css/class-abstract-js-optimization.php.
|
|
$cache_url = apply_filters( 'rocket_js_url', $cache_url );
|
|
break;
|
|
}
|
|
|
|
return $cache_url;
|
|
}
|
|
|
|
/**
|
|
* Gets content from an URL
|
|
*
|
|
* @since 3.1
|
|
*
|
|
* @param string $url URL to get the content from.
|
|
*
|
|
* @return string|bool
|
|
*/
|
|
protected function get_url_content( $url ) {
|
|
$content = wp_remote_retrieve_body( wp_remote_get( $url ) );
|
|
|
|
if ( ! $content ) {
|
|
return false;
|
|
}
|
|
|
|
return $content;
|
|
}
|
|
}
|