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
sqlite-object-cache / includes / class-sqlite-object-cache-settings.php

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

1,022 lines 33.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Settings class file.
4 *
5 * @package SQLite Object Cache
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12 /**
13 * Only make one instance of this, please.
14 *
15 * Settings class.
16 */
17 class SQLite_Object_Cache_Settings {
18
19 static $statistics_help_url = 'https://www.plumislandmedia.net/wordpress-plugins/sqlite-object-cache/statistics-from-sqlite-object-cache/';
20
21 /**
22 * The main plugin object.
23 *
24 * @var object
25 */
26 public $parent;
27
28 /**
29 * Prefix for plugin settings.
30 *
31 * @var string
32 */
33 public $base = '';
34
35 /**
36 * Available settings for plugin.
37 *
38 * @var array
39 */
40 public $settings = array();
41
42 /**
43 * SQLite3 is available.
44 *
45 * @var string|bool If it's true, we're good. If it's a string, an explanation.
46 */
47 public $has = '';
48
49 /**
50 * @var string The plugin file name.
51 */
52 private $plugin_file;
53 /**
54 * @var mixed
55 */
56 private $caught_option_value = false;
57
58 /**
59 * Constructor function.
60 *
61 * @param object $parent Parent object.
62 * @param string $plugin_file Plugin top-level file name.
63 */
64 public function __construct( $parent, $plugin_file ) {
65 $this->parent = $parent;
66 $this->has = $parent->has_sqlite();
67 $this->base = 'sqlite_object_cache_';
68 $this->plugin_file = $plugin_file;
69
70 // Register plugin settings.
71 add_action( 'admin_init', array( $this, 'register_my_settings' ) );
72
73 if ( is_main_site() ) {
74 // Information for Site Health Info
75 add_filter( 'debug_information', array( $this, 'debug_information' ) );
76
77 // Add settings page to menu.
78 add_action( 'admin_menu', array( $this, 'add_menu_item' ) );
79
80 // Add settings link to plugins page.
81 add_filter(
82 'plugin_action_links_' . plugin_basename( $this->parent->file ),
83 array(
84 $this,
85 'add_settings_link',
86 )
87 );
88 }
89
90 // For multisite, propagate the option value to all subsites.
91 if ( is_multisite() ) {
92 add_action( 'update_option_' . $this->parent->_token . '_settings', array( $this, 'catch_option' ), 20, 3 );
93 }
94
95 // Configure placement of plugin settings page. See readme for implementation.
96 add_filter( $this->base . 'menu_settings', array( $this, 'configure_settings' ) );
97
98 // Spoonsor link.
99 add_filter( 'plugin_row_meta', array( $this, 'filter_plugin_row_meta' ), 10, 2 );
100
101 }
102
103 /**
104 * Fires after the value of our option has been successfully updated.
105 *
106 * @param mixed $old_value The old option value.
107 * @param mixed $value The new option value.
108 * @param string $option Option name.
109 */
110 public function catch_option( $old_value, $value, $option ) {
111 if ( false === $this->caught_option_value ) {
112 add_action( 'shutdown', array( $this, 'propagate_option' ) );
113 }
114 $this->caught_option_value = $value;
115 }
116
117 /**
118 * Shutdown action handler to copy our option to all subsites.
119 *
120 * We use this rather than a site option because this gets us autoloading.
121 *
122 * @return void
123 */
124 public function propagate_option() {
125 remove_action( 'update_option_' . $this->parent->_token . '_settings', array( $this, 'catch_option' ), 20 );
126 $current_site = get_current_blog_id();
127 foreach (
128 get_sites( array(
129 'number' => 0,
130 'fields' => 'ids',
131 'no_found_rows' => true,
132 'orderby' => false
133 ) ) as $site_id
134 ) {
135 if ( $site_id !== $current_site ) {
136 try {
137 switch_to_blog( $site_id );
138 update_option( $this->parent->_token . '_settings', $this->caught_option_value, true );
139 } catch ( Exception $ex ) {
140 /* Avoid crashes on option propagation */
141 error_log( 'SQLite Object Cache problem propagaging option value. Blog ' . $site_id . ':' . $ex->getMessage() );
142 } finally {
143 restore_current_blog();
144 }
145 }
146 }
147 }
148
149 /**
150 * Filters the array of row meta for each plugin in the Plugins list table.
151 *
152 * @param array<int, string> $plugin_meta An array of the plugin's metadata.
153 * @param string $plugin_file Path to the plugin file relative to the plugins directory.
154 *
155 * @return array<int, string> Updated array of the plugin's metadata.
156 */
157 public function filter_plugin_row_meta( array $plugin_meta, $plugin_file ) {
158 if ( $this->plugin_file !== $plugin_file ) {
159 return $plugin_meta;
160 }
161
162 if ( is_multisite() && (is_network_admin() || ! is_main_site() ) ) {
163 $plugin_meta[] =
164 esc_html__( 'See the main site\'s dashboard for settings.', 'sqlite-object-cache' );
165 }
166
167 $plugin_meta[] = sprintf(
168 '<a href="%1$s"><span class="dashicons dashicons-star-filled" aria-hidden="true" style="font-size:14px;line-height:1.3"></span>%2$s</a>',
169 'https://github.com/sponsors/OllieJones',
170 esc_html_x( 'Sponsor', 'verb', 'sqlite-object-cache' )
171 );
172
173 return $plugin_meta;
174 }
175
176 /**
177 * Build settings fields
178 *
179 * @return array Fields to be displayed on settings page
180 */
181 private function settings_fields() {
182
183 $fields = array(
184 array(
185 'id' => 'flush',
186 'label' => __( 'Flush now', 'sqlite-object-cache' ),
187 'description' => __( 'Check to flush the cache (delete all its entries) now.', 'sqlite-object-cache' ) . ' ' .
188 __( 'This briefly puts your site into maintenance mode.', 'sqlite-object-cache' ),
189 'type' => 'checkbox',
190 'default' => '',
191 'reset' => '',
192 ),
193 array(
194 'id' => 'vacuum',
195 'label' => __( 'Vacuum now', 'sqlite-object-cache' ),
196 'description' => __( 'Check to vacuum (defragment) the cache now.', 'sqlite-object-cache' ) . ' ' .
197 __( 'This briefly puts your site into maintenance mode.', 'sqlite-object-cache' ),
198 'type' => 'checkbox',
199 'default' => '',
200 'reset' => '',
201 ),
202 array(
203 'id' => 'target_size',
204 'label' => __( 'Cached data size', 'sqlite-object-cache' ),
205 'description' => __( 'MiB. When data in the cache grows larger than this, hourly cleanup removes the oldest entries.', 'sqlite-object-cache' ),
206 'type' => 'number',
207 'default' => 16,
208 'min' => 1,
209 'step' => 'any',
210 'cssclass' => 'narrow',
211 'placeholder' => __( 'MiB.', 'sqlite-object-cache' ),
212 ),
213 array(
214 'id' => 'cleanup',
215 'label' => __( 'Clean up now', 'sqlite-object-cache' ),
216 'description' => __( 'Check to clean up the cache (delete old data) now.', 'sqlite-object-cache' ),
217 'type' => 'checkbox',
218 'default' => '',
219 'reset' => '',
220 ),
221 array(
222 'id' => 'capture',
223 'label' => __( 'Measure performance', 'sqlite-object-cache' ),
224 'description' => __( 'Check to enable cache performance measurement. ', 'sqlite-object-cache' ),
225 'type' => 'checkbox',
226 'default' => '',
227 ),
228 array(
229 'id' => 'samplerate',
230 'label' => __( 'Measure', 'sqlite-object-cache' ),
231 'description' => __( 'percent of requests, randomly sampled.', 'sqlite-object-cache' ),
232 'type' => 'number',
233 'default' => 1,
234 'max' => 100,
235 'min' => 0,
236 'step' => 'any',
237 'cssclass' => 'narrow',
238 'placeholder' => __( 'Sampling percentage.', 'sqlite-object-cache' ),
239 ),
240 array(
241 'id' => 'retainmeasurements',
242 'label' => __( 'Retain measurements for', 'sqlite-object-cache' ),
243 'description' => __( 'hours.', 'sqlite-object-cache' ),
244 'type' => 'number',
245 'default' => 2,
246 'min' => 0.1,
247 'step' => 'any',
248 'cssclass' => 'narrow',
249 'placeholder' => __( 'Hours to retain.', 'sqlite-object-cache' ),
250 )
251 );
252
253 if ( $this->parent->apcu_extension_is_enabled() ) {
254 array_unshift( $fields, array(
255 'id' => 'use_apcu',
256 'label' => __( 'Use APCu', 'sqlite-object-cache' ),
257 'description' => __( 'Check to enable the use of php\'s APCu cache to improve performance.', 'sqlite-object-cache' ),
258 'type' => 'checkbox',
259 'default' => '',
260 ) );
261
262 }
263
264 $settings['standard'] = array(
265 'title' => __( 'Settings' ),
266 'submit' => __( 'Save Changes' ),
267 'description' => '',
268 'render_section_header' => array( $this, 'settings_section_header' ),
269 'form_post_callback' => array( $this, 'validate_settings' ),
270 'fields' => $fields,
271 );
272
273 $settings['stats'] = array(
274 'title' => __( 'Statistics', 'sqlite-object-cache' ),
275 'submit' => __( 'Reset Statistics', 'sqlite-object-cache' ),
276 'render_section_header' => array( $this, 'stats_section_header' ),
277 'form_post_callback' => array( $this, 'validate_reset_stats' ),
278 );
279
280 return apply_filters( $this->parent->_token . '_settings_fields', $settings );
281 }
282
283 /**
284 * Validate the options presented to the Settings tab.
285 *
286 * This is an options update filter. It filters an option value following sanitization.
287 *
288 * @param mixed $option The sanitized option value.
289 * @param string $name The option name.
290 * @param string $original_value The original value passed to the function.
291 *
292 * @throws Exception Announce SQLite failure.
293 * @since 2.3.0
294 * @since 4.3.0 Added the `$original_value` parameter.
295 *
296 * @noinspection PhpUnusedParameterInspection
297 */
298 public function validate_settings( $option, $name, $original_value ) {
299 if ( ! is_array( $option ) ) {
300 /* Weird. not an option array */
301 return $option;
302 }
303 global $wp_object_cache;
304 $former_value = get_option( $name, array() );
305
306 /* Opt in or opt out of the APCu. */
307 if ( $this->parent->apcu_extension_is_enabled() ) {
308 $apcu_choice = array_key_exists( 'use_apcu', $option ) && $option ['use_apcu'] === 'on';
309 $apcu_former = array_key_exists( 'use_apcu', $former_value ) && $former_value ['use_apcu'] === 'on';
310 if ( $apcu_choice !== $apcu_former ) {
311 if ( method_exists( $wp_object_cache, 'apcu_clear_cache' ) ) {
312 $wp_object_cache->apcu_clear_cache();
313 }
314 $this->update_wp_config_constant( 'WP_SQLITE_OBJECT_CACHE_APCU', $apcu_choice );
315 $option['use_apcu_updated'] = true;
316 }
317 $option['use_apcu'] = $apcu_choice ? 'on' : 'off';
318 }
319 if ( array_key_exists( 'flush', $option ) && $option ['flush'] === 'on' ) {
320 if ( method_exists( $wp_object_cache, 'flush' ) ) {
321 try {
322 $this->enter_maintenance_mode();
323 $wp_object_cache->flush( true );
324 } finally {
325 $this->exit_maintenance_mode();
326 }
327 } else {
328 wp_cache_flush();
329 }
330 unset ( $option['flush'] );
331 }
332 if ( array_key_exists( 'vacuum', $option ) && $option ['vacuum'] === 'on' ) {
333 if ( method_exists( $wp_object_cache, 'vacuum' ) ) {
334 try {
335 $this->enter_maintenance_mode();
336 $wp_object_cache->vacuum();
337 } finally {
338 $this->exit_maintenance_mode();
339 }
340 } else {
341 wp_cache_flush();
342 }
343 unset ( $option['vacuum'] );
344 }
345 if ( array_key_exists( 'cleanup', $option ) && $option ['cleanup'] === 'on' ) {
346 $this->parent->clean_job();
347 unset ( $option['cleanup'] );
348 }
349
350 $this->numeric_option( $option, 'target_size', 4 );
351 $this->numeric_option( $option, 'samplerate', 10 );
352 $this->numeric_option( $option, 'retainmeasurements', 2 );
353
354 if ( array_key_exists( 'capture', $option ) && $option['capture'] === 'on' ) {
355 $option['previouscapture'] = 0;
356 }
357
358 return $option;
359 }
360
361 /**
362 * Retrieve a numeric option, and set the default if it isn't present.
363 *
364 * @param array $option Option array value.
365 * @param string $name Name of the element in the option array.
366 * @param mixed $default Default value to use.
367 *
368 * @return float|int|mixed|string
369 */
370 private function numeric_option( &$option, $name, $default ) {
371 $result = $default;
372 if ( array_key_exists( $name, $option ) && is_numeric( $option[ $name ] ) ) {
373 $result = $option[ $name ];
374 $result = $result ?: $default;
375 } else {
376 $option[ $name ] = $default;
377 }
378
379 return $result;
380 }
381
382 /**
383 * Filters an option value following sanitization.
384 *
385 * @param string $option The sanitized option value.
386 * @param string $name The option name.
387 * @param string $original_value The original value passed to the function.
388 *
389 * @throws Exception Announce SQLite failure.
390 * @since 2.3.0
391 * @since 4.3.0 Added the `$original_value` parameter.
392 *
393 * @noinspection PhpUnusedParameterInspection
394 */
395 public function validate_reset_stats( $option, $name, $original_value ) {
396
397 global $wp_object_cache;
398 if ( method_exists( $wp_object_cache, 'sqlite_reset_statistics' ) ) {
399 $wp_object_cache->sqlite_reset_statistics();
400 }
401
402 /* for this post operation, we don't want to change any plugin options. */
403
404 return get_option( $name );
405 }
406
407 /**
408 * Add settings page to admin menu
409 *
410 * @return void
411 */
412 public function add_menu_item() {
413
414 $args = $this->menu_settings();
415
416 // Do nothing if wrong location key is set.
417 if ( is_array( $args ) && isset( $args['location'] ) && function_exists( 'add_' . $args['location'] . '_page' ) ) {
418 switch ( $args['location'] ) {
419 case 'options':
420 case 'submenu':
421 $page =
422 add_submenu_page( $args['parent_slug'], $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'] );
423 break;
424 case 'menu':
425 $page =
426 add_menu_page( $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'], $args['icon_url'], $args['position'] );
427 break;
428 default:
429 return;
430 }
431 add_action( 'admin_print_styles-' . $page, array( $this, 'settings_assets' ) );
432 }
433 }
434
435 /**
436 * Prepare default settings page arguments
437 *
438 * @return mixed|void
439 */
440 private function menu_settings() {
441 $this->complain_if_sqlite3_unavailable();
442
443 return apply_filters(
444 $this->base . 'menu_settings',
445 array(
446 'location' => 'options', // Possible settings: options, menu, submenu.
447 'parent_slug' => 'options-general.php',
448 'page_title' => __( 'SQLite Persistent Object Cache', 'sqlite-object-cache' ),
449 'menu_title' => __( 'Object Cache', 'sqlite-object-cache' ),
450 'capability' => 'manage_options',
451 'menu_slug' => $this->parent->_token . '_settings',
452 'function' => array( $this, 'settings_page' ),
453 'icon_url' => '',
454 'position' => null,
455 )
456 );
457 }
458
459 /**
460 * Emit a message if SQLite3 is not available.
461 *
462 * @param bool $verbose False means emit a notice. True means emit a longer explanation.
463 *
464 * @return void
465 */
466 private function complain_if_sqlite3_unavailable( $verbose = false ) {
467 if ( true !== $this->has ) {
468 if ( ! $verbose ) {
469 echo '<div class="notice notice-error sql-object-cache">';
470 echo esc_html( $this->has );
471 echo '</div>';
472 } else {
473 echo '<div>';
474 echo '<h3 style="color: #d63638">' . esc_html__( 'Server configuration problem', 'sqlite-object-cache' ) . '</h3>';
475 echo '<p>';
476
477 if ( ! class_exists( 'SQLite3' ) || ! extension_loaded( 'sqlite3' ) ) {
478 echo esc_html__( 'The SQLite Persistent Object Cache plugin requires php\'s SQLite3 extension.', 'sqlite-object-cache' ) . ' ';
479 echo esc_html__( 'That extension is not installed in your server, so the plugin cannot work.', 'sqlite-object-cache' ) . ' ';
480 }
481
482 $sqlite_version = $this->parent->sqlite_get_version();
483 if ( version_compare( $sqlite_version, $this->parent->minimum_sqlite_version ) < 0 ) {
484 echo esc_html( sprintf(
485 /* translators: 1 actual SQLite version. 2 required SQLite version) */
486 __( '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' ),
487 $sqlite_version, $this->parent->minimum_sqlite_version ) );
488 }
489
490 echo '</p></div>' . PHP_EOL;
491 }
492 }
493 }
494
495 /**
496 * Container for settings page arguments
497 *
498 * @param array $settings Settings array.
499 *
500 * @return array
501 */
502 public function configure_settings( $settings = array() ) {
503 return $settings;
504 }
505
506 /**
507 * Load settings JS & CSS
508 *
509 * @return void
510 */
511 public function settings_assets() {
512
513 }
514
515 /**
516 * Add settings link to plugin list table
517 *
518 * @param array $links Existing links.
519 *
520 * @return array Modified links.
521 */
522 public function add_settings_link( $links ) {
523 if ( true !== $this->has ) {
524 $settings_link =
525 '<a style="color: #d63638" href="options-general.php?page=' . $this->parent->_token . '_settings">' . __( 'Settings', 'sqlite-object-cache' ) . '</a>';
526 array_unshift( $links, $settings_link );
527 } else {
528 $settings_link =
529 '<a href="options-general.php?page=' . $this->parent->_token . '_settings">' . __( 'Settings', 'sqlite-object-cache' ) . '</a>';
530 $statistics_link =
531 '<a href="options-general.php?page=' . $this->parent->_token . '_settings&tab=stats">' . __( 'Statistics', 'sqlite-object-cache' ) . '</a>';
532 array_unshift( $links, $settings_link, $statistics_link );
533 }
534
535 return $links;
536 }
537
538 /**
539 * Register plugin settings
540 *
541 * @return void
542 */
543 public function register_my_settings() {
544 $this->parent->sync_apcu_global_to_option( false );
545 $this->settings = $this->settings_fields();
546
547 if ( is_array( $this->settings ) ) {
548
549 /* get the tab chosen by the user ('standard' or 'stats') */
550 $tab = isset ( $_REQUEST['tab'] ) ? sanitize_key( $_REQUEST['tab'] ) : 'standard';
551
552 $default_option = array();
553 $option_name = $this->base . 'settings';
554
555 foreach ( $this->settings as $section => $data ) {
556
557 if ( $tab && $tab !== $section ) {
558 continue;
559 }
560
561 $setting_args = array(
562 'description' => $data['title'],
563 'default' => $default_option,
564 );
565
566 // Validation callback for section.
567 if ( isset( $data['form_post_callback'] ) ) {
568 add_filter( "sanitize_option_$option_name", $data['form_post_callback'], 10, 3 );
569 }
570
571 // Add section to page.
572 add_settings_section( $section, '',
573 $data ['render_section_header'],
574 $this->parent->_token . '_settings' );
575
576 if ( array_key_exists( 'fields', $data ) && is_array( $data['fields'] ) ) {
577 foreach ( $data['fields'] as $field ) {
578 $default_option [ $field['id'] ] = $field['default'];
579 }
580
581 foreach ( $data['fields'] as $field ) {
582 // Add field to page.
583 add_settings_field(
584 $field['id'],
585 $field['label'],
586 array( $this->parent->admin, 'echo_field' ),
587 $this->parent->_token . '_settings',
588 $section,
589 array(
590 'field' => $field,
591 'option' => $option_name,
592 )
593 );
594 }
595 }
596 /* register the settings object */
597 register_setting( $this->parent->_token . '_settings', $option_name, $setting_args );
598 }
599 }
600 }
601
602 /**
603 * Settings section.
604 *
605 * @param array $section Array of section ids.
606 *
607 * @return void
608 * @throws Exception Announce Database Failure.
609 */
610 public function settings_section_header( $section ) {
611
612 $this->support_links();
613 $this->apcu_admonition();
614 $this->versions();
615
616 if ( array_key_exists( 'description', $this->settings[ $section['id'] ] ) ) {
617 echo '<p> ' . esc_html( $this->settings[ $section['id'] ]['description'] ) . '</p>' . PHP_EOL;
618 }
619 }
620
621 /**
622 * Display support and rating links.
623 *
624 * @return void
625 */
626 private function support_links() {
627
628 $supportUrl = "https://wordpress.org/support/plugin/sqlite-object-cache/";
629 $reviewUrl = "https://wordpress.org/support/plugin/sqlite-object-cache/reviews/";
630 echo '<p>';
631 echo esc_html__( 'For support please', 'sqlite-object-cache' ) . ' ';
632 echo '<a href="' . esc_url( $supportUrl ) . '">' . esc_html__( 'click here', 'sqlite-object-cache' ) . '</a>. ';
633 echo esc_html__( 'To rate this plugin please', 'sqlite-object-cache' ) . ' ';
634 echo '<a href="' . esc_url( $reviewUrl ) . '">' . esc_html__( 'click here', 'sqlite-object-cache' ) . '</a>. ';
635 echo esc_html__( 'Your feedback helps make it better, faster, and more useful', 'sqlite-object-cache' ) . '.';
636 echo '</p>';
637
638 }
639
640 private function apcu_admonition() {
641 echo '<p>';
642 echo esc_html__( 'You can use WP-CLI to configure this plugin. Please type', 'sqlite-object-cache' ) . ' ';
643 echo '<code>wp help sqlite-object-cache</code> ';
644 echo esc_html__( 'into your shell for details', 'sqlite-object-cache' ) . '.';
645 echo '</p>';
646 $option = get_option( $this->parent->_token . '_settings', array() );
647 $apcu_active = array_key_exists( 'use_apcu', $option ) && 'on' == $option['use_apcu'];
648 $apcu_enabled = $this->parent->apcu_extension_is_enabled();
649 if ( $apcu_enabled && ! $apcu_active ) {
650 echo '<p>';
651 echo esc_html__( 'On your site you can use php\'s', 'sqlite-object-cache' ) . ' ';
652 echo '<a href="https://www.php.net/manual/en/book.apcu.php" target="_blank">' . esc_html__( 'APCu User Cache', 'sqlite-object-cache' ) . '</a> ';
653 echo esc_html__( 'to improve the performance of this SQLite Object Cache. ', 'sqlite-object-cache' );
654 echo esc_html__( 'To enable APCu caching please check the "Use APCu" box.', 'sqlite-object-cache' ) . ' ';
655 echo '</p>';
656 }
657 if ( $apcu_enabled && $apcu_active ) {
658 echo '<p>';
659 echo esc_html__( 'You are using php\'s', 'sqlite-object-cache' ) . ' ';
660 echo '<a href="https://www.php.net/manual/en/book.apcu.php" target="_blank">' . esc_html__( 'APCu User Cache', 'sqlite-object-cache' ) . '</a> ';
661 echo esc_html__( 'to improve the performance of this SQLite Object Cache. ', 'sqlite-object-cache' ) . ' ';
662 echo esc_html__( 'To disable APCu caching please uncheck the "Use APCu" box.', 'sqlite-object-cache' ) . ' ';
663 echo '</p>';
664 }
665 if ( ! $apcu_enabled ) {
666 echo '<p>';
667 echo esc_html__( 'This object cache performs better when used with php\'s', 'sqlite-object-cache' ) . ' ';
668 echo '<a href="https://www.php.net/manual/en/book.apcu.php" target="_blank">' . esc_html__( 'APCu User Cache', 'sqlite-object-cache' ) . '</a> ';
669 echo esc_html__( 'extension. ', 'sqlite-object-cache' ) . ' ';
670 echo esc_html__( 'However, it is not available on your server.', 'sqlite-object-cache' ) . ' ';
671 echo esc_html__( 'It may be possible for you or your hosting service to install it.', 'sqlite-object-cache' ) . ' ';
672 echo '</p>';
673 }
674 }
675
676 /**
677 * Display versions.
678 *
679 * @return void
680 * @throws Exception Announce database failure.
681 */
682 private function versions() {
683 global $wp_object_cache;
684 global $wp_version;
685 $igbinary = function_exists( 'igbinary_serialize' ) && function_exists( 'igbinary_unserialize' )
686 ? phpversion( 'igbinary' )
687 : __( 'unavailable', 'sqlite-object-cache' );
688 $force_serialize = defined( 'WP_SQLITE_OBJECT_CACHE_SERIALIZE' ) && WP_SQLITE_OBJECT_CACHE_SERIALIZE;
689 $igbinary .= $force_serialize ? esc_html__( '(disabled by WP_SQLITE_OBJECT_CACHE_SERIALIZE)', 'sqlite-object-cache' ) : '';
690
691 $apcu_version = $this->parent->apcu_extension_is_enabled()
692 ? phpversion( "apcu" )
693 : __( 'unavailable', 'sqlite-object-cache' );
694
695 if ( method_exists( $wp_object_cache, 'sqlite_get_version' ) ) {
696 echo '<p>' . esc_html( sprintf(
697 /* translators: 1: version for sqlite 2: version for php 3: webserver version 4: version for plugin 5: igbinary 6:APCu 7:WordPress */
698 __( 'Versions: WordPress: %7$s SQLite: %1$s php: %2$s Server: %3$s Plugin: %4$s APCu: %6$s igbinary: %5$s.', 'sqlite-object-cache' ),
699 $wp_object_cache->sqlite_get_version(),
700 PHP_VERSION,
701 $_SERVER['SERVER_SOFTWARE'],
702 $this->parent->_version,
703 $igbinary,
704 $apcu_version,
705 $wp_version ) ) . '</p>';
706 }
707 }
708
709 /**
710 * Statistics tab section.
711 *
712 * @param array $section Array of section ids.
713 *
714 * @return void
715 * @throws Exception Announce Database Failure.
716 * @noinspection PhpUnusedParameterInspection
717 */
718 public function stats_section_header( $section ) {
719
720 $this->support_links();
721 $this->versions();
722
723 $stats = new SQLite_Object_Cache_Statistics ( get_option( $this->parent->_token . '_settings', array() ) );
724 $stats->init();
725 $stats->render();
726 $stats->render_usage();
727 }
728
729 /**
730 * Load settings page content.
731 *
732 * @return void
733 */
734 public function settings_page() {
735
736 $this->enqueue_assets();
737 /* get the tab chosen by the user ('standard' by default or 'stats') */
738 $tab = isset ( $_REQUEST['tab'] ) ? sanitize_key( $_REQUEST['tab'] ) : 'standard';
739
740 // Build page HTML.
741 echo '<div class="wrap" id="' . esc_attr( $this->parent->_token . '_settings' ) . '">' . PHP_EOL;
742 echo '<h2>' . esc_html__( 'SQLite Persistent Object Cache', 'sqlite-object-cache' ) . '</h2>' . PHP_EOL;
743
744 $submit_caption = __( 'Save Settings', 'sqlite-object-cache' );
745 $this->complain_if_sqlite3_unavailable( true );
746
747 // Show page tabs.
748 if ( is_array( $this->settings ) && 1 < count( $this->settings ) ) {
749
750 echo '<h2 class="nav-tab-wrapper">' . PHP_EOL;
751
752 foreach ( $this->settings as $section => $data ) {
753
754 // Set tab class.
755 $class = 'nav-tab';
756 if ( $section === $tab ) {
757 $class .= ' nav-tab-active';
758 $submit_caption = $data['submit'];
759 }
760
761 // Set tab link.
762 $tab_link = add_query_arg( array( 'tab' => $section ) );
763 $tab_link = remove_query_arg( 'settings-updated', $tab_link );
764
765 // Output tab.
766 echo '<a href="' . esc_url( $tab_link ) . '" class="' . esc_attr( $class ) . '">' . esc_html( $data['title'] ) . '</a>' . PHP_EOL;
767 }
768
769 if ( 'stats' === $tab ) {
770 echo '<a href="' . esc_url( self::$statistics_help_url ) . '" class="nav-tab" target="_blank">' . esc_html__( 'Help', 'sqlite-object-cache' ) . '</a>' . PHP_EOL;
771 }
772
773 echo '</h2>' . PHP_EOL;
774 }
775
776 /** @noinspection HtmlUnknownTarget */
777 echo '<form method="post" action="options.php" enctype="multipart/form-data">' . PHP_EOL;
778
779 // Get settings fields.
780 settings_fields( $this->parent->_token . '_settings' );
781 do_settings_sections( $this->parent->_token . '_settings' );
782
783 echo '<p class="submit">' . PHP_EOL;
784 echo '<input type="hidden" name="tab" value="' . esc_attr( $tab ) . '" />' . PHP_EOL;
785 echo '<input name="Submit" type="submit" class="button-primary" value="' . esc_attr( $submit_caption ) . '" />' . PHP_EOL;
786 echo '</p></form></div>' . PHP_EOL;
787 }
788
789 /**
790 * Admin enqueue assets
791 *
792 * @param string $hook Hook parameter.
793 *
794 * @return void
795 * @noinspection PhpUnusedParameterInspection
796 */
797 public function enqueue_assets( $hook = '' ) {
798 wp_register_style( $this->parent->_token . '-admin',
799 esc_url( $this->parent->assets_url ) . 'css/admin.css',
800 array(), $this->parent->_version );
801 wp_enqueue_style( $this->parent->_token . '-admin' );
802 }
803
804 /**
805 * Enters maintenance mode.
806 *
807 * @return void
808 */
809 private function enter_maintenance_mode() {
810 $maintenanceFileName = ABSPATH . '.maintenance';
811 $maintain = array();
812 array_push( $maintain,
813 '<?php',
814 '$upgrading = ' . time() . ';',
815 '?>' );
816 file_put_contents( $maintenanceFileName, implode( PHP_EOL, $maintain ) );
817 }
818
819 /**
820 * Exits maintenance mode.
821 *
822 * @return void
823 */
824 private function exit_maintenance_mode() {
825 $maintenanceFileName = ABSPATH . '.maintenance';
826 unlink( $maintenanceFileName );
827 }
828
829 /**
830 * Update a variable in the wp-config.php file.
831 *
832 * If enabling, check if the variable is defined, and if not, define it.
833 * Vice versa for disabling.
834 */
835 private function update_wp_config_constant( $symbol, $enable ) {
836
837 /**
838 * Follow WP's logic to locate wp-config file
839 * @see wp-load.php
840 */
841 $conf_file = ABSPATH . 'wp-config.php';
842 if ( ! file_exists( $conf_file ) ) {
843 $conf_file = dirname( ABSPATH ) . '/wp-config.php';
844 }
845 $config = new SQLite_Object_Cache_File( $conf_file );
846 // Remove the line `define('WHATEVER', true/false);` first
847 $removed = $config->remove( $symbol );
848 $inserted = false;
849 if ( $enable ) {
850 $cmd = "define( '" . $symbol . "', true );";
851 $inserted = $config->insert_before( $cmd );
852 }
853 if ( $removed !== $inserted ) {
854 $config->save();
855 }
856 return true;
857 }
858
859 /**
860 * Put troubleshooting information into Site Health - Info
861 *
862 * @param array $info
863 *
864 * @return array
865 */
866 public function debug_information( $info ) {
867 global $wp_object_cache;
868
869 $stanza = array(
870 'label' => 'SQLite Object Cache',
871 'fields' => array()
872 );
873
874 try {
875 $option = get_option( $this->parent->_token . '_settings', array() );
876
877 $stanza['fields']['configuration'] = array(
878 'label' => __( 'Configuration', 'sqlite-object-cache' ),
879 'value' => $this->flatten( $option ),
880 );
881
882 } catch ( \Exception $e ) {
883 /* empty, intentionally */
884 }
885
886 ob_start();
887 try {
888 $this->versions();
889 $versions = wp_strip_all_tags( ob_get_clean() );
890 $stanza['fields']['versions'] = array(
891 'label' => __( 'Versions', 'sqlite-object-cache' ),
892 'value' => $versions,
893 );
894 } catch ( \Exception $e ) {
895 ob_get_clean();
896 }
897
898 try {
899 $settings = array();
900 if ( $this->parent->apcu_extension_is_enabled() ) {
901 $settings = array_merge( $settings, ini_get_all( 'apcu' ) );
902 }
903 $settings = array_merge( $settings, ini_get_all( 'sqlite3' ) );
904 $settings = array_merge( $settings, $this->inis_get( 'session.save_handler', 'session.auto_start', 'session.lifetime', 'session.gc_maxlifetime' ) );
905
906 $stanza['fields']['settings'] = array(
907 'label' => __( 'Settings', 'sqlite-object-cache' ),
908 'value' => $this->flatten( $settings ),
909 );
910 } catch ( \Exception $e ) {
911 /* empty, intentionally */
912 }
913
914 try {
915 if ( $this->parent->apcu_extension_is_enabled() ) {
916
917 $sizes = array();
918 $counts = array();
919 $totalsize = 0;
920 $totalcount = 0;
921 foreach ( new APCUIterator( null, APC_ITER_KEY | APC_ITER_MEM_SIZE ) as $item ) {
922 $salt = '?';
923 $splits = explode( '|', $item['key'], 2 );
924 if ( count( $splits ) >= 2 ) {
925 $salt = $splits[0];
926 }
927 if ( ! array_key_exists( $salt, $sizes ) ) {
928 $sizes[ $salt ] = 0;
929 $counts[ $salt ] = 0;
930 }
931 $sizes[ $salt ] += $item['mem_size'];
932 $totalsize += $item['mem_size'];
933 $counts [ $salt ] += 1;
934 $totalcount += 1;
935 }
936
937 $apcusalt = trim( property_exists( $wp_object_cache, 'apcusalt' ) ? $wp_object_cache->apcusalt : '', '|' );
938 $settings = array();
939 $settings ['salt'] = $apcusalt;
940 $settings ['total'] = "$totalsize($totalcount)";
941 $siteno = 1;
942 foreach ( $sizes as $salt => $val ) {
943 switch ( $salt ) {
944 case $apcusalt:
945 $csalt = 'mine';
946 break;
947 case '?':
948 $csalt = 'no salt';
949 break;
950 default:
951 $csalt = 'site' . $siteno ++;
952 break;
953 }
954 $settings[ $csalt ] = "$val($counts[$salt])";
955 }
956
957
958 $stanza['fields']['apcu_utilization'] = array(
959 'label' => __( 'APCu utilization', 'sqlite-object-cache' ),
960 'value' => $this->flatten( $settings ),
961 );
962
963 $apcu_cache_info = array();
964 $apcu_cache_info = array_merge( $apcu_cache_info, apcu_cache_info( true ) );
965 $apcu_cache_info = array_merge( $apcu_cache_info, apcu_sma_info( true ) );
966
967 $stanza['fields']['apcu_cache_info'] = array(
968 'label' => __( 'APCu info', 'sqlite-object-cache' ),
969 'value' => $this->flatten( $apcu_cache_info ),
970 );
971
972
973 }
974 } catch ( \Exception $e ) {
975 /* empty, intentionally */
976 }
977
978 try {
979 global $wp_object_cache;
980 if ( method_exists( $wp_object_cache, 'sqlite_sizes' ) ) {
981 $stanza['fields']['sqlite_sizes'] = array(
982 'label' => __( 'SQLite utilization', 'sqlite-object-cache' ),
983 'value' => $this->flatten( $wp_object_cache->sqlite_sizes() ),
984 );
985 }
986 } catch ( \Exception $e ) {
987 /* empty, intentionally */
988 }
989
990
991 $info['sqlite-object-cache'] = $stanza;
992 return $info;
993 }
994
995 private function inis_get( ...$list ) {
996 $result = array();
997 foreach ( $list as $item ) {
998 $val = ini_get( $item );
999 $result[ $item ] = $val ?: '-missing-';
1000 }
1001 return $result;
1002
1003 }
1004
1005 private function flatten( $a ) {
1006 $result = array();
1007 foreach ( $a as $k => $v ) {
1008 if ( is_array( $v ) ) {
1009 if ( array_key_exists( 'local_value', $v ) ) {
1010 $v = $v['local_value'];
1011 } else {
1012 $v = var_export( $v, true );
1013 }
1014 }
1015 $result [] = $k . ':' . $v;
1016 }
1017 return implode( '; ', $result );
1018 }
1019
1020
1021 }
1022