oont-contents/plugins/wp-rocket/inc/Engine/CDN/Subscriber.php
2025-02-08 15:10:23 +01:00

349 lines
7.5 KiB
PHP

<?php
namespace WP_Rocket\Engine\CDN;
use WP_Rocket\Admin\Options_Data;
use WP_Rocket\Event_Management\Subscriber_Interface;
/**
* Subscriber for the CDN feature
*
* @since 3.4
*/
class Subscriber implements Subscriber_Interface {
/**
* WP Rocket Options instance
*
* @var Options_Data
*/
private $options;
/**
* CDN instance
*
* @var CDN
*/
private $cdn;
/**
* Constructor
*
* @param Options_Data $options WP Rocket Options instance.
* @param CDN $cdn CDN instance.
*/
public function __construct( Options_Data $options, CDN $cdn ) {
$this->options = $options;
$this->cdn = $cdn;
}
/**
* Return an array of events that this subscriber wants to listen to.
*
* @since 3.4
*
* @return array
*/
public static function get_subscribed_events() {
return [
'rocket_buffer' => [
[ 'rewrite', 20 ],
[ 'rewrite_srcset', 21 ],
],
'rocket_css_content' => 'rewrite_css_properties',
'rocket_usedcss_content' => 'rewrite_css_properties',
'rocket_cdn_hosts' => [ 'get_cdn_hosts', 10, 2 ],
'rocket_dns_prefetch' => 'add_dns_prefetch_cdn',
'rocket_facebook_sdk_url' => 'add_cdn_url',
'rocket_css_url' => [ 'add_cdn_url', 10, 2 ],
'rocket_js_url' => [ 'add_cdn_url', 10, 2 ],
'rocket_asset_url' => [ 'maybe_replace_url', 10, 2 ],
'wp_resource_hints' => [ 'add_preconnect_cdn', 10, 2 ],
'rocket_font_url' => [ 'add_cdn_url', 10, 2 ],
];
}
/**
* Rewrites URLs to the CDN URLs if allowed
*
* @since 3.4
*
* @param string $html HTML content.
*
* @return string
*/
public function rewrite( $html ) {
if ( ! $this->is_allowed() ) {
return $html;
}
return $this->cdn->rewrite( $html );
}
/**
* Rewrites URLs in srcset attributes to the CDN URLs if allowed
*
* @since 3.4.0.4
*
* @param string $html HTML content.
*
* @return string
*/
public function rewrite_srcset( $html ) {
if ( ! $this->is_allowed() ) {
return $html;
}
return $this->cdn->rewrite_srcset( $html );
}
/**
* Rewrites URLs to the CDN URLs in CSS files
*
* @since 3.4
*
* @param string $content CSS content.
*
* @return string
*/
public function rewrite_css_properties( $content ) {
/**
* Filters the application of the CDN on CSS properties
*
* @since 2.6
*
* @param bool true to apply CDN to properties, false otherwise
*/
$do_rewrite = apply_filters( 'do_rocket_cdn_css_properties', true ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals
if ( ! $do_rewrite ) {
return $content;
}
if ( ! $this->is_cdn_enabled() ) {
return $content;
}
return $this->cdn->rewrite_css_properties( $content );
}
/**
* Gets the host value for each CDN URLs
*
* @since 3.4
*
* @param array $hosts Base hosts.
* @param array $zones Zones to get the CND URLs associated with.
*
* @return array
*/
public function get_cdn_hosts( array $hosts = [], array $zones = [ 'all' ] ) {
$cdn_urls = $this->cdn->get_cdn_urls( $zones );
if ( empty( $cdn_urls ) ) {
return $hosts;
}
foreach ( $cdn_urls as $cdn_url ) {
$parsed = get_rocket_parse_url( rocket_add_url_protocol( $cdn_url ) );
if ( empty( $parsed['host'] ) ) {
continue;
}
$hosts[] = untrailingslashit( $parsed['host'] . $parsed['path'] );
}
return array_unique( $hosts );
}
/**
* Adds CDN URLs to the DNS prefetch links
*
* @since 3.4
*
* @param array $domains Domain names to DNS prefetch.
*
* @return array
*/
public function add_dns_prefetch_cdn( $domains ) {
if ( ! $this->is_allowed() ) {
return $domains;
}
$cdn_urls = $this->cdn->get_cdn_urls( [ 'all', 'images', 'css_and_js', 'css', 'js' ] );
if ( ! $cdn_urls ) {
return $domains;
}
return array_merge( $domains, $cdn_urls );
}
/**
* Adds the CDN URL on the provided URL
*
* @since 3.4
*
* @param string $url URL to rewrite.
* @param string $original_url Original URL for this URL. Optional.
*
* @return string
*/
public function add_cdn_url( $url, $original_url = '' ) {
if ( ! empty( $original_url ) ) {
if ( $this->cdn->is_excluded( $original_url ) ) {
return $url;
}
}
return $this->cdn->rewrite_url( $url );
}
/**
* Replace CDN URL with site URL on the provided asset URL.
*
* @since 3.5.3
*
* @param string $url URL of the asset.
* @param array $zones Array of corresponding zones for the asset.
*
* @return string
*/
public function maybe_replace_url( $url, array $zones = [ 'all' ] ) {
if ( ! $this->is_allowed() ) {
return $url;
}
$url_parts = get_rocket_parse_url( $url );
if ( empty( $url_parts['host'] ) ) {
return $url;
}
$site_url_parts = get_rocket_parse_url( site_url() );
if ( empty( $site_url_parts['host'] ) ) {
return $url;
}
if ( $url_parts['host'] === $site_url_parts['host'] ) {
return $url;
}
$cdn_urls = $this->cdn->get_cdn_urls( $zones );
if ( empty( $cdn_urls ) ) {
return $url;
}
$cdn_urls = array_map( 'rocket_add_url_protocol', $cdn_urls );
$site_url = $site_url_parts['scheme'] . '://' . $site_url_parts['host'];
foreach ( $cdn_urls as $cdn_url ) {
if ( false === strpos( $url, $cdn_url ) ) {
continue;
}
return str_replace( $cdn_url, $site_url, $url );
}
return $url;
}
/**
* Add a preconnect tag for the CDN.
*
* @since 3.8.3
*
* @param array $urls The initial array of wp_resource_hint urls.
* @param string $relation_type The relation type for the hint: eg., 'preconnect', 'prerender', etc.
*
* @return array The filtered urls.
*/
public function add_preconnect_cdn( array $urls, string $relation_type ): array {
if (
'preconnect' !== $relation_type
||
rocket_bypass()
||
! $this->is_allowed()
||
! $this->is_cdn_enabled()
) {
return $urls;
}
$cdn_urls = $this->cdn->get_cdn_urls( [ 'all', 'images', 'css_and_js', 'css', 'js' ] );
if ( empty( $cdn_urls ) ) {
return $urls;
}
foreach ( $cdn_urls as $url ) {
$url_parts = get_rocket_parse_url( $url );
if ( empty( $url_parts['scheme'] ) ) {
if ( preg_match( '/^(?![\/])(?=[^\.]+\/).+/i', $url ) ) {
continue;
}
$url = '//' . $url;
$url_parts = get_rocket_parse_url( $url );
}
$domain = empty( $url_parts['scheme'] )
? '//' . $url_parts['host']
: $url_parts['scheme'] . '://' . $url_parts['host'];
// Note: As of 22 Feb, 2021 we cannot add more than one instance of a domain url
// on the wp_resource_hint() hook -- wp_resource_hint() will
// only actually print the first one.
// Ideally, we want both because CSS resources will use the crossorigin version,
// But JS resources will not.
// Jonathan has submitted a ticket to change this behavior:
// @see https://core.trac.wordpress.org/ticket/52465
// Until then, we order these to prefer/print the non-crossorigin version.
$urls[] = [ 'href' => $domain ];
$urls[] = [
'href' => $domain,
'crossorigin' => 'anonymous',
];
}
return $urls;
}
/**
* Checks if CDN can be applied
*
* @since 3.4
*
* @return boolean
*/
private function is_allowed() {
if ( rocket_get_constant( 'DONOTROCKETOPTIMIZE' ) ) {
return false;
}
if ( ! $this->is_cdn_enabled() ) {
return false;
}
if ( is_rocket_post_excluded_option( 'cdn' ) ) {
return false;
}
return true;
}
/**
* Checks if the CDN option is enabled
*
* @since 3.5.5
*
* @return bool
*/
private function is_cdn_enabled() {
return (bool) $this->options->get( 'cdn', 0 );
}
}