PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / 3.6.3
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score v3.6.3
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 +219 -28 2.0.23.6.3 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
@@ -83,9 +94,9 @@
83 94 *
84 95 * @since 1.0
85 96 */
86 97 public function admin_bar_menu( $wp_admin_bar ) {
87 - if ( is_multisite() && ! current_user_can( 'manage_network' ) ) {
98 + if ( POWERED_CACHE_IS_NETWORK && ! current_user_can( 'manage_network' ) ) {
88 99 return;
89 100 }
90 101
91 102 if ( ! current_user_can( 'manage_options' ) ) {
@@ -101,26 +112,48 @@
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 + if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'powered_cache_preload_cache' ) ) { // phpcs:ignore
110 135 wp_nonce_ays( '' );
111 136 }
112 137
113 - if ( is_multisite() && ! current_user_can( 'manage_network' ) ) {
138 + if ( POWERED_CACHE_IS_NETWORK && ! current_user_can( 'manage_network' ) ) {
114 139 $redirect_url = add_query_arg( 'pc_action', 'start_preload_err_permission', wp_get_referer() );
115 - wp_safe_redirect( $redirect_url );
140 + wp_safe_redirect( esc_url_raw( $redirect_url ) );
116 141 exit;
117 142 }
118 143
144 + if ( ! current_user_can( 'manage_options' ) ) {
145 + $redirect_url = add_query_arg( 'pc_action', 'start_preload_err_permission', wp_get_referer() );
146 + wp_safe_redirect( esc_url_raw( $redirect_url ) );
147 + exit;
148 + }
149 +
150 + \PoweredCache\Utils\log( sprintf( 'Preload triggered from admin bar.' ) );
151 +
119 152 $this->setup_preload_queue();
120 153
121 154 $redirect_url = add_query_arg( 'pc_action', 'start_preload', wp_get_referer() );
122 - wp_safe_redirect( $redirect_url );
155 + wp_safe_redirect( esc_url_raw( $redirect_url ) );
123 156 exit;
124 157 }
125 158
126 159 /**
@@ -126,8 +159,11 @@
126 159 /**
127 160 * Setup preload
128 161 */
129 162 public function setup_preload_queue() {
163 + // cancel existing process before populating
164 + $this->get_preloader()->cancel_process();
165 +
130 166 if ( POWERED_CACHE_IS_NETWORK ) {
131 167 $sites = get_sites( [ 'fields' => 'ids' ] );
132 168 foreach ( $sites as $site_id ) {
133 169 switch_to_blog( $site_id );
@@ -137,8 +173,12 @@
137 173 }
138 174 } else {
139 175 $this->populate_preload_queue();
140 176 }
177 +
178 + if ( $this->settings['enable_sitemap_preload'] && function_exists( '\PoweredCachePremium\Utils\preload_sitemap' ) ) {
179 + \PoweredCachePremium\Utils\preload_sitemap();
180 + }
141 181 }
142 182
143 183 /**
144 184 * Populate preload queue based on settings
@@ -145,14 +185,30 @@
145 185 */
146 186 protected function populate_preload_queue() {
147 187 $preload_urls = [];
148 188
149 - // cancel existing process before populating
150 - $this->cache_preloader->cancel_process();
189 + if ( $this->settings['preload_homepage'] ) {
190 + $front_page = get_option( 'page_on_front' );
191 + if ( ! empty( $front_page ) ) {
192 + $front_page_url = get_permalink( $front_page );
193 + $preload_urls[] = $front_page_url;
194 + \PoweredCache\Utils\log( sprintf( 'Front page URL added to preload queue: %s', $front_page_url ) );
195 + }
151 196
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() ) );
197 + $posts_page = get_option( 'page_for_posts' );
198 + if ( ! empty( $posts_page ) ) {
199 + $posts_page_url = get_permalink( $posts_page );
200 + $preload_urls[] = $posts_page_url;
201 + \PoweredCache\Utils\log( sprintf( 'Posts Page URL added to preload queue: %s', $posts_page_url ) );
202 + }
203 +
204 + /**
205 + * trailingslashit important here,
206 + * likely redirection is not followed for non-blocking preload request
207 + */
208 + $home_url = trailingslashit( get_home_url() );
209 + $preload_urls[] = $home_url;
210 + \PoweredCache\Utils\log( sprintf( 'Home URL added to preload queue: %s', $home_url ) );
155 211 }
156 212
157 213 if ( $this->settings['preload_public_posts'] ) {
158 214 $public_post_urls = $this->prepare_public_posts_urls();
@@ -165,17 +221,43 @@
165 221 $preload_urls = array_merge( $preload_urls, $public_tax_term_urls );
166 222 \PoweredCache\Utils\log( sprintf( 'Public tax terms added to preload queue. ' ) );
167 223 }
168 224
225 + /**
226 + * Filters preload urls before sending to queue
227 + *
228 + * @hook populate_preload_queue_urls
229 + *
230 + * @param {array} $preload_urls The list of preload urls
231 + *
232 + * @return {array} New value.
233 + * @since 2.4
234 + */
235 + $preload_urls = apply_filters( 'populate_preload_queue_urls', $preload_urls );
236 +
169 237 foreach ( $preload_urls as $url ) {
170 - $this->cache_preloader->push_to_queue( $url );
238 + $this->add_url_to_preload_queue( $url );
171 239 }
240 + }
172 241
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 - }
242 + /**
243 + * Add URLs to preload queue with a delay
244 + *
245 + * @param int $post_id Post ID
246 + * @param array $urls The URL list of the related pages that will be preloaded
247 + *
248 + * @since 3.6
249 + */
250 + public function deferred_preload_queue( $post_id, $urls ) {
251 + \PoweredCache\Utils\log( sprintf( 'Post ID %d purged from cache, adding related URLs to preload queue with a delay.', $post_id ) );
252 + wp_schedule_single_event(
253 + time() + 10,
254 + DEFERRED_PRELOAD_QUEUE_CRON_NAME,
255 + [
256 + 'post_id' => $post_id,
257 + 'urls' => $urls,
258 + ]
259 + );
178 260 }
179 261
180 262
181 263 /**
@@ -186,18 +268,23 @@
186 268 *
187 269 * @since 2.0
188 270 */
189 271 public function add_purged_urls_to_preload_queue( $post_id, $urls ) {
272 + $post_url = get_permalink( $post_id );
273 +
274 + // include post itself all the time
275 + if ( ! empty( $post_url ) ) {
276 + $this->add_url_to_preload_queue( $post_url );
277 + }
278 +
190 279 if ( ! $urls ) {
191 280 return;
192 281 }
193 282
194 283 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 ) );
284 + $this->add_url_to_preload_queue( $url );
197 285 }
198 286
199 - $this->cache_preloader->save()->dispatch();
200 287 }
201 288
202 289 /**
203 290 * Requeue deleted URLs
@@ -241,13 +328,11 @@
241 328 foreach ( $expired_urls as $url ) {
242 329 if ( $has_trailingslash ) {
243 330 $url = trailingslashit( $url );
244 331 }
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 ) );
332 + $this->add_url_to_preload_queue( $url );
247 333 }
248 334
249 - $this->cache_preloader->save()->dispatch();
250 335 }
251 336
252 337 /**
253 338 * Prep. public post urls for preload queue
@@ -421,8 +506,9 @@
421 506 * @param array $urls URLs to print for resource hints.
422 507 * @param string $relation_type The relation type the URLs are printed for, e.g. 'preconnect' or 'prerender'.
423 508 *
424 509 * @return array $urls resource hints
510 + * @since 2.2 "preconnect" hint split from dns prefetch
425 511 */
426 512 public function dns_prefetch( $urls, $relation_type ) {
427 513 $domains = $this->get_prefetch_dns();
428 514 if ( $domains && is_array( $domains ) ) {
@@ -427,11 +513,30 @@
427 513 $domains = $this->get_prefetch_dns();
428 514 if ( $domains && is_array( $domains ) ) {
429 515 foreach ( $domains as $domain ) {
430 516 if ( 'dns-prefetch' === $relation_type ) {
517 + $domain = str_replace( [ 'http://', 'https://' ], '//', $domain );
431 518 $urls[] = $domain;
432 519 }
520 + }
521 + }
433 522
523 + return $urls;
524 + }
525 +
526 + /**
527 + * Add "preconnect" hint for critical resources
528 + *
529 + * @param array $urls URLs
530 + * @param string $relation_type the type of hint
531 + *
532 + * @return array $urls Resource list
533 + * @since 2.2
534 + */
535 + public function preconnect_resources( $urls, $relation_type ) {
536 + $domains = $this->get_preconnect_resources();
537 + if ( $domains && is_array( $domains ) ) {
538 + foreach ( $domains as $domain ) {
434 539 if ( 'preconnect' === $relation_type ) {
435 540 $parsed = wp_parse_url( $domain );
436 541 if ( empty( $parsed['scheme'] ) ) {
437 542 $domain = set_url_scheme( $domain );
@@ -469,9 +574,33 @@
469 574 */
470 575 return apply_filters( 'powered_cache_prefetch_dns', $prefetch_dns );
471 576 }
472 577
578 + /**
579 + * Get the list of preconnect domains
580 + *
581 + * @return mixed|void
582 + * @since 2.2
583 + */
584 + public function get_preconnect_resources() {
585 + $settings = \PoweredCache\Utils\get_settings();
473 586
587 + $preconnect_resources = preg_split( '#(\r\n|\r|\n)#', $settings['preconnect_resource'], - 1, PREG_SPLIT_NO_EMPTY );
588 +
589 + /**
590 + * Filters Preconnect resource list.
591 + *
592 + * @hook powered_cache_preconnect_resource
593 + *
594 + * @param {array} $preconnect_resources The list of prefetch domains.
595 + *
596 + * @return {array} New value.
597 + * @since 2.2
598 + */
599 + return apply_filters( 'powered_cache_preconnect_resource', $preconnect_resources );
600 + }
601 +
602 +
474 603 /**
475 604 * Make preload request
476 605 *
477 606 * @param string $url Target URL
@@ -514,9 +643,9 @@
514 643 * @since 2.0
515 644 */
516 645 do_action( 'powered_cache_preload_http_request', $url, $args );
517 646
518 - \PoweredCache\Utils\log( sprintf( 'Preloading...%s: %s', $url, print_r( $args, true ) ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
647 + \PoweredCache\Utils\log( sprintf( 'Processing..: %s', $url ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
519 648
520 649 return wp_remote_get( esc_url_raw( $url ), $args );
521 650 }
522 651
@@ -530,14 +659,29 @@
530 659 * Filters wait time between preload requests.
531 660 *
532 661 * @hook powered_cache_preload_request_interval
533 662 *
534 - * @param {int} $delay Delay time in milliseconds.
663 + * @param {int} $delay Delay time in microseconds.
535 664 *
536 665 * @return {int} New value.
537 666 * @since 2.0
538 667 */
539 - usleep( absint( apply_filters( 'powered_cache_preload_request_interval', $delay ) ) );
668 + $delay = absint( apply_filters( 'powered_cache_preload_request_interval', $delay ) );
669 +
670 + // Convert the delay to seconds and microseconds
671 + $seconds = intdiv( $delay, 1000000 ); // Get full seconds
672 + $microseconds = $delay % 1000000; // Get remaining microseconds
673 +
674 + // Use sleep() for full second delays
675 + if ( $seconds > 0 ) {
676 + sleep( $seconds );
677 + }
678 +
679 + // Use usleep() for remaining microseconds
680 + // sleeping more than 1 seconds may not be supported by the operating system with usleep
681 + if ( $microseconds > 0 ) {
682 + usleep( $microseconds );
683 + }
540 684 }
541 685
542 686
543 687 /**
@@ -556,7 +700,54 @@
556 700 * @return {string} New value.
557 701 * @since 2.0
558 702 */
559 703 return apply_filters( 'powered_cache_preload_mobile_user_agent', 'Powered Cache Preloader mobile iPhone' );
704 + }
705 +
706 + /**
707 + * Add a URL to the preload queue with optional filtering.
708 + *
709 + * @param string $url The URL to add to the preload queue.
710 + *
711 + * @since 3.4
712 + */
713 + protected function add_url_to_preload_queue( $url ) {
714 + /**
715 + * Filters whether a URL should be added to the preload queue.
716 + *
717 + * @hook powered_cache_preload_add_url_to_queue
718 + *
719 + * @param {boolean} $preload Whether to preload the URL. Default true.
720 + * @param {string} $url The URL to be preloaded.
721 + *
722 + * @return {boolean} Whether to preload the URL.
723 + * @since 3.4
724 + */
725 + $do_preload = apply_filters( 'powered_cache_preload_add_url_to_queue', true, $url );
726 +
727 + if ( $do_preload ) {
728 + $this->get_preloader()->push_to_queue( $url );
729 + $this->queue_dirty = true;
730 + \PoweredCache\Utils\log( sprintf( 'URL added to preload queue : %s', $url ) );
731 + } else {
732 + \PoweredCache\Utils\log( sprintf( 'URL skipped from preload queue: %s', $url ) );
733 + }
734 + }
735 +
736 + /**
737 + * Dispatch the preload queue if it has been modified.
738 + *
739 + * @return void
740 + * @since 3.6
741 + */
742 + public function dispatch_preload_queue() {
743 + if ( ! $this->queue_dirty ) {
744 + return;
745 + }
746 +
747 + // This will serialize the queue into the DB and fire off the async request
748 + $this->get_preloader()->save()->dispatch();
749 +
750 + \PoweredCache\Utils\log( 'Dispatched preload queue.' );
560 751 }
561 752
562 753 }