PluginProbe
SQLite Object Cache / 1.6.0
SQLite Object Cache v1.6.0
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
← All changes | includes/class-sqlite-object-cache.php +510 -442 1.3.71.6.0 View file →
@@ -5,9 +5,9 @@
5 5 * @package WordPress Plugin Template/Includes
6 6 */
7 7
8 8 if ( ! defined( 'ABSPATH' ) ) {
9 - exit;
9 + exit;
10 10 }
11 11
12 12 /**
13 13 * Main plugin class.
@@ -14,525 +14,593 @@
14 14 *
15 15 * Only make one instance of this, please.
16 16 */
17 17 class SQLite_Object_Cache {
18 - const CLEAN_EVENT_HOOK = 'sqlite_object_cache_clean';
18 + const CLEAN_EVENT_HOOK = 'sqlite_object_cache_clean';
19 19
20 - /**
21 - * Local instance of SQLite_Object_Cache_Admin_API
22 - *
23 - * @var SQLite_Object_Cache_Admin_API|null
24 - */
25 - public $admin;
20 + /**
21 + * Local instance of SQLite_Object_Cache_Admin_API
22 + *
23 + * @var SQLite_Object_Cache_Admin_API|null
24 + */
25 + public $admin;
26 26
27 - /**
28 - * Settings class object
29 - *
30 - * @var object
31 - * @access public
32 - * @since 1.0.0
33 - */
34 - public $settings;
27 + /**
28 + * Settings class object
29 + *
30 + * @var object
31 + * @access public
32 + * @since 1.0.0
33 + */
34 + public $settings;
35 35
36 - /**
37 - * The version number.
38 - *
39 - * @var string
40 - * @access public
41 - * @since 1.0.0
42 - */
43 - public $_version;
36 + /**
37 + * The version number.
38 + *
39 + * @var string
40 + * @access public
41 + * @since 1.0.0
42 + */
43 + public $_version;
44 44
45 - /**
46 - * The token.
47 - *
48 - * @var string
49 - * @access public
50 - * @since 1.0.0
51 - */
52 - public $_token;
45 + /**
46 + * The token.
47 + *
48 + * @var string
49 + * @access public
50 + * @since 1.0.0
51 + */
52 + public $_token;
53 53
54 - /**
55 - * The main plugin file.
56 - *
57 - * @var string
58 - * @access public
59 - * @since 1.0.0
60 - */
61 - public $file;
54 + /**
55 + * The main plugin file.
56 + *
57 + * @var string
58 + * @access public
59 + * @since 1.0.0
60 + */
61 + public $file;
62 62
63 - /**
64 - * The main plugin directory.
65 - *
66 - * @var string
67 - * @access public
68 - * @since 1.0.0
69 - */
70 - public $dir;
63 + /**
64 + * The main plugin directory.
65 + *
66 + * @var string
67 + * @access public
68 + * @since 1.0.0
69 + */
70 + public $dir;
71 71
72 - /**
73 - * The plugin assets directory.
74 - *
75 - * @var string
76 - * @access public
77 - * @since 1.0.0
78 - */
79 - public $assets_dir;
72 + /**
73 + * The plugin assets directory.
74 + *
75 + * @var string
76 + * @access public
77 + * @since 1.0.0
78 + */
79 + public $assets_dir;
80 80
81 - /**
82 - * The plugin assets URL.
83 - *
84 - * @var string
85 - * @access public
86 - * @since 1.0.0
87 - */
88 - public $assets_url;
81 + /**
82 + * The plugin assets URL.
83 + *
84 + * @var string
85 + * @access public
86 + * @since 1.0.0
87 + */
88 + public $assets_url;
89 89
90 - /**
91 - * Suffix for JavaScripts.
92 - *
93 - * @var string
94 - * @access public
95 - * @since 1.0.0
96 - */
97 - public $script_suffix;
90 + /**
91 + * Suffix for JavaScripts.
92 + *
93 + * @var string
94 + * @access public
95 + * @since 1.0.0
96 + */
97 + public $script_suffix;
98 98
99 - /**
100 - * Path for the drop-in when it is working.
101 - * @var string
102 - */
103 - public $dropinfiledest;
99 + /**
100 + * Path for the drop-in when it is working.
101 + * @var string
102 + */
103 + public $dropinfiledest;
104 104
105 - /**
106 - * Path for the drop-in in the plugin tree.
107 - * @var string
108 - */
109 - public $dropinfilesource;
105 + /**
106 + * Path for the drop-in in the plugin tree.
107 + * @var string
108 + */
109 + public $dropinfilesource;
110 110
111 - /**
112 - * Minimum required sqlite version.
113 - *
114 - * @var string
115 - */
116 - public $minimum_sqlite_version = '3.7.0';
111 + /**
112 + * Minimum required sqlite version.
113 + *
114 + * @var string
115 + */
116 + public $minimum_sqlite_version = '3.7.0';
117 117
118 - /**
119 - * Constructor funtion.
120 - *
121 - * @param string $file File constructor.
122 - * @param string $version Plugin version.
123 - */
124 - public function __construct( $file = '', $version = '1.3.7' ) {
125 - $this->_version = $version;
126 - $this->_token = 'sqlite_object_cache';
118 + /**
119 + * Constructor function.
120 + *
121 + * @param string $file File constructor.
122 + * @param string $version Plugin version.
123 + */
124 + public function __construct( $file = '', $version = '1.6.0' ) {
125 + $this->_version = $version;
126 + $this->_token = 'sqlite_object_cache';
127 127
128 - // Load plugin environment variables.
129 - $this->file = $file;
130 - $this->dir =
131 - trailingslashit( dirname( WP_CONTENT_DIR . '/plugins/' . plugin_basename( $this->file ) ) );
132 - $this->assets_dir = trailingslashit( $this->dir . 'assets' );
133 - $this->dropinfilesource = $this->assets_dir . 'drop-in/object-cache.php';
134 - $this->dropinfiledest = trailingslashit( WP_CONTENT_DIR ) . 'object-cache.php';
128 + // Load plugin environment variables.
129 + $this->file = $file;
130 + $this->dir =
131 + trailingslashit( dirname( trailingslashit( WP_PLUGIN_DIR ) . plugin_basename( $this->file ) ) );
132 + $this->assets_dir = trailingslashit( $this->dir . 'assets' );
133 + $this->dropinfilesource = $this->assets_dir . 'drop-in/object-cache.php';
134 + $this->dropinfiledest = trailingslashit( WP_CONTENT_DIR ) . 'object-cache.php';
135 135
136 - $this->assets_url = esc_url( trailingslashit( plugins_url( '/assets/', $this->file ) ) );
136 + $this->assets_url = esc_url( trailingslashit( plugins_url( '/assets/', $this->file ) ) );
137 137
138 - $this->script_suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
138 + $this->script_suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
139 139
140 - register_activation_hook( $this->file, array( $this, 'on_activation' ) );
141 - register_deactivation_hook( $this->file, array( $this, 'on_deactivation' ) );
140 + register_activation_hook( $this->file, array( $this, 'on_activation' ) );
141 + register_deactivation_hook( $this->file, array( $this, 'on_deactivation' ) );
142 142
143 - if ( is_admin() ) {
144 - // Load API for generic admin functions.
145 - $this->admin = new SQLite_Object_Cache_Admin_API();
146 - // Suppress backups
147 - new SQLite_Backup_Exclusion();
148 - }
143 + if ( is_admin() ) {
144 + // Load API for generic admin functions.
145 + $this->admin = new SQLite_Object_Cache_Admin_API();
146 + // Suppress backups
147 + new SQLite_Backup_Exclusion();
149 148
150 - // Handle localization.
151 - $this->load_plugin_textdomain();
152 - add_action( 'admin-init', array( $this, 'load_localization' ), 0 );
153 - add_action( 'admin_init', array( $this, 'maybe_update_dropin' ) );
149 + add_action( 'admin_notices', function () {
150 + $purged = get_site_transient( 'sqlite-object-cache-flush-on-update' );
151 + if ( $purged ) {
152 + delete_site_transient( 'sqlite-object-cache-flush-on-update' );
153 + $message = esc_html( __( 'A software installation or upgrade operation flushed the SQLite Object Cache.', 'sqlite-object-cache' ) )
154 + ?>
155 + <div class="notice notice-info sqlite-object-cache is-dismissible">
156 + <p><?php echo $message ?></p>
157 + </div>
158 + <?php
159 + }
160 + } );
161 + }
154 162
155 - /* handle cron cache cleanup */
156 - add_action( self::CLEAN_EVENT_HOOK, array( $this, 'clean_job' ), 10, 0 );
157 - if ( ! wp_next_scheduled( self::CLEAN_EVENT_HOOK ) ) {
158 - wp_schedule_event( time() + HOUR_IN_SECONDS, 'hourly', self::CLEAN_EVENT_HOOK );
159 - }
160 - /* Handle probabilistic non-cron cleanup, one request in 2000. */
161 - if ( 1 === rand( 1, 2000 ) ) {
162 - add_action( 'shutdown', function () {
163 - $this->clean_job( 1.25 );
164 - }, 999, 0 );
165 - }
166 - }
163 + add_action( 'admin_init', array( $this, 'maybe_update_dropin' ) );
167 164
168 - /**
169 - * Load plugin textdomain
170 - *
171 - * @access public
172 - * @return void
173 - * @since 1.0.0
174 - */
175 - public function load_plugin_textdomain() {
176 - $domain = 'sqlite-object-cache';
165 + /* Ensure nothing is cached after software upgrades. */
166 + add_action( 'upgrader_process_complete', function () {
167 + add_action( 'shutdown', function () {
168 + wp_cache_flush();
169 + set_site_transient( 'sqlite-object-cache-flush-on-update', true, HOUR_IN_SECONDS );
170 + }, 999 );
171 + wp_cache_flush();
172 + }, 0 );
177 173
178 - $locale = apply_filters( 'plugin_locale', get_locale(), $domain );
179 174
180 - load_textdomain( $domain, WP_LANG_DIR . '/' . $domain . '/' . $domain . '-' . $locale . '.mo' );
181 - load_plugin_textdomain( $domain, false, dirname( plugin_basename( $this->file ) ) . '/languages/' );
182 - }
175 + /* handle cron cache cleanup */
176 + add_action( self::CLEAN_EVENT_HOOK, array( $this, 'clean_job' ), 10, 0 );
177 + if ( ! wp_next_scheduled( self::CLEAN_EVENT_HOOK ) ) {
178 + wp_schedule_event( time() + HOUR_IN_SECONDS, 'hourly', self::CLEAN_EVENT_HOOK );
179 + }
180 + /* Handle probabilistic non-cron cleanup, one request in 2000. */
181 + if ( 1 === rand( 1, 2000 ) ) {
182 + add_action( 'shutdown', function () {
183 + $this->clean_job( 1.25 );
184 + }, 999, 0 );
185 + }
186 + }
183 187
184 - /**
185 - * WP_Cron task to clean cache entries.
186 - *
187 - * @param float $grace_factor Default 1.0
188 - *
189 - * @return void
190 - */
191 - public function clean_job( $grace_factor = 1.0 ) {
192 - $option = get_option( $this->_token . '_settings', array() );
193 - $target_size = empty ( $option['target_size'] ) ? 16 : $option['target_size'];
194 - $target_size *= ( 1024 * 1024 );
195 - $threshold_size = (int) ( $target_size * $grace_factor );
188 + /**
189 + * WP_Cron task to clean cache entries.
190 + *
191 + * @param float $grace_factor Default 1.0
192 + *
193 + * @return void
194 + */
195 + public function clean_job( $grace_factor = 1.0 ) {
196 + $option = get_option( $this->_token . '_settings', array() );
197 + $target_size = empty ( $option['target_size'] ) ? 16 : $option['target_size'];
198 + $target_size *= ( 1024 * 1024 );
199 + $threshold_size = (int) ( $target_size * $grace_factor );
196 200
197 - global $wp_object_cache;
198 - if ( ! method_exists( $wp_object_cache, 'sqlite_get_size' ) ) {
199 - return;
200 - }
201 - $current_size = $wp_object_cache->sqlite_get_size();
202 - /* Skip this if the current size is small enough. */
203 - if ( $current_size <= $threshold_size ) {
204 - return;
205 - }
201 + global $wp_object_cache;
202 + if ( ! method_exists( $wp_object_cache, 'sqlite_get_size' ) ) {
203 + return;
204 + }
205 + /* Clean up old statistics. Do this even when the cache is not over size. */
206 + $retention = empty ( $option['retainmeasurements'] ) ? 24 : $option['retainmeasurements'];
207 + $wp_object_cache->sqlite_reset_statistics( $retention * HOUR_IN_SECONDS );
206 208
207 - /* Remove expired items (transients mostly). */
208 - if ( $wp_object_cache->sqlite_remove_expired( ) ) {
209 - /* If anything was removed, get the size again. */
210 - $current_size = $wp_object_cache->sqlite_get_size();
211 - }
209 + $current_size = $wp_object_cache->sqlite_get_size();
210 + /* Skip this if the current size is small enough. */
211 + if ( $current_size <= $threshold_size ) {
212 + return;
213 + }
212 214
213 - /* Skip this if the size is small enough, after removing expired items. */
214 - if ( $current_size <= $threshold_size ) {
215 - return;
216 - }
215 + /* Remove expired items (transients mostly). */
216 + if ( $wp_object_cache->sqlite_remove_expired() ) {
217 + /* If anything was removed, get the size again. */
218 + $current_size = $wp_object_cache->sqlite_get_size();
219 + }
217 220
218 - /* Clean up old statistics. */
219 - $retention = empty ( $option['retention'] ) ? 24 : $option['retention'];
220 - $wp_object_cache->sqlite_reset_statistics( $retention * HOUR_IN_SECONDS );
221 + /* Skip this if the size is small enough, after removing expired items. */
222 + if ( $current_size <= $threshold_size ) {
223 + return;
224 + }
221 225
222 - /* Delete the least-recently-updated items to get to the target size. */
223 - $wp_object_cache->sqlite_delete_old( $target_size, $current_size );
224 - }
226 + /* Delete the least-recently-updated items to get to the target size. */
227 + $wp_object_cache->sqlite_delete_old( $target_size, $current_size );
228 + }
225 229
226 - /**
227 - * Plugin activation hook
228 - *
229 - * @return void
230 - */
231 - public
232 - function on_activation() {
233 - /* make sure the autoloaded option is set when activating; avoid an extra dbms or cache hit to fetch it */
234 - $option = get_option( $this->_token . '_settings', 'default' );
235 - if ( 'default' === $option ) {
236 - update_option( $this->_token . '_settings', array(), true );
237 - }
238 - if ( true === $this->has_sqlite() ) {
239 - add_action( 'shutdown', array( $this, 'update_dropin' ) );
240 - }
241 - }
230 + /**
231 + * Plugin activation hook
232 + *
233 + * @return void
234 + */
235 + public function on_activation() {
242 236
243 - /**
244 - * Determine whether we can use SQLite3.
245 - *
246 - * @return bool|string true, or an error message.
247 - */
248 - public
249 - function has_sqlite() {
250 - if ( ! class_exists( 'SQLite3' ) || ! extension_loaded( 'sqlite3' ) ) {
251 - return __( 'You cannot use the SQLite Object Cache plugin. Your server does not have php\'s SQLite3 extension installed.', 'sqlite-object-cache' );
252 - }
237 + $this->sync_apcu_global_to_option( true );
238 + if ( true === $this->has_sqlite() ) {
239 + add_action( 'shutdown', array( $this, 'update_dropin' ) );
240 + add_action( 'shutdown', array( $this, 'delete_all_transients_from_db' ) );
241 + }
242 + }
253 243
254 - $sqlite_version = $this->sqlite_get_version();
255 - if ( version_compare( $sqlite_version, $this->minimum_sqlite_version ) < 0 ) {
256 - return sprintf(
257 - /* translators: 1 actual SQLite version. 2 required SQLite version) */
258 - __( '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' ),
259 - $sqlite_version, $this->minimum_sqlite_version );
260 - }
244 + /**
245 + * Determine whether we can use SQLite3.
246 + *
247 + * @return bool|string true, or an error message.
248 + */
249 + public function has_sqlite() {
250 + if ( ! class_exists( 'SQLite3' ) || ! extension_loaded( 'sqlite3' ) ) {
251 + return __( 'You cannot use the SQLite Object Cache plugin. Your server does not have php\'s SQLite3 extension installed.', 'sqlite-object-cache' );
252 + }
261 253
262 - return true;
263 - }
254 + $sqlite_version = $this->sqlite_get_version();
255 + if ( version_compare( $sqlite_version, $this->minimum_sqlite_version ) < 0 ) {
256 + return sprintf(
257 + /* translators: 1 actual SQLite version. 2 required SQLite version) */
258 + __( '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' ),
259 + $sqlite_version, $this->minimum_sqlite_version );
260 + }
264 261
265 - /**
266 - * Get the version number for the SQLite extension.
267 - *
268 - * @return string|false SQLite's version number.
269 - */
270 - public
271 - function sqlite_get_version() {
262 + return true;
263 + }
272 264
273 - if ( class_exists( 'SQLite3' ) ) {
274 - $version = SQLite3::version();
265 + /**
266 + * Get the version number for the SQLite extension.
267 + *
268 + * @return string|false SQLite's version number.
269 + */
270 + public function sqlite_get_version() {
275 271
276 - return $version['versionString'];
277 - }
272 + if ( class_exists( 'SQLite3' ) ) {
273 + $version = SQLite3::version();
278 274
279 - return false;
280 - }
275 + return $version['versionString'];
276 + }
281 277
282 - /**
283 - * Load plugin localization
284 - *
285 - * @access public
286 - * @return void
287 - * @since 1.0.0
288 - */
289 - public
290 - function load_localization() {
291 - load_plugin_textdomain( 'sqlite-object-cache', false, dirname( plugin_basename( $this->file ) ) . '/languages/' );
292 - }
278 + return false;
279 + }
293 280
294 - /**
295 - * Test if we can write in the WP_CONTENT_DIR and modify the `object-cache.php` drop-in
296 - *
297 - * @return true|WP_Error
298 - * @author Till Krüss
299 - *
300 - */
301 - public
302 - function test_filesystem_writing() {
303 - global $wp_filesystem;
281 + /**
282 + * Test if we can write in the WP_CONTENT_DIR and modify the `object-cache.php` drop-in
283 + *
284 + * @return true|WP_Error
285 + * @author Till Krüss
286 + *
287 + */
288 + public function test_filesystem_writing() {
289 + global $wp_filesystem;
304 290
305 - if ( ! $this->initialize_filesystem( '', true ) ) {
306 - return new WP_Error( 'fs', __( 'Could not initialize filesystem.', 'sqlite-object-cache' ) );
307 - }
291 + if ( ! $this->initialize_filesystem( '', true ) ) {
292 + return new WP_Error( 'fs', __( 'Could not initialize filesystem.', 'sqlite-object-cache' ) );
293 + }
308 294
309 - $testfiledest = WP_CONTENT_DIR . '/sqlite-write-test.tmp';
295 + $testfiledest = WP_CONTENT_DIR . '/sqlite-write-test.tmp';
310 296
311 - if ( ! $wp_filesystem->exists( $this->dropinfilesource ) ) {
312 - return new WP_Error( 'exists', __( 'Object cache drop-in file doesn’t exist.', 'sqlite-object-cache' ) );
313 - }
297 + if ( ! $wp_filesystem->exists( $this->dropinfilesource ) ) {
298 + return new WP_Error( 'exists', __( 'Object cache drop-in file doesn’t exist.', 'sqlite-object-cache' ) );
299 + }
314 300
315 - if ( $wp_filesystem->exists( $testfiledest ) && ! $wp_filesystem->delete( $testfiledest ) ) {
316 - return new WP_Error( 'delete', __( 'Test file exists, but couldn’t be deleted.', 'sqlite-object-cache' ) );
317 - }
301 + if ( $wp_filesystem->exists( $testfiledest ) && ! $wp_filesystem->delete( $testfiledest ) ) {
302 + return new WP_Error( 'delete', __( 'Test file exists, but couldn’t be deleted.', 'sqlite-object-cache' ) );
303 + }
318 304
319 - if ( ! $wp_filesystem->is_writable( WP_CONTENT_DIR ) ) {
320 - return new WP_Error( 'copy', __( 'Content directory is not writable.', 'sqlite-object-cache' ) );
321 - }
305 + if ( ! $wp_filesystem->is_writable( WP_CONTENT_DIR ) ) {
306 + return new WP_Error( 'copy', __( 'Content directory is not writable.', 'sqlite-object-cache' ) );
307 + }
322 308
323 - if ( ! $wp_filesystem->copy( $this->dropinfilesource, $testfiledest, true, FS_CHMOD_FILE ) ) {
324 - return new WP_Error( 'copy', __( 'Failed to copy test file.', 'sqlite-object-cache' ) );
325 - }
309 + if ( ! $wp_filesystem->copy( $this->dropinfilesource, $testfiledest, true, FS_CHMOD_FILE ) ) {
310 + return new WP_Error( 'copy', __( 'Failed to copy test file.', 'sqlite-object-cache' ) );
311 + }
326 312
327 - if ( ! $wp_filesystem->exists( $testfiledest ) ) {
328 - return new WP_Error( 'exists', __( 'Copied test file doesn’t exist.', 'sqlite-object-cache' ) );
329 - }
313 + if ( ! $wp_filesystem->exists( $testfiledest ) ) {
314 + return new WP_Error( 'exists', __( 'Copied test file doesn’t exist.', 'sqlite-object-cache' ) );
315 + }
330 316
331 - $meta = get_file_data( $testfiledest, array( 'Version' => 'Version' ) );
317 + $meta = get_file_data( $testfiledest, array( 'Version' => 'Version' ) );
332 318
333 - if ( $meta['Version'] !== $this->_version ) {
334 - return new WP_Error( 'version', __( 'Couldn’t verify test file contents.', 'sqlite-object-cache' ) );
335 - }
319 + if ( $meta['Version'] !== $this->_version ) {
320 + return new WP_Error( 'version', __( 'Couldn’t verify test file contents.', 'sqlite-object-cache' ) );
321 + }
336 322
337 - if ( ! $wp_filesystem->delete( $testfiledest ) ) {
338 - return new WP_Error( 'delete', __( 'Copied test file couldn’t be deleted.', 'sqlite-object-cache' ) );
339 - }
323 + if ( ! $wp_filesystem->delete( $testfiledest ) ) {
324 + return new WP_Error( 'delete', __( 'Copied test file couldn’t be deleted.', 'sqlite-object-cache' ) );
325 + }
340 326
341 - return true;
342 - }
327 + return true;
328 + }
343 329
344 - /**
345 - * Initializes the WP filesystem API to be ready for use
346 - *
347 - * @param string $url The URL to post the form to.
348 - * @param bool $silent Whether to ask the user for credentials if necessary or not.
349 - *
350 - * @return bool
351 - * @author Till Krüss
352 - *
353 - */
354 - public
355 - function initialize_filesystem(
356 - $url, $silent = false
357 - ) {
358 - require_once ABSPATH . 'wp-admin/includes/file.php';
359 - if ( $silent ) {
360 - ob_start();
361 - }
330 + /**
331 + * Initializes the WP filesystem API to be ready for use
332 + *
333 + * @param string $url The URL to post the form to.
334 + * @param bool $silent Whether to ask the user for credentials if necessary or not.
335 + *
336 + * @return bool
337 + * @author Till Krüss
338 + *
339 + */
340 + public function initialize_filesystem( $url, $silent = false ) {
341 + require_once ABSPATH . 'wp-admin/includes/file.php';
342 + if ( $silent ) {
343 + ob_start();
344 + }
362 345
363 - $credentials = request_filesystem_credentials( $url );
346 + $credentials = request_filesystem_credentials( $url );
364 347
365 - if ( false === $credentials ) {
366 - if ( $silent ) {
367 - ob_end_clean();
368 - }
348 + if ( false === $credentials ) {
349 + if ( $silent ) {
350 + ob_end_clean();
351 + }
369 352
370 - return false;
371 - }
353 + return false;
354 + }
372 355
373 - if ( ! WP_Filesystem( $credentials ) ) {
374 - request_filesystem_credentials( $url );
356 + if ( ! WP_Filesystem( $credentials ) ) {
357 + request_filesystem_credentials( $url );
375 358
376 - if ( $silent ) {
377 - ob_end_clean();
378 - }
359 + if ( $silent ) {
360 + ob_end_clean();
361 + }
379 362
380 - return false;
381 - }
363 + return false;
364 + }
382 365
383 - return true;
384 - }
366 + return true;
367 + }
385 368
386 - /**
387 - * Calls the drop-in update method if necessary
388 - *
389 - * @return void
390 - * @author Till Krüss
391 - */
392 - public
393 - function maybe_update_dropin() {
394 - if ( defined( 'WP_SQLITE_OBJECT_CACHE_DISABLE_DROPIN_AUTOUPDATE' ) && WP_SQLITE_OBJECT_CACHE_DISABLE_DROPIN_AUTOUPDATE ) {
395 - return;
396 - }
369 + /**
370 + * Calls the drop-in update method if necessary
371 + *
372 + * @return void
373 + * @author Till Krüss
374 + */
375 + public function maybe_update_dropin() {
376 + if ( defined( 'WP_SQLITE_OBJECT_CACHE_DISABLE_DROPIN_AUTOUPDATE' ) && WP_SQLITE_OBJECT_CACHE_DISABLE_DROPIN_AUTOUPDATE ) {
377 + return;
378 + }
397 379
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 - }
380 + $has = $this->has_sqlite();
381 + if ( ( true === $has ) && $this->object_cache_dropin_needs_updating() ) {
382 + add_action( 'shutdown', array( $this, 'update_dropin' ) );
383 + }
384 + }
403 385
404 - /**
405 - * Checks if the `object-cache.php` drop-in is outdated
406 - * @return bool
407 - * @author Till Krüss
408 - *
409 - */
410 - public
411 - function object_cache_dropin_needs_updating() {
412 - if ( ! $this->object_cache_dropin_exists() ) {
413 - return true;
414 - }
386 + /**
387 + * Checks if the `object-cache.php` drop-in is outdated
388 + * @return bool
389 + * @author Till Krüss
390 + *
391 + */
392 + public function object_cache_dropin_needs_updating() {
393 + global $wp_object_cache;
394 + if ( ! method_exists( $wp_object_cache, 'dropin_get_version' ) ) {
395 + return true;
396 + }
397 + return version_compare( $wp_object_cache->dropin_get_version(), $this->_version, '<' );
398 + }
415 399
416 - $dropin = get_plugin_data( $this->dropinfiledest );
417 - $plugin = get_plugin_data( $this->dropinfilesource );
400 + /**
401 + * Updates the `object-cache.php` drop-in
402 + *
403 + * @return void
404 + * @author Till Krüss
405 + */
406 + public function update_dropin() {
407 + global $wp_filesystem;
408 + $has = $this->has_sqlite();
409 + if ( true === $has && $this->initialize_filesystem( '', true ) ) {
418 410
419 - if ( $dropin['PluginURI'] === $plugin['PluginURI'] ) {
420 - return version_compare( $dropin['Version'], $plugin['Version'], '<' );
421 - }
411 + $this->delete_sqlite_files();
412 + $result = $wp_filesystem->copy( $this->dropinfilesource, $this->dropinfiledest, true, FS_CHMOD_FILE );
413 + /**
414 + * Fires on cache enable event
415 + *
416 + * @param bool $result Whether the filesystem event (copy of the `object-cache.php` file) was successful.
417 + *
418 + * @since 0.1.0
419 + */
420 + do_action( 'sqlite_object_cache_update_dropin', $result );
421 + }
422 + }
422 423
423 - return false;
424 - }
424 + /**
425 + * Validates the `object-cache.php` drop-in
426 + *
427 + * @return bool
428 + * @author Till Krüss
429 + */
430 + public function validate_object_cache_dropin() {
431 + global $wp_object_cache;
425 432
426 - /**
427 - * Checks if the `object-cache.php` drop-in exists
428 - *
429 - * @return bool
430 - * @author Till Krüss
431 - */
432 - public
433 - function object_cache_dropin_exists() {
434 - return @file_exists( $this->dropinfiledest );
435 - }
433 + if ( ! method_exists( $wp_object_cache, 'dropin_get_version' ) ) {
434 + return false;
435 + }
436 436
437 - /**
438 - * Updates the `object-cache.php` drop-in
439 - *
440 - * @return void
441 - * @author Till Krüss
442 - */
443 - public
444 - function update_dropin() {
445 - global $wp_filesystem;
446 - $has = $this->has_sqlite();
447 - if ( true === $has && $this->initialize_filesystem( '', true ) ) {
437 + $dropin = get_plugin_data( $this->dropinfiledest );
438 + $plugin = get_plugin_data( $this->dropinfilesource );
448 439
449 - $this->delete_sqlite_files();
450 - $result = $wp_filesystem->copy( $this->dropinfilesource, $this->dropinfiledest, true, FS_CHMOD_FILE );
451 - /**
452 - * Fires on cache enable event
453 - *
454 - * @param bool $result Whether the filesystem event (copy of the `object-cache.php` file) was successful.
455 - *
456 - * @since 0.1.0
457 - */
458 - do_action( 'sqlite_object_cache_update_dropin', $result );
459 - }
460 - }
440 + /**
441 + * Filters the drop-in validation state
442 + *
443 + * @param bool $state The validation state of the drop-in.
444 + * @param string $dropin The `PluginURI` of the drop-in.
445 + * @param string $plugin The `PluginURI` of the plugin.
446 + *
447 + * @since 2.0.16
448 + */
449 + return apply_filters(
450 + 'sqlite_object_cache_validate_dropin',
451 + $dropin['PluginURI'] === $plugin['PluginURI'],
452 + $dropin['PluginURI'],
453 + $plugin['PluginURI']
454 + );
455 + }
461 456
462 - /**
463 - * Validates the `object-cache.php` drop-in
464 - *
465 - * @return bool
466 - * @author Till Krüss
467 - */
468 - public function validate_object_cache_dropin() {
469 - if ( ! $this->object_cache_dropin_exists() ) {
470 - return false;
471 - }
457 + /**
458 + * Plugin deactivation hook
459 + *
460 + * @param string $plugin Plugin basename.
461 + *
462 + * @return void
463 + * @author Till Krüss
464 + */
465 + public function on_deactivation( $plugin ) {
472 466
473 - $dropin = get_plugin_data( $this->dropinfiledest );
474 - $plugin = get_plugin_data( $this->dropinfilesource );
467 + wp_cache_flush();
475 468
476 - /**
477 - * Filters the drop-in validation state
478 - *
479 - * @param bool $state The validation state of the drop-in.
480 - * @param string $dropin The `PluginURI` of the drop-in.
481 - * @param string $plugin The `PluginURI` of the plugin.
482 - *
483 - * @since 2.0.16
484 - */
485 - return apply_filters(
486 - 'sqlite_object_cache_validate_dropin',
487 - $dropin['PluginURI'] === $plugin['PluginURI'],
488 - $dropin['PluginURI'],
489 - $plugin['PluginURI']
490 - );
491 - }
469 + wp_unschedule_hook( self::CLEAN_EVENT_HOOK );
470 + $this->delete_sqlite_files();
471 + $this->delete_dropin();
472 + $this->delete_all_transients_from_db();
473 + }
492 474
493 - /**
494 - * Plugin deactivation hook
495 - *
496 - * @param string $plugin Plugin basename.
497 - *
498 - * @return void
499 - * @author Till Krüss
500 - */
501 - public
502 - function on_deactivation( $plugin ) {
503 - wp_cache_flush();
475 + private function delete_sqlite_files() {
476 + global $wp_filesystem;
477 + global $wp_object_cache;
504 478
505 - wp_unschedule_hook( self::CLEAN_EVENT_HOOK );
506 - $this->delete_sqlite_files();
507 - $this->delete_dropin();
508 - }
479 + if ( method_exists( $wp_object_cache, 'sqlite_files' ) ) {
480 + if ( $this->initialize_filesystem( '', true ) ) {
481 + foreach ( $wp_object_cache->sqlite_files() as $file ) {
482 + $wp_filesystem->delete( $file );
483 + }
484 + }
485 + }
486 + }
509 487
510 - private function delete_sqlite_files() {
511 - global $wp_filesystem;
512 - global $wp_object_cache;
488 + private function delete_dropin() {
489 + global $wp_filesystem;
490 + ob_start();
513 491
514 - ob_start();
492 + if ( $this->validate_object_cache_dropin() && $this->initialize_filesystem( '', true ) ) {
493 + $wp_filesystem->delete( $this->dropinfiledest );
494 + }
515 495
516 - if ( method_exists( $wp_object_cache, 'sqlite_files' ) ) {
517 - if ( $this->validate_object_cache_dropin() && $this->initialize_filesystem( '', true ) ) {
518 - foreach ( $wp_object_cache->sqlite_files() as $file ) {
519 - $wp_filesystem->delete( $file );
520 - }
521 - }
522 - }
496 + ob_end_clean();
497 + }
523 498
524 - ob_end_clean();
525 - }
499 + /**
500 + * Deletes all transients from the database.
501 + *
502 + * It is necessary to do this when deactivating a persistent object cache
503 + * because the transients are kept there, and the ones in the database are
504 + * possibly stale.
505 + *
506 + * This hammers performance. But deactivating a persistent object cache is
507 + * not a common operation.
508 + *
509 + * @global wpdb $wpdb WordPress database abstraction object.
510 + */
511 + public function delete_all_transients_from_db() {
512 + global $wpdb;
513 + if ( ! is_multisite() ) {
514 + $this->clear_blog_transients();
515 + // Single site stores site transients in the options table.
516 + $this->clear_blog_transients( '_site_transient_' );
517 + } else {
518 + foreach ( get_sites( array( 'number' => 0, 'fields' => 'ids', 'no_found_rows' => true, 'orderby' => false ) ) as $site_id ) {
519 + try {
520 + switch_to_blog( $site_id );
521 + $this->clear_blog_transients();
522 + } catch ( Exception $ex ) {
523 + /* Avoid crashes on transient clearing */
524 + error_log( 'SQLite Object Cache problem clearing transients. Blog ' . $site_id . ':' . $ex->getMessage() );
525 + } finally {
526 + restore_current_blog();
527 + }
528 + }
529 + // Multisite stores site transients in the sitemeta table.
530 + $wpdb->query(
531 + $wpdb->prepare(
532 + "DELETE FROM {$wpdb->sitemeta} WHERE a.meta_key LIKE %s",
533 + $wpdb->esc_like( '_site_transient_' ) . '%'
534 + )
535 + );
536 + }
537 + }
526 538
527 - private
528 - function delete_dropin() {
529 - global $wp_filesystem;
530 - ob_start();
539 + /**
540 + * DELETE transients from the current blog's options table.
541 + *
542 + * @param string $tag _site_transient_ or _transient_, the default.
543 + *
544 + * @return void
545 + */
546 + private function clear_blog_transients( $tag = '_transient_') {
547 + global $wpdb;
548 + $wpdb->query(
549 + $wpdb->prepare(
550 + "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
551 + $wpdb->esc_like( $tag ) . '%'
552 + )
553 + );
554 + }
531 555
532 - if ( $this->validate_object_cache_dropin() && $this->initialize_filesystem( '', true ) ) {
533 - $wp_filesystem->delete( $this->dropinfiledest );
534 - }
556 + /**
557 + * @return bool True if APCu support is activated for this plugin.
558 + */
559 + public function apcu_is_activated() {
560 + return defined( 'WP_SQLITE_OBJECT_CACHE_APCU' ) && WP_SQLITE_OBJECT_CACHE_APCU
561 + && $this->apcu_extension_is_enabled();
562 + }
535 563
536 - ob_end_clean();
537 - }
564 + /**
565 + * @return bool True if the APCu extension is loaded and enabled.
566 + */
567 + public function apcu_extension_is_enabled() {
568 + return function_exists( 'apcu_enabled' ) && apcu_enabled();
569 + }
570 +
571 + /**
572 + * Make sure WP_SQLITE_OBJECT_CACHE_APCU and $option['use_apcu'] match.
573 + *
574 + * @param bool $unconditional true if we should always change the option to match WP_SQLITE_OBJECT_CACHE_APCU.
575 + *
576 + * @return string 'on' or 'off': the current activation state of epcu.
577 + */
578 + public function sync_apcu_global_to_option( $unconditional = true ) {
579 + global $wp_object_cache;
580 + $option = get_option( $this->_token . '_settings', array() );
581 + $option_dirty = false;
582 + $updated_flag = array_key_exists( 'use_apcu_updated', $option );
583 + $use_apcu = array_key_exists( 'use_apcu', $option ) && 'on' === $option['use_apcu'] ? 'on' : 'off';
584 + if ( $updated_flag ) {
585 + unset ( $option['use_apcu_updated'] );
586 + $option_dirty = true;
587 + }
588 + $target_use_apcu = $this->apcu_is_activated() ? 'on' : 'off';
589 + if ( ! $unconditional && $updated_flag ) {
590 + $target_use_apcu = $use_apcu;
591 + }
592 + if ( $target_use_apcu !== $use_apcu ) {
593 + $option['use_apcu'] = $target_use_apcu;
594 + $option_dirty = true;
595 + }
596 + if ( $option_dirty ) {
597 + if ( method_exists( $wp_object_cache, 'apcu-clear_cache' ) ) {
598 + $wp_object_cache->apcu_clear_cache();
599 + }
600 + update_option( $this->_token . '_settings', $option, true );
601 + }
602 +
603 + return $target_use_apcu;
604 + }
605 +
538 606 }