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 +220 -28 2.0.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
@@ -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,49 @@
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 - if ( is_multisite() && ! current_user_can( 'manage_network' ) ) {
139 + if ( POWERED_CACHE_IS_NETWORK && ! current_user_can( 'manage_network' ) ) {
114 140 $redirect_url = add_query_arg( 'pc_action', 'start_preload_err_permission', wp_get_referer() );
115 - wp_safe_redirect( $redirect_url );
141 + wp_safe_redirect( esc_url_raw( $redirect_url ) );
116 142 exit;
117 143 }
118 144
145 + if ( ! current_user_can( 'manage_options' ) ) {
146 + $redirect_url = add_query_arg( 'pc_action', 'start_preload_err_permission', wp_get_referer() );
147 + wp_safe_redirect( esc_url_raw( $redirect_url ) );
148 + exit;
149 + }
150 +
151 + \PoweredCache\Utils\log( sprintf( 'Preload triggered from admin bar.' ) );
152 +
119 153 $this->setup_preload_queue();
120 154
121 155 $redirect_url = add_query_arg( 'pc_action', 'start_preload', wp_get_referer() );
122 - wp_safe_redirect( $redirect_url );
156 + wp_safe_redirect( esc_url_raw( $redirect_url ) );
123 157 exit;
124 158 }
125 159
126 160 /**
@@ -126,8 +160,11 @@
126 160 /**
127 161 * Setup preload
128 162 */
129 163 public function setup_preload_queue() {
164 + // cancel existing process before populating
165 + $this->get_preloader()->cancel_process();
166 +
130 167 if ( POWERED_CACHE_IS_NETWORK ) {
131 168 $sites = get_sites( [ 'fields' => 'ids' ] );
132 169 foreach ( $sites as $site_id ) {
133 170 switch_to_blog( $site_id );
@@ -137,8 +174,12 @@
137 174 }
138 175 } else {
139 176 $this->populate_preload_queue();
140 177 }
178 +
179 + if ( $this->settings['enable_sitemap_preload'] && function_exists( '\PoweredCachePremium\Utils\preload_sitemap' ) ) {
180 + \PoweredCachePremium\Utils\preload_sitemap();
181 + }
141 182 }
142 183
143 184 /**
144 185 * Populate preload queue based on settings
@@ -145,14 +186,30 @@
145 186 */
146 187 protected function populate_preload_queue() {
147 188 $preload_urls = [];
148 189
149 - // cancel existing process before populating
150 - $this->cache_preloader->cancel_process();
190 + if ( $this->settings['preload_homepage'] ) {
191 + $front_page = get_option( 'page_on_front' );
192 + if ( ! empty( $front_page ) ) {
193 + $front_page_url = get_permalink( $front_page );
194 + $preload_urls[] = $front_page_url;
195 + \PoweredCache\Utils\log( sprintf( 'Front page URL added to preload queue: %s', $front_page_url ) );
196 + }
151 197
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() ) );
198 + $posts_page = get_option( 'page_for_posts' );
199 + if ( ! empty( $posts_page ) ) {
200 + $posts_page_url = get_permalink( $posts_page );
201 + $preload_urls[] = $posts_page_url;
202 + \PoweredCache\Utils\log( sprintf( 'Posts Page URL added to preload queue: %s', $posts_page_url ) );
203 + }
204 +
205 + /**
206 + * trailingslashit important here,
207 + * likely redirection is not followed for non-blocking preload request
208 + */
209 + $home_url = trailingslashit( get_home_url() );
210 + $preload_urls[] = $home_url;
211 + \PoweredCache\Utils\log( sprintf( 'Home URL added to preload queue: %s', $home_url ) );
155 212 }
156 213
157 214 if ( $this->settings['preload_public_posts'] ) {
158 215 $public_post_urls = $this->prepare_public_posts_urls();
@@ -165,17 +222,43 @@
165 222 $preload_urls = array_merge( $preload_urls, $public_tax_term_urls );
166 223 \PoweredCache\Utils\log( sprintf( 'Public tax terms added to preload queue. ' ) );
167 224 }
168 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 +
169 238 foreach ( $preload_urls as $url ) {
170 - $this->cache_preloader->push_to_queue( $url );
239 + $this->add_url_to_preload_queue( $url );
171 240 }
241 + }
172 242
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 - }
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 + );
178 261 }
179 262
180 263
181 264 /**
@@ -186,18 +269,23 @@
186 269 *
187 270 * @since 2.0
188 271 */
189 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 +
190 280 if ( ! $urls ) {
191 281 return;
192 282 }
193 283
194 284 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 ) );
285 + $this->add_url_to_preload_queue( $url );
197 286 }
198 287
199 - $this->cache_preloader->save()->dispatch();
200 288 }
201 289
202 290 /**
203 291 * Requeue deleted URLs
@@ -241,13 +329,11 @@
241 329 foreach ( $expired_urls as $url ) {
242 330 if ( $has_trailingslash ) {
243 331 $url = trailingslashit( $url );
244 332 }
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 ) );
333 + $this->add_url_to_preload_queue( $url );
247 334 }
248 335
249 - $this->cache_preloader->save()->dispatch();
250 336 }
251 337
252 338 /**
253 339 * Prep. public post urls for preload queue
@@ -421,8 +507,9 @@
421 507 * @param array $urls URLs to print for resource hints.
422 508 * @param string $relation_type The relation type the URLs are printed for, e.g. 'preconnect' or 'prerender'.
423 509 *
424 510 * @return array $urls resource hints
511 + * @since 2.2 "preconnect" hint split from dns prefetch
425 512 */
426 513 public function dns_prefetch( $urls, $relation_type ) {
427 514 $domains = $this->get_prefetch_dns();
428 515 if ( $domains && is_array( $domains ) ) {
@@ -427,11 +514,30 @@
427 514 $domains = $this->get_prefetch_dns();
428 515 if ( $domains && is_array( $domains ) ) {
429 516 foreach ( $domains as $domain ) {
430 517 if ( 'dns-prefetch' === $relation_type ) {
518 + $domain = str_replace( [ 'http://', 'https://' ], '//', $domain );
431 519 $urls[] = $domain;
432 520 }
521 + }
522 + }
433 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 ) {
434 540 if ( 'preconnect' === $relation_type ) {
435 541 $parsed = wp_parse_url( $domain );
436 542 if ( empty( $parsed['scheme'] ) ) {
437 543 $domain = set_url_scheme( $domain );
@@ -469,9 +575,33 @@
469 575 */
470 576 return apply_filters( 'powered_cache_prefetch_dns', $prefetch_dns );
471 577 }
472 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();
473 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 +
474 604 /**
475 605 * Make preload request
476 606 *
477 607 * @param string $url Target URL
@@ -514,9 +644,9 @@
514 644 * @since 2.0
515 645 */
516 646 do_action( 'powered_cache_preload_http_request', $url, $args );
517 647
518 - \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
519 649
520 650 return wp_remote_get( esc_url_raw( $url ), $args );
521 651 }
522 652
@@ -530,14 +660,29 @@
530 660 * Filters wait time between preload requests.
531 661 *
532 662 * @hook powered_cache_preload_request_interval
533 663 *
534 - * @param {int} $delay Delay time in milliseconds.
664 + * @param {int} $delay Delay time in microseconds.
535 665 *
536 666 * @return {int} New value.
537 667 * @since 2.0
538 668 */
539 - 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 + }
540 685 }
541 686
542 687
543 688 /**
@@ -556,7 +701,54 @@
556 701 * @return {string} New value.
557 702 * @since 2.0
558 703 */
559 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.' );
560 752 }
561 753
562 754 }