PluginProbe
SQLite Object Cache / trunk
SQLite Object Cache vtrunk
1.6.5 trunk 0.1.7 1.0.0 1.1.0 1.1.1 1.2.0 1.2.1 1.2.2 1.2.3 1.3.0 1.3.1 1.3.2 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.4.0 1.4.1 1.5.1 1.5.4 1.5.5 1.5.6 1.5.7 All 30 releases
sqlite-object-cache / includes / class-sqlite-object-cache.php

class-sqlite-object-cache.php in SQLite Object Cache trunk, at includes/class-sqlite-object-cache.php

738 lines 23.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Main plugin class file.
4 *
5 * @package WordPress Plugin Template/Includes
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12 /**
13 * Main plugin class.
14 *
15 * Only make one instance of this, please.
16 */
17 class SQLite_Object_Cache {
18 const CLEAN_EVENT_HOOK = 'sqlite_object_cache_clean';
19
20 /**
21 * Settings class object
22 *
23 * @var object
24 * @access public
25 * @since 1.0.0
26 */
27 public $settings;
28
29 /**
30 * The version number.
31 *
32 * @var string
33 * @access public
34 * @since 1.0.0
35 */
36 public $_version;
37
38 /**
39 * The token.
40 *
41 * @var string
42 * @access public
43 * @since 1.0.0
44 */
45 public $_token;
46
47 /**
48 * The main plugin file.
49 *
50 * @var string
51 * @access public
52 * @since 1.0.0
53 */
54 public $file;
55
56 /**
57 * The main plugin directory.
58 *
59 * @var string
60 * @access public
61 * @since 1.0.0
62 */
63 public $dir;
64
65 /**
66 * The plugin assets directory.
67 *
68 * @var string
69 * @access public
70 * @since 1.0.0
71 */
72 public $assets_dir;
73
74 /**
75 * The plugin assets URL.
76 *
77 * @var string
78 * @access public
79 * @since 1.0.0
80 */
81 public $assets_url;
82
83 /**
84 * Suffix for JavaScripts.
85 *
86 * @var string
87 * @access public
88 * @since 1.0.0
89 */
90 public $script_suffix;
91
92 /**
93 * Path for the drop-in when it is working.
94 * @var string
95 */
96 public $dropinfiledest;
97
98 /**
99 * Path for the drop-in in the plugin tree.
100 * @var string
101 */
102 public $dropinfilesource;
103
104 /**
105 * Minimum required sqlite version.
106 *
107 * @var string
108 */
109 public $minimum_sqlite_version = '3.7.0';
110
111 /**
112 * Constructor function.
113 *
114 * @param string $file File constructor.
115 * @param string $version Plugin version.
116 */
117 public function __construct( $file = '', $version = '1.6.5' ) {
118 $this->_version = $version;
119 $this->_token = 'sqlite_object_cache';
120
121 // Load plugin environment variables.
122 $this->file = $file;
123 $this->dir =
124 trailingslashit( dirname( trailingslashit( WP_PLUGIN_DIR ) . plugin_basename( $this->file ) ) );
125 $this->assets_dir = trailingslashit( $this->dir . 'assets' );
126 $this->dropinfilesource = $this->assets_dir . 'drop-in/object-cache.php';
127 $this->dropinfiledest = trailingslashit( WP_CONTENT_DIR ) . 'object-cache.php';
128
129 $this->assets_url = esc_url( trailingslashit( plugins_url( '/assets/', $this->file ) ) );
130
131 register_activation_hook( $this->file, array( $this, 'on_activation' ) );
132 register_deactivation_hook( $this->file, array( $this, 'on_deactivation' ) );
133
134 if ( is_admin() ) {
135 /* Suppress backups of .sqlite files if possible. */
136 new SQLite_Backup_Exclusion();
137
138 /* Health check */
139 new SQLite_Object_Cache_Opcache();
140
141 add_action( 'admin_notices', function () {
142 $flushed = get_site_transient( 'sqlite-object-cache-flush-on-update' );
143 if ( $flushed ) {
144 delete_site_transient( 'sqlite-object-cache-flush-on-update' );
145 $message = __( 'Flushed the SQLite Object Cache successfully after a software installation or upgrade.', 'sqlite-object-cache' );
146 ?>
147 <div class="notice notice-success sqlite-object-cache is-dismissible">
148 <p><?php echo esc_html( $message ); ?></p>
149 </div>
150 <?php
151 }
152 } );
153 }
154
155 add_action( 'admin_init', array( $this, 'maybe_update_dropin' ) );
156
157 /* Ensure nothing is cached after software upgrades. */
158 add_action( 'upgrader_process_complete', function () {
159 add_action( 'shutdown', function () {
160 wp_cache_flush();
161 set_site_transient( 'sqlite-object-cache-flush-on-update', true, HOUR_IN_SECONDS );
162 }, 999 );
163 wp_cache_flush();
164 }, 0 );
165
166
167 /* Handle cron cache cleanup, but only on main site. */
168 add_action( self::CLEAN_EVENT_HOOK, array( $this, 'clean_job' ), 10, 0 );
169 $next_event = wp_next_scheduled( self::CLEAN_EVENT_HOOK );
170 if ( false === $next_event && is_main_site() ) {
171 wp_schedule_event( time() + HOUR_IN_SECONDS, 'hourly', self::CLEAN_EVENT_HOOK );
172 } else if (false !== $next_event && is_multisite() && ! is_main_site() ) {
173 wp_unschedule_event ( $next_event, self::CLEAN_EVENT_HOOK);
174 }
175 /* Handle probabilistic non-cron cleanup, one request in 2500. */
176 /* We don't need cryptographic-grade random numbering here. */
177 // phpcs:ignore WordPress.WP.AlternativeFunctions.rand_rand
178 if ( 1 === rand( 1, 2500 ) ) {
179 add_action( 'shutdown', function () {
180 $this->clean_job( 1.25 );
181 }, 999, 0 );
182 }
183
184 /* Admin bar flush button - works on both frontend and backend. */
185 $option = get_option( $this->_token . '_settings', array() );
186 if ( array_key_exists ('adminbarflush', $option ) && 'on' === $option['adminbarflush'] ) {
187 add_action( 'init', array( $this, 'handle_admin_bar_flush' ) );
188 add_action ( 'init', function() {
189 if ( ! ( is_multisite() && ! is_main_site() ) && current_user_can( 'manage_options') ) {
190 add_action( 'admin_bar_menu', array( $this, 'admin_bar_flush_button' ), 100 );
191 add_action( 'admin_notices', array( $this, 'maybe_show_flush_notice' ) );
192 }
193 });
194 }
195 }
196
197 /**
198 * WP_Cron task to clean cache entries.
199 *
200 * @param float $grace_factor Default 1.0
201 *
202 * @return void
203 */
204 public function clean_job( $grace_factor = 1.0 ) {
205 $current_blogid = null;
206 if ( is_multisite() && ! is_main_site() ) {
207 $current_blogid = get_current_blog_id();
208 switch_to_blog( get_main_site_id() );
209 }
210 $option = get_option( $this->_token . '_settings', array() );
211 $target_size = empty ( $option['target_size'] ) ? 16 : $option['target_size'];
212 $target_size *= ( 1024 * 1024 );
213 $threshold_size = (int) ( $target_size * $grace_factor );
214
215 global $wp_object_cache;
216 if ( ! method_exists( $wp_object_cache, 'sqlite_get_size' ) ) {
217 return;
218 }
219 /* Clean up old statistics. Do this even when the cache is not over size. */
220 $retention = empty ( $option['retainmeasurements'] ) ? 24 : $option['retainmeasurements'];
221 $wp_object_cache->sqlite_reset_statistics( $retention * HOUR_IN_SECONDS );
222
223 $current_size = $wp_object_cache->sqlite_get_size();
224 /* Skip this if the current size is small enough. */
225 if ( $current_size <= $threshold_size ) {
226 return;
227 }
228
229 /* Remove expired items (transients mostly). */
230 if ( $wp_object_cache->sqlite_remove_expired() ) {
231 /* If anything was removed, get the size again. */
232 $current_size = $wp_object_cache->sqlite_get_size();
233 }
234
235 /* Skip this if the size is small enough, after removing expired items. */
236 if ( $current_size <= $threshold_size ) {
237 return;
238 }
239
240 /* Delete the least-recently-updated items to get to the target size. */
241 $wp_object_cache->sqlite_delete_old( $target_size, $current_size );
242
243 if ( null !== $current_blogid ) {
244 switch_to_blog ( $current_blogid );
245 }
246 }
247
248 /**
249 * Plugin activation hook
250 *
251 * @return void
252 */
253 public function on_activation() {
254
255 $this->sync_apcu_global_to_option( true );
256 if ( true === $this->has_sqlite() ) {
257 add_action( 'shutdown', array( $this, 'update_dropin' ) );
258 add_action( 'shutdown', array( $this, 'delete_all_transients_from_db' ) );
259 }
260 }
261
262 /**
263 * Determine whether we can use SQLite3.
264 *
265 * @return bool|string true, or an error message.
266 */
267 public function has_sqlite() {
268 if ( ! class_exists( 'SQLite3' ) || ! extension_loaded( 'sqlite3' ) ) {
269 return __( 'You cannot use the SQLite Object Cache plugin. Your server does not have php\'s SQLite3 extension installed.', 'sqlite-object-cache' );
270 }
271
272 $sqlite_version = $this->sqlite_get_version();
273 if ( version_compare( $sqlite_version, $this->minimum_sqlite_version ) < 0 ) {
274 return sprintf(
275 /* translators: 1 actual SQLite version. 2 required SQLite version) */
276 __( 'You cannot use the SQLite Object Cache plugin. Your server only offers SQLite3 version %1$s, but at least %2$s is required.', 'sqlite-object-cache' ),
277 $sqlite_version, $this->minimum_sqlite_version );
278 }
279
280 return true;
281 }
282
283 /**
284 * Get the version number for the SQLite extension.
285 *
286 * @return string|false SQLite's version number.
287 */
288 public function sqlite_get_version() {
289
290 if ( class_exists( 'SQLite3' ) ) {
291 $version = SQLite3::version();
292
293 return $version['versionString'];
294 }
295
296 return false;
297 }
298
299 /**
300 * Test if we can write in the WP_CONTENT_DIR and modify the `object-cache.php` drop-in
301 *
302 * @return true|WP_Error
303 * @author Till Krüss
304 *
305 */
306 public function test_filesystem_writing() {
307 global $wp_filesystem;
308
309 if ( ! $this->initialize_filesystem( '', true ) ) {
310 return new WP_Error( 'fs', __( 'Could not initialize filesystem.', 'sqlite-object-cache' ) );
311 }
312
313 $testfiledest = WP_CONTENT_DIR . '/sqlite-write-test.tmp';
314
315 if ( ! $wp_filesystem->exists( $this->dropinfilesource ) ) {
316 return new WP_Error( 'exists', __( 'Object cache drop-in file doesn’t exist.', 'sqlite-object-cache' ) );
317 }
318
319 if ( $wp_filesystem->exists( $testfiledest ) && ! $wp_filesystem->delete( $testfiledest ) ) {
320 return new WP_Error( 'delete', __( 'Test file exists, but couldn’t be deleted.', 'sqlite-object-cache' ) );
321 }
322
323 if ( ! $wp_filesystem->is_writable( WP_CONTENT_DIR ) ) {
324 return new WP_Error( 'copy', __( 'Content directory is not writable.', 'sqlite-object-cache' ) );
325 }
326
327 if ( ! $wp_filesystem->copy( $this->dropinfilesource, $testfiledest, true, FS_CHMOD_FILE ) ) {
328 return new WP_Error( 'copy', __( 'Failed to copy test file.', 'sqlite-object-cache' ) );
329 }
330
331 if ( ! $wp_filesystem->exists( $testfiledest ) ) {
332 return new WP_Error( 'exists', __( 'Copied test file doesn’t exist.', 'sqlite-object-cache' ) );
333 }
334
335 $meta = get_file_data( $testfiledest, array( 'Version' => 'Version' ) );
336
337 if ( $meta['Version'] !== $this->_version ) {
338 return new WP_Error( 'version', __( 'Couldn’t verify test file contents.', 'sqlite-object-cache' ) );
339 }
340
341 if ( ! $wp_filesystem->delete( $testfiledest ) ) {
342 return new WP_Error( 'delete', __( 'Copied test file couldn’t be deleted.', 'sqlite-object-cache' ) );
343 }
344
345 return true;
346 }
347
348 /**
349 * Initializes the WP filesystem API to be ready for use
350 *
351 * @param string $url The URL to post the form to.
352 * @param bool $silent Whether to ask the user for credentials if necessary or not.
353 *
354 * @return bool
355 * @author Till Krüss
356 *
357 */
358 public function initialize_filesystem( $url, $silent = false ) {
359 require_once ABSPATH . 'wp-admin/includes/file.php';
360 if ( $silent ) {
361 ob_start();
362 }
363
364 $credentials = request_filesystem_credentials( $url );
365
366 if ( false === $credentials ) {
367 if ( $silent ) {
368 ob_end_clean();
369 }
370
371 return false;
372 }
373
374 if ( ! WP_Filesystem( $credentials ) ) {
375 request_filesystem_credentials( $url );
376
377 if ( $silent ) {
378 ob_end_clean();
379 }
380
381 return false;
382 }
383
384 return true;
385 }
386
387 /**
388 * Calls the drop-in update method if necessary
389 *
390 * @return void
391 * @author Till Krüss
392 */
393 public function maybe_update_dropin() {
394 if ( defined( 'WP_SQLITE_OBJECT_CACHE_DISABLE_DROPIN_AUTOUPDATE' ) && WP_SQLITE_OBJECT_CACHE_DISABLE_DROPIN_AUTOUPDATE ) {
395 return;
396 }
397
398 $has = $this->has_sqlite();
399 if ( ( true === $has ) && $this->object_cache_dropin_needs_updating() ) {
400 add_action( 'shutdown', array( $this, 'update_dropin' ) );
401 }
402 }
403
404 /**
405 * Checks if the `object-cache.php` drop-in is outdated
406 * @return bool
407 * @author Till Krüss
408 *
409 */
410 public function object_cache_dropin_needs_updating() {
411 global $wp_object_cache;
412 if ( ! method_exists( $wp_object_cache, 'dropin_get_version' ) ) {
413 return true;
414 }
415 return version_compare( $wp_object_cache->dropin_get_version(), $this->_version, '<' );
416 }
417
418 /**
419 * Updates the `object-cache.php` drop-in
420 *
421 * @return void
422 * @author Till Krüss
423 */
424 public function update_dropin() {
425 global $wp_filesystem;
426 $has = $this->has_sqlite();
427 if ( true === $has && $this->initialize_filesystem( '', true ) ) {
428
429 $this->delete_sqlite_files();
430 $result = $wp_filesystem->copy( $this->dropinfilesource, $this->dropinfiledest, true, FS_CHMOD_FILE );
431 /**
432 * Fires on cache enable event
433 *
434 * @param bool $result Whether the filesystem event (copy of the `object-cache.php` file) was successful.
435 *
436 * @since 0.1.0
437 */
438 do_action( 'sqlite_object_cache_update_dropin', $result );
439 }
440 }
441
442 /**
443 * Validates the `object-cache.php` drop-in
444 *
445 * @return bool
446 * @author Till Krüss
447 */
448 public function validate_object_cache_dropin() {
449 global $wp_object_cache;
450
451 if ( ! method_exists( $wp_object_cache, 'dropin_get_version' ) ) {
452 return false;
453 }
454
455 $dropin = get_plugin_data( $this->dropinfiledest );
456 $plugin = get_plugin_data( $this->dropinfilesource );
457
458 /**
459 * Filters the drop-in validation state
460 *
461 * @param bool $state The validation state of the drop-in.
462 * @param string $dropin The `PluginURI` of the drop-in.
463 * @param string $plugin The `PluginURI` of the plugin.
464 *
465 * @since 2.0.16
466 */
467 return apply_filters(
468 'sqlite_object_cache_validate_dropin',
469 $dropin['PluginURI'] === $plugin['PluginURI'],
470 $dropin['PluginURI'],
471 $plugin['PluginURI']
472 );
473 }
474
475 /**
476 * Plugin deactivation hook
477 *
478 * @param string $plugin Plugin basename.
479 *
480 * @return void
481 * @author Till Krüss
482 */
483 public function on_deactivation( $plugin ) {
484
485 wp_cache_flush();
486
487 wp_unschedule_hook( self::CLEAN_EVENT_HOOK );
488 $this->delete_sqlite_files();
489 $this->delete_dropin();
490 $this->delete_all_transients_from_db();
491 }
492
493 private function delete_sqlite_files() {
494 global $wp_filesystem;
495 global $wp_object_cache;
496
497 if ( method_exists( $wp_object_cache, 'sqlite_files' ) ) {
498 if ( $this->initialize_filesystem( '', true ) ) {
499 foreach ( $wp_object_cache->sqlite_files() as $file ) {
500 $wp_filesystem->delete( $file );
501 }
502 }
503 }
504 }
505
506 private function delete_dropin() {
507 global $wp_filesystem;
508 ob_start();
509
510 if ( $this->validate_object_cache_dropin() && $this->initialize_filesystem( '', true ) ) {
511 $wp_filesystem->delete( $this->dropinfiledest );
512 }
513
514 ob_end_clean();
515 }
516
517 /**
518 * Deletes all transients from the database.
519 *
520 * It is necessary to do this when deactivating a persistent object cache
521 * because the transients are kept there, and the ones in the database are
522 * possibly stale.
523 *
524 * This hammers performance. But deactivating a persistent object cache is
525 * not a common operation.
526 *
527 * @global wpdb $wpdb WordPress database abstraction object.
528 */
529 public function delete_all_transients_from_db() {
530 global $wpdb;
531 if ( ! is_multisite() ) {
532 $this->clear_blog_transients();
533 // Single site stores site transients in the options table.
534 $this->clear_blog_transients( '_site_transient_' );
535 } else {
536 foreach ( get_sites( array( 'number' => 0, 'fields' => 'ids', 'no_found_rows' => true, 'orderby' => false ) ) as $site_id ) {
537 try {
538 switch_to_blog( $site_id );
539 $this->clear_blog_transients();
540 } catch ( Exception $ex ) {
541 /* Empty, intentionally. Don't crash clearing transients. */
542 } finally {
543 restore_current_blog();
544 }
545 }
546 // Multisite stores site transients in the sitemeta table.
547 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
548 $wpdb->query(
549 $wpdb->prepare(
550 "DELETE FROM {$wpdb->sitemeta} WHERE a.meta_key LIKE %s",
551 $wpdb->esc_like( '_site_transient_' ) . '%'
552 )
553 );
554
555 }
556 }
557
558 /**
559 * DELETE transients from the current blog's options table.
560 *
561 * @param string $tag _site_transient_ or _transient_, the default.
562 *
563 * @return void
564 */
565 private function clear_blog_transients( $tag = '_transient_') {
566 global $wpdb;
567 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
568 $wpdb->query(
569 $wpdb->prepare(
570 "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
571 $wpdb->esc_like( $tag ) . '%'
572 )
573 );
574 }
575
576 /**
577 * @return bool True if APCu support is activated for this plugin.
578 */
579 public function apcu_is_activated() {
580 return defined( 'WP_SQLITE_OBJECT_CACHE_APCU' ) && WP_SQLITE_OBJECT_CACHE_APCU
581 && $this->apcu_extension_is_enabled();
582 }
583
584 /**
585 * @return bool True if the APCu extension is loaded and enabled.
586 */
587 public function apcu_extension_is_enabled() {
588 return function_exists( 'apcu_enabled' ) && apcu_enabled();
589 }
590
591 /**
592 * Make sure WP_SQLITE_OBJECT_CACHE_APCU and $option['use_apcu'] match.
593 *
594 * @param bool $unconditional true if we should always change the option to match WP_SQLITE_OBJECT_CACHE_APCU.
595 *
596 * @return string 'on' or 'off': the current activation state of epcu.
597 */
598 public function sync_apcu_global_to_option( $unconditional = true ) {
599 global $wp_object_cache;
600 $option = get_option( $this->_token . '_settings', array() );
601 $option_dirty = false;
602 $updated_flag = array_key_exists( 'use_apcu_updated', $option );
603 $use_apcu = array_key_exists( 'use_apcu', $option ) && 'on' === $option['use_apcu'] ? 'on' : 'off';
604 if ( $updated_flag ) {
605 unset ( $option['use_apcu_updated'] );
606 $option_dirty = true;
607 }
608 $target_use_apcu = $this->apcu_is_activated() ? 'on' : 'off';
609 if ( ! $unconditional && $updated_flag ) {
610 $target_use_apcu = $use_apcu;
611 }
612 if ( $target_use_apcu !== $use_apcu ) {
613 $option['use_apcu'] = $target_use_apcu;
614 $option_dirty = true;
615 }
616 if ( $option_dirty ) {
617 if ( method_exists( $wp_object_cache, 'apcu-clear_cache' ) ) {
618 $wp_object_cache->apcu_clear_cache();
619 }
620 update_option( $this->_token . '_settings', $option, true );
621 }
622
623 return $target_use_apcu;
624 }
625
626 /**
627 * Add a flush button to the admin bar.
628 *
629 * @param WP_Admin_Bar $wp_admin_bar The admin bar instance.
630 *
631 * @return void
632 */
633 public function admin_bar_flush_button( $wp_admin_bar ) {
634 if ( ! is_admin_bar_showing() ) {
635 return;
636 }
637
638 if ( ! current_user_can( 'manage_options' ) ) {
639 return;
640 }
641
642 // Only show on main site for multisite installations.
643 if ( is_multisite() && ! is_main_site() ) {
644 return;
645 }
646
647 $flush_url = add_query_arg(
648 array( 'sqlite_object_cache_action' => 'flush' ),
649 remove_query_arg( 'sqlite_object_cache_action' )
650 );
651 $nonced_url = wp_nonce_url( $flush_url, 'sqlite_object_cache_flush' );
652
653 $wp_admin_bar->add_node(
654 array(
655 'id' => 'sqlite-object-cache-flush',
656 'title' => __( 'Flush Object Cache', 'sqlite-object-cache' ),
657 'href' => $nonced_url,
658 'meta' => array(
659 'title' => __( 'Delete all object cache entries now, including all transients.', 'sqlite-object-cache' ),
660 ),
661 )
662 );
663 }
664
665 /**
666 * Handle the admin bar flush action.
667 *
668 * @return void
669 */
670 public function handle_admin_bar_flush() {
671 if ( ! isset( $_GET['sqlite_object_cache_action'] ) ) {
672 return;
673 }
674
675 $action = sanitize_key( wp_unslash( $_GET['sqlite_object_cache_action'] ) );
676
677 if ( 'flush' !== $action ) {
678 return;
679 }
680
681 if ( ! current_user_can( 'manage_options' ) ) {
682 wp_die( esc_html__( 'You do not have permission to flush the object cache.', 'sqlite-object-cache' ) );
683 }
684
685 // Only allow flush on main site for multisite installations (matches button visibility).
686 if ( is_multisite() && ! is_main_site() ) {
687 wp_die( esc_html__( 'Cache flush is only available on the main site.', 'sqlite-object-cache' ) );
688 }
689
690 check_admin_referer( 'sqlite_object_cache_flush' );
691
692 wp_cache_flush();
693
694 // Use a user-specific transient so only the initiating user sees the flush notice (applies to both single-site and multisite).
695 $transient_key = 'sqlite_object_cache_flushed_' . get_current_user_id();
696 set_transient( $transient_key, true, 30 );
697
698 $referer = wp_get_referer();
699 if ( $referer ) {
700 $redirect_url = $referer;
701 } else {
702 $redirect_url = is_admin() ? admin_url() : home_url();
703 }
704 // Remove action and nonce query parameters from URL.
705 $redirect_url = remove_query_arg( array( 'sqlite_object_cache_action', '_wpnonce' ), $redirect_url );
706
707 wp_safe_redirect( $redirect_url );
708 exit;
709 }
710
711 /**
712 * Check for flush transient and show notice if set.
713 *
714 * @return void
715 */
716 public function maybe_show_flush_notice() {
717 $transient_key = 'sqlite_object_cache_flushed_' . get_current_user_id();
718 if ( get_transient( $transient_key ) ) {
719 delete_transient( $transient_key );
720 $this->show_flush_notice();
721 }
722 }
723
724 /**
725 * Display a notice when the object cache was flushed via admin bar.
726 *
727 * @return void
728 */
729 public function show_flush_notice() {
730 ?>
731 <div class="notice notice-success sqlite-object-cache is-dismissible">
732 <p><?php esc_html_e( 'SQLite Object Cache flushed successfully.', 'sqlite-object-cache' ); ?></p>
733 </div>
734 <?php
735 }
736
737 }
738