PluginProbe
WPSSO Core – Complete Schema Markup and Meta Tags / 22.7.0
WPSSO Core – Complete Schema Markup and Meta Tags v22.7.0
22.7.0 22.6.1 22.6.0 22.5.3 22.5.2 22.5.1 22.4.0 22.3.0 22.2.1 22.2.0 22.1.3 22.1.2 22.1.1 22.1.0 22.0.0 21.13.3 21.13.2 trunk 21.13.1
wpsso / lib / util-cache.php

util-cache.php in WPSSO Core – Complete Schema Markup and Meta Tags 22.7.0, at lib/util-cache.php

1,308 lines 35.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 * License: GPLv3
4 * License URI: https://www.gnu.org/licenses/gpl.txt
5 * Copyright 2012-2026 Jean-Sebastien Morisset (https://wpsso.com/)
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9
10 die( 'These aren\'t the droids you\'re looking for.' );
11 }
12
13 if ( ! defined( 'WPSSO_PLUGINDIR' ) ) {
14
15 die( 'Do. Or do not. There is no try.' );
16 }
17
18 if ( ! class_exists( 'WpssoUtilCache' ) ) {
19
20 class WpssoUtilCache {
21
22 private $p; // Wpsso class object.
23 private $u; // WpssoUtil class object.
24
25 /*
26 * Instantiated by WpssoUtil->__construct().
27 */
28 public function __construct( &$plugin, &$util ) {
29
30 $this->p =& $plugin;
31 $this->u =& $util;
32
33 if ( $this->p->debug->enabled ) {
34
35 $this->p->debug->mark();
36 }
37
38 add_action( 'wp_scheduled_delete', array( $this, 'clear_cache_files_expired' ) );
39
40 add_action( 'wpsso_refresh_cache', array( $this, 'refresh' ), 10, 2 ); // Single scheduled task.
41
42 if ( $this->is_disabled() ) {
43
44 if ( $this->p->debug->enabled ) {
45
46 $this->p->debug->log( 'plugin cache for head markup, xml feeds, and file content is disabled' );
47 }
48
49 $this->u->add_plugin_filters( $this, array(
50 'cache_expire_head_markup' => '__return_zero', // Used by WpssoHead->get_head_array().
51 'cache_expire_cmcf_feed_xml' => '__return_zero', // Used by WpssoCmcfXml->get().
52 'cache_expire_gmf_feed_xml' => '__return_zero', // Used by WpssoGmfXml->get().
53 'cache_expire_gmf_inventory_xml' => '__return_zero', // Used by WpssoGmfXml->get().
54 'cache_expire_file_content' => '__return_zero',
55 ), -1000 );
56
57 } else {
58
59 if ( $this->p->debug->enabled ) {
60
61 $this->p->debug->log( 'plugin cache for head markup, xml feeds, and file content is enabled' );
62 }
63
64 if ( ! empty( $this->p->options[ 'plugin_page_cache_active' ] ) ) {
65
66 if ( $this->p->debug->enabled ) {
67
68 $this->p->debug->log( 'plugin_page_cache_active option is true' );
69 $this->p->debug->log( 'head markup cache is disabled' );
70 }
71
72 $this->u->add_plugin_filters( $this, array(
73 'cache_expire_head_markup' => '__return_hour_in_seconds', // Used by WpssoHead->get_head_array().
74 ), -1000 );
75
76 } else {
77
78 if ( $this->p->debug->enabled ) {
79
80 $this->p->debug->log( 'plugin_page_cache_active option is false' );
81 $this->p->debug->log( 'head markup cache is enabled' );
82 }
83 }
84 }
85 }
86
87 /*
88 * The WPSSO_CACHE_DISABLE constant is true or the 'plugin_cache_disable' option is checked.
89 *
90 * See Wpsso->debug_reminder().
91 * See WpssoUtilCache->__construct().
92 */
93 public function is_disabled() {
94
95 if ( defined( 'WPSSO_CACHE_DISABLE' ) ) {
96
97 $is_disabled = WPSSO_CACHE_DISABLE ? true : false;
98
99 if ( $this->p->debug->enabled ) {
100
101 $this->p->debug->log( 'WPSSO_CACHE_DISABLE defined and is ' . SucomUtil::get_bool_string( $is_disabled ) );
102 }
103
104 } else {
105
106 $is_disabled = empty( $this->p->options[ 'plugin_cache_disable' ] ) ? false : true;
107
108 if ( $this->p->debug->enabled ) {
109
110 $this->p->debug->log( 'WPSSO_CACHE_DISABLE is not defined' );
111
112 $this->p->debug->log( 'plugin_cache_disable option is ' . SucomUtil::get_bool_string( $is_disabled ) );
113 }
114 }
115
116 return $is_disabled;
117 }
118
119 public function show_admin_notices() {
120
121 if ( $this->p->debug->enabled ) {
122
123 $this->p->debug->mark();
124 }
125
126 $user_id = get_current_user_id(); // Always returns an integer.
127
128 if ( ! $this->show_refresh_running( $user_id ) ) {
129
130 $this->show_refresh_pending( $user_id );
131 }
132 }
133
134 /*
135 * See WpssoCmcfRewrite::template_redirect().
136 * See WpssoCmcfSubmenuGmfGeneral->add_form_buttons().
137 * See WpssoCmcfSubmenuGmfGeneral->get_table_rows().
138 * See WpssoGmfRewrite::template_redirect().
139 * See WpssoGmfSubmenuGmfGeneral->add_form_buttons().
140 * See WpssoGmfSubmenuGmfGeneral->get_table_rows().
141 */
142 public function is_refresh_running() {
143
144 if ( $this->p->debug->enabled ) {
145
146 $this->p->debug->mark();
147 }
148
149 $task_name = 'refresh the cache';
150 $running_task = $this->get_running_task( $task_name, WPSSO_CACHE_REFRESH_MAX_TIME ); // Returns false or an array.
151
152 return isset( $running_task[ 'user_id' ] ) ? true : false;
153 }
154
155 /*
156 * See WpssoUtilCache->show_admin_notices().
157 */
158 public function show_refresh_running( $user_id = null ) {
159
160 if ( $this->p->debug->enabled ) {
161
162 $this->p->debug->mark();
163 }
164
165 $task_name = 'refresh the cache';
166 $running_task = $this->get_running_task( $task_name, WPSSO_CACHE_REFRESH_MAX_TIME ); // Returns false or an array.
167
168 if ( isset( $running_task[ 'user_id' ] ) ) { // Task is running.
169
170 /*
171 * Show the cache refresh status to the user who started the cache refresh.
172 */
173 $user_id = $this->u->maybe_change_user_id( $user_id ); // Maybe change textdomain for user ID.
174
175 if ( $user_id === $running_task[ 'user_id' ] ) {
176
177 $task_name_transl = _x( $task_name, 'task name', 'wpsso' );
178 $notice_msg = sprintf( __( 'A task to %s is currently running.', 'wpsso' ), $task_name_transl ) . ' ';
179 $notice_key = $task_name . '-task-info';
180
181 if ( ! empty( $running_task[ 'task_info_transl' ] ) ) { // Null or string.
182
183 $notice_msg .= $running_task[ 'task_info_transl' ] . ' ';
184 }
185
186 $this->p->notice->inf( $notice_msg, $user_id, $notice_key );
187 }
188
189 return true;
190 }
191
192 return false;
193 }
194
195 /*
196 * See WpssoUtilCache->show_admin_notices().
197 */
198 public function show_refresh_pending( $user_id = null ) {
199
200 if ( $this->p->debug->enabled ) {
201
202 $this->p->debug->mark();
203 }
204
205 $event_hook = 'wpsso_refresh_cache';
206 $crons = _get_cron_array();
207
208 if ( ! is_array( $crons ) ) { // Just in case.
209
210 return;
211 }
212
213 foreach ( $crons as $timestamp => $cron_hooks ) {
214
215 if ( ! is_array( $cron_hooks ) ) { // Just in case.
216
217 continue;
218 }
219
220 foreach ( $cron_hooks as $cron_hook => $hook_args ) {
221
222 if ( ! is_array( $hook_args ) ) { // Just in case.
223
224 continue;
225
226 } elseif ( $event_hook !== $cron_hook ) { // Only check our own event hook.
227
228 continue;
229 }
230
231 if ( $this->p->debug->enabled ) {
232
233 $this->p->debug->log( 'event hook ' . $event_hook . ' found' );
234
235 $this->p->debug->log_arr( 'hook_args', $hook_args );
236 }
237
238 /*
239 * The $hook_key value is a checksum of the $hook_arr.
240 *
241 * The same task could be scheduled several times provided the $hook_arr elements are different.
242 */
243 foreach ( $hook_args as $hook_key => $hook_arr ) {
244
245 if ( ! is_array( $hook_arr ) ) { // Just in case.
246
247 continue;
248 }
249
250 $user_id = $this->u->maybe_change_user_id( $user_id ); // Maybe change textdomain for user ID.
251 $task_name = 'refresh the cache';
252 $task_name_transl = _x( 'refresh the cache', 'task name', 'wpsso' );
253 $time_now = time();
254 $human_time = human_time_diff( $time_now, $timestamp );
255 $notice_key = $task_name . '-task-info';
256 $event_args = $hook_arr[ 'args' ];
257
258 if ( $time_now < $timestamp ) {
259
260 if ( $this->p->debug->enabled ) {
261
262 $this->p->debug->log( 'task to ' . $task_name . ' for user id ' . $event_args[ 0 ] .
263 ' scheduled to start in ' . $human_time );
264 }
265
266 if ( $user_id === $event_args[ 0 ] ) {
267
268 $notice_msg = sprintf( __( 'A background task will begin in %1$s to %2$s for posts, terms and users.',
269 'wpsso' ), $human_time, $task_name_transl );
270
271 $this->p->notice->inf( $notice_msg, $user_id, $notice_key );
272 }
273
274 continue; // Get the next $hook_args.
275 }
276
277 if ( $this->p->debug->enabled ) {
278
279 $this->p->debug->log( 'task to ' . $task_name . ' for user id ' . $event_args[ 0 ] .
280 ' was scheduled to start ' . $human_time . ' ago' );
281 }
282
283 if ( $time_now > $timestamp + 60 && $user_id === $event_args[ 0 ] ) { // Add a 60 second buffer.
284
285 $notice_msg = sprintf( __( 'A background task was scheduled to begin %1$s ago to %2$s for posts, terms and users.',
286 'wpsso' ), $human_time, $task_name_transl ) . ' ';
287
288 $notice_msg .= sprintf( __( 'WordPress should have run the %s event hook at that time.',
289 'wpsso' ), '<code>' . $event_hook . '</code>' ) . ' ';
290
291 $notice_msg .= sprintf( __( 'If the task does not run, this could indicate a problem with your hosting provider\'s event scheduler and/or a lack of support for the WordPress %s function.', 'wpsso' ), '<code>wp_schedule_single_event()</code>' ) . ' ';
292
293 $notice_msg .= sprintf( __( 'You can activate a plugin like %s to manage scheduled events.',
294 'wpsso' ), '<a href="https://wordpress.org/plugins/wp-crontrol/">' .
295 esc_html__( 'WP Crontrol', 'wp-crontrol' ) . '</a>' ) . ' ';
296
297 $this->p->notice->warn( $notice_msg, $user_id, $notice_key );
298 }
299
300 } // End of $hook_args loop.
301
302 return true; // Found one or more $event_hook.
303
304 } // End of $cron_hooks loop.
305
306 } // End of $crons loop.
307
308 if ( $this->p->debug->enabled ) {
309
310 $this->p->debug->log( 'event hook ' . $event_hook . ' not found' );
311 }
312
313 return false;
314 }
315
316 /*
317 * See WpssoAdmin->activated_plugin().
318 * See WpssoAdmin->wp_site_option_changed().
319 * See WpssoAdmin->load_settings_page().
320 * See WpssoOptions->save_options().
321 * See WpssoRegister->activate_plugin().
322 */
323 public function schedule_refresh( $user_id = null, $clear_plugins = true ) {
324
325 if ( $this->p->debug->enabled ) {
326
327 $this->p->debug->mark();
328 }
329
330 $user_id = $this->u->maybe_change_user_id( $user_id ); // Maybe change textdomain for user ID.
331 $task_name = 'refresh the cache';
332 $task_name_transl = _x( 'refresh the cache', 'task name', 'wpsso' );
333 $event_time = time() + WPSSO_SCHEDULE_SINGLE_EVENT_TIME; // Default event time is now + 10 seconds.
334 $human_time = human_time_diff( 0, WPSSO_SCHEDULE_SINGLE_EVENT_TIME );
335 $event_hook = 'wpsso_refresh_cache';
336 $event_args = array( $user_id, $clear_plugins );
337
338 if ( $user_id ) { // Just in case.
339
340 $notice_msg = sprintf( __( 'A background task will begin in %1$s to %2$s for posts, terms and users.', 'wpsso' ),
341 $human_time, $task_name_transl );
342
343 $notice_key = $task_name . '-task-info';
344
345 $this->p->notice->inf( $notice_msg, $user_id, $notice_key );
346 }
347
348 wp_schedule_single_event( $event_time, $event_hook, $event_args );
349
350 do_action( 'wpsso_cache_refresh_scheduled', $event_time, $event_hook, $event_args );
351 }
352
353 /*
354 * Called by the 'wpsso_refresh_cache' single scheduled event action.
355 *
356 * See wpsso_refresh_cache().
357 * See WpssoRarActions->action_refresh_cache().
358 */
359 public function refresh( $user_id = null, $clear_plugins = true ) {
360
361 if ( $this->p->debug->enabled ) {
362
363 $this->p->debug->mark();
364 }
365
366 $user_id = $this->u->maybe_change_user_id( $user_id ); // Maybe change textdomain for user ID.
367 $task_name = 'refresh the cache';
368 $task_name_transl = _x( 'refresh the cache', 'task name', 'wpsso' );
369
370 if ( ! $this->task_start( $user_id, $task_name, WPSSO_CACHE_REFRESH_MAX_TIME ) ) {
371
372 return; // Stop here - background task already running.
373 }
374
375 if ( $user_id ) {
376
377 $mtime_start = microtime( $get_float = true );
378 $time_on_date = SucomUtil::sprintf_date_time( _x( '%2$s on %1$s', 'time on date', 'wpsso' ) );
379 $notice_msg = sprintf( __( 'A task to %1$s was started at %2$s.', 'wpsso' ), $task_name_transl, $time_on_date );
380 $notice_key = $task_name . '-task-info';
381
382 $this->p->notice->inf( $notice_msg, $user_id, $notice_key );
383 }
384
385 if ( 0 === get_current_user_id() ) { // User is the scheduler.
386
387 /*
388 * Leave a few minutes for 'wpsso_cache_refreshed_notice' filters.
389 */
390 $other_refresh = SucomUtilWP::get_filter_hook_ids( 'wpsso_cache_refreshed_notice' );
391 $max_time_secs = WPSSO_CACHE_REFRESH_MAX_TIME + ( 300 * count( $other_refresh ) );
392
393 $this->set_task_limit( $user_id, $task_name, $max_time_secs );
394 }
395
396 if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
397
398 /*
399 * Register image sizes and include WooCommerce front-end libs.
400 *
401 * See WpssoIntegEcomWooCommerce->action_scheduled_task_started().
402 * See WpssoUtil->action_scheduled_task_started().
403 */
404 do_action( 'wpsso_scheduled_task_started', $user_id );
405 }
406
407 /*
408 * Clear cache files but preserve HTML files, like the YouTube video webpage HTML.
409 */
410 $this->clear_cache_files( $file_exp_secs = null, $include = array( '/\.(css|js|txt)$/' ) );
411
412 /*
413 * Refresh the Schema types transient cache.
414 */
415 $this->p->schema->refresh_schema_types();
416
417 /*
418 * Refresh the minimized notice stylesheet.
419 */
420 $this->p->notice->refresh_notice_style();
421
422 /*
423 * Refresh the cache for each public post, term, and user ID.
424 */
425 $total_count = array(
426 'post' => 0,
427 'term' => 0,
428 'user' => 0,
429 );
430
431 $notice_msg = '';
432 $og_type_key = WpssoAbstractWpMeta::get_column_meta_keys( 'og_type' ); // Example: '_wpsso_head_info_og_type'.
433 $abort_time = time() + WPSSO_CACHE_REFRESH_MAX_TIME; // 30 mins by default.
434
435 foreach ( $total_count as $obj_name => &$count ) {
436
437 /*
438 * Refresh post, term, and user IDs with missing cache metadata first.
439 */
440 $prio_args = array(
441 'meta_query' => array(
442 'relation' => 'AND',
443 array(
444 'key' => $og_type_key,
445 'compare' => 'NOT EXISTS',
446 ),
447 ),
448 );
449
450 $prio_ids = call_user_func( array( 'wpsso' . $obj_name, 'get_public_ids' ), $prio_args );
451 $obj_ids = call_user_func( array( 'wpsso' . $obj_name, 'get_public_ids' ) );
452
453 if ( ! empty( $prio_ids ) ) {
454
455 $obj_ids = array_unique( $prio_ids + $obj_ids );
456 }
457
458 unset( $prio_args, $prio_ids );
459
460 $obj_count = count( $obj_ids );
461
462 foreach ( $obj_ids as $obj_num => $obj_id ) {
463
464 $mod = $this->p->$obj_name->get_mod( $obj_id );
465
466 $this->task_update( $task_name, sprintf( __( 'Processing %1$s ID #%2$d (%1$s %3$d of %4$d).', 'wpsso' ),
467 $mod[ 'name_transl' ], $obj_id, $obj_num + 1, $obj_count ) );
468
469 $this->refresh_mod_head_meta( $mod );
470
471 $count++; // Reference to post, term, or user total count.
472
473 if ( time() > $abort_time ) {
474
475 $notice_msg .= sprintf( __( 'The cache refresh time limit of %s has been reached.', 'wpsso' ),
476 human_time_diff( 0, WPSSO_CACHE_REFRESH_MAX_TIME ) ) . ' '; // 30 mins by default.
477
478 $notice_msg .= sprintf( __( 'The cache refresh task aborted after %1$s ID #%2$d (%1$s %3$d of %4$d).', 'wpsso' ),
479 $mod[ 'name_transl' ], $obj_id, $obj_num + 1, $obj_count ) . ' ';
480
481 $error_pre = sprintf( __( '%s warning:', 'wpsso' ), __METHOD__ );
482
483 SucomUtil::safe_error_log( $error_pre . ' ' . $notice_msg );
484
485 break 2; // Stop here
486 }
487 }
488 }
489
490 unset( $mod, $obj_ids, $obj_id, $obj_num, $obj_count );
491
492 $this->task_update( $task_name );
493
494 /*
495 * Create a notification for the end of this task.
496 */
497 $notice_msg .= sprintf( __( 'The transient cache for %1$d posts, %2$d terms, and %3$d users has been refreshed.',
498 'wpsso' ), $total_count[ 'post' ], $total_count[ 'term' ], $total_count[ 'user' ] ) . ' ';
499
500 /*
501 * The 'wpsso_cache_refreshed_notice' filter allows add-ons to execute additional refresh tasks and append
502 * a notice message.
503 *
504 * See WpssoCmcfFilters->filter_cache_refreshed_notice().
505 * See WpssoGmfFilters->filter_cache_refreshed_notice().
506 */
507 $notice_msg = trim( apply_filters( 'wpsso_cache_refreshed_notice', $notice_msg, $user_id ) ) . ' ';
508
509 /*
510 * Clear WP cache and known cache plugins.
511 */
512 $notice_msg .= $this->clear_cache( $clear_plugins );
513
514 if ( $user_id && $notice_msg ) {
515
516 $mtime_total = microtime( $get_float = true ) - $mtime_start;
517 $human_time = human_time_diff( 0, $mtime_total );
518 $human_mem = SucomUtil::format_human_bytes( memory_get_peak_usage(), $dec = 2 );
519 $notice_msg .= sprintf( __( 'The total execution time for this task was %s (%s peak memory use).', 'wpsso' ), $human_time, $human_mem ) . ' ';
520 $notice_key = $task_name . '-task-info';
521
522 $this->p->notice->inf( $notice_msg, $user_id, $notice_key );
523 }
524
525 $this->task_end( $user_id, $task_name );
526 }
527
528 /*
529 * See WpssoPost->after_insert_post().
530 * See WpssoPost->ajax_get_metabox_sso().
531 * See WpssoPost->load_meta_page().
532 * See WpssoPost->refresh_cache().
533 * See WpssoTerm->load_meta_page().
534 * See WpssoTerm->refresh_cache().
535 * See WpssoUser->load_meta_page().
536 * See WpssoUser->refresh_cache().
537 */
538 public function refresh_mod_head_meta( array $mod ) {
539
540 if ( $this->p->debug->enabled ) {
541
542 $this->p->debug->mark();
543 }
544
545 $use_post = 'post' === $mod[ 'name' ] ? $mod[ 'id' ] : false;
546 $head_tags = $this->p->head->get_head_array( $use_post, $mod, $read_cache = false );
547 $head_info = $this->p->head->extract_head_info( $head_tags, $mod );
548
549 return array( $head_tags, $head_info );
550 }
551
552 /*
553 * See WpssoUser->add_person_role().
554 * See WpssoUtilCache->refresh().
555 */
556 public function task_start( $user_id, $task_name, $cache_exp_secs ) {
557
558 if ( $this->p->debug->enabled ) {
559
560 $this->p->debug->mark();
561 }
562
563 $running_task = $this->get_running_task( $task_name, $cache_exp_secs ); // Returns false or an array.
564
565 if ( isset( $running_task[ 'user_id' ] ) ) { // Task is running.
566
567 $task_name_transl = _x( $task_name, 'task name', 'wpsso' );
568 $notice_msg = sprintf( __( 'Ignoring request to %s - this task is already running.', 'wpsso' ), $task_name_transl );
569 $notice_key = $task_name . '-task-ignored';
570
571 $this->p->notice->warn( $notice_msg, $user_id, $notice_key );
572
573 return false;
574 }
575
576 $task_cache_id = $this->get_task_cache_id( $task_name );
577
578 return set_transient( $task_cache_id, array(
579 'user_id' => $user_id,
580 'task_name' => $task_name,
581 'task_start' => time(),
582 'task_limit' => time() + $cache_exp_secs,
583 ), $cache_exp_secs );
584
585 return true;
586 }
587
588 public function get_running_task( $task_name, $cache_exp_secs = null ) {
589
590 if ( $this->p->debug->enabled ) {
591
592 $this->p->debug->mark();
593 }
594
595 $task_cache_id = $this->get_task_cache_id( $task_name );
596 $running_task = get_transient( $task_cache_id );
597
598 if ( is_array( $running_task ) ) {
599
600 if ( isset( $running_task[ 'user_id' ] ) ) { // Just in case.
601
602 if ( null !== $cache_exp_secs ) { // Check that transient is not expired.
603
604 if ( ! empty( $running_task[ 'task_start' ] ) ) {
605
606 if ( $running_task[ 'task_start' ] < time() - $cache_exp_secs ) {
607
608 delete_transient( $task_cache_id );
609
610 return false;
611 }
612 }
613 }
614
615 return $running_task;
616 }
617
618 delete_transient( $task_cache_id ); // Remove invalid array.
619 }
620
621 return false;
622 }
623
624 public function task_update( $task_name, $task_info_transl = null ) {
625
626 $running_task = $this->get_running_task( $task_name ); // Returns false or an array.
627
628 if ( isset( $running_task[ 'user_id' ] ) ) { // Task is running.
629
630 if ( isset( $running_task[ 'task_limit' ] ) ) { // Just in case.
631
632 $task_cache_id = $this->get_task_cache_id( $task_name );
633 $cache_exp_secs = $running_task[ 'task_limit' ] - time(); // Time left.
634
635 $running_task[ 'task_info_transl' ] = $task_info_transl;
636
637 return set_transient( $task_cache_id, $running_task, $cache_exp_secs );
638 }
639 }
640
641 return false;
642 }
643
644 public function task_end( $user_id, $task_name ) {
645
646 if ( $this->p->debug->enabled ) {
647
648 $this->p->debug->mark();
649 }
650
651 $task_cache_id = $this->get_task_cache_id( $task_name );
652
653 return delete_transient( $task_cache_id );
654 }
655
656 public function set_task_limit( $user_id, $task_name, $cache_exp_secs ) {
657
658 if ( $this->p->debug->enabled ) {
659
660 $this->p->debug->mark();
661 }
662
663 $ret = set_time_limit( $cache_exp_secs );
664
665 if ( ! $ret ) {
666
667 $human_time = human_time_diff( 0, $cache_exp_secs );
668 $task_name_transl = _x( $task_name, 'task name', 'wpsso' );
669 $error_pre = sprintf( __( '%s error:', 'wpsso' ), __METHOD__ );
670 $notice_key = $task_name . '-task-set-time-limit-error';
671 $notice_msg = sprintf( __( 'The PHP %1$s function failed to set a maximum execution time of %2$s to %3$s.', 'wpsso' ),
672 '<code>set_time_limit()</code>', $human_time, $task_name_transl );
673
674 $this->p->notice->err( $notice_msg, $user_id, $notice_key );
675
676 SucomUtil::safe_error_log( $error_pre . ' ' . $notice_msg, $strip_html = true );
677 }
678
679 return $ret;
680 }
681
682 /*
683 * Returns a transient cache id.
684 *
685 * See WpssoUser->add_person_role().
686 * See WpssoUser->remove_person_role().
687 * See WpssoUtilCache->refresh().
688 * See WpssoUtilCache->get_running_task().
689 */
690 public function get_task_cache_id( $task_name ) {
691
692 if ( $this->p->debug->enabled ) {
693
694 $this->p->debug->mark();
695 }
696
697 return 'wpsso_!_' . md5( __CLASS__ . '::running-task-'. $task_name );
698 }
699
700 /*
701 * Clear WP cache and (by default) all active known cache plugins.
702 *
703 * See WpssoUtilCache->refresh().
704 */
705 public function clear_cache( $clear_plugins = true ) {
706
707 if ( $this->p->debug->enabled ) {
708
709 $this->p->debug->mark();
710 }
711
712 /*
713 * Prevent recursion in case a cache plugin calls our refresh method (which in turn calls this method).
714 */
715 static $do_once = null;
716
717 if ( $do_once ) return; // Stop here.
718
719 $do_once = true;
720
721 $cleared_msg = __( 'The cache for <strong>%s</strong> has also been cleared.', 'wpsso' ) . ' ';
722
723 $notice_msg = '';
724
725 /*
726 * WordPress object cache.
727 */
728 wp_cache_flush();
729
730 $notice_msg .= sprintf( $cleared_msg, __( 'WordPress object cache', 'wpsso' ) );
731
732 if ( ! $clear_plugins ) {
733
734 return $notice_msg;
735 }
736
737 /*
738 * Autoptimize.
739 *
740 * See https://wordpress.org/plugins/autoptimize/.
741 *
742 * Note that Autoptimize is not a page caching plugin - it optimizes CSS and JavaScript.
743 */
744 if ( $this->p->avail[ 'util' ][ 'autoptimize' ] ) {
745
746 if ( method_exists( 'autoptimizeCache', 'clearall' ) ) { // Just in case.
747
748 autoptimizeCache::clearall();
749
750 $notice_msg .= sprintf( $cleared_msg, __( 'Autoptimize', 'wpsso' ) );
751 }
752 }
753
754 /*
755 * Cache Enabler.
756 *
757 * See https://wordpress.org/plugins/cache-enabler/.
758 */
759 if ( $this->p->avail[ 'cache' ][ 'enabler' ] ) {
760
761 if ( method_exists( 'Cache_Enabler', 'clear_total_cache') ) {
762
763 Cache_Enabler::clear_total_cache();
764
765 $notice_msg .= sprintf( $cleared_msg, __( 'Cache Enabler', 'wpsso' ) );
766 }
767 }
768
769 /*
770 * Comet Cache.
771 *
772 * See https://wordpress.org/plugins/comet-cache/.
773 */
774 if ( $this->p->avail[ 'cache' ][ 'comet' ] ) {
775
776 $GLOBALS[ 'comet_cache' ]->wipe_cache();
777
778 $notice_msg .= sprintf( $cleared_msg, __( 'Comet Cache', 'wpsso' ) );
779 }
780
781 /*
782 * Hummingbird Cache.
783 *
784 * See https://wordpress.org/plugins/hummingbird-performance/.
785 */
786 if ( $this->p->avail[ 'cache' ][ 'hummingbird' ] ) {
787
788 if ( method_exists( '\Hummingbird\WP_Hummingbird', 'flush_cache' ) ) {
789
790 \Hummingbird\WP_Hummingbird::flush_cache();
791
792 $notice_msg .= sprintf( $cleared_msg, __( 'Hummingbird Cache', 'wpsso' ) );
793 }
794 }
795
796 /*
797 * LiteSpeed Cache.
798 *
799 * See https://wordpress.org/plugins/litespeed-cache/.
800 */
801 if ( $this->p->avail[ 'cache' ][ 'litespeed' ] ) {
802
803 if ( method_exists( 'LiteSpeed_Cache_API', 'purge_all' ) ) {
804
805 do_action( 'litespeed_purge_all', 'WPSSO Core' );
806
807 $notice_msg .= sprintf( $cleared_msg, __( 'LiteSpeed Cache', 'wpsso' ) );
808 }
809 }
810
811 /*
812 * Pagely Cache.
813 */
814 if ( $this->p->avail[ 'cache' ][ 'pagely' ] ) {
815
816 if ( method_exists( 'PagelyCachePurge', 'purgeAll' ) ) {
817
818 PagelyCachePurge::purgeAll();
819
820 $notice_msg .= sprintf( $cleared_msg, __( 'Pagely', 'wpsso' ) );
821 }
822 }
823
824 /*
825 * SiteGround Cache.
826 */
827 if ( $this->p->avail[ 'cache' ][ 'siteground' ] ) {
828
829 sg_cachepress_purge_cache();
830
831 $notice_msg .= sprintf( $cleared_msg, __( 'Siteground Cache', 'wpsso' ) );
832 }
833
834 /*
835 * W3 Total Cache (aka W3TC).
836 */
837 if ( $this->p->avail[ 'cache' ][ 'w3tc' ] ) {
838
839 w3tc_pgcache_flush();
840
841 if ( function_exists( 'w3tc_objectcache_flush' ) ) {
842
843 w3tc_objectcache_flush();
844 }
845
846 $notice_msg .= sprintf( $cleared_msg, __( 'W3 Total Cache', 'wpsso' ) );
847 }
848
849 /*
850 * WP Engine Cache.
851 */
852 if ( $this->p->avail[ 'cache' ][ 'wp-engine' ] ) {
853
854 if ( method_exists( 'WpeCommon', 'purge_memcached' ) ) {
855
856 WpeCommon::purge_memcached();
857 }
858
859 if ( method_exists( 'WpeCommon', 'purge_varnish_cache' ) ) {
860
861 WpeCommon::purge_varnish_cache();
862 }
863
864 $notice_msg .= sprintf( $cleared_msg, __( 'WP Engine Cache', 'wpsso' ) );
865 }
866
867 /*
868 * WP Fastest Cache.
869 *
870 * See https://wordpress.org/plugins/wp-fastest-cache/.
871 */
872 if ( $this->p->avail[ 'cache' ][ 'wp-fastest' ] ) {
873
874 wpfc_clear_all_cache( true );
875
876 $notice_msg .= sprintf( $cleared_msg, __( 'WP Fastest Cache', 'wpsso' ) );
877 }
878
879 /*
880 * WP Rocket Cache.
881 */
882 if ( $this->p->avail[ 'cache' ][ 'wp-rocket' ] ) {
883
884 rocket_clean_domain();
885
886 $notice_msg .= sprintf( $cleared_msg, __( 'WP Rocket Cache', 'wpsso' ) );
887 }
888
889 /*
890 * WP Super Cache.
891 *
892 * See https://wordpress.org/plugins/wp-super-cache/.
893 */
894 if ( $this->p->avail[ 'cache' ][ 'wp-super' ] ) {
895
896 wp_cache_clear_cache();
897
898 $notice_msg .= sprintf( $cleared_msg, __( 'WP Super Cache', 'wpsso' ) );
899 }
900
901 return $notice_msg;
902 }
903
904 /*
905 * Clear cache files older than WPSSO_CACHE_FILES_EXP_SECS.
906 */
907 public function clear_cache_files_expired() {
908
909 return $this->clear_cache_files( WPSSO_CACHE_FILES_EXP_SECS );
910 }
911
912 /*
913 * Clear cache files.
914 *
915 * See WpssoUtilCache->refresh().
916 * See WpssoAdmin->load_settings_page().
917 */
918 public function clear_cache_files( $file_exp_secs = null, $include = array(), $exclude = array() ) {
919
920 $cleared_count = 0;
921 $cache_files = $this->get_cache_files(); // Excludes hidden files and index.php.
922
923 foreach ( $cache_files as $cache_file ) {
924
925 if ( ! empty( $include ) ) {
926
927 foreach ( $include as $preg_match ) {
928
929 if ( ! preg_match( $preg_match, $cache_file ) ) {
930
931 if ( $this->p->debug->enabled ) {
932
933 $this->p->debug->log( 'skipping ' . $cache_file . ' (not included)' );
934 }
935
936 continue 2;
937 }
938 }
939 }
940
941 if ( ! empty( $exclude ) ) {
942
943 foreach ( $exclude as $preg_match ) {
944
945 if ( preg_match( $preg_match, $cache_file ) ) {
946
947 if ( $this->p->debug->enabled ) {
948
949 $this->p->debug->log( 'skipping ' . $cache_file . ' (excluded)' );
950 }
951
952 continue 2;
953 }
954 }
955 }
956
957 if ( null !== $file_exp_secs ) {
958
959 /*
960 * Skip cache files that are newer than the expiration time.
961 */
962 if ( filemtime( $cache_file ) > time() - $file_exp_secs ) {
963
964 if ( $this->p->debug->enabled ) {
965
966 $this->p->debug->log( 'skipping ' . $cache_file . ' (newer than expiration time)' );
967 }
968
969 continue;
970 }
971 }
972
973 if ( @unlink( $cache_file ) ) {
974
975 if ( $this->p->debug->enabled ) {
976
977 $this->p->debug->log( 'removed the cache file ' . $cache_file );
978 }
979
980 $cleared_count++;
981
982 } else {
983
984 if ( $this->p->debug->enabled ) {
985
986 $this->p->debug->log( 'error removing cache file ' . $cache_file );
987 }
988
989 $error_pre = sprintf( '%s error:', __METHOD__ );
990 $error_msg = sprintf( __( 'Error removing cache file %s.', 'wpsso' ), $cache_file );
991
992 $this->p->notice->err( $error_msg );
993
994 SucomUtil::safe_error_log( $error_pre . ' ' . $error_msg );
995 }
996 }
997
998 return $cleared_count++;
999 }
1000
1001 /*
1002 * See WpssoSubmenuTools->add_form_buttons().
1003 */
1004 public function count_cache_files() {
1005
1006 $cache_files = $this->get_cache_files(); // Excludes hidden files and index.php.
1007
1008 return count( $cache_files );
1009 }
1010
1011 public function get_cache_files() {
1012
1013 $cache_files = array();
1014
1015 if ( $dh = @opendir( WPSSO_CACHE_DIR ) ) {
1016
1017 while ( $file_name = @readdir( $dh ) ) {
1018
1019 $cache_file = WPSSO_CACHE_DIR . $file_name;
1020
1021 if ( ! preg_match( '/^(\..*|index\.php)$/', $file_name ) && is_file( $cache_file ) ) {
1022
1023 $cache_files[] = $cache_file;
1024
1025 }
1026 }
1027
1028 closedir( $dh );
1029
1030 } else {
1031
1032 if ( $this->p->debug->enabled ) {
1033
1034 $this->p->debug->log( 'failed to open the cache folder ' . WPSSO_CACHE_DIR . ' for reading' );
1035 }
1036
1037 $error_pre = sprintf( '%s error:', __METHOD__ );
1038
1039 $error_msg = sprintf( __( 'Failed to open the cache folder %s for reading.', 'wpsso' ), WPSSO_CACHE_DIR );
1040
1041 $this->p->notice->err( $error_msg );
1042
1043 SucomUtil::safe_error_log( $error_pre . ' ' . $error_msg );
1044 }
1045
1046 return $cache_files;
1047 }
1048
1049 /*
1050 * See WpssoAdmin->load_settings_page().
1051 */
1052 public function clear_ignored_urls() {
1053
1054 return $this->p->cache->clear_ignored_urls();
1055 }
1056
1057 /*
1058 * See WpssoSubmenuTools->add_form_buttons().
1059 */
1060 public function count_ignored_urls() {
1061
1062 return $this->p->cache->count_ignored_urls();
1063 }
1064
1065 /*
1066 * See WpssoSubmenuTools->add_form_buttons().
1067 */
1068 public function get_ignored_urls() {
1069
1070 return $this->p->cache->get_ignored_urls();
1071 }
1072
1073 public function clear_db_transients_expired() {
1074
1075 return $this->clear_db_transients( $key_prefix = '', $incl_shortened = true, $only_expired = true );
1076 }
1077
1078 /*
1079 * Clear database transients, excluding transients that must be preserved (key begins with 'wpsso_!_'), and
1080 * optionally exclude shortened URL transients.
1081 *
1082 * See WpssoAdmin->load_settings_page().
1083 * See WpssoUtilCache->clear_db_transients_expired().
1084 */
1085 public function clear_db_transients( $key_prefix = '', $incl_shortened = true, $only_expired = false ) {
1086
1087 $cleared_count = 0;
1088
1089 $transients_subset = $this->get_db_transients_subset( $key_prefix, $incl_shortened, $only_expired );
1090
1091 foreach ( $transients_subset as $key ) {
1092
1093 if ( delete_transient( $key ) ) {
1094
1095 $cleared_count++;
1096 }
1097 }
1098
1099 return $cleared_count;
1100 }
1101
1102 public function count_db_transients_expired() {
1103
1104 return $this->count_db_transients( $key_prefix = '', $incl_shortened = true, $only_expired = true );
1105 }
1106
1107 /*
1108 * Count database transients, excluding transients that must be preserved (key begins with 'wpsso_!_'), and
1109 * optionally exclude shortened URL transients.
1110 *
1111 * See WpssoSubmenuTools->add_form_buttons().
1112 */
1113 public function count_db_transients( $key_prefix = '', $incl_shortened = true, $only_expired = false ) {
1114
1115 $transients_subset = $this->get_db_transients_subset( $key_prefix, $incl_shortened, $only_expired );
1116
1117 return count( $transients_subset );
1118 }
1119
1120 /*
1121 * A wrapper for WpssoUtilCache->get_db_transients_keys() to exclude transients that must be preserved (key begins
1122 * with 'wpsso_!_'), and optionally exclude shortened URL transients.
1123 *
1124 * See WpssoUtilCache->clear_db_transients().
1125 * See WpssoUtilCache->count_db_transients().
1126 */
1127 public function get_db_transients_subset( $key_prefix = '', $incl_shortened = true, $only_expired = false ) {
1128
1129 $transients_subset = array();
1130
1131 $transients_keys = $this->get_db_transients_keys( $key_prefix, $only_expired );
1132
1133 foreach ( $transients_keys as $key ) {
1134
1135 if ( '' !== $key_prefix ) { // We're only clearing a specific prefix.
1136
1137 if ( 0 !== strpos( $key, $key_prefix ) ) { // Transient does not match that prefix.
1138
1139 continue;
1140 }
1141 }
1142
1143 if ( 0 === strpos( $key, 'wpsso_!_' ) ) { // Preserve transients that begin with "wpsso_!_".
1144
1145 continue;
1146
1147 } elseif ( ! $incl_shortened ) { // Not clearing short URLs.
1148
1149 if ( 0 === strpos( $key, 'wpsso_s_' ) ) { // This is a shortened URL.
1150
1151 continue;
1152 }
1153 }
1154
1155 $transients_subset[] = $key;
1156 }
1157
1158 return $transients_subset;
1159 }
1160
1161 /*
1162 * Get transients from the database, and optionally only those that are expired.
1163 *
1164 * See WpssoAdmin->show_metabox_cache_status().
1165 * See WpssoUtilCache->get_db_transients_subset().
1166 */
1167 public function get_db_transients_keys( $key_prefix = '', $only_expired = false ) {
1168
1169 global $wpdb;
1170
1171 $transients_keys = array();
1172 $transient_prefix = $only_expired ? '_transient_timeout_' : '_transient_';
1173 $current_time = isset( $_SERVER[ 'REQUEST_TIME' ] ) ? (int) $_SERVER[ 'REQUEST_TIME' ] : time() ;
1174
1175 $query = 'SELECT option_name';
1176 $query .= ' FROM ' . $wpdb->options;
1177 $query .= ' WHERE option_name LIKE \'' . $transient_prefix . $key_prefix . '%\'';
1178
1179 if ( $only_expired ) { // Only check the value when querying for expires transients (ie. prefix is '_transient_timeout_').
1180
1181 $query .= ' AND option_value < ' . $current_time; // Expiration time older than current time.
1182 }
1183
1184 $query .= ';'; // End of query.
1185
1186 $result = $wpdb->get_col( $query );
1187
1188 /*
1189 * Remove '_transient_' or '_transient_timeout_' prefix from option name.
1190 */
1191 foreach( $result as $option_name ) {
1192
1193 $transients_keys[] = str_replace( $transient_prefix, '', $option_name );
1194 }
1195
1196 return $transients_keys;
1197 }
1198
1199 /*
1200 * See WpssoAdmin->show_metabox_cache_status().
1201 */
1202 public function get_db_transients_size_mb( $key_prefix = '' ) {
1203
1204 global $wpdb;
1205
1206 /*
1207 * The CHAR_LENGTH() function returns the number of characters in a string. It is important to note that
1208 * this function counts characters, not bytes, and it is Unicode-aware — meaning it accurately counts the
1209 * number of multibyte characters in a string.
1210 *
1211 * The LENGTH() function, on the other hand, returns the length of a string in bytes. It differs from
1212 * CHAR_LENGTH() because it is not Unicode-aware and counts bytes — which can yield different results,
1213 * especially for multibyte character sets.
1214 */
1215 $query = 'SELECT LENGTH( option_value )';
1216 $query .= ' FROM ' . $wpdb->options;
1217 $query .= ' WHERE option_name LIKE \'_transient_' . $key_prefix . '%\'';
1218 $query .= ';'; // End of query.
1219
1220 $result = $wpdb->get_col( $query );
1221
1222 return array_sum( $result ) / 1024 / 1024; // Return size in MB.
1223 }
1224
1225 /*
1226 * See WpssoCmcfXml->get().
1227 * See WpssoGmfXml->get().
1228 * See WpssoAdmin->settings_sanitation().
1229 * See WpssoAdmin->show_metabox_cache_status().
1230 * See WpssoHead->get_head_array().
1231 * See WpssoMessagesTooltipPlugin->get().
1232 * See WpssoPage->get_the_content().
1233 * See WpssoProMediaFacebook->filter_video_details().
1234 * See WpssoProMediaSlideshare->filter_video_details().
1235 * See WpssoProMediaVimeo->filter_video_details().
1236 * See WpssoProMediaWistia->filter_video_details().
1237 * See WpssoProMediaYoutube->filter_video_details().
1238 * See WpssoProReviewJudgeme->filter_og().
1239 * See WpssoProReviewShopperApproved->filter_og().
1240 * See WpssoProReviewStamped->filter_og().
1241 * See WpssoProUtilShorten->get_short_url().
1242 * See WpssoSchema->get_schema_types().
1243 * See WpssoSchema->get_schema_type_child_family().
1244 * See WpssoSchema->get_schema_type_children().
1245 * See WpssoSchema->get_schema_type_row_class().
1246 * See WpssoUtil->get_image_url_info().
1247 */
1248 public function get_cache_exp_secs( $cache_key, $cache_type = 'transient', $mod = false ) {
1249
1250 if ( $this->p->debug->enabled ) {
1251
1252 $this->p->debug->mark();
1253 }
1254
1255 $cache_exp_secs = 0; // No caching by default.
1256
1257 if ( empty( $this->p->cf[ 'wp' ][ 'cache' ][ $cache_type ][ $cache_key ] ) ) { // No cache expiration defined.
1258
1259 return $cache_exp_secs;
1260 }
1261
1262 $cache_info = $this->p->cf[ 'wp' ][ 'cache' ][ $cache_type ][ $cache_key ];
1263
1264 if ( isset( $cache_info[ 'value' ] ) ) { // Allow for 0.
1265
1266 $cache_exp_secs = (int) $cache_info[ 'value' ];
1267
1268 } elseif ( isset( $cache_info[ 'opt_key' ] ) && isset( $this->p->options[ $cache_info[ 'opt_key' ] ] ) ) { // Allow for 0.
1269
1270 $cache_exp_secs = (int) $this->p->options[ $cache_info[ 'opt_key' ] ];
1271 }
1272
1273 if ( is_array( $mod ) ) {
1274
1275 if ( ! empty( $cache_info[ 'conditional_values' ] ) ) {
1276
1277 foreach ( $cache_info[ 'conditional_values' ] as $cond => $val ) {
1278
1279 /*
1280 * If the $mod array condition is true, use the associated value.
1281 */
1282 if ( ! empty( $mod[ $cond ] ) ) {
1283
1284 $cache_exp_secs = (int) $val;
1285
1286 break; // Stop here.
1287 }
1288 }
1289 }
1290 }
1291
1292 if ( ! empty( $cache_info[ 'filter' ] ) ) {
1293
1294 $filter_name = SucomUtil::sanitize_hookname( $cache_info[ 'filter' ] );
1295
1296 if ( $this->p->debug->enabled ) {
1297
1298 $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
1299 }
1300
1301 $cache_exp_secs = (int) apply_filters( $filter_name, $cache_exp_secs, $cache_type, $mod );
1302 }
1303
1304 return $cache_exp_secs;
1305 }
1306 }
1307 }
1308