PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / 2.0
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score v2.0
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.0, at includes/classes/CDN.php

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