PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / 2.8.23.4
Pods – Custom Content Types and Fields v2.8.23.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
540 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 public function offsetExists( $offset ) {
384 return isset( $this->non_persistent_keys[ $offset ] );
385 }
386
387 /**
388 * Offset to retrieve.
389 *
390 * @link http://php.net/manual/en/arrayaccess.offsetget.php
391 *
392 * @since 4.11.0
393 *
394 * @param mixed $offset The offset to retrieve.
395 *
396 * @return mixed Can return all value types.
397 */
398 public function offsetGet( $offset ) {
399 return $this->get( $offset );
400 }
401
402 /**
403 * Offset to set.
404 *
405 * @since 4.11.0
406 *
407 * @link http://php.net/manual/en/arrayaccess.offsetset.php
408 *
409 * @param mixed $offset The offset to assign the value to.
410 * @param mixed $value The value to set.
411 *
412 * @return void
413 */
414 public function offsetSet( $offset, $value ) {
415 $this->set( $offset, $value, self::NON_PERSISTENT );
416 }
417
418 /**
419 * Offset to unset.
420 *
421 * @since 4.11.0
422 *
423 * @link http://php.net/manual/en/arrayaccess.offsetunset.php
424 *
425 * @param mixed $offset The offset to unset.
426 *
427 * @return void
428 */
429 public function offsetUnset( $offset ) {
430 $this->delete( $offset );
431 }
432
433 /**
434 * Warms up the caches for a collection of posts.
435 *
436 * @since 4.10.2
437 *
438 * @param array|int $post_ids A post ID, or a collection of post IDs.
439 * @param bool $update_post_meta_cache Whether to warm-up the post meta cache for the posts or not.
440 */
441 public function warmup_post_caches( $post_ids, $update_post_meta_cache = false ) {
442 if ( empty( $post_ids ) ) {
443 return;
444 }
445
446 $post_ids = (array) $post_ids;
447
448 global $wpdb;
449
450 $already_cached_ids = [];
451 foreach ( $post_ids as $post_id ) {
452 if ( wp_cache_get( $post_id, 'posts' ) instanceof \WP_Post ) {
453 $already_cached_ids[] = $post_id;
454 }
455 }
456
457 $required = array_diff( $post_ids, $already_cached_ids );
458
459 if ( empty( $required ) ) {
460 return;
461 }
462
463 /** @var Tribe__Feature_Detection $feature_detection */
464 $feature_detection = tribe( 'feature-detection' );
465 $limit = $feature_detection->mysql_limit_for_example( 'post_result' );
466
467 /**
468 * Filters the LIMIT that should be used to warm-up post caches and postmeta caches (if the
469 * `$update_post_meta_cache` parameter is `true`).
470 *
471 * Lower this value on less powerful hosts. Return `0` to disable the warm-up completely, and `-1` to remove the
472 * limit (not recommended).
473 *
474 * @since 4.10.2
475 *
476 * @param int $limit The number of posts whose caches will be warmed up, per query.
477 */
478 $limit = (int) apply_filters( 'tribe_cache_warmup_post_cache_limit', min( $limit, count( $post_ids ) ) );
479
480 if ( 0 === $limit ) {
481 // Warmup disabled.
482 return;
483 }
484
485 $buffer = $post_ids;
486 $page = 0;
487
488 do {
489 $limit_clause = $limit < 0 ? sprintf( 'LIMIT %d,%d', $limit * $page, $limit ) : '';
490 $page++;
491 $these_ids = array_splice( $buffer, 0, $limit );
492 $interval = implode( ',', array_map( 'absint', $these_ids ) );
493 $posts_query = "SELECT * FROM {$wpdb->posts} WHERE ID IN ({$interval}) {$limit_clause}";
494 $post_objects = $wpdb->get_results( $posts_query );
495 if ( is_array( $post_objects ) && ! empty( $post_objects ) ) {
496 foreach ( $post_objects as $post_object ) {
497 $post = new \WP_Post( $post_object );
498 wp_cache_set( $post_object->ID, $post, 'posts' );
499 }
500
501 if ( $update_post_meta_cache ) {
502 update_meta_cache( 'post', $these_ids );
503 }
504 }
505 } while ( ! empty( $post_objects ) && is_array( $post_objects ) && count( $post_objects ) < count( $post_ids ) );
506 }
507
508 /**
509 * If NOT using an external object caching system, then check if the size, in bytes, of the data
510 * to write to the database would fit into the `max_allowed_packet` setting or not.
511 *
512 * @since 4.12.14
513 *
514 * @param string|array|object $value The value to check.
515 *
516 * @return bool Whether the data, in its serialized form, would fit into the current database `max_allowed_packet`
517 * setting or not.
518 */
519 public function data_size_over_packet_size( $value ) {
520 if ( wp_using_ext_object_cache() ) {
521 // We cannot know and that is a concern of the external caching system.
522 return false;
523 }
524
525 try {
526 $serialized_value = maybe_serialize( $value );
527 $size = strlen( $serialized_value );
528 } catch ( Exception $e ) {
529 // The underlying function would run into the same issue, bail and do not set the transient.
530 return true;
531 }
532
533 /** @var Tribe__Feature_Detection $feature_detection */
534 $feature_detection = tribe( 'feature-detection' );
535
536 // 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.
537 return $size > ( $feature_detection->get_mysql_max_packet_size() * .9 );
538 }
539 }
540