PluginProbe
SQLite Object Cache / 1.3.0
SQLite Object Cache v1.3.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
sqlite-object-cache / includes / class-sqlite-object-cache.php

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

529 lines 12.7 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 * Local instance of SQLite_Object_Cache_Admin_API
22 *
23 * @var SQLite_Object_Cache_Admin_API|null
24 */
25 public $admin;
26
27 /**
28 * Settings class object
29 *
30 * @var object
31 * @access public
32 * @since 1.0.0
33 */
34 public $settings;
35
36 /**
37 * The version number.
38 *
39 * @var string
40 * @access public
41 * @since 1.0.0
42 */
43 public $_version;
44
45 /**
46 * The token.
47 *
48 * @var string
49 * @access public
50 * @since 1.0.0
51 */
52 public $_token;
53
54 /**
55 * The main plugin file.
56 *
57 * @var string
58 * @access public
59 * @since 1.0.0
60 */
61 public $file;
62
63 /**
64 * The main plugin directory.
65 *
66 * @var string
67 * @access public
68 * @since 1.0.0
69 */
70 public $dir;
71
72 /**
73 * The plugin assets directory.
74 *
75 * @var string
76 * @access public
77 * @since 1.0.0
78 */
79 public $assets_dir;
80
81 /**
82 * The plugin assets URL.
83 *
84 * @var string
85 * @access public
86 * @since 1.0.0
87 */
88 public $assets_url;
89
90 /**
91 * Suffix for JavaScripts.
92 *
93 * @var string
94 * @access public
95 * @since 1.0.0
96 */
97 public $script_suffix;
98
99 /**
100 * Path for the drop-in when it is working.
101 * @var string
102 */
103 public $dropinfiledest;
104
105 /**
106 * Path for the drop-in in the plugin tree.
107 * @var string
108 */
109 public $dropinfilesource;
110
111 /**
112 * Minimum required sqlite version.
113 *
114 * @var string
115 */
116 public $minimum_sqlite_version = '3.7.0';
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.0' ) {
125 $this->_version = $version;
126 $this->_token = 'sqlite_object_cache';
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';
135
136 $this->assets_url = esc_url( trailingslashit( plugins_url( '/assets/', $this->file ) ) );
137
138 $this->script_suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
139
140 register_activation_hook( $this->file, array( $this, 'on_activation' ) );
141 register_deactivation_hook( $this->file, array( $this, 'on_deactivation' ) );
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 }
149
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' ) );
154
155 /* handle cron cache cleanup */
156 add_action( self::CLEAN_EVENT_HOOK, array( $this, 'clean' ), 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 }
161
162 /**
163 * Load plugin textdomain
164 *
165 * @access public
166 * @return void
167 * @since 1.0.0
168 */
169 public function load_plugin_textdomain() {
170 $domain = 'sqlite-object-cache';
171
172 $locale = apply_filters( 'plugin_locale', get_locale(), $domain );
173
174 load_textdomain( $domain, WP_LANG_DIR . '/' . $domain . '/' . $domain . '-' . $locale . '.mo' );
175 load_plugin_textdomain( $domain, false, dirname( plugin_basename( $this->file ) ) . '/languages/' );
176 }
177
178 /**
179 * WP_Cron task to clean cache entries.
180 *
181 * @return void
182 */
183 public function clean() {
184 global $wp_object_cache;
185 $option = get_option( $this->_token . '_settings', array() );
186 if ( method_exists( $wp_object_cache, 'sqlite_delete_old' ) ) {
187 $target_size = array_key_exists( 'target_size', $option ) ? $option['target_size'] : 4;
188 $target_size *= ( 1024 * 1024 );
189 try {
190 $wp_object_cache->sqlite_delete_old( $target_size );
191 } catch ( Exception $ex ) {
192 error_log( 'sqlite_object_cache cache cleanup failure: ', $ex->getMessage() );
193 }
194 }
195
196 if ( method_exists( $wp_object_cache, 'sqlite_remove_expired' ) ) {
197 try {
198 $wp_object_cache->sqlite_remove_expired( true );
199 } catch ( Exception $ex ) {
200 error_log( 'sqlite_object_cache cache cleanup failure: ', $ex->getMessage() );
201 }
202 }
203
204 if ( method_exists( $wp_object_cache, 'sqlite_reset_statistics' ) ) {
205 $retention = array_key_exists( 'retainmeasurements', $option ) ? $option['retainmeasurements'] : 24;
206 try {
207 $wp_object_cache->sqlite_reset_statistics( $retention * HOUR_IN_SECONDS );
208 } catch ( Exception $ex ) {
209 error_log( 'sqlite_object_cache stats cleanup failure: ', $ex->getMessage() );
210 }
211 }
212 }
213
214 /**
215 * Plugin activation hook
216 *
217 * @return void
218 */
219 public
220 function on_activation() {
221 /* make sure the autoloaded option is set when activating; avoid an extra dbms or cache hit to fetch it */
222 $option = get_option( $this->_token . '_settings', 'default' );
223 if ( 'default' === $option ) {
224 update_option( $this->_token . '_settings', array(), true );
225 }
226 if ( true === $this->has_sqlite() ) {
227 add_action( 'shutdown', array( $this, 'update_dropin' ) );
228 }
229 }
230
231 /**
232 * Determine whether we can use SQLite3.
233 *
234 * @return bool|string true, or an error message.
235 */
236 public
237 function has_sqlite() {
238 if ( ! class_exists( 'SQLite3' ) || ! extension_loaded( 'sqlite3' ) ) {
239 return __( 'You cannot use the SQLite Object Cache plugin. Your server does not have php\'s SQLite3 extension installed.', 'sqlite-object-cache' );
240 }
241
242 $sqlite_version = $this->sqlite_get_version();
243 if ( version_compare( $sqlite_version, $this->minimum_sqlite_version ) < 0 ) {
244 return sprintf(
245 /* translators: 1 actual SQLite version. 2 required SQLite version) */
246 __( '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' ),
247 $sqlite_version, $this->minimum_sqlite_version );
248 }
249
250 return true;
251 }
252
253 /**
254 * Get the version number for the SQLite extension.
255 *
256 * @return string|false SQLite's version number.
257 */
258 public
259 function sqlite_get_version() {
260
261 if ( class_exists( 'SQLite3' ) ) {
262 $version = SQLite3::version();
263
264 return $version['versionString'];
265 }
266
267 return false;
268 }
269
270 /**
271 * Load plugin localization
272 *
273 * @access public
274 * @return void
275 * @since 1.0.0
276 */
277 public
278 function load_localization() {
279 load_plugin_textdomain( 'sqlite-object-cache', false, dirname( plugin_basename( $this->file ) ) . '/languages/' );
280 }
281
282 /**
283 * Test if we can write in the WP_CONTENT_DIR and modify the `object-cache.php` drop-in
284 *
285 * @return true|WP_Error
286 * @author Till Krüss
287 *
288 */
289 public
290 function test_filesystem_writing() {
291 global $wp_filesystem;
292
293 if ( ! $this->initialize_filesystem( '', true ) ) {
294 return new WP_Error( 'fs', __( 'Could not initialize filesystem.', 'sqlite-object-cache' ) );
295 }
296
297 $testfiledest = WP_CONTENT_DIR . '/sqlite-write-test.tmp';
298
299 if ( ! $wp_filesystem->exists( $this->dropinfilesource ) ) {
300 return new WP_Error( 'exists', __( 'Object cache drop-in file doesn’t exist.', 'sqlite-object-cache' ) );
301 }
302
303 if ( $wp_filesystem->exists( $testfiledest ) && ! $wp_filesystem->delete( $testfiledest ) ) {
304 return new WP_Error( 'delete', __( 'Test file exists, but couldn’t be deleted.', 'sqlite-object-cache' ) );
305 }
306
307 if ( ! $wp_filesystem->is_writable( WP_CONTENT_DIR ) ) {
308 return new WP_Error( 'copy', __( 'Content directory is not writable.', 'sqlite-object-cache' ) );
309 }
310
311 if ( ! $wp_filesystem->copy( $this->dropinfilesource, $testfiledest, true, FS_CHMOD_FILE ) ) {
312 return new WP_Error( 'copy', __( 'Failed to copy test file.', 'sqlite-object-cache' ) );
313 }
314
315 if ( ! $wp_filesystem->exists( $testfiledest ) ) {
316 return new WP_Error( 'exists', __( 'Copied test file doesn’t exist.', 'sqlite-object-cache' ) );
317 }
318
319 $meta = get_file_data( $testfiledest, array( 'Version' => 'Version' ) );
320
321 if ( $meta['Version'] !== $this->_version ) {
322 return new WP_Error( 'version', __( 'Couldn’t verify test file contents.', 'sqlite-object-cache' ) );
323 }
324
325 if ( ! $wp_filesystem->delete( $testfiledest ) ) {
326 return new WP_Error( 'delete', __( 'Copied test file couldn’t be deleted.', 'sqlite-object-cache' ) );
327 }
328
329 return true;
330 }
331
332 /**
333 * Initializes the WP filesystem API to be ready for use
334 *
335 * @param string $url The URL to post the form to.
336 * @param bool $silent Whether to ask the user for credentials if necessary or not.
337 *
338 * @return bool
339 * @author Till Krüss
340 *
341 */
342 public
343 function initialize_filesystem(
344 $url, $silent = false
345 ) {
346 require_once ABSPATH . 'wp-admin/includes/file.php';
347 if ( $silent ) {
348 ob_start();
349 }
350
351 $credentials = request_filesystem_credentials( $url );
352
353 if ( false === $credentials ) {
354 if ( $silent ) {
355 ob_end_clean();
356 }
357
358 return false;
359 }
360
361 if ( ! WP_Filesystem( $credentials ) ) {
362 request_filesystem_credentials( $url );
363
364 if ( $silent ) {
365 ob_end_clean();
366 }
367
368 return false;
369 }
370
371 return true;
372 }
373
374 /**
375 * Calls the drop-in update method if necessary
376 *
377 * @return void
378 * @author Till Krüss
379 */
380 public
381 function maybe_update_dropin() {
382 if ( defined( 'WP_SQLITE_OBJECT_CACHE_DISABLE_DROPIN_AUTOUPDATE' ) && WP_SQLITE_OBJECT_CACHE_DISABLE_DROPIN_AUTOUPDATE ) {
383 return;
384 }
385
386 $has = $this->has_sqlite();
387 if ( ( true === $has ) && $this->object_cache_dropin_needs_updating() ) {
388 add_action( 'shutdown', array( $this, 'update_dropin' ) );
389 }
390 }
391
392 /**
393 * Checks if the `object-cache.php` drop-in is outdated
394 * @return bool
395 * @author Till Krüss
396 *
397 */
398 public
399 function object_cache_dropin_needs_updating() {
400 if ( ! $this->object_cache_dropin_exists() ) {
401 return true;
402 }
403
404 $dropin = get_plugin_data( $this->dropinfiledest );
405 $plugin = get_plugin_data( $this->dropinfilesource );
406
407 if ( $dropin['PluginURI'] === $plugin['PluginURI'] ) {
408 return version_compare( $dropin['Version'], $plugin['Version'], '<' );
409 }
410
411 return false;
412 }
413
414 /**
415 * Checks if the `object-cache.php` drop-in exists
416 *
417 * @return bool
418 * @author Till Krüss
419 */
420 public
421 function object_cache_dropin_exists() {
422 return @file_exists( $this->dropinfiledest );
423 }
424
425 /**
426 * Updates the `object-cache.php` drop-in
427 *
428 * @return void
429 * @author Till Krüss
430 */
431 public
432 function update_dropin() {
433 global $wp_filesystem;
434 $has = $this->has_sqlite();
435 if ( true === $has && $this->initialize_filesystem( '', true ) ) {
436
437 $this->delete_sqlite_files();
438 $result = $wp_filesystem->copy( $this->dropinfilesource, $this->dropinfiledest, true, FS_CHMOD_FILE );
439 /**
440 * Fires on cache enable event
441 *
442 * @param bool $result Whether the filesystem event (copy of the `object-cache.php` file) was successful.
443 *
444 * @since 0.1.0
445 */
446 do_action( 'sqlite_object_cache_update_dropin', $result );
447 }
448 }
449
450 /**
451 * Validates the `object-cache.php` drop-in
452 *
453 * @return bool
454 * @author Till Krüss
455 */
456 public function validate_object_cache_dropin() {
457 if ( ! $this->object_cache_dropin_exists() ) {
458 return false;
459 }
460
461 $dropin = get_plugin_data( $this->dropinfiledest );
462 $plugin = get_plugin_data( $this->dropinfilesource );
463
464 /**
465 * Filters the drop-in validation state
466 *
467 * @param bool $state The validation state of the drop-in.
468 * @param string $dropin The `PluginURI` of the drop-in.
469 * @param string $plugin The `PluginURI` of the plugin.
470 *
471 * @since 2.0.16
472 */
473 return apply_filters(
474 'sqlite_object_cache_validate_dropin',
475 $dropin['PluginURI'] === $plugin['PluginURI'],
476 $dropin['PluginURI'],
477 $plugin['PluginURI']
478 );
479 }
480
481 /**
482 * Plugin deactivation hook
483 *
484 * @param string $plugin Plugin basename.
485 *
486 * @return void
487 * @author Till Krüss
488 */
489 public
490 function on_deactivation(
491 $plugin
492 ) {
493 wp_cache_flush();
494
495 wp_unschedule_hook( self::CLEAN_EVENT_HOOK );
496 $this->delete_sqlite_files();
497 $this->delete_dropin();
498 }
499
500 private function delete_sqlite_files() {
501 global $wp_filesystem;
502 global $wp_object_cache;
503
504 ob_start();
505
506 if ( method_exists( $wp_object_cache, 'sqlite_files' ) ) {
507 if ( $this->validate_object_cache_dropin() && $this->initialize_filesystem( '', true ) ) {
508 foreach ( $wp_object_cache->sqlite_files() as $file ) {
509 $wp_filesystem->delete( $file );
510 }
511 }
512 }
513
514 ob_end_clean();
515 }
516
517 private
518 function delete_dropin() {
519 global $wp_filesystem;
520 ob_start();
521
522 if ( $this->validate_object_cache_dropin() && $this->initialize_filesystem( '', true ) ) {
523 $wp_filesystem->delete( $this->dropinfiledest );
524 }
525
526 ob_end_clean();
527 }
528 }
529