PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / trunk
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score vtrunk
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
← All changes | includes/classes/Preloader.php +184 -17 2.1.2trunk View file →
@@ -9,8 +9,9 @@
9 9
10 10 use PoweredCache\Async\CachePreloader;
11 11 use function PoweredCache\Utils\permalink_structure_has_trailingslash;
12 12 use function PoweredCache\Utils\site_cache_dir;
13 +use const PoweredCache\Constants\DEFERRED_PRELOAD_QUEUE_CRON_NAME;
13 14
14 15 /**
15 16 * Class Preloader
16 17 */
@@ -29,8 +30,15 @@
29 30 */
30 31 private $cache_preloader;
31 32
32 33 /**
34 + * Tracks whether we’ve added any URLs this request
35 + *
36 + * @var $queue_dirty
37 + */
38 + private $queue_dirty = false;
39 +
40 + /**
33 41 * Return an instance of the current class
34 42 *
35 43 * @return Preloader
36 44 * @since 1.0
@@ -51,8 +59,10 @@
51 59 * Setup routine
52 60 */
53 61 public function setup() {
54 62 $this->settings = \PoweredCache\Utils\get_settings();
63 + add_filter( 'wp_resource_hints', [ $this, 'dns_prefetch' ], 10, 2 );
64 + add_filter( 'wp_resource_hints', [ $this, 'preconnect_resources' ], 10, 2 );
55 65
56 66 // bail if the preload not activated
57 67 if ( ! $this->settings['enable_cache_preload'] ) {
58 68 return;
@@ -57,10 +67,9 @@
57 67 if ( ! $this->settings['enable_cache_preload'] ) {
58 68 return;
59 69 }
60 70
61 - $this->cache_preloader = CachePreloader::factory();
62 - add_filter( 'wp_resource_hints', [ $this, 'dns_prefetch' ], 10, 2 );
71 + $this->get_preloader();
63 72
64 73 /**
65 74 * Page cache needs to be activated to get benefits of preloading
66 75 */
@@ -71,10 +80,12 @@
71 80 add_action( 'admin_bar_menu', [ $this, 'admin_bar_menu' ] );
72 81 add_action( 'admin_post_powered_cache_preload_cache', [ $this, 'start_preload' ] );
73 82 add_action( 'powered_cache_purge_all_cache', [ $this, 'setup_preload_queue' ] );
74 83 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 );
84 + add_action( 'powered_cache_advanced_cache_purge_post', [ $this, 'deferred_preload_queue' ], 10, 2 );
76 85 add_action( 'powered_cache_expired_files_deleted', [ $this, 'add_expired_urls_to_preload_queue' ], 10, 2 );
86 + add_action( DEFERRED_PRELOAD_QUEUE_CRON_NAME, [ $this, 'add_purged_urls_to_preload_queue' ], 10, 2 );
87 + add_action( 'shutdown', [ $this, 'dispatch_preload_queue' ], 0 );
77 88 }
78 89
79 90 /**
80 91 * Preload Admin bar menu
@@ -101,13 +112,28 @@
101 112 )
102 113 );
103 114 }
104 115
116 +
105 117 /**
118 + * Get the instance of CachePreloader
119 + *
120 + * @return CachePreloader
121 + */
122 + private function get_preloader() {
123 + if ( ! $this->cache_preloader ) {
124 + $this->cache_preloader = CachePreloader::factory();
125 + }
126 +
127 + return $this->cache_preloader;
128 + }
129 +
130 + /**
106 131 * Add preloading items to queue
107 132 */
108 133 public function start_preload() {
109 - if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'powered_cache_preload_cache' ) ) {
134 + $nonce = isset( $_GET['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ) : '';
135 + if ( ! wp_verify_nonce( $nonce, 'powered_cache_preload_cache' ) ) {
110 136 wp_nonce_ays( '' );
111 137 }
112 138
113 139 if ( POWERED_CACHE_IS_NETWORK && ! current_user_can( 'manage_network' ) ) {
@@ -121,8 +147,10 @@
121 147 wp_safe_redirect( esc_url_raw( $redirect_url ) );
122 148 exit;
123 149 }
124 150
151 + \PoweredCache\Utils\log( sprintf( 'Preload triggered from admin bar.' ) );
152 +
125 153 $this->setup_preload_queue();
126 154
127 155 $redirect_url = add_query_arg( 'pc_action', 'start_preload', wp_get_referer() );
128 156 wp_safe_redirect( esc_url_raw( $redirect_url ) );
@@ -133,9 +161,9 @@
133 161 * Setup preload
134 162 */
135 163 public function setup_preload_queue() {
136 164 // cancel existing process before populating
137 - $this->cache_preloader->cancel_process();
165 + $this->get_preloader()->cancel_process();
138 166
139 167 if ( POWERED_CACHE_IS_NETWORK ) {
140 168 $sites = get_sites( [ 'fields' => 'ids' ] );
141 169 foreach ( $sites as $site_id ) {
@@ -147,10 +175,8 @@
147 175 } else {
148 176 $this->populate_preload_queue();
149 177 }
150 178
151 - $this->cache_preloader->save()->dispatch();
152 -
153 179 if ( $this->settings['enable_sitemap_preload'] && function_exists( '\PoweredCachePremium\Utils\preload_sitemap' ) ) {
154 180 \PoweredCachePremium\Utils\preload_sitemap();
155 181 }
156 182 }
@@ -196,14 +222,46 @@
196 222 $preload_urls = array_merge( $preload_urls, $public_tax_term_urls );
197 223 \PoweredCache\Utils\log( sprintf( 'Public tax terms added to preload queue. ' ) );
198 224 }
199 225
226 + /**
227 + * Filters preload urls before sending to queue
228 + *
229 + * @hook populate_preload_queue_urls
230 + *
231 + * @param {array} $preload_urls The list of preload urls
232 + *
233 + * @return {array} New value.
234 + * @since 2.4
235 + */
236 + $preload_urls = apply_filters( 'populate_preload_queue_urls', $preload_urls );
237 +
200 238 foreach ( $preload_urls as $url ) {
201 - $this->cache_preloader->push_to_queue( $url );
239 + $this->add_url_to_preload_queue( $url );
202 240 }
203 241 }
204 242
243 + /**
244 + * Add URLs to preload queue with a delay
245 + *
246 + * @param int $post_id Post ID
247 + * @param array $urls The URL list of the related pages that will be preloaded
248 + *
249 + * @since 3.6
250 + */
251 + public function deferred_preload_queue( $post_id, $urls ) {
252 + \PoweredCache\Utils\log( sprintf( 'Post ID %d purged from cache, adding related URLs to preload queue with a delay.', $post_id ) );
253 + wp_schedule_single_event(
254 + time() + 10,
255 + DEFERRED_PRELOAD_QUEUE_CRON_NAME,
256 + [
257 + 'post_id' => $post_id,
258 + 'urls' => $urls,
259 + ]
260 + );
261 + }
205 262
263 +
206 264 /**
207 265 * Add related pages to preload queue when the cache got cleared
208 266 *
209 267 * @param int $post_id Post ID
@@ -211,18 +269,23 @@
211 269 *
212 270 * @since 2.0
213 271 */
214 272 public function add_purged_urls_to_preload_queue( $post_id, $urls ) {
273 + $post_url = get_permalink( $post_id );
274 +
275 + // include post itself all the time
276 + if ( ! empty( $post_url ) ) {
277 + $this->add_url_to_preload_queue( $post_url );
278 + }
279 +
215 280 if ( ! $urls ) {
216 281 return;
217 282 }
218 283
219 284 foreach ( $urls as $url ) {
220 - $this->cache_preloader->push_to_queue( $url );
221 - \PoweredCache\Utils\log( sprintf( 'Pushed to preload queue [add_purged_urls_to_preload_queue]: %s', $url ) );
285 + $this->add_url_to_preload_queue( $url );
222 286 }
223 287
224 - $this->cache_preloader->save()->dispatch();
225 288 }
226 289
227 290 /**
228 291 * Requeue deleted URLs
@@ -266,13 +329,11 @@
266 329 foreach ( $expired_urls as $url ) {
267 330 if ( $has_trailingslash ) {
268 331 $url = trailingslashit( $url );
269 332 }
270 - $this->cache_preloader->push_to_queue( $url );
271 - \PoweredCache\Utils\log( sprintf( 'Pushed to preload queue [add_expired_urls_to_preload_queue]: %s', $url ) );
333 + $this->add_url_to_preload_queue( $url );
272 334 }
273 335
274 - $this->cache_preloader->save()->dispatch();
275 336 }
276 337
277 338 /**
278 339 * Prep. public post urls for preload queue
@@ -446,8 +507,9 @@
446 507 * @param array $urls URLs to print for resource hints.
447 508 * @param string $relation_type The relation type the URLs are printed for, e.g. 'preconnect' or 'prerender'.
448 509 *
449 510 * @return array $urls resource hints
511 + * @since 2.2 "preconnect" hint split from dns prefetch
450 512 */
451 513 public function dns_prefetch( $urls, $relation_type ) {
452 514 $domains = $this->get_prefetch_dns();
453 515 if ( $domains && is_array( $domains ) ) {
@@ -452,11 +514,30 @@
452 514 $domains = $this->get_prefetch_dns();
453 515 if ( $domains && is_array( $domains ) ) {
454 516 foreach ( $domains as $domain ) {
455 517 if ( 'dns-prefetch' === $relation_type ) {
518 + $domain = str_replace( [ 'http://', 'https://' ], '//', $domain );
456 519 $urls[] = $domain;
457 520 }
521 + }
522 + }
458 523
524 + return $urls;
525 + }
526 +
527 + /**
528 + * Add "preconnect" hint for critical resources
529 + *
530 + * @param array $urls URLs
531 + * @param string $relation_type the type of hint
532 + *
533 + * @return array $urls Resource list
534 + * @since 2.2
535 + */
536 + public function preconnect_resources( $urls, $relation_type ) {
537 + $domains = $this->get_preconnect_resources();
538 + if ( $domains && is_array( $domains ) ) {
539 + foreach ( $domains as $domain ) {
459 540 if ( 'preconnect' === $relation_type ) {
460 541 $parsed = wp_parse_url( $domain );
461 542 if ( empty( $parsed['scheme'] ) ) {
462 543 $domain = set_url_scheme( $domain );
@@ -494,9 +575,33 @@
494 575 */
495 576 return apply_filters( 'powered_cache_prefetch_dns', $prefetch_dns );
496 577 }
497 578
579 + /**
580 + * Get the list of preconnect domains
581 + *
582 + * @return mixed|void
583 + * @since 2.2
584 + */
585 + public function get_preconnect_resources() {
586 + $settings = \PoweredCache\Utils\get_settings();
498 587
588 + $preconnect_resources = preg_split( '#(\r\n|\r|\n)#', $settings['preconnect_resource'], - 1, PREG_SPLIT_NO_EMPTY );
589 +
590 + /**
591 + * Filters Preconnect resource list.
592 + *
593 + * @hook powered_cache_preconnect_resource
594 + *
595 + * @param {array} $preconnect_resources The list of prefetch domains.
596 + *
597 + * @return {array} New value.
598 + * @since 2.2
599 + */
600 + return apply_filters( 'powered_cache_preconnect_resource', $preconnect_resources );
601 + }
602 +
603 +
499 604 /**
500 605 * Make preload request
501 606 *
502 607 * @param string $url Target URL
@@ -539,9 +644,9 @@
539 644 * @since 2.0
540 645 */
541 646 do_action( 'powered_cache_preload_http_request', $url, $args );
542 647
543 - \PoweredCache\Utils\log( sprintf( 'Preloading...%s: %s', $url, print_r( $args, true ) ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
648 + \PoweredCache\Utils\log( sprintf( 'Processing..: %s', $url ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
544 649
545 650 return wp_remote_get( esc_url_raw( $url ), $args );
546 651 }
547 652
@@ -555,14 +660,29 @@
555 660 * Filters wait time between preload requests.
556 661 *
557 662 * @hook powered_cache_preload_request_interval
558 663 *
559 - * @param {int} $delay Delay time in milliseconds.
664 + * @param {int} $delay Delay time in microseconds.
560 665 *
561 666 * @return {int} New value.
562 667 * @since 2.0
563 668 */
564 - usleep( absint( apply_filters( 'powered_cache_preload_request_interval', $delay ) ) );
669 + $delay = absint( apply_filters( 'powered_cache_preload_request_interval', $delay ) );
670 +
671 + // Convert the delay to seconds and microseconds
672 + $seconds = intdiv( $delay, 1000000 ); // Get full seconds
673 + $microseconds = $delay % 1000000; // Get remaining microseconds
674 +
675 + // Use sleep() for full second delays
676 + if ( $seconds > 0 ) {
677 + sleep( $seconds );
678 + }
679 +
680 + // Use usleep() for remaining microseconds
681 + // sleeping more than 1 seconds may not be supported by the operating system with usleep
682 + if ( $microseconds > 0 ) {
683 + usleep( $microseconds );
684 + }
565 685 }
566 686
567 687
568 688 /**
@@ -581,7 +701,54 @@
581 701 * @return {string} New value.
582 702 * @since 2.0
583 703 */
584 704 return apply_filters( 'powered_cache_preload_mobile_user_agent', 'Powered Cache Preloader mobile iPhone' );
705 + }
706 +
707 + /**
708 + * Add a URL to the preload queue with optional filtering.
709 + *
710 + * @param string $url The URL to add to the preload queue.
711 + *
712 + * @since 3.4
713 + */
714 + protected function add_url_to_preload_queue( $url ) {
715 + /**
716 + * Filters whether a URL should be added to the preload queue.
717 + *
718 + * @hook powered_cache_preload_add_url_to_queue
719 + *
720 + * @param {boolean} $preload Whether to preload the URL. Default true.
721 + * @param {string} $url The URL to be preloaded.
722 + *
723 + * @return {boolean} Whether to preload the URL.
724 + * @since 3.4
725 + */
726 + $do_preload = apply_filters( 'powered_cache_preload_add_url_to_queue', true, $url );
727 +
728 + if ( $do_preload ) {
729 + $this->get_preloader()->push_to_queue( $url );
730 + $this->queue_dirty = true;
731 + \PoweredCache\Utils\log( sprintf( 'URL added to preload queue : %s', $url ) );
732 + } else {
733 + \PoweredCache\Utils\log( sprintf( 'URL skipped from preload queue: %s', $url ) );
734 + }
735 + }
736 +
737 + /**
738 + * Dispatch the preload queue if it has been modified.
739 + *
740 + * @return void
741 + * @since 3.6
742 + */
743 + public function dispatch_preload_queue() {
744 + if ( ! $this->queue_dirty ) {
745 + return;
746 + }
747 +
748 + // This will serialize the queue into the DB and fire off the async request
749 + $this->get_preloader()->save()->dispatch();
750 +
751 + \PoweredCache\Utils\log( 'Dispatched preload queue.' );
585 752 }
586 753
587 754 }