PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / 2.9.19.4
Pods – Custom Content Types and Fields v2.9.19.4
2.7.31.4 2.8.23.5 2.9.19.5 3.0.10.5 3.1.4.3 3.2.8.4 3.3.9.2 2.8.23.4 2.9.19.4 3.0.10.4 3.1.4.2 3.2.8.3 3.3.9.1 trunk 1.14.8 2.7.31.3 2.8.23.3 2.9.19.3 3.0.10.3 3.1.4.1 3.2.0 3.2.1 3.2.1.1 3.2.2 3.2.4 3.2.5 3.2.6 3.2.7 3.2.7.1 3.2.8 3.2.8.1 3.2.8.2 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9
pods / tribe-common / src / Tribe / Cache.php
pods / tribe-common / src / Tribe Last commit date
Admin 2 weeks ago Ajax 2 weeks ago Asset 2 weeks ago Context 2 weeks ago Customizer 2 weeks ago Debug_Bar 2 weeks ago Dialog 2 weeks ago Documentation 2 weeks ago Duplicate 2 weeks ago Editor 2 weeks ago Image 2 weeks ago JSON_LD 2 weeks ago Languages 2 weeks ago Log 2 weeks ago Meta 2 weeks ago Models 2 weeks ago PUE 2 weeks ago Process 2 weeks ago Promoter 2 weeks ago REST 2 weeks ago Repository 2 weeks ago Service_Providers 2 weeks ago Shortcode 2 weeks ago Support 2 weeks ago Tabbed_View 2 weeks ago Tooltip 2 weeks ago Traits 2 weeks ago Utils 2 weeks ago Validator 2 weeks ago Widget 2 weeks ago Abstract_Deactivation.php 2 weeks ago Abstract_Plugin_Register.php 2 weeks ago App_Shop.php 2 weeks ago Assets.php 2 weeks ago Assets_Pipeline.php 2 weeks ago Autoloader.php 2 weeks ago Cache.php 2 weeks ago Cache_Listener.php 2 weeks ago Changelog_Reader.php 2 weeks ago Container.php 2 weeks ago Context.php 2 weeks ago Cost_Utils.php 2 weeks ago Credits.php 2 weeks ago Customizer.php 2 weeks ago DB_Lock.php 2 weeks ago Data.php 2 weeks ago Date_Utils.php 2 weeks ago Db.php 2 weeks ago Debug.php 2 weeks ago Dependency.php 2 weeks ago Deprecation.php 2 weeks ago Editor.php 2 weeks ago Error.php 2 weeks ago Exception.php 2 weeks ago Extension.php 2 weeks ago Extension_Loader.php 2 weeks ago Feature_Detection.php 2 weeks ago Field.php 2 weeks ago Field_Conditional.php 2 weeks ago Freemius.php 2 weeks ago Log.php 2 weeks ago Main.php 2 weeks ago Notices.php 2 weeks ago Plugin_Meta_Links.php 2 weeks ago Plugins.php 2 weeks ago Plugins_API.php 2 weeks ago Post_History.php 2 weeks ago Post_Transient.php 2 weeks ago Promise.php 2 weeks ago Repository.php 2 weeks ago Rewrite.php 2 weeks ago Settings.php 2 weeks ago Settings_Manager.php 2 weeks ago Settings_Tab.php 2 weeks ago Simple_Table.php 2 weeks ago Support.php 2 weeks ago Tabbed_View.php 2 weeks ago Template.php 2 weeks ago Template_Factory.php 2 weeks ago Template_Part_Cache.php 2 weeks ago Templates.php 2 weeks ago Terms.php 2 weeks ago Timezones.php 2 weeks ago Tracker.php 2 weeks ago Updater.php 2 weeks ago Validate.php 2 weeks ago View_Helpers.php 2 weeks ago
Cache.php
544 lines
1 <?php
2
3 /**
4 * Manage setting and expiring cached data
5 *
6 * Select actions can be used to force cached
7 * data to expire. Implemented so far:
8 * - save_post
9 *
10 * When used in its ArrayAccess API the cache will provide non persistent storage.
11 */
12 class Tribe__Cache implements ArrayAccess {
13 const SCHEDULED_EVENT_DELETE_TRANSIENT = 'tribe_schedule_transient_purge';
14
15 const NO_EXPIRATION = 0;
16
17 const NON_PERSISTENT = -1;
18
19 /**
20 * @var array
21 */
22 protected $non_persistent_keys = [];
23
24 /**
25 * Bootstrap hook
26 *
27 * @since 4.11.0
28 */
29 public function hook() {
30 if ( ! wp_next_scheduled( self::SCHEDULED_EVENT_DELETE_TRANSIENT ) ) {
31 wp_schedule_event( time(), 'twicedaily', self::SCHEDULED_EVENT_DELETE_TRANSIENT );
32 }
33
34 add_action( self::SCHEDULED_EVENT_DELETE_TRANSIENT, [ $this, 'delete_expired_transients' ] );
35
36 add_action( 'shutdown', [ $this, 'maybe_delete_expired_transients' ] );
37 }
38
39 public static function setup() {
40 wp_cache_add_non_persistent_groups( [ 'tribe-events-non-persistent' ] );
41 }
42
43 /**
44 * @param string $id
45 * @param mixed $value
46 * @param int $expiration
47 * @param string|array $expiration_trigger
48 *
49 * @return bool
50 */
51 public function set( $id, $value, $expiration = 0, $expiration_trigger = '' ) {
52 $key = $this->get_id( $id, $expiration_trigger );
53
54 /**
55 * Filters the expiration for cache objects to provide the ability
56 * to make non-persistent objects be treated as persistent.
57 *
58 * @since 4.8
59 *
60 * @param int $expiration Cache expiration time.
61 * @param string $id Cache ID.
62 * @param mixed $value Cache value.
63 * @param string|array $expiration_trigger Action that triggers automatic expiration.
64 * @param string $key Unique cache key based on Cache ID and expiration trigger last run time.
65 */
66 $expiration = apply_filters( 'tribe_cache_expiration', $expiration, $id, $value, $expiration_trigger, $key );
67
68 if ( self::NON_PERSISTENT === $expiration ) {
69 $group = 'tribe-events-non-persistent';
70 $expiration = 1;
71
72 // Add so we know what group to use in the future.
73 $this->non_persistent_keys[ $id ] = $id;
74 } else {
75 $group = 'tribe-events';
76 }
77
78 return wp_cache_set( $key, $value, $group, $expiration );
79 }
80
81 /**
82 * @param $id
83 * @param $value
84 * @param int $expiration
85 * @param string|array $expiration_trigger
86 *
87 * @return bool
88 */
89 public function set_transient( $id, $value, $expiration = 0, $expiration_trigger = '' ) {
90 if ( $this->data_size_over_packet_size( $value ) ) {
91 return false;
92 }
93
94 return set_transient( $this->get_id( $id, $expiration_trigger ), $value, $expiration );
95 }
96
97 /**
98 * Get cached data. Optionally set data if not previously set.
99 *
100 * Note: When a default value or callback is specified, this value gets set in the cache.
101 *
102 * @param string $id The key for the cached value.
103 * @param string|array $expiration_trigger Optional. Hook to trigger cache invalidation.
104 * @param mixed $default Optional. A default value or callback that returns a default value.
105 * @param int $expiration Optional. When the default value expires, if it gets set.
106 * @param mixed $args Optional. Args passed to callback.
107 *
108 * @return mixed
109 */
110 public function get( $id, $expiration_trigger = '', $default = false, $expiration = 0, $args = [] ) {
111 $group = isset( $this->non_persistent_keys[ $id ] ) ? 'tribe-events-non-persistent' : 'tribe-events';
112 $value = wp_cache_get( $this->get_id( $id, $expiration_trigger ), $group );
113
114 // Value found.
115 if ( false !== $value ) {
116 return $value;
117 }
118
119 if ( is_callable( $default ) ) {
120 // A callback has been specified.
121 $value = call_user_func_array( $default, $args );
122 } else {
123 // Default is a value.
124 $value = $default;
125 }
126
127 // No need to set a cache value to false since non-existent values return false.
128 if ( false !== $value ) {
129 $this->set( $id, $value, $expiration, $expiration_trigger );
130 }
131
132 return $value;
133 }
134
135 /**
136 * @param string $id
137 * @param string|array $expiration_trigger
138 *
139 * @return mixed
140 */
141 public function get_transient( $id, $expiration_trigger = '' ) {
142 return get_transient( $this->get_id( $id, $expiration_trigger ) );
143 }
144
145 /**
146 * @param string $id
147 * @param string|array $expiration_trigger
148 *
149 * @return bool
150 */
151 public function delete( $id, $expiration_trigger = '' ) {
152 $group = isset( $this->non_persistent_keys[ $id ] ) ? 'tribe-events-non-persistent' : 'tribe-events';
153
154 // Delete from non-persistent keys list.
155 if ( 'tribe-events-non-persistent' === $group ) {
156 unset( $this->non_persistent_keys[ $id ] );
157 }
158
159 return wp_cache_delete( $this->get_id( $id, $expiration_trigger ), $group );
160 }
161
162 /**
163 * @param string $id
164 * @param string|array $expiration_trigger
165 *
166 * @return bool
167 */
168 public function delete_transient( $id, $expiration_trigger = '' ) {
169 return delete_transient( $this->get_id( $id, $expiration_trigger ) );
170 }
171
172 /**
173 * Purge all expired tribe_ transients.
174 *
175 * This uses a modification of the the query from https://core.trac.wordpress.org/ticket/20316
176 *
177 * @since 4.11.0
178 *
179 * @return void Just execute the database SQL no return required.
180 */
181 public function delete_expired_transients() {
182 if ( tribe_get_var( 'has_deleted_expired_transients', false ) ) {
183 return;
184 }
185
186 global $wpdb;
187
188 $time = time();
189
190 $sql = "
191 DELETE
192 a,
193 b
194 FROM
195 {$wpdb->options} a
196 INNER JOIN {$wpdb->options} b
197 ON b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) )
198 AND b.option_value < {$time}
199 WHERE
200 a.option_name LIKE '\_transient\_tribe\_%'
201 AND a.option_name NOT LIKE '\_transient\_timeout\_tribe\_%'
202 ";
203
204 /**
205 * Allow third party filtering of the SQL used for deleting expired transients.
206 *
207 * @since 4.11.5
208 *
209 * @param string $sql The SQL we execute to delete all the expired transients.
210 * @param int $time Time we are using to determine what is expired.
211 */
212 $sql = apply_filters( 'tribe_cache_delete_expired_transients_sql', $sql, $time );
213
214 if ( empty( $sql ) ) {
215 return;
216 }
217
218 $wpdb->query( $sql );
219
220 // Set the variable to prevent this call from running twice.
221 tribe_set_var( 'has_deleted_expired_transients', true );
222 }
223
224 /**
225 * Flag if we should delete
226 *
227 * @since 4.11.5
228 *
229 * @param boolean $value If we should delete transients or not on shutdown.
230 *
231 * @return void No return for setting the flag.
232 */
233 public function flag_required_delete_transients( $value = true ) {
234 tribe_set_var( 'should_delete_expired_transients', $value );
235 }
236
237 /**
238 * Runs on hook `shutdown` and will delete transients on the end of the request.
239 *
240 * @since 4.11.5
241 *
242 * @return void No return for action hook method.
243 */
244 public function maybe_delete_expired_transients() {
245 if ( ! tribe_get_var( 'should_delete_expired_transients', false ) ) {
246 return;
247 }
248
249 $this->delete_expired_transients();
250 }
251
252 /**
253 * @param string $key
254 * @param string|array $expiration_trigger
255 *
256 * @return string
257 */
258 public function get_id( $key, $expiration_trigger = '' ) {
259 if ( is_array( $expiration_trigger ) ) {
260 $triggers = $expiration_trigger;
261 } else {
262 $triggers = array_filter( explode( '|', $expiration_trigger ) );
263 }
264
265 $last = 0;
266 foreach ( $triggers as $trigger ) {
267 // Bail on empty trigger otherwise it creates a `tribe_last_` opt on the DB.
268 if ( empty( $trigger ) ) {
269 continue;
270 }
271
272 $occurrence = $this->get_last_occurrence( $trigger );
273
274 if ( $occurrence > $last ) {
275 $last = $occurrence;
276 }
277 }
278
279 $last = empty( $last ) ? '' : $last;
280 $id = $key . $last;
281 if ( strlen( $id ) > 80 ) {
282 $id = 'tribe_' . md5( $id );
283 }
284
285 return $id;
286 }
287
288 /**
289 * Returns the time of an action last occurrence.
290 *
291 * @since 4.9.14 Changed the return value type from `int` to `float`.
292 *
293 * @param string $action The action to return the time for.
294 *
295 * @return float The time (microtime) an action last occurred, or the current microtime if it never occurred.
296 */
297 public function get_last_occurrence( $action ) {
298 static $cache_var_name = __METHOD__;
299
300 $cache_last_actions = tribe_get_var( $cache_var_name, [] );
301
302 if ( isset( $cache_last_actions[ $action ] ) ) {
303 return $cache_last_actions[ $action ];
304 }
305
306 $last_action = (float) get_option( 'tribe_last_' . $action, null );
307
308 if ( ! $last_action ) {
309 $last_action = microtime( true );
310 $this->set_last_occurrence( $action, $last_action );
311 }
312
313 $cache_last_actions[ $action ] = (float) $last_action;
314
315 tribe_set_var( $cache_var_name, $cache_last_actions );
316
317 return $cache_last_actions[ $action ];
318 }
319
320 /**
321 * Sets the time (microtime) for an action last occurrence.
322 *
323 * @since 4.9.14 Changed the type of the time stored from an `int` to a `float`.
324 *
325 * @param string $action The action to record the last occurrence of.
326 * @param int|float $timestamp The timestamp to assign to the action last occurrence or the current time (microtime).
327 *
328 * @return boolean IF we were able to set the last occurrence or not.
329 */
330 public function set_last_occurrence( $action, $timestamp = 0 ) {
331 if ( empty( $timestamp ) ) {
332 $timestamp = microtime( true );
333 }
334 $updated = update_option( 'tribe_last_' . $action, (float) $timestamp );
335
336 // For performance reasons we will only expire cache once per request, when needed.
337 if ( $updated ) {
338 $this->flag_required_delete_transients( true );
339 }
340
341 return $updated;
342 }
343
344 /**
345 * Builds a key from an array of components and an optional prefix.
346 *
347 * @param mixed $components Either a single component of the key or an array of key components.
348 * @param string $prefix
349 * @param bool $sort Whether component arrays should be sorted or not to generate the key; defaults to
350 * `true`.
351 *
352 * @return string The resulting key.
353 */
354 public function make_key( $components, $prefix = '', $sort = true ) {
355 $key = '';
356 $components = is_array( $components ) ? $components : [ $components ];
357 foreach ( $components as $component ) {
358 if ( $sort && is_array( $component ) ) {
359 $is_associative = count( array_filter( array_keys( $component ), 'is_numeric' ) ) < count( array_keys( $component ) );
360 if ( $is_associative ) {
361 ksort( $component );
362 } else {
363 sort( $component );
364 }
365 }
366 $key .= maybe_serialize( $component );
367 }
368
369 return $this->get_id( $prefix . md5( $key ) );
370 }
371
372 /**
373 * Whether a offset exists.
374 *
375 * @since 4.11.0
376 *
377 * @link http://php.net/manual/en/arrayaccess.offsetexists.php
378 *
379 * @param mixed $offset An offset to check for.
380 *
381 * @return boolean Whether the offset exists in the cache.
382 */
383 #[\ReturnTypeWillChange]
384 public function offsetExists( $offset ) {
385 return isset( $this->non_persistent_keys[ $offset ] );
386 }
387
388 /**
389 * Offset to retrieve.
390 *
391 * @link http://php.net/manual/en/arrayaccess.offsetget.php
392 *
393 * @since 4.11.0
394 *
395 * @param mixed $offset The offset to retrieve.
396 *
397 * @return mixed Can return all value types.
398 */
399 #[\ReturnTypeWillChange]
400 public function offsetGet( $offset ) {
401 return $this->get( $offset );
402 }
403
404 /**
405 * Offset to set.
406 *
407 * @since 4.11.0
408 *
409 * @link http://php.net/manual/en/arrayaccess.offsetset.php
410 *
411 * @param mixed $offset The offset to assign the value to.
412 * @param mixed $value The value to set.
413 *
414 * @return void
415 */
416 #[\ReturnTypeWillChange]
417 public function offsetSet( $offset, $value ) {
418 $this->set( $offset, $value, self::NON_PERSISTENT );
419 }
420
421 /**
422 * Offset to unset.
423 *
424 * @since 4.11.0
425 *
426 * @link http://php.net/manual/en/arrayaccess.offsetunset.php
427 *
428 * @param mixed $offset The offset to unset.
429 *
430 * @return void
431 */
432 #[\ReturnTypeWillChange]
433 public function offsetUnset( $offset ) {
434 $this->delete( $offset );
435 }
436
437 /**
438 * Warms up the caches for a collection of posts.
439 *
440 * @since 4.10.2
441 *
442 * @param array|int $post_ids A post ID, or a collection of post IDs.
443 * @param bool $update_post_meta_cache Whether to warm-up the post meta cache for the posts or not.
444 */
445 public function warmup_post_caches( $post_ids, $update_post_meta_cache = false ) {
446 if ( empty( $post_ids ) ) {
447 return;
448 }
449
450 $post_ids = (array) $post_ids;
451
452 global $wpdb;
453
454 $already_cached_ids = [];
455 foreach ( $post_ids as $post_id ) {
456 if ( wp_cache_get( $post_id, 'posts' ) instanceof \WP_Post ) {
457 $already_cached_ids[] = $post_id;
458 }
459 }
460
461 $required = array_diff( $post_ids, $already_cached_ids );
462
463 if ( empty( $required ) ) {
464 return;
465 }
466
467 /** @var Tribe__Feature_Detection $feature_detection */
468 $feature_detection = tribe( 'feature-detection' );
469 $limit = $feature_detection->mysql_limit_for_example( 'post_result' );
470
471 /**
472 * Filters the LIMIT that should be used to warm-up post caches and postmeta caches (if the
473 * `$update_post_meta_cache` parameter is `true`).
474 *
475 * Lower this value on less powerful hosts. Return `0` to disable the warm-up completely, and `-1` to remove the
476 * limit (not recommended).
477 *
478 * @since 4.10.2
479 *
480 * @param int $limit The number of posts whose caches will be warmed up, per query.
481 */
482 $limit = (int) apply_filters( 'tribe_cache_warmup_post_cache_limit', min( $limit, count( $post_ids ) ) );
483
484 if ( 0 === $limit ) {
485 // Warmup disabled.
486 return;
487 }
488
489 $buffer = $post_ids;
490 $page = 0;
491
492 do {
493 $limit_clause = $limit < 0 ? sprintf( 'LIMIT %d,%d', $limit * $page, $limit ) : '';
494 $page++;
495 $these_ids = array_splice( $buffer, 0, $limit );
496 $interval = implode( ',', array_map( 'absint', $these_ids ) );
497 $posts_query = "SELECT * FROM {$wpdb->posts} WHERE ID IN ({$interval}) {$limit_clause}";
498 $post_objects = $wpdb->get_results( $posts_query );
499 if ( is_array( $post_objects ) && ! empty( $post_objects ) ) {
500 foreach ( $post_objects as $post_object ) {
501 $post = new \WP_Post( $post_object );
502 wp_cache_set( $post_object->ID, $post, 'posts' );
503 }
504
505 if ( $update_post_meta_cache ) {
506 update_meta_cache( 'post', $these_ids );
507 }
508 }
509 } while ( ! empty( $post_objects ) && is_array( $post_objects ) && count( $post_objects ) < count( $post_ids ) );
510 }
511
512 /**
513 * If NOT using an external object caching system, then check if the size, in bytes, of the data
514 * to write to the database would fit into the `max_allowed_packet` setting or not.
515 *
516 * @since 4.12.14
517 *
518 * @param string|array|object $value The value to check.
519 *
520 * @return bool Whether the data, in its serialized form, would fit into the current database `max_allowed_packet`
521 * setting or not.
522 */
523 public function data_size_over_packet_size( $value ) {
524 if ( wp_using_ext_object_cache() ) {
525 // We cannot know and that is a concern of the external caching system.
526 return false;
527 }
528
529 try {
530 $serialized_value = maybe_serialize( $value );
531 $size = strlen( $serialized_value );
532 } catch ( Exception $e ) {
533 // The underlying function would run into the same issue, bail and do not set the transient.
534 return true;
535 }
536
537 /** @var Tribe__Feature_Detection $feature_detection */
538 $feature_detection = tribe( 'feature-detection' );
539
540 // If the size of the string is above 90% of the database `max_allowed_packet` setting, then it should not be written to the db.
541 return $size > ( $feature_detection->get_mysql_max_packet_size() * .9 );
542 }
543 }
544