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

588 lines 15.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 ( POWERED_CACHE_IS_NETWORK && ! 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 ( POWERED_CACHE_IS_NETWORK && ! current_user_can( 'manage_network' ) ) {
114 $redirect_url = add_query_arg( 'pc_action', 'start_preload_err_permission', wp_get_referer() );
115 wp_safe_redirect( esc_url_raw( $redirect_url ) );
116 exit;
117 }
118
119 if ( ! current_user_can( 'manage_options' ) ) {
120 $redirect_url = add_query_arg( 'pc_action', 'start_preload_err_permission', wp_get_referer() );
121 wp_safe_redirect( esc_url_raw( $redirect_url ) );
122 exit;
123 }
124
125 $this->setup_preload_queue();
126
127 $redirect_url = add_query_arg( 'pc_action', 'start_preload', wp_get_referer() );
128 wp_safe_redirect( esc_url_raw( $redirect_url ) );
129 exit;
130 }
131
132 /**
133 * Setup preload
134 */
135 public function setup_preload_queue() {
136 // cancel existing process before populating
137 $this->cache_preloader->cancel_process();
138
139 if ( POWERED_CACHE_IS_NETWORK ) {
140 $sites = get_sites( [ 'fields' => 'ids' ] );
141 foreach ( $sites as $site_id ) {
142 switch_to_blog( $site_id );
143 \PoweredCache\Utils\log( sprintf( 'Populating preload queue for site: %d', $site_id ) );
144 $this->populate_preload_queue();
145 restore_current_blog();
146 }
147 } else {
148 $this->populate_preload_queue();
149 }
150
151 $this->cache_preloader->save()->dispatch();
152
153 if ( $this->settings['enable_sitemap_preload'] && function_exists( '\PoweredCachePremium\Utils\preload_sitemap' ) ) {
154 \PoweredCachePremium\Utils\preload_sitemap();
155 }
156 }
157
158 /**
159 * Populate preload queue based on settings
160 */
161 protected function populate_preload_queue() {
162 $preload_urls = [];
163
164 if ( $this->settings['preload_homepage'] ) {
165 $front_page = get_option( 'page_on_front' );
166 if ( ! empty( $front_page ) ) {
167 $front_page_url = get_permalink( $front_page );
168 $preload_urls[] = $front_page_url;
169 \PoweredCache\Utils\log( sprintf( 'Front page URL added to preload queue: %s', $front_page_url ) );
170 }
171
172 $posts_page = get_option( 'page_for_posts' );
173 if ( ! empty( $posts_page ) ) {
174 $posts_page_url = get_permalink( $posts_page );
175 $preload_urls[] = $posts_page_url;
176 \PoweredCache\Utils\log( sprintf( 'Posts Page URL added to preload queue: %s', $posts_page_url ) );
177 }
178
179 /**
180 * trailingslashit important here,
181 * likely redirection is not followed for non-blocking preload request
182 */
183 $home_url = trailingslashit( get_home_url() );
184 $preload_urls[] = $home_url;
185 \PoweredCache\Utils\log( sprintf( 'Home URL added to preload queue: %s', $home_url ) );
186 }
187
188 if ( $this->settings['preload_public_posts'] ) {
189 $public_post_urls = $this->prepare_public_posts_urls();
190 $preload_urls = array_merge( $preload_urls, $public_post_urls );
191 \PoweredCache\Utils\log( sprintf( 'Public posts added to preload queue. ' ) );
192 }
193
194 if ( $this->settings['preload_public_tax'] ) {
195 $public_tax_term_urls = $this->prepare_public_tax_terms_urls();
196 $preload_urls = array_merge( $preload_urls, $public_tax_term_urls );
197 \PoweredCache\Utils\log( sprintf( 'Public tax terms added to preload queue. ' ) );
198 }
199
200 foreach ( $preload_urls as $url ) {
201 $this->cache_preloader->push_to_queue( $url );
202 }
203 }
204
205
206 /**
207 * Add related pages to preload queue when the cache got cleared
208 *
209 * @param int $post_id Post ID
210 * @param array $urls The URL list of the related pages that cleared during post update
211 *
212 * @since 2.0
213 */
214 public function add_purged_urls_to_preload_queue( $post_id, $urls ) {
215 if ( ! $urls ) {
216 return;
217 }
218
219 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 ) );
222 }
223
224 $this->cache_preloader->save()->dispatch();
225 }
226
227 /**
228 * Requeue deleted URLs
229 *
230 * @param array $expired_files Full path of cached item
231 */
232 public function add_expired_urls_to_preload_queue( $expired_files ) {
233
234 /**
235 * Filters expired urls preloading status
236 *
237 * @hook powered_cache_preload_expired_urls
238 *
239 * @param {boolean} $status True by default for preloading urls.
240 *
241 * @return {boolean} New value.
242 * @since 2.0
243 */
244 $preload_expired_urls = apply_filters( 'powered_cache_preload_expired_urls', true );
245
246 if ( true !== $preload_expired_urls ) {
247 return;
248 }
249
250 /**
251 * Get dir path without cache file name (eg index.html)
252 */
253 $expired_files = array_map(
254 function ( $item ) {
255 return dirname( $item );
256 },
257 $expired_files
258 );
259
260 $expired_files = array_unique( $expired_files ); // prevent double request due to meta.php + index.php file
261
262 // replace path with site url
263 $expired_urls = str_replace( site_cache_dir(), trailingslashit( get_site_url() ), $expired_files );
264 $has_trailingslash = permalink_structure_has_trailingslash();
265
266 foreach ( $expired_urls as $url ) {
267 if ( $has_trailingslash ) {
268 $url = trailingslashit( $url );
269 }
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 ) );
272 }
273
274 $this->cache_preloader->save()->dispatch();
275 }
276
277 /**
278 * Prep. public post urls for preload queue
279 */
280 public function prepare_public_posts_urls() {
281 global $wpdb;
282
283 $public_posts_url = [];
284 /**
285 * Filters posts offset for preload.
286 *
287 * @hook powered_cache_preload_public_posts_offset
288 *
289 * @param {int} $offset The offset of the post query.
290 *
291 * @return {int} New value.
292 * @since 2.0
293 */
294 $offset = (int) apply_filters( 'powered_cache_preload_public_posts_offset', 0 );
295 $max_preload_item = $this->preload_max_post_count();
296
297 \PoweredCache\Utils\log( sprintf( 'Adding public posts to queue' ) );
298 \PoweredCache\Utils\log( sprintf( 'OFFSET: %s', $offset ) );
299 \PoweredCache\Utils\log( sprintf( 'LIMIT: %s', $max_preload_item ) );
300
301 /**
302 * Filters public post types.
303 *
304 * @hook powered_cache_preload_post_types
305 *
306 * @param {array} $types Public post types.
307 *
308 * @return {array} New value.
309 * @since 1.0
310 */
311 $types = apply_filters(
312 'powered_cache_preload_post_types',
313 get_post_types(
314 array(
315 'public' => true,
316 'publicly_queryable' => true,
317 ),
318 'names',
319 'or'
320 )
321 );
322
323 $types = array_map( 'esc_sql', $types );
324 $types = "'" . implode( "','", $types ) . "'";
325 $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
326
327 \PoweredCache\Utils\log( sprintf( 'Found posts: %s', count( $posts ) ) );
328
329 foreach ( $posts as $post_id ) {
330 $public_posts_url[] = get_permalink( $post_id );
331 }
332
333 return $public_posts_url;
334 }
335
336 /**
337 * Prep. public tax terms urls for preload queue
338 */
339 public function prepare_public_tax_terms_urls() {
340 $public_tax_term_urls = [];
341 $taxonomies = get_taxonomies( [ 'public' => true ] );
342
343 /**
344 * Filters public taxonomies.
345 *
346 * @hook powered_cache_preload_taxonomies
347 *
348 * @param {array} $taxonomies Public taxonomies.
349 *
350 * @return {array} New value.
351 * @since 1.0
352 */
353 $taxonomies = apply_filters( 'powered_cache_preload_taxonomies', $taxonomies );
354
355 foreach ( $taxonomies as $tax ) {
356 \PoweredCache\Utils\log( sprintf( 'Taxonomy added to queue: %s', $tax ) );
357
358 /**
359 * Filters taxonomy offset for preload.
360 *
361 * @hook powered_cache_preload_public_taxonomies_offset
362 *
363 * @param {int} $offset Public taxonomies.
364 *
365 * @return {int} New value.
366 * @since 2.0
367 */
368 $offset = (int) apply_filters( 'powered_cache_preload_public_taxonomies_offset', 0 );
369 $limit = $this->preload_max_term_count();
370
371 /**
372 * Filters term query args.
373 *
374 * @hook powered_cache_preload_tax_term_args
375 *
376 * @param {array} $args Arguments for terms.
377 *
378 * @return {array} New value.
379 * @since 2.0
380 */
381 $args = apply_filters(
382 'powered_cache_preload_tax_term_args',
383 [
384 'hide_empty' => false,
385 'orderby' => 'count',
386 'offset' => $offset,
387 'number' => $limit,
388 ],
389 $tax
390 );
391
392 \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
393
394 $terms = get_terms( $tax, $args );
395
396 foreach ( $terms as $term ) {
397 $public_tax_term_urls[] = get_term_link( $term );
398 }
399 }
400
401 return $public_tax_term_urls;
402 }
403
404
405 /**
406 * Max number of posts that will preloaded
407 *
408 * @return mixed|void
409 */
410 public function preload_max_post_count() {
411 /**
412 * Filters preload post count
413 *
414 * @hook powered_cache_preload_post_limit
415 *
416 * @param {int} $limit The number of posts that will preloaded.
417 *
418 * @return {int} New value.
419 * @since 2.0
420 */
421 return apply_filters( 'powered_cache_preload_post_limit', 1000 );
422 }
423
424 /**
425 * Max number of public terms that will preloaded
426 *
427 * @return mixed|void
428 */
429 public function preload_max_term_count() {
430 /**
431 * Filters preload term count
432 *
433 * @hook powered_cache_preload_term_limit
434 *
435 * @param {int} $limit The number of terms that will preloaded.
436 *
437 * @return {int} New value.
438 * @since 2.0
439 */
440 return apply_filters( 'powered_cache_preload_term_limit', 100 );
441 }
442
443 /**
444 * Filters domains and URLs for resource hints of relation type
445 *
446 * @param array $urls URLs to print for resource hints.
447 * @param string $relation_type The relation type the URLs are printed for, e.g. 'preconnect' or 'prerender'.
448 *
449 * @return array $urls resource hints
450 */
451 public function dns_prefetch( $urls, $relation_type ) {
452 $domains = $this->get_prefetch_dns();
453 if ( $domains && is_array( $domains ) ) {
454 foreach ( $domains as $domain ) {
455 if ( 'dns-prefetch' === $relation_type ) {
456 $urls[] = $domain;
457 }
458
459 if ( 'preconnect' === $relation_type ) {
460 $parsed = wp_parse_url( $domain );
461 if ( empty( $parsed['scheme'] ) ) {
462 $domain = set_url_scheme( $domain );
463 }
464
465 $urls[] = $domain;
466 }
467 }
468 }
469
470 return $urls;
471 }
472
473
474 /**
475 *
476 * Get the list of prefetch domains
477 *
478 * @return mixed|void
479 */
480 public function get_prefetch_dns() {
481 $settings = \PoweredCache\Utils\get_settings();
482
483 $prefetch_dns = preg_split( '#(\r\n|\r|\n)#', $settings['prefetch_dns'], - 1, PREG_SPLIT_NO_EMPTY );
484
485 /**
486 * Filters Prefetched DNS list.
487 *
488 * @hook powered_cache_prefetch_dns
489 *
490 * @param {array} $prefetch_dns The list of prefetch domains.
491 *
492 * @return {array} New value.
493 * @since 2.0
494 */
495 return apply_filters( 'powered_cache_prefetch_dns', $prefetch_dns );
496 }
497
498
499 /**
500 * Make preload request
501 *
502 * @param string $url Target URL
503 * @param array $args request args
504 *
505 * @return array|\WP_Error
506 */
507 public static function preload_request( $url, $args = [] ) {
508
509 /**
510 * Filters args of preload requests.
511 *
512 * @hook powered_cache_preload_url_request_args
513 *
514 * @param {array} $args Request defaults.
515 *
516 * @return {array} New value.
517 * @since 2.0
518 */
519 $request_args = apply_filters(
520 'powered_cache_preload_url_request_args',
521 [
522 'timeout' => 0.01,
523 'blocking' => false,
524 'user-agent' => 'Powered Cache Preloader',
525 'sslverify' => false,
526 ]
527 );
528
529 $args = wp_parse_args( $args, $request_args );
530
531 /**
532 * Fires before doing preload HTTP request.
533 *
534 * @hook powered_cache_preload_http_request
535 *
536 * @param {string} $url Preload URL.
537 * @param {array} $args Request arguments.
538 *
539 * @since 2.0
540 */
541 do_action( 'powered_cache_preload_http_request', $url, $args );
542
543 \PoweredCache\Utils\log( sprintf( 'Preloading...%s: %s', $url, print_r( $args, true ) ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
544
545 return wp_remote_get( esc_url_raw( $url ), $args );
546 }
547
548 /**
549 * Wait internal between HTTP reqs
550 *
551 * @param int $delay delay time in microseconds
552 */
553 public static function wait( $delay = 500000 ) {
554 /**
555 * Filters wait time between preload requests.
556 *
557 * @hook powered_cache_preload_request_interval
558 *
559 * @param {int} $delay Delay time in milliseconds.
560 *
561 * @return {int} New value.
562 * @since 2.0
563 */
564 usleep( absint( apply_filters( 'powered_cache_preload_request_interval', $delay ) ) );
565 }
566
567
568 /**
569 * Get mobile user agent for preload requests
570 *
571 * @return mixed|void
572 */
573 public static function mobile_user_agent() {
574 /**
575 * Filters mobile agent name for mobile preload requests
576 *
577 * @hook powered_cache_preload_mobile_user_agent
578 *
579 * @param {string} $agent Mobile agent name.
580 *
581 * @return {string} New value.
582 * @since 2.0
583 */
584 return apply_filters( 'powered_cache_preload_mobile_user_agent', 'Powered Cache Preloader mobile iPhone' );
585 }
586
587 }
588