PluginProbe
SQLite Object Cache / 1.5.5
SQLite Object Cache v1.5.5
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.5.5, at includes/class-sqlite-object-cache-settings.php

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