PluginProbe
SQLite Object Cache / 1.2.0
SQLite Object Cache v1.2.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.2.0, at includes/class-sqlite-object-cache.php

521 lines 12.4 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.2.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, [ $this, 'on_activation' ] );
141 register_deactivation_hook( $this->file, [ $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', [ $this, 'load_localization' ], 0 );
153 add_action( 'admin_init', [ $this, 'maybe_update_dropin' ] );
154
155 /* handle cron cache cleanup */
156 add_action( self::CLEAN_EVENT_HOOK, [ $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
184 function clean() {
185 global $wp_object_cache;
186 $option = get_option( $this->_token . '_settings', [] );
187 if ( method_exists( $wp_object_cache, 'sqlite_clean_up_cache' ) ) {
188 $retention = array_key_exists( 'retention', $option ) ? $option['retention'] : 24;
189 try {
190 $wp_object_cache->sqlite_clean_up_cache( $retention * HOUR_IN_SECONDS, true, true );
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_reset_statistics' ) ) {
197 $retention = array_key_exists( 'retainmeasurements', $option ) ? $option['retainmeasurements'] : 24;
198 try {
199 $wp_object_cache->sqlite_reset_statistics( $retention * HOUR_IN_SECONDS );
200 } catch ( Exception $ex ) {
201 error_log( 'sqlite_object_cache stats cleanup failure: ', $ex->getMessage() );
202 }
203 }
204 } // End enqueue_styles ()
205
206 /**
207 * Plugin activation hook
208 *
209 * @return void
210 */
211 public
212 function on_activation() {
213 /* make sure the autoloaded option is set when activating; avoid an extra dbms or cache hit to fetch it */
214 $option = get_option( $this->_token . '_settings', 'default' );
215 if ( 'default' === $option ) {
216 update_option( $this->_token . '_settings', [], true );
217 }
218 if ( true === $this->has_sqlite() ) {
219 add_action( 'shutdown', [ $this, 'update_dropin' ] );
220 }
221 }
222
223 /**
224 * Determine whether we can use SQLite3.
225 *
226 * @return bool|string true, or an error message.
227 */
228 public
229 function has_sqlite() {
230 if ( ! class_exists( 'SQLite3' ) || ! extension_loaded( 'sqlite3' ) ) {
231 return __( 'You cannot use the SQLite Object Cache plugin. Your server does not have php\'s SQLite3 extension installed.', 'sqlite-object-cache' );
232 }
233
234 $sqlite_version = $this->sqlite_get_version();
235 if ( version_compare( $sqlite_version, $this->minimum_sqlite_version ) < 0 ) {
236 return sprintf(
237 /* translators: 1 actual SQLite version. 2 required SQLite version) */
238 __( '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' ),
239 $sqlite_version, $this->minimum_sqlite_version );
240 }
241
242 return true;
243 }
244
245 /**
246 * Get the version number for the SQLite extension.
247 *
248 * @return string|false SQLite's version number.
249 */
250 public
251 function sqlite_get_version() {
252
253 if ( class_exists( 'SQLite3' ) ) {
254 $version = SQLite3::version();
255
256 return $version['versionString'];
257 }
258
259 return false;
260 }
261
262 /**
263 * Load plugin localization
264 *
265 * @access public
266 * @return void
267 * @since 1.0.0
268 */
269 public
270 function load_localization() {
271 load_plugin_textdomain( 'sqlite-object-cache', false, dirname( plugin_basename( $this->file ) ) . '/languages/' );
272 }
273
274 /**
275 * Test if we can write in the WP_CONTENT_DIR and modify the `object-cache.php` drop-in
276 *
277 * @return true|WP_Error
278 * @author Till Krüss
279 *
280 */
281 public
282 function test_filesystem_writing() {
283 global $wp_filesystem;
284
285 if ( ! $this->initialize_filesystem( '', true ) ) {
286 return new WP_Error( 'fs', __( 'Could not initialize filesystem.', 'sqlite-object-cache' ) );
287 }
288
289 $testfiledest = WP_CONTENT_DIR . '/sqlite-write-test.tmp';
290
291 if ( ! $wp_filesystem->exists( $this->dropinfilesource ) ) {
292 return new WP_Error( 'exists', __( 'Object cache drop-in file doesn’t exist.', 'sqlite-object-cache' ) );
293 }
294
295 if ( $wp_filesystem->exists( $testfiledest ) && ! $wp_filesystem->delete( $testfiledest ) ) {
296 return new WP_Error( 'delete', __( 'Test file exists, but couldn’t be deleted.', 'sqlite-object-cache' ) );
297 }
298
299 if ( ! $wp_filesystem->is_writable( WP_CONTENT_DIR ) ) {
300 return new WP_Error( 'copy', __( 'Content directory is not writable.', 'sqlite-object-cache' ) );
301 }
302
303 if ( ! $wp_filesystem->copy( $this->dropinfilesource, $testfiledest, true, FS_CHMOD_FILE ) ) {
304 return new WP_Error( 'copy', __( 'Failed to copy test file.', 'sqlite-object-cache' ) );
305 }
306
307 if ( ! $wp_filesystem->exists( $testfiledest ) ) {
308 return new WP_Error( 'exists', __( 'Copied test file doesn’t exist.', 'sqlite-object-cache' ) );
309 }
310
311 $meta = get_file_data( $testfiledest, [ 'Version' => 'Version' ] );
312
313 if ( $meta['Version'] !== $this->_version ) {
314 return new WP_Error( 'version', __( 'Couldn’t verify test file contents.', 'sqlite-object-cache' ) );
315 }
316
317 if ( ! $wp_filesystem->delete( $testfiledest ) ) {
318 return new WP_Error( 'delete', __( 'Copied test file couldn’t be deleted.', 'sqlite-object-cache' ) );
319 }
320
321 return true;
322 }
323
324 /**
325 * Initializes the WP filesystem API to be ready for use
326 *
327 * @param string $url The URL to post the form to.
328 * @param bool $silent Whether to ask the user for credentials if necessary or not.
329 *
330 * @return bool
331 * @author Till Krüss
332 *
333 */
334 public
335 function initialize_filesystem(
336 $url, $silent = false
337 ) {
338 require_once ABSPATH . 'wp-admin/includes/file.php';
339 if ( $silent ) {
340 ob_start();
341 }
342
343 $credentials = request_filesystem_credentials( $url );
344
345 if ( false === $credentials ) {
346 if ( $silent ) {
347 ob_end_clean();
348 }
349
350 return false;
351 }
352
353 if ( ! WP_Filesystem( $credentials ) ) {
354 request_filesystem_credentials( $url );
355
356 if ( $silent ) {
357 ob_end_clean();
358 }
359
360 return false;
361 }
362
363 return true;
364 }
365
366 /**
367 * Calls the drop-in update method if necessary
368 *
369 * @return void
370 * @author Till Krüss
371 */
372 public
373 function maybe_update_dropin() {
374 if ( defined( 'WP_SQLITE_OBJECT_CACHE_DISABLE_DROPIN_AUTOUPDATE' ) && WP_SQLITE_OBJECT_CACHE_DISABLE_DROPIN_AUTOUPDATE ) {
375 return;
376 }
377
378 $has = $this->has_sqlite();
379 if ( ( true === $has ) && $this->object_cache_dropin_needs_updating() ) {
380 add_action( 'shutdown', [ $this, 'update_dropin' ] );
381 }
382 }
383
384 /**
385 * Checks if the `object-cache.php` drop-in is outdated
386 * @return bool
387 * @author Till Krüss
388 *
389 */
390 public
391 function object_cache_dropin_needs_updating() {
392 if ( ! $this->object_cache_dropin_exists() ) {
393 return true;
394 }
395
396 $dropin = get_plugin_data( $this->dropinfiledest );
397 $plugin = get_plugin_data( $this->dropinfilesource );
398
399 if ( $dropin['PluginURI'] === $plugin['PluginURI'] ) {
400 return version_compare( $dropin['Version'], $plugin['Version'], '<' );
401 }
402
403 return false;
404 }
405
406 /**
407 * Checks if the `object-cache.php` drop-in exists
408 *
409 * @return bool
410 * @author Till Krüss
411 */
412 public
413 function object_cache_dropin_exists() {
414 return @file_exists( $this->dropinfiledest );
415 }
416
417 /**
418 * Updates the `object-cache.php` drop-in
419 *
420 * @return void
421 * @author Till Krüss
422 */
423 public
424 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 private function delete_sqlite_files() {
443 global $wp_filesystem;
444 global $wp_object_cache;
445
446 ob_start();
447
448 if ( method_exists( $wp_object_cache, 'sqlite_files' ) ) {
449 if ( $this->validate_object_cache_dropin() && $this->initialize_filesystem( '', true ) ) {
450 foreach ( $wp_object_cache->sqlite_files() as $file ) {
451 $wp_filesystem->delete( $file );
452 }
453 }
454 }
455
456 ob_end_clean();
457 }
458
459 /**
460 * Validates the `object-cache.php` drop-in
461 *
462 * @return bool
463 * @author Till Krüss
464 */
465 public function validate_object_cache_dropin() {
466 if ( ! $this->object_cache_dropin_exists() ) {
467 return false;
468 }
469
470 $dropin = get_plugin_data( $this->dropinfiledest );
471 $plugin = get_plugin_data( $this->dropinfilesource );
472
473 /**
474 * Filters the drop-in validation state
475 *
476 * @param bool $state The validation state of the drop-in.
477 * @param string $dropin The `PluginURI` of the drop-in.
478 * @param string $plugin The `PluginURI` of the plugin.
479 *
480 * @since 2.0.16
481 */
482 return apply_filters(
483 'sqlite_object_cache_validate_dropin',
484 $dropin['PluginURI'] === $plugin['PluginURI'],
485 $dropin['PluginURI'],
486 $plugin['PluginURI']
487 );
488 }
489
490 /**
491 * Plugin deactivation hook
492 *
493 * @param string $plugin Plugin basename.
494 *
495 * @return void
496 * @author Till Krüss
497 */
498 public
499 function on_deactivation(
500 $plugin
501 ) {
502 wp_cache_flush();
503
504 wp_unschedule_hook( self::CLEAN_EVENT_HOOK );
505 $this->delete_sqlite_files();
506 $this->delete_dropin();
507 }
508
509 private
510 function delete_dropin() {
511 global $wp_filesystem;
512 ob_start();
513
514 if ( $this->validate_object_cache_dropin() && $this->initialize_filesystem( '', true ) ) {
515 $wp_filesystem->delete( $this->dropinfiledest );
516 }
517
518 ob_end_clean();
519 }
520 }
521