PluginProbe
DecaLog / 4.4.0
DecaLog v4.4.0
3.0.2 3.1.0 3.10.0 3.2.0 3.3.0 3.4.0 3.4.1 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.6.3 3.7.0 3.7.1 3.8.0 3.9.0 3.9.1 4.0.0 4.1.0 4.2.0 4.3.0 4.3.1 4.4.0 4.5.0 All 75 releases
decalog / includes / system / class-cache.php

class-cache.php in DecaLog 4.4.0, at includes/system/class-cache.php

681 lines 21.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin cache handling.
4 *
5 * @package System
6 * @author Pierre Lannoy <https://pierre.lannoy.fr/>.
7 * @since 1.0.0
8 * @noinspection PhpCSValidationInspection
9 */
10
11 namespace Decalog\System;
12
13 use Decalog\Logger;
14 use Decalog\System\Conversion;
15 use Decalog\System\Option;
16
17 /**
18 * The class responsible to handle cache management.
19 *
20 * @package System
21 * @author Pierre Lannoy <https://pierre.lannoy.fr/>.
22 * @since 1.0.0
23 */
24 class Cache {
25
26 /**
27 * The pool's name, specific to the calling plugin.
28 *
29 * @since 1.0.0
30 * @var string $pool_name The pool's name.
31 */
32 private static $pool_name = DECALOG_SLUG;
33
34 /**
35 * The apcu pool's prefix, specific to the WordPress instance.
36 *
37 * @since 1.0.0
38 * @var string $apcu_pool_prefix The pool's name.
39 */
40 private static $apcu_pool_prefix = '';
41
42 /**
43 * Available TTLs.
44 *
45 * @since 1.0.0
46 * @var array $ttls The TTLs array.
47 */
48 private static $ttls = [];
49
50 /**
51 * Default TTL.
52 *
53 * @since 1.0.0
54 * @var integer $default_ttl The default TTL in seconds.
55 */
56 private static $default_ttl = 3600;
57
58 /**
59 * Is APCu available.
60 *
61 * @since 1.0.0
62 * @var boolean $apcu_available Is APCu available.
63 */
64 public static $apcu_available = false;
65
66 /**
67 * Initializes the class and set its properties.
68 *
69 * @since 1.0.0
70 */
71 public function __construct() {
72 self::init();
73 }
74
75 /**
76 * Verify if cache is in memory.
77 *
78 * @since 1.0.0
79 */
80 public static function is_memory() {
81 return ( wp_using_ext_object_cache() || self::$apcu_available );
82 }
83
84 /**
85 * Get cache analytics.
86 *
87 * @return array The cache analytics.
88 * @since 1.0.0
89 */
90 public static function get_analytics() {
91 $result = [];
92 if ( wp_using_ext_object_cache() ) {
93 $result['type'] = 'object_cache';
94 } elseif ( self::$apcu_available ) {
95 $result['type'] = 'apcu';
96 } else {
97 $result['type'] = 'db_transient';
98 }
99 return $result;
100 }
101
102 /**
103 * Initializes properties.
104 *
105 * @since 1.0.0
106 */
107 public static function init() {
108 self::$ttls = [
109 'ephemeral' => 1 * MINUTE_IN_SECONDS,
110 'infinite' => 10 * YEAR_IN_SECONDS,
111 'diagnosis' => HOUR_IN_SECONDS,
112 'plugin-statistics' => DAY_IN_SECONDS,
113 'metrics' => HOUR_IN_SECONDS,
114 'm-query' => 10 * MINUTE_IN_SECONDS,
115 'longquery' => 6 * HOUR_IN_SECONDS,
116 ];
117 if ( wp_using_ext_object_cache() ) {
118 wp_cache_add_global_groups( self::$pool_name );
119 }
120 if ( ! defined( 'APCU_CACHE_PREFIX' ) ) {
121 define( 'APCU_CACHE_PREFIX', '_' . md5( ABSPATH ) . '_' );
122 }
123 self::$apcu_pool_prefix = APCU_CACHE_PREFIX;
124 self::$apcu_available = function_exists( 'apcu_delete' ) && function_exists( 'apcu_fetch' ) && function_exists( 'apcu_store' );
125 }
126
127 /**
128 * Get an ID for caching.
129 *
130 * @since 1.0.0
131 */
132 public static function id( $args, $path = 'data/' ) {
133 if ( is_array( $args ) ) {
134 $args = serialize( $args );
135 }
136 if ( '/' === $path[0] ) {
137 $path = substr( $path, 1 );
138 }
139 if ( '/' !== $path[ strlen( $path ) - 1 ] ) {
140 $path = $path . '/';
141 }
142 return $path . md5( (string) $args );
143 }
144
145 /**
146 * Full item name.
147 *
148 * @param string $item_name Item name. Expected to not be SQL-escaped.
149 * @param boolean $blog_aware Optional. Has the name must take care of blog.
150 * @param boolean $locale_aware Optional. Has the name must take care of locale.
151 * @param boolean $user_aware Optional. Has the name must take care of user.
152 * @return string The full item name.
153 * @since 1.0.0
154 */
155 private static function full_item_name( $item_name, $blog_aware = false, $locale_aware = false, $user_aware = false ) {
156 $name = '';
157 if ( $blog_aware ) {
158 $name .= (string) get_current_blog_id() . '/';
159 }
160 if ( $locale_aware ) {
161 $name .= (string) L10n::get_display_locale() . '/';
162 }
163 if ( $user_aware ) {
164 $name .= (string) User::get_current_user_id() . '/';
165 }
166 $name .= $item_name;
167 return substr( trim( $name ), 0, 172 - strlen( self::$apcu_pool_prefix . self::$pool_name ) );
168 }
169
170 /**
171 * Normalized item name.
172 *
173 * @param string $item_name Item name. Expected to not be SQL-escaped.
174 * @return string The normalized item name.
175 * @since 1.0.0
176 */
177 private static function normalized_item_name( $item_name ) {
178 if ( '/' === $item_name[0] ) {
179 $item_name = substr( $item_name, 1 );
180 }
181 while ( 0 !== substr_count( $item_name, '//' ) ) {
182 $item_name = str_replace( '//', '/', $item_name );
183 }
184 $item_name = str_replace( '/', '_', $item_name );
185 return strtolower( $item_name );
186 }
187
188 /**
189 * Get the value of a fully named cache item.
190 *
191 * If the item does not exist, does not have a value, or has expired,
192 * then the return value will be false.
193 *
194 * @param string $item_name Item name. Expected to not be SQL-escaped.
195 * @return mixed Value of item.
196 * @since 1.0.0
197 */
198 private static function get_for_full_name( $item_name ) {
199 $item_name = self::normalized_item_name( $item_name );
200 $found = false;
201 if ( self::$apcu_available && Option::network_get( 'use_apcu', true ) ) {
202 $result = apcu_fetch( self::$pool_name . self::$apcu_pool_prefix . $item_name, $found );
203 } elseif ( wp_using_ext_object_cache() ) {
204 $result = wp_cache_get( $item_name, self::$pool_name, false, $found );
205 } else {
206 $result = get_transient( self::$pool_name . '_' . $item_name );
207 $found = false !== $result;
208 }
209 if ( $found ) {
210 return $result;
211 } else {
212 return null;
213 }
214 }
215
216 /**
217 * Get the value of a fully named apcu cache item.
218 *
219 * If the item does not exist, does not have a value, or has expired,
220 * then the return value will be false.
221 *
222 * @param string $item_name Item name. Expected to not be SQL-escaped.
223 * @return mixed Value of item.
224 * @since 3.4.0
225 */
226 private static function get_apcu_for_full_name( $item_name ) {
227 $chrono = microtime( true );
228 $item_name = self::normalized_item_name( $item_name );
229 $found = false;
230 if ( self::$apcu_available ) {
231 $result = apcu_fetch( self::$pool_name . self::$apcu_pool_prefix . $item_name, $found );
232 }
233 if ( $found ) {
234 return $result;
235 } else {
236 return null;
237 }
238 }
239
240 /**
241 * Get the value of a global cache item.
242 *
243 * If the item does not exist, does not have a value, or has expired,
244 * then the return value will be false.
245 *
246 * @param string $item_name Item name. Expected to not be SQL-escaped.
247 * @return mixed Value of item.
248 * @since 1.0.0
249 */
250 public static function get_global( $item_name ) {
251 return self::get_for_full_name( self::full_item_name( $item_name ) );
252 }
253
254 /**
255 * Get the value of a global apcu cache item.
256 *
257 * If the item does not exist, does not have a value, or has expired,
258 * then the return value will be false.
259 *
260 * @param string $item_name Item name. Expected to not be SQL-escaped.
261 * @return mixed Value of item.
262 * @since 3.4.0
263 */
264 public static function get_global_apcu( $item_name ) {
265 return self::get_apcu_for_full_name( self::full_item_name( $item_name ) );
266 }
267
268 /**
269 * Get the value of a standard cache item.
270 *
271 * If the item does not exist, does not have a value, or has expired,
272 * then the return value will be false.
273 *
274 * @param string $item_name Item name. Expected to not be SQL-escaped.
275 * @param boolean $blog_aware Optional. Has the name must take care of blog.
276 * @param boolean $locale_aware Optional. Has the name must take care of locale.
277 * @param boolean $user_aware Optional. Has the name must take care of user.
278 * @return mixed Value of item.
279 * @since 1.0.0
280 */
281 public static function get( $item_name, $blog_aware = false, $locale_aware = false, $user_aware = false ) {
282 return self::get_for_full_name( self::full_item_name( $item_name, $blog_aware, $locale_aware, $user_aware ) );
283 }
284
285 /**
286 * Set the value of a fully named cache item.
287 *
288 * You do not need to serialize values. If the value needs to be serialized, then
289 * it will be serialized before it is set.
290 *
291 * @param string $item_name Item name. Expected to not be SQL-escaped.
292 * @param mixed $value Item value. Must be serializable if non-scalar.
293 * Expected to not be SQL-escaped.
294 * @param int|string $ttl Optional. The previously defined ttl @see self::init() if it's a string.
295 * The ttl value in seconds if it's and integer.
296 * @return bool False if value was not set and true if value was set.
297 * @since 1.0.0
298 */
299 private static function set_for_full_name( $item_name, $value, $ttl = 'default' ) {
300 $item_name = self::normalized_item_name( $item_name );
301 $expiration = self::$default_ttl;
302 if ( is_string( $ttl ) && array_key_exists( $ttl, self::$ttls ) ) {
303 $expiration = self::$ttls[ $ttl ];
304 }
305 if ( is_integer( $ttl ) && 0 < (int) $ttl ) {
306 $expiration = (int) $ttl;
307 }
308 if ( $expiration > 0 ) {
309 if ( self::$apcu_available && Option::network_get( 'use_apcu', true ) ) {
310 $result = apcu_store( self::$pool_name . self::$apcu_pool_prefix . $item_name, $value, $expiration );
311 } elseif ( wp_using_ext_object_cache() ) {
312 $result = wp_cache_set( $item_name, $value, self::$pool_name, $expiration );
313 } else {
314 $result = set_transient( self::$pool_name . '_' . $item_name, $value, $expiration );
315 }
316 } else {
317 $result = false;
318 }
319 return $result;
320 }
321
322 /**
323 * Set the value of a fully named apcu cache item.
324 *
325 * You do not need to serialize values. If the value needs to be serialized, then
326 * it will be serialized before it is set.
327 *
328 * @param string $item_name Item name. Expected to not be SQL-escaped.
329 * @param mixed $value Item value. Must be serializable if non-scalar.
330 * Expected to not be SQL-escaped.
331 * @param int|string $ttl Optional. The previously defined ttl @see self::init() if it's a string.
332 * The ttl value in seconds if it's and integer.
333 * @return bool False if value was not set and true if value was set.
334 * @since 1.0.0
335 */
336 private static function set_apcu_for_full_name( $item_name, $value, $ttl = 'default' ) {
337 $item_name = self::normalized_item_name( $item_name );
338 $expiration = self::$default_ttl;
339 if ( is_string( $ttl ) && array_key_exists( $ttl, self::$ttls ) ) {
340 $expiration = self::$ttls[ $ttl ];
341 }
342 if ( is_integer( $ttl ) && 0 < (int) $ttl ) {
343 $expiration = (int) $ttl;
344 }
345 if ( $expiration > 0 ) {
346 if ( self::$apcu_available ) {
347 $result = apcu_store( self::$pool_name . self::$apcu_pool_prefix . $item_name, $value, $expiration );
348 }
349 } else {
350 $result = false;
351 }
352 return $result;
353 }
354
355 /**
356 * Set the value of a global cache item.
357 *
358 * You do not need to serialize values. If the value needs to be serialized, then
359 * it will be serialized before it is set.
360 *
361 * @param string $item_name Item name. Expected to not be SQL-escaped.
362 * @param mixed $value Item value. Must be serializable if non-scalar.
363 * Expected to not be SQL-escaped.
364 * @param int|string $ttl Optional. The previously defined ttl @see self::init() if it's a string.
365 * The ttl value in seconds if it's and integer.
366 * @return bool False if value was not set and true if value was set.
367 * @since 1.0.0
368 */
369 public static function set_global( $item_name, $value, $ttl = 'default' ) {
370 return self::set_for_full_name( self::full_item_name( $item_name ), $value, $ttl );
371 }
372
373 /**
374 * Set the value of a global cache item.
375 *
376 * You do not need to serialize values. If the value needs to be serialized, then
377 * it will be serialized before it is set.
378 *
379 * @param string $item_name Item name. Expected to not be SQL-escaped.
380 * @param mixed $value Item value. Must be serializable if non-scalar.
381 * Expected to not be SQL-escaped.
382 * @param int|string $ttl Optional. The previously defined ttl @see self::init() if it's a string.
383 * The ttl value in seconds if it's and integer.
384 * @return bool False if value was not set and true if value was set.
385 * @since 1.0.0
386 */
387 public static function set_global_apcu( $item_name, $value, $ttl = 'default' ) {
388 return self::set_apcu_for_full_name( self::full_item_name( $item_name ), $value, $ttl );
389 }
390
391 /**
392 * Set the value of a standard cache item.
393 *
394 * You do not need to serialize values. If the value needs to be serialized, then
395 * it will be serialized before it is set.
396 *
397 * @param string $item_name Item name. Expected to not be SQL-escaped.
398 * @param mixed $value Item value. Must be serializable if non-scalar.
399 * Expected to not be SQL-escaped.
400 * @param int|string $ttl Optional. The previously defined ttl @see self::init() if it's a string.
401 * The ttl value in seconds if it's and integer.
402 * @param boolean $blog_aware Optional. Has the name must take care of blog.
403 * @param boolean $locale_aware Optional. Has the name must take care of locale.
404 * @param boolean $user_aware Optional. Has the name must take care of user.
405 * @return bool False if value was not set and true if value was set.
406 * @since 1.0.0
407 */
408 public static function set( $item_name, $value, $ttl = 'default', $blog_aware = false, $locale_aware = false, $user_aware = false ) {
409 return self::set_for_full_name( self::full_item_name( $item_name, $blog_aware, $locale_aware, $user_aware ), $value, $ttl );
410 }
411
412 /**
413 * Delete the value of a fully named cache item.
414 *
415 * This function accepts generic car "*" for transients.
416 *
417 * @param string $item_name Item name. Expected to not be SQL-escaped.
418 * @return integer Number of deleted items.
419 * @since 1.0.0
420 */
421 private static function delete_for_ful_name( $item_name ) {
422 $item_name = self::normalized_item_name( $item_name );
423 $result = 0;
424 if ( self::$apcu_available && Option::network_get( 'use_apcu', true ) ) {
425 if ( strlen( $item_name ) - 1 === strpos( $item_name, '_*' ) ) {
426 return false;
427 } else {
428 return apcu_delete( self::$pool_name . self::$apcu_pool_prefix . $item_name );
429 }
430 }
431 if ( wp_using_ext_object_cache() ) {
432 if ( strlen( $item_name ) - 1 === strpos( $item_name, '_*' ) ) {
433 return false;
434 } else {
435 return wp_cache_delete( $item_name, self::$pool_name );
436 }
437 }
438 global $wpdb;
439 $item_name = self::$pool_name . '_' . $item_name;
440 if ( strlen( $item_name ) - 1 === strpos( $item_name, '_*' ) ) {
441 // phpcs:ignore
442 $delete = $wpdb->get_col( "SELECT option_name FROM {$wpdb->options} WHERE option_name = '_transient_timeout_" . str_replace( '_*', '', $item_name ) . "' OR option_name LIKE '_transient_timeout_" . str_replace( '_*', '_%', $item_name ) . "';" );
443 } else {
444 // phpcs:ignore
445 $delete = $wpdb->get_col( "SELECT option_name FROM {$wpdb->options} WHERE option_name = '_transient_timeout_" . $item_name . "';" );
446 }
447 foreach ( $delete as $transient ) {
448 $key = str_replace( '_transient_timeout_', '', $transient );
449 if ( delete_transient( $key ) ) {
450 ++$result;
451 }
452 }
453 return $result;
454 }
455
456 /**
457 * Delete the value of a fully named apcu cache item.
458 *
459 * @param string $item_name Item name. Expected to not be SQL-escaped.
460 * @return integer Number of deleted items.
461 * @since 3.4.0
462 */
463 private static function delete_apcu_for_ful_name( $item_name ) {
464 $item_name = self::normalized_item_name( $item_name );
465 $result = 0;
466 if ( self::$apcu_available ) {
467 if ( strlen( $item_name ) - 1 === strpos( $item_name, '_*' ) ) {
468 return false;
469 } else {
470 return apcu_delete( self::$pool_name . self::$apcu_pool_prefix . $item_name );
471 }
472 }
473 return $result;
474 }
475
476 /**
477 * Delete the full pool.
478 *
479 * @return integer Number of deleted items.
480 * @since 1.0.0
481 */
482 public static function delete_pool() {
483 $result = 0;
484 if ( self::$apcu_available && Option::network_get( 'use_apcu', true ) ) {
485 if ( function_exists( 'apcu_cache_info' ) && function_exists( 'apcu_delete' ) ) {
486 try {
487 $infos = apcu_cache_info( false );
488 if ( array_key_exists( 'cache_list', $infos ) && is_array( $infos['cache_list'] ) ) {
489 foreach ( $infos['cache_list'] as $script ) {
490 if ( 0 === strpos( $script['info'], self::$pool_name . self::$apcu_pool_prefix ) ) {
491 apcu_delete( $script['info'] );
492 $result++;
493 }
494 }
495 }
496 } catch ( \Throwable $e ) {
497 $result = 0;
498 }
499 }
500 } elseif ( wp_using_ext_object_cache() ) {
501 $result = 0;
502 } else {
503 $result = self::delete_global( '/*' );
504 }
505 return $result;
506 }
507
508 /**
509 * Delete the value of a global cache item.
510 *
511 * This function accepts generic car "*" for transients.
512 *
513 * @param string $item_name Item name. Expected to not be SQL-escaped.
514 * @return integer Number of deleted items.
515 * @since 1.0.0
516 */
517 public static function delete_global( $item_name ) {
518 return self::delete_for_ful_name( self::full_item_name( $item_name ) );
519 }
520
521 /**
522 * Delete the value of a global apcu cache item.
523 *
524 * @param string $item_name Item name. Expected to not be SQL-escaped.
525 * @return integer Number of deleted items.
526 * @since 3.4.0
527 */
528 public static function delete_global_apcu( $item_name ) {
529 return self::delete_apcu_for_ful_name( self::full_item_name( $item_name ) );
530 }
531
532 /**
533 * Delete the value of a standard cache item.
534 *
535 * This function accepts generic car "*" for transients.
536 *
537 * @param string $item_name Item name. Expected to not be SQL-escaped.
538 * @param boolean $blog_aware Optional. Has the name must take care of blog.
539 * @param boolean $locale_aware Optional. Has the name must take care of locale.
540 * @param boolean $user_aware Optional. Has the name must take care of user.
541 * @return integer Number of deleted items.
542 * @since 1.0.0
543 */
544 public static function delete( $item_name, $blog_aware = false, $locale_aware = false, $user_aware = false ) {
545 return self::delete_for_ful_name( self::full_item_name( $item_name, $blog_aware, $locale_aware, $user_aware ) );
546 }
547
548 /**
549 * Get the minimum value of a ttl time range.
550 *
551 * @param string $ttl_range The time range in seconds. May be something like '0', '200' or '15-600:15'.
552 * @return integer The ttl in seconds.
553 * @since 1.0.0
554 */
555 public static function get_min( $ttl_range ) {
556 if ( ! is_string( $ttl_range) ) {
557 return 0;
558 }
559 $ttls = explode( '-', $ttl_range );
560 if ( 1 === count( $ttls ) ) {
561 return (int) $ttls[0];
562 }
563 if ( false !== strpos( $ttls[1], ':' ) ) {
564 $steps = explode( ':', $ttls[1] );
565 $ttls[1] = $steps[0];
566 }
567 return (int) min( (int) $ttls[0], (int) $ttls[1] );
568 }
569
570 /**
571 * Get the maximum value of a ttl time range.
572 *
573 * @param string $ttl_range The time range in seconds. May be something like '0', '200' or '15-600:15'.
574 * @return integer The ttl in seconds.
575 * @since 1.0.0
576 */
577 public static function get_max( $ttl_range ) {
578 if ( ! is_string( $ttl_range) ) {
579 return 0;
580 }
581 $ttls = explode( '-', $ttl_range );
582 if ( 1 === count( $ttls ) ) {
583 return (int) $ttls[0];
584 }
585 if ( false !== strpos( $ttls[1], ':' ) ) {
586 $steps = explode( ':', $ttls[1] );
587 $ttls[1] = $steps[0];
588 }
589 return (int) max( (int) $ttls[0], (int) $ttls[1] );
590 }
591
592 /**
593 * Get the step of a ttl time range.
594 *
595 * @param string $ttl_range The time range in seconds. May be something like '0', '200' or '15-600:15'.
596 * @return integer The ttl in seconds.
597 * @since 1.0.0
598 */
599 public static function get_step( $ttl_range ) {
600 if ( ! is_string( $ttl_range) ) {
601 return 0;
602 }
603 $ttls = explode( '-', $ttl_range );
604 if ( 1 === count( $ttls ) ) {
605 return 0;
606 }
607 if ( false !== strpos( $ttls[1], ':' ) ) {
608 $steps = explode( ':', $ttls[1] );
609 if ( 2 === count( $ttls ) ) {
610 return $steps[1];
611 }
612 }
613 return 1;
614 }
615
616 /**
617 * Get the medium value of a ttl time range.
618 *
619 * This function accepts generic car "*" for transients.
620 *
621 * @param string $ttl_range The time range in seconds. May be something like '5-600' or '200'.
622 * @return integer The ttl in seconds.
623 * @since 1.0.0
624 */
625 public static function get_med( $ttl_range ) {
626 $min = self::get_min( $ttl_range );
627 $max = self::get_max( $ttl_range );
628 $step = self::get_step( $ttl_range );
629 $factor = $step * (int) round( ( $max - $min ) / ( 2 * $step ) );
630 return $min + (int) round( $factor );
631 }
632
633 /**
634 * Get the options infos for Site Health "info" tab.
635 *
636 * @since 1.0.0
637 */
638 public static function debug_info() {
639 if ( self::$apcu_available ) {
640 $result['product'] = [
641 'label' => 'Product',
642 'value' => 'APCu',
643 ];
644 foreach ( [ 'enabled', 'shm_segments', 'shm_size', 'entries_hint', 'ttl', 'gc_ttl', 'mmap_file_mask', 'slam_defense', 'enable_cli', 'use_request_time', 'serializer', 'coredump_unmap', 'preload_path' ] as $key ) {
645 $result[ 'directive_' . $key ] = [
646 'label' => '[Directive] ' . $key,
647 'value' => ini_get( 'apc.' . $key ),
648 ];
649 }
650 if ( function_exists( 'apcu_sma_info' ) && function_exists( 'apcu_cache_info' ) ) {
651 $raw = apcu_sma_info();
652 foreach ( $raw as $key => $status ) {
653 if ( ! is_array( $status ) ) {
654 $result[ 'status_' . $key ] = [
655 'label' => '[Status] ' . $key,
656 'value' => $status,
657 ];
658 }
659 }
660 $raw = apcu_cache_info();
661 foreach ( $raw as $key => $status ) {
662 if ( ! is_array( $status ) ) {
663 $result[ 'status_' . $key ] = [
664 'label' => '[Status] ' . $key,
665 'value' => $status,
666 ];
667 }
668 }
669 }
670 } else {
671 $result['product'] = [
672 'label' => 'Product',
673 'value' => 'Database transients',
674 ];
675 }
676 return $result;
677 }
678
679 }
680
681 \Decalog\System\Cache::init();