PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / 2.1
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score v2.1
trunk 1.0 1.0.1 1.1 1.1.1 1.1.2 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 2.0 2.0.1 2.0.2 2.0.3 2.0.4 2.1 2.1.1 2.1.2 2.2 2.2.1 All 69 releases
powered-cache / includes / classes / CDN.php

CDN.php in Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score 2.1, at includes/classes/CDN.php

334 lines 8.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * CDN functionalities
4 *
5 * @package PoweredCache
6 */
7
8 namespace PoweredCache;
9
10 use \DOMDocument as DOMDocument;
11 use function PoweredCache\Utils\cdn_addresses;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 /**
18 * Class CDN
19 */
20 class CDN {
21
22 /**
23 * Return an instance of the current class
24 *
25 * @return CDN
26 * @since 1.0
27 */
28 public static function factory() {
29 static $instance = false;
30
31 if ( ! $instance ) {
32 $instance = new self();
33 $instance->setup();
34 }
35
36 return $instance;
37 }
38
39 /**
40 * Setup hooks
41 *
42 * @since 1.0
43 */
44 public function setup() {
45 $settings = \PoweredCache\Utils\get_settings();
46
47 if ( ! $settings['enable_cdn'] ) {
48 return;
49 }
50
51 /**
52 * Filters CDN integration
53 *
54 * @hook powered_cache_cdn_disable
55 *
56 * @param {boolean} False by default.
57 *
58 * @return {array} New value.
59 * @since 2.1
60 */
61 $disable_cdn = apply_filters( 'powered_cache_cdn_disable', false );
62
63 if ( $disable_cdn ) {
64 return;
65 }
66
67 add_filter( 'wp_get_attachment_url', array( $this, 'cdn_url' ), 9999 );
68 add_filter( 'stylesheet_uri', array( $this, 'cdn_url' ), 9999 );
69 add_filter( 'smilies_src', array( $this, 'cdn_url' ), 9999 );
70 add_filter( 'bp_core_fetch_avatar_url', array( $this, 'cdn_url' ), 9999 );
71 add_filter( 'style_loader_src', array( $this, 'cdn_url' ), 9999 );
72 add_filter( 'script_loader_src', array( $this, 'cdn_url' ), 9999 );
73 add_filter( 'powered_cache_cdn_assets', array( $this, 'cdn_url' ), 9999 );
74 add_filter( 'wp_calculate_image_srcset', array( $this, 'srcset_url' ), 9999 );
75 add_filter( 'wp_get_attachment_image_src', array( $this, 'attachment_image_src' ), 9999 );
76 add_filter( 'the_content', array( $this, 'cdn_images' ), 9999 );
77 add_filter( 'post_thumbnail_html', array( $this, 'cdn_images' ), 9999 );
78 add_filter( 'get_avatar', array( $this, 'cdn_images' ), 9999 );
79 add_filter( 'bp_core_fetch_avatar', array( $this, 'cdn_images' ), 9999 );
80 add_filter( 'widget_text', array( $this, 'cdn_images' ), 9999 );
81 add_filter( 'media_image', array( $this, 'cdn_images' ), 9999 );
82 add_filter( 'powered_cache_page_caching_buffer', array( $this, 'cdn_images' ), 9999 );
83 add_filter( 'powered_cache_fo_optimized_url', array( $this, 'cdn_optimizer_url' ), 9999, 2 );
84
85 /**
86 * Fires after setup CDN hooks.
87 *
88 * @hook powered_cache_cdn_setup
89 *
90 * @since 1.0
91 */
92 do_action( 'powered_cache_cdn_setup' );
93 }
94
95 /**
96 * CDN hostname replacement for optimized URLs
97 *
98 * @param string $optimized_url Optimized assets URL
99 * @param string $path rel path of the files
100 *
101 * @return mixed
102 */
103 public function cdn_optimizer_url( $optimized_url, $path ) {
104 if ( '-' === $path[0] ) {
105 $path = @gzuncompress( base64_decode( substr( $path, 1 ) ) ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged,WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
106 }
107
108 $zone = 'all';
109 if ( $path && false !== strpos( $path, '.css' ) ) {
110 $zone = 'css';
111 } elseif ( $path && false !== strpos( $path, '.js' ) ) {
112 $zone = 'js';
113 }
114
115 $cdn_url = self::get_best_possible_cdn_host( $zone );
116
117 $optimized_url = str_replace( home_url(), $cdn_url, $optimized_url );
118
119 return $optimized_url;
120 }
121
122 /**
123 * Replace CDN url for srcset
124 *
125 * @param array $sources Source files
126 *
127 * @return mixed
128 * @since 1.2
129 */
130 public function srcset_url( $sources ) {
131 foreach ( (array) $sources as $key => $source ) {
132 $sources[ $key ]['url'] = $this->cdn_url( $source['url'] );
133 }
134
135 return $sources;
136 }
137
138 /**
139 * Replace given url with the CDN host
140 *
141 * @param string $url URL
142 *
143 * @return string
144 */
145 public function cdn_url( $url ) {
146 if ( is_admin() || is_preview() ) {
147 return $url;
148 }
149
150 return $this->maybe_cdn_replace( $url );
151 }
152
153
154 /**
155 * Replace URL for wp_get_attachment_image_src function
156 *
157 * @param array|false $image Either array with src, width & height, icon src, or false
158 *
159 * @return array $image
160 * @since 1.2.4
161 */
162 public function attachment_image_src( $image ) {
163 if ( is_admin() || is_preview() ) {
164 return $image;
165 }
166
167 if ( ! (bool) $image ) {
168 return $image;
169 }
170
171 $cdn_url = self::get_best_possible_cdn_host( 'image' );
172 // no host found
173 if ( false === $cdn_url ) {
174 return $image;
175 }
176
177 $image[0] = str_replace( home_url(), $cdn_url, $image[0] );
178
179 return $image;
180 }
181
182 /**
183 * Replace images with CDN host
184 *
185 * @param string $content Content
186 *
187 * @return mixed
188 */
189 public function cdn_images( $content ) {
190 if ( is_admin() || is_preview() || empty( $content ) ) {
191 return $content;
192 }
193
194 if ( ! class_exists( 'DOMDocument' ) ) {
195 return $content;
196 }
197
198 if ( function_exists( 'libxml_use_internal_errors' ) ) {
199 libxml_use_internal_errors( true );
200 }
201
202 $document = new DOMDocument();
203 @$document->loadHTML( $content ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
204 $images = $document->getElementsByTagName( 'img' );
205
206 $img_url = array();
207 foreach ( $images as $img ) {
208 if ( $img->hasAttribute( 'src' ) ) {
209 $img_url[] = $img->getAttribute( 'src' );
210 }
211
212 if ( $img->hasAttribute( 'srcset' ) ) {
213 $imgset = explode( ',', $img->getAttribute( 'srcset' ) );
214 foreach ( $imgset as $src_item ) {
215 $imgsrc = explode( ' ', trim( $src_item ) );
216 // first item is url, second width
217 $img_url[] = $imgsrc[0];
218 }
219 }
220 }
221
222 $img_url = array_unique( $img_url );
223 $cdn_url = array();
224
225 foreach ( $img_url as $replace_url ) {
226 $cdn_url[] = $this->maybe_cdn_replace( $replace_url );
227 }
228
229 return str_replace( $img_url, $cdn_url, $content );
230 }
231
232
233 /**
234 * this method decide to url replacement.
235 * rejected files & external resources will be ignored
236 *
237 * @param string $url URL
238 *
239 * @return string cdn url
240 * @since 1.0
241 */
242 public static function maybe_cdn_replace( $url ) {
243 $settings = \PoweredCache\Utils\get_settings();
244
245 // rejected file
246 if ( ! empty( $settings['cdn_rejected_files'] ) ) {
247 $cdn_rejected_files = preg_split( '#(\r\n|\r|\n)#', $settings['cdn_rejected_files'], - 1, PREG_SPLIT_NO_EMPTY );
248 $cdn_rejected_files = implode( '|', $cdn_rejected_files );
249
250 if ( preg_match( '#(' . $cdn_rejected_files . ')#', $url ) ) {
251 return $url;
252 }
253 }
254
255 // external resource
256 if ( false === strpos( $url, home_url() ) ) {
257 return $url;
258 }
259
260 // don't replace for base64 encoded images
261 if ( false !== strpos( $url, 'data:image' ) ) {
262 return $url;
263 }
264
265 $url_path = wp_parse_url( $url, PHP_URL_PATH );
266 $ext = explode( '.', $url_path );
267 $ext = strtolower( end( $ext ) );
268
269 $zone = 'all';
270
271 /**
272 * Filters supported image extensions.
273 *
274 * @hook powered_cache_cdn_image_extensions
275 *
276 * @param {array} $image_extensions Supported image extensions.
277 *
278 * @return {array} New value.
279 * @since 1.0
280 */
281 $image_extensions = apply_filters( 'powered_cache_cdn_image_extensions', array( 'jpg', 'jpeg', 'gif', 'png', 'bmp', 'ico', 'webp' ) );
282
283 if ( in_array( $ext, $image_extensions, true ) ) {
284 $zone = 'image';
285 } elseif ( 'css' === $ext ) {
286 $zone = 'css';
287 } elseif ( 'js' === $ext ) {
288 $zone = 'js';
289 }
290
291 $cdn_url = self::get_best_possible_cdn_host( $zone );
292
293 // no host found
294 if ( false === $cdn_url ) {
295 return $url;
296 }
297
298 return str_replace( home_url(), $cdn_url, $url );
299 }
300
301 /**
302 * try to catch best cdn address to given zone
303 *
304 * @param string $zone keys of Powered_Cache_Admin_Helper::cdn_zones
305 *
306 * @return mixed string | false
307 */
308 public static function get_best_possible_cdn_host( $zone = 'all' ) {
309 global $powered_cache_cdn_addresses;
310
311 if ( ! isset( $powered_cache_cdn_addresses ) ) {
312 $powered_cache_cdn_addresses = cdn_addresses();
313 }
314
315 if ( isset( $powered_cache_cdn_addresses[ $zone ] ) && is_array( $powered_cache_cdn_addresses[ $zone ] ) ) {
316 // if we have multiple host for the same resource get randomly
317 $random_key = array_rand( $powered_cache_cdn_addresses[ $zone ] );
318
319 return $powered_cache_cdn_addresses[ $zone ][ $random_key ];
320 }
321
322 // fallback to primary host
323 if ( isset( $powered_cache_cdn_addresses['all'] ) && is_array( $powered_cache_cdn_addresses['all'] ) ) {
324 $random_key = array_rand( $powered_cache_cdn_addresses['all'] );
325
326 return $powered_cache_cdn_addresses['all'][ $random_key ];
327 }
328
329 return false;
330 }
331
332 }
333
334