PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / 2.0.4
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score v2.0.4
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 / Preloader.php

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

563 lines 14.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Cache Preloading functionalities
4 *
5 * @package PoweredCache
6 */
7
8 namespace PoweredCache;
9
10 use PoweredCache\Async\CachePreloader;
11 use function PoweredCache\Utils\permalink_structure_has_trailingslash;
12 use function PoweredCache\Utils\site_cache_dir;
13
14 /**
15 * Class Preloader
16 */
17 class Preloader {
18 /**
19 * Plugin settings
20 *
21 * @var $settings
22 */
23 private $settings;
24
25 /**
26 * Instance of CachePreloader
27 *
28 * @var CachePreloader
29 */
30 private $cache_preloader;
31
32 /**
33 * Return an instance of the current class
34 *
35 * @return Preloader
36 * @since 1.0
37 */
38 public static function factory() {
39
40 static $instance;
41
42 if ( ! $instance ) {
43 $instance = new self();
44 $instance->setup();
45 }
46
47 return $instance;
48 }
49
50 /**
51 * Setup routine
52 */
53 public function setup() {
54 $this->settings = \PoweredCache\Utils\get_settings();
55
56 // bail if the preload not activated
57 if ( ! $this->settings['enable_cache_preload'] ) {
58 return;
59 }
60
61 $this->cache_preloader = CachePreloader::factory();
62 add_filter( 'wp_resource_hints', [ $this, 'dns_prefetch' ], 10, 2 );
63
64 /**
65 * Page cache needs to be activated to get benefits of preloading
66 */
67 if ( ! $this->settings['enable_page_cache'] ) {
68 return;
69 }
70
71 add_action( 'admin_bar_menu', [ $this, 'admin_bar_menu' ] );
72 add_action( 'admin_post_powered_cache_preload_cache', [ $this, 'start_preload' ] );
73 add_action( 'powered_cache_purge_all_cache', [ $this, 'setup_preload_queue' ] );
74 add_action( 'powered_cache_clean_site_cache_dir', [ $this, 'setup_preload_queue' ] );
75 add_action( 'powered_cache_advanced_cache_purge_post', [ $this, 'add_purged_urls_to_preload_queue' ], 10, 2 );
76 add_action( 'powered_cache_expired_files_deleted', [ $this, 'add_expired_urls_to_preload_queue' ], 10, 2 );
77 }
78
79 /**
80 * Preload Admin bar menu
81 *
82 * @param object $wp_admin_bar Admin bar object
83 *
84 * @since 1.0
85 */
86 public function admin_bar_menu( $wp_admin_bar ) {
87 if ( is_multisite() && ! current_user_can( 'manage_network' ) ) {
88 return;
89 }
90
91 if ( ! current_user_can( 'manage_options' ) ) {
92 return;
93 }
94
95 $wp_admin_bar->add_menu(
96 array(
97 'id' => 'preload-cache',
98 'title' => __( 'Preload Cache', 'powered-cache' ),
99 'href' => wp_nonce_url( admin_url( 'admin-post.php?action=powered_cache_preload_cache' ), 'powered_cache_preload_cache' ),
100 'parent' => 'powered-cache',
101 )
102 );
103 }
104
105 /**
106 * Add preloading items to queue
107 */
108 public function start_preload() {
109 if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'powered_cache_preload_cache' ) ) {
110 wp_nonce_ays( '' );
111 }
112
113 if ( is_multisite() && ! current_user_can( 'manage_network' ) ) {
114 $redirect_url = add_query_arg( 'pc_action', 'start_preload_err_permission', wp_get_referer() );
115 wp_safe_redirect( $redirect_url );
116 exit;
117 }
118
119 $this->setup_preload_queue();
120
121 $redirect_url = add_query_arg( 'pc_action', 'start_preload', wp_get_referer() );
122 wp_safe_redirect( $redirect_url );
123 exit;
124 }
125
126 /**
127 * Setup preload
128 */
129 public function setup_preload_queue() {
130 if ( POWERED_CACHE_IS_NETWORK ) {
131 $sites = get_sites( [ 'fields' => 'ids' ] );
132 foreach ( $sites as $site_id ) {
133 switch_to_blog( $site_id );
134 \PoweredCache\Utils\log( sprintf( 'Populating preload queue for site: %d', $site_id ) );
135 $this->populate_preload_queue();
136 restore_current_blog();
137 }
138 } else {
139 $this->populate_preload_queue();
140 }
141 }
142
143 /**
144 * Populate preload queue based on settings
145 */
146 protected function populate_preload_queue() {
147 $preload_urls = [];
148
149 // cancel existing process before populating
150 $this->cache_preloader->cancel_process();
151
152 if ( $this->settings['preload_homepage'] ) {
153 $preload_urls[] = get_home_url();
154 \PoweredCache\Utils\log( sprintf( 'Home URL added to preload queue: %s', get_home_url() ) );
155 }
156
157 if ( $this->settings['preload_public_posts'] ) {
158 $public_post_urls = $this->prepare_public_posts_urls();
159 $preload_urls = array_merge( $preload_urls, $public_post_urls );
160 \PoweredCache\Utils\log( sprintf( 'Public posts added to preload queue. ' ) );
161 }
162
163 if ( $this->settings['preload_public_tax'] ) {
164 $public_tax_term_urls = $this->prepare_public_tax_terms_urls();
165 $preload_urls = array_merge( $preload_urls, $public_tax_term_urls );
166 \PoweredCache\Utils\log( sprintf( 'Public tax terms added to preload queue. ' ) );
167 }
168
169 foreach ( $preload_urls as $url ) {
170 $this->cache_preloader->push_to_queue( $url );
171 }
172
173 $this->cache_preloader->save()->dispatch();
174
175 if ( $this->settings['enable_sitemap_preload'] && function_exists( '\PoweredCachePremium\Utils\preload_sitemap' ) ) {
176 \PoweredCachePremium\Utils\preload_sitemap();
177 }
178 }
179
180
181 /**
182 * Add related pages to preload queue when the cache got cleared
183 *
184 * @param int $post_id Post ID
185 * @param array $urls The URL list of the related pages that cleared during post update
186 *
187 * @since 2.0
188 */
189 public function add_purged_urls_to_preload_queue( $post_id, $urls ) {
190 if ( ! $urls ) {
191 return;
192 }
193
194 foreach ( $urls as $url ) {
195 $this->cache_preloader->push_to_queue( $url );
196 \PoweredCache\Utils\log( sprintf( 'Pushed to preload queue [add_purged_urls_to_preload_queue]: %s', $url ) );
197 }
198
199 $this->cache_preloader->save()->dispatch();
200 }
201
202 /**
203 * Requeue deleted URLs
204 *
205 * @param array $expired_files Full path of cached item
206 */
207 public function add_expired_urls_to_preload_queue( $expired_files ) {
208
209 /**
210 * Filters expired urls preloading status
211 *
212 * @hook powered_cache_preload_expired_urls
213 *
214 * @param {boolean} $status True by default for preloading urls.
215 *
216 * @return {boolean} New value.
217 * @since 2.0
218 */
219 $preload_expired_urls = apply_filters( 'powered_cache_preload_expired_urls', true );
220
221 if ( true !== $preload_expired_urls ) {
222 return;
223 }
224
225 /**
226 * Get dir path without cache file name (eg index.html)
227 */
228 $expired_files = array_map(
229 function ( $item ) {
230 return dirname( $item );
231 },
232 $expired_files
233 );
234
235 $expired_files = array_unique( $expired_files ); // prevent double request due to meta.php + index.php file
236
237 // replace path with site url
238 $expired_urls = str_replace( site_cache_dir(), trailingslashit( get_site_url() ), $expired_files );
239 $has_trailingslash = permalink_structure_has_trailingslash();
240
241 foreach ( $expired_urls as $url ) {
242 if ( $has_trailingslash ) {
243 $url = trailingslashit( $url );
244 }
245 $this->cache_preloader->push_to_queue( $url );
246 \PoweredCache\Utils\log( sprintf( 'Pushed to preload queue [add_expired_urls_to_preload_queue]: %s', $url ) );
247 }
248
249 $this->cache_preloader->save()->dispatch();
250 }
251
252 /**
253 * Prep. public post urls for preload queue
254 */
255 public function prepare_public_posts_urls() {
256 global $wpdb;
257
258 $public_posts_url = [];
259 /**
260 * Filters posts offset for preload.
261 *
262 * @hook powered_cache_preload_public_posts_offset
263 *
264 * @param {int} $offset The offset of the post query.
265 *
266 * @return {int} New value.
267 * @since 2.0
268 */
269 $offset = (int) apply_filters( 'powered_cache_preload_public_posts_offset', 0 );
270 $max_preload_item = $this->preload_max_post_count();
271
272 \PoweredCache\Utils\log( sprintf( 'Adding public posts to queue' ) );
273 \PoweredCache\Utils\log( sprintf( 'OFFSET: %s', $offset ) );
274 \PoweredCache\Utils\log( sprintf( 'LIMIT: %s', $max_preload_item ) );
275
276 /**
277 * Filters public post types.
278 *
279 * @hook powered_cache_preload_post_types
280 *
281 * @param {array} $types Public post types.
282 *
283 * @return {array} New value.
284 * @since 1.0
285 */
286 $types = apply_filters(
287 'powered_cache_preload_post_types',
288 get_post_types(
289 array(
290 'public' => true,
291 'publicly_queryable' => true,
292 ),
293 'names',
294 'or'
295 )
296 );
297
298 $types = array_map( 'esc_sql', $types );
299 $types = "'" . implode( "','", $types ) . "'";
300 $posts = $wpdb->get_col( "SELECT ID FROM {$wpdb->posts} WHERE ( post_type IN ( $types ) ) AND post_status = 'publish' ORDER BY ID ASC LIMIT {$offset},{$max_preload_item}" ); // phpcs:ignore
301
302 \PoweredCache\Utils\log( sprintf( 'Found posts: %s', count( $posts ) ) );
303
304 foreach ( $posts as $post_id ) {
305 $public_posts_url[] = get_permalink( $post_id );
306 }
307
308 return $public_posts_url;
309 }
310
311 /**
312 * Prep. public tax terms urls for preload queue
313 */
314 public function prepare_public_tax_terms_urls() {
315 $public_tax_term_urls = [];
316 $taxonomies = get_taxonomies( [ 'public' => true ] );
317
318 /**
319 * Filters public taxonomies.
320 *
321 * @hook powered_cache_preload_taxonomies
322 *
323 * @param {array} $taxonomies Public taxonomies.
324 *
325 * @return {array} New value.
326 * @since 1.0
327 */
328 $taxonomies = apply_filters( 'powered_cache_preload_taxonomies', $taxonomies );
329
330 foreach ( $taxonomies as $tax ) {
331 \PoweredCache\Utils\log( sprintf( 'Taxonomy added to queue: %s', $tax ) );
332
333 /**
334 * Filters taxonomy offset for preload.
335 *
336 * @hook powered_cache_preload_public_taxonomies_offset
337 *
338 * @param {int} $offset Public taxonomies.
339 *
340 * @return {int} New value.
341 * @since 2.0
342 */
343 $offset = (int) apply_filters( 'powered_cache_preload_public_taxonomies_offset', 0 );
344 $limit = $this->preload_max_term_count();
345
346 /**
347 * Filters term query args.
348 *
349 * @hook powered_cache_preload_tax_term_args
350 *
351 * @param {array} $args Arguments for terms.
352 *
353 * @return {array} New value.
354 * @since 2.0
355 */
356 $args = apply_filters(
357 'powered_cache_preload_tax_term_args',
358 [
359 'hide_empty' => false,
360 'orderby' => 'count',
361 'offset' => $offset,
362 'number' => $limit,
363 ],
364 $tax
365 );
366
367 \PoweredCache\Utils\log( 'Taxonomy preload args: [powered_cache_preload_tax_term_args]:' . print_r( $args, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
368
369 $terms = get_terms( $tax, $args );
370
371 foreach ( $terms as $term ) {
372 $public_tax_term_urls[] = get_term_link( $term );
373 }
374 }
375
376 return $public_tax_term_urls;
377 }
378
379
380 /**
381 * Max number of posts that will preloaded
382 *
383 * @return mixed|void
384 */
385 public function preload_max_post_count() {
386 /**
387 * Filters preload post count
388 *
389 * @hook powered_cache_preload_post_limit
390 *
391 * @param {int} $limit The number of posts that will preloaded.
392 *
393 * @return {int} New value.
394 * @since 2.0
395 */
396 return apply_filters( 'powered_cache_preload_post_limit', 1000 );
397 }
398
399 /**
400 * Max number of public terms that will preloaded
401 *
402 * @return mixed|void
403 */
404 public function preload_max_term_count() {
405 /**
406 * Filters preload term count
407 *
408 * @hook powered_cache_preload_term_limit
409 *
410 * @param {int} $limit The number of terms that will preloaded.
411 *
412 * @return {int} New value.
413 * @since 2.0
414 */
415 return apply_filters( 'powered_cache_preload_term_limit', 100 );
416 }
417
418 /**
419 * Filters domains and URLs for resource hints of relation type
420 *
421 * @param array $urls URLs to print for resource hints.
422 * @param string $relation_type The relation type the URLs are printed for, e.g. 'preconnect' or 'prerender'.
423 *
424 * @return array $urls resource hints
425 */
426 public function dns_prefetch( $urls, $relation_type ) {
427 $domains = $this->get_prefetch_dns();
428 if ( $domains && is_array( $domains ) ) {
429 foreach ( $domains as $domain ) {
430 if ( 'dns-prefetch' === $relation_type ) {
431 $urls[] = $domain;
432 }
433
434 if ( 'preconnect' === $relation_type ) {
435 $parsed = wp_parse_url( $domain );
436 if ( empty( $parsed['scheme'] ) ) {
437 $domain = set_url_scheme( $domain );
438 }
439
440 $urls[] = $domain;
441 }
442 }
443 }
444
445 return $urls;
446 }
447
448
449 /**
450 *
451 * Get the list of prefetch domains
452 *
453 * @return mixed|void
454 */
455 public function get_prefetch_dns() {
456 $settings = \PoweredCache\Utils\get_settings();
457
458 $prefetch_dns = preg_split( '#(\r\n|\r|\n)#', $settings['prefetch_dns'], - 1, PREG_SPLIT_NO_EMPTY );
459
460 /**
461 * Filters Prefetched DNS list.
462 *
463 * @hook powered_cache_prefetch_dns
464 *
465 * @param {array} $prefetch_dns The list of prefetch domains.
466 *
467 * @return {array} New value.
468 * @since 2.0
469 */
470 return apply_filters( 'powered_cache_prefetch_dns', $prefetch_dns );
471 }
472
473
474 /**
475 * Make preload request
476 *
477 * @param string $url Target URL
478 * @param array $args request args
479 *
480 * @return array|\WP_Error
481 */
482 public static function preload_request( $url, $args = [] ) {
483
484 /**
485 * Filters args of preload requests.
486 *
487 * @hook powered_cache_preload_url_request_args
488 *
489 * @param {array} $args Request defaults.
490 *
491 * @return {array} New value.
492 * @since 2.0
493 */
494 $request_args = apply_filters(
495 'powered_cache_preload_url_request_args',
496 [
497 'timeout' => 0.01,
498 'blocking' => false,
499 'user-agent' => 'Powered Cache Preloader',
500 'sslverify' => false,
501 ]
502 );
503
504 $args = wp_parse_args( $args, $request_args );
505
506 /**
507 * Fires before doing preload HTTP request.
508 *
509 * @hook powered_cache_preload_http_request
510 *
511 * @param {string} $url Preload URL.
512 * @param {array} $args Request arguments.
513 *
514 * @since 2.0
515 */
516 do_action( 'powered_cache_preload_http_request', $url, $args );
517
518 \PoweredCache\Utils\log( sprintf( 'Preloading...%s: %s', $url, print_r( $args, true ) ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
519
520 return wp_remote_get( esc_url_raw( $url ), $args );
521 }
522
523 /**
524 * Wait internal between HTTP reqs
525 *
526 * @param int $delay delay time in microseconds
527 */
528 public static function wait( $delay = 500000 ) {
529 /**
530 * Filters wait time between preload requests.
531 *
532 * @hook powered_cache_preload_request_interval
533 *
534 * @param {int} $delay Delay time in milliseconds.
535 *
536 * @return {int} New value.
537 * @since 2.0
538 */
539 usleep( absint( apply_filters( 'powered_cache_preload_request_interval', $delay ) ) );
540 }
541
542
543 /**
544 * Get mobile user agent for preload requests
545 *
546 * @return mixed|void
547 */
548 public static function mobile_user_agent() {
549 /**
550 * Filters mobile agent name for mobile preload requests
551 *
552 * @hook powered_cache_preload_mobile_user_agent
553 *
554 * @param {string} $agent Mobile agent name.
555 *
556 * @return {string} New value.
557 * @since 2.0
558 */
559 return apply_filters( 'powered_cache_preload_mobile_user_agent', 'Powered Cache Preloader mobile iPhone' );
560 }
561
562 }
563