PluginProbe
SQLite Object Cache / 1.6.2
SQLite Object Cache v1.6.2
1.6.5 trunk 0.1.7 1.0.0 1.1.0 1.1.1 1.2.0 1.2.1 1.2.2 1.2.3 1.3.0 1.3.1 1.3.2 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.4.0 1.4.1 1.5.1 1.5.4 1.5.5 1.5.6 1.5.7 All 30 releases
← All changes | includes/class-sqlite-object-cache-settings.php +621 -97 1.3.71.6.2 View file →
@@ -15,8 +15,10 @@
15 15 * Settings class.
16 16 */
17 17 class SQLite_Object_Cache_Settings {
18 18
19 + static $statistics_help_url = 'https://www.plumislandmedia.net/wordpress-plugins/sqlite-object-cache/statistics-from-sqlite-object-cache/';
20 +
19 21 /**
20 22 * The main plugin object.
21 23 *
22 24 * @var object
@@ -44,52 +46,135 @@
44 46 */
45 47 public $has = '';
46 48
47 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 + /**
48 59 * Constructor function.
49 60 *
50 61 * @param object $parent Parent object.
62 + * @param string $plugin_file Plugin top-level file name.
51 63 */
52 - public function __construct( $parent ) {
53 - $this->parent = $parent;
54 - $this->has = $parent->has_sqlite();
55 - $this->base = 'sqlite_object_cache_';
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;
56 69
57 - // Initialise settings.
58 - add_action( 'init', array( $this, 'init_settings' ), 11 );
59 -
60 70 // Register plugin settings.
61 71 add_action( 'admin_init', array( $this, 'register_my_settings' ) );
62 72
63 - // Add settings page to menu.
64 - add_action( 'admin_menu', array( $this, 'add_menu_item' ) );
73 + if ( is_main_site() ) {
74 + // Information for Site Health Info
75 + add_filter( 'debug_information', array( $this, 'debug_information' ) );
65 76
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 - );
77 + // Add settings page to menu.
78 + add_action( 'admin_menu', array( $this, 'add_menu_item' ) );
74 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 +
75 95 // Configure placement of plugin settings page. See readme for implementation.
76 96 add_filter( $this->base . 'menu_settings', array( $this, 'configure_settings' ) );
77 97
78 - // Load admin JS & CSS.
79 - add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ), 10, 1 );
98 + // Spoonsor link.
99 + add_filter( 'plugin_row_meta', array( $this, 'filter_plugin_row_meta' ), 10, 2 );
100 +
80 101 }
81 102
82 103 /**
83 - * Initialize settings
104 + * Fires after the value of our option has been successfully updated.
84 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 + *
85 122 * @return void
86 123 */
87 - public function init_settings() {
88 - $this->settings = $this->settings_fields();
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 + /* Empty, intentionally. Don't crash on asset cleanup. */
141 + } finally {
142 + restore_current_blog();
143 + }
144 + }
145 + }
89 146 }
90 147
91 148 /**
149 + * Filters the array of row meta for each plugin in the Plugins list table.
150 + *
151 + * @param array<int, string> $plugin_meta An array of the plugin's metadata.
152 + * @param string $plugin_file Path to the plugin file relative to the plugins directory.
153 + *
154 + * @return array<int, string> Updated array of the plugin's metadata.
155 + */
156 + public function filter_plugin_row_meta( array $plugin_meta, $plugin_file ) {
157 + if ( $this->plugin_file !== $plugin_file ) {
158 + return $plugin_meta;
159 + }
160 +
161 + if ( is_multisite() && ( is_network_admin() || ! is_main_site() ) ) {
162 + $plugin_meta[] =
163 + esc_html__( 'See the main site\'s dashboard for settings.', 'sqlite-object-cache' );
164 + }
165 +
166 + /** @noinspection HtmlUnknownTarget */
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 + /**
92 177 * Build settings fields
93 178 *
94 179 * @return array Fields to be displayed on settings page
95 180 */
@@ -94,74 +179,106 @@
94 179 * @return array Fields to be displayed on settings page
95 180 */
96 181 private function settings_fields() {
97 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, including all transients) 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 + array(
252 + 'id' => 'adminbarflush',
253 + 'label' => __( 'Show', 'sqlite-object-cache' ),
254 + 'description' => __( 'Flush Object Cache button in the admin bar.', 'sqlite-object-cache' ),
255 + 'type' => 'checkbox',
256 + 'default' => '',
257 + )
258 + );
259 +
260 + if ( $this->parent->apcu_extension_is_enabled() ) {
261 + array_unshift( $fields, array(
262 + 'id' => 'use_apcu',
263 + 'label' => __( 'Use APCu', 'sqlite-object-cache' ),
264 + 'description' => __( 'Check to enable the use of php\'s APCu cache to improve performance.', 'sqlite-object-cache' ),
265 + 'type' => 'checkbox',
266 + 'default' => '',
267 + ) );
268 +
269 + }
270 +
98 271 $settings['standard'] = array(
99 - 'title' => __( 'Settings', 'sqlite-object-cache' ),
100 - 'submit' => __( 'Save Settings', 'sqlite-object-cache' ),
272 + // Using core I18n.
273 + // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
274 + 'title' => __( 'Settings' ),
275 + // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
276 + 'submit' => __( 'Save Changes' ),
101 277 'description' => '',
102 278 'render_section_header' => array( $this, 'settings_section_header' ),
103 279 'form_post_callback' => array( $this, 'validate_settings' ),
104 - 'fields' => array(
105 - array(
106 - 'id' => 'flush',
107 - 'label' => __( 'Flush now', 'sqlite-object-cache' ),
108 - 'description' => __( 'Check to flush the cache (delete all its entries) now.', 'sqlite-object-cache' ) . ' ' .
109 - __( 'This briefly puts your site into maintenance mode.', 'sqlite-object-cache' ),
110 - 'type' => 'checkbox',
111 - 'default' => '',
112 - 'reset' => '',
113 - ),
114 - array(
115 - 'id' => 'target_size',
116 - 'label' => __( 'Cached data size', 'sqlite-object-cache' ),
117 - 'description' => __( 'MiB. When data in the cache grows larger than this, hourly cleanup removes the oldest entries.', 'sqlite-object-cache' ),
118 - 'type' => 'number',
119 - 'default' => 16,
120 - 'min' => 1,
121 - 'step' => 'any',
122 - 'cssclass' => 'narrow',
123 - 'placeholder' => __( 'MiB.', 'sqlite-object-cache' ),
124 - ),
125 - array(
126 - 'id' => 'cleanup',
127 - 'label' => __( 'Clean up now', 'sqlite-object-cache' ),
128 - 'description' => __( 'Check to clean up the cache (delete old data) now.', 'sqlite-object-cache' ),
129 - 'type' => 'checkbox',
130 - 'default' => '',
131 - 'reset' => '',
132 - ),
133 - array(
134 - 'id' => 'capture',
135 - 'label' => __( 'Measure performance', 'sqlite-object-cache' ),
136 - 'description' => __( 'Check to measure cache performance. ', 'sqlite-object-cache' ),
137 - 'type' => 'checkbox',
138 - 'default' => '',
139 - ),
140 - array(
141 - 'id' => 'samplerate',
142 - 'label' => __( 'Measure', 'sqlite-object-cache' ),
143 - 'description' => __( 'percent of requests, randomly sampled.', 'sqlite-object-cache' ),
144 - 'type' => 'number',
145 - 'default' => 1,
146 - 'max' => 100,
147 - 'min' => 0,
148 - 'step' => 'any',
149 - 'cssclass' => 'narrow',
150 - 'placeholder' => __( 'Sampling percentage.', 'sqlite-object-cache' ),
151 - ),
152 - array(
153 - 'id' => 'retainmeasurements',
154 - 'label' => __( 'Retain measurements for', 'sqlite-object-cache' ),
155 - 'description' => __( 'hours.', 'sqlite-object-cache' ),
156 - 'type' => 'number',
157 - 'default' => 2,
158 - 'min' => 0.1,
159 - 'step' => 'any',
160 - 'cssclass' => 'narrow',
161 - 'placeholder' => __( 'Hours to retain.', 'sqlite-object-cache' ),
162 - ),
163 - ),
280 + 'fields' => $fields,
164 281 );
165 282
166 283 $settings['stats'] = array(
167 284 'title' => __( 'Statistics', 'sqlite-object-cache' ),
@@ -169,9 +286,9 @@
169 286 'render_section_header' => array( $this, 'stats_section_header' ),
170 287 'form_post_callback' => array( $this, 'validate_reset_stats' ),
171 288 );
172 289
173 - return apply_filters( $this->parent->_token . '_settings_fields', $settings );
290 + return apply_filters( 'sqlite_object_cache_settings_fields', $settings );
174 291 }
175 292
176 293 /**
177 294 * Validate the options presented to the Settings tab.
@@ -189,13 +306,27 @@
189 306 * @noinspection PhpUnusedParameterInspection
190 307 */
191 308 public function validate_settings( $option, $name, $original_value ) {
192 309 if ( ! is_array( $option ) ) {
193 - /* weird. not an option array */
310 + /* Weird. not an option array */
194 311 return $option;
195 312 }
196 313 global $wp_object_cache;
314 + $former_value = get_option( $name, array() );
197 315
316 + /* Opt in or opt out of the APCu. */
317 + if ( $this->parent->apcu_extension_is_enabled() ) {
318 + $apcu_choice = array_key_exists( 'use_apcu', $option ) && $option ['use_apcu'] === 'on';
319 + $apcu_former = array_key_exists( 'use_apcu', $former_value ) && $former_value ['use_apcu'] === 'on';
320 + if ( $apcu_choice !== $apcu_former ) {
321 + if ( method_exists( $wp_object_cache, 'apcu_clear_cache' ) ) {
322 + $wp_object_cache->apcu_clear_cache();
323 + }
324 + $this->update_wp_config_constant( 'WP_SQLITE_OBJECT_CACHE_APCU', $apcu_choice );
325 + $option['use_apcu_updated'] = true;
326 + }
327 + $option['use_apcu'] = $apcu_choice ? 'on' : 'off';
328 + }
198 329 if ( array_key_exists( 'flush', $option ) && $option ['flush'] === 'on' ) {
199 330 if ( method_exists( $wp_object_cache, 'flush' ) ) {
200 331 try {
201 332 $this->enter_maintenance_mode();
@@ -207,8 +338,21 @@
207 338 wp_cache_flush();
208 339 }
209 340 unset ( $option['flush'] );
210 341 }
342 + if ( array_key_exists( 'vacuum', $option ) && $option ['vacuum'] === 'on' ) {
343 + if ( method_exists( $wp_object_cache, 'vacuum' ) ) {
344 + try {
345 + $this->enter_maintenance_mode();
346 + $wp_object_cache->vacuum();
347 + } finally {
348 + $this->exit_maintenance_mode();
349 + }
350 + } else {
351 + wp_cache_flush();
352 + }
353 + unset ( $option['vacuum'] );
354 + }
211 355 if ( array_key_exists( 'cleanup', $option ) && $option ['cleanup'] === 'on' ) {
212 356 $this->parent->clean_job();
213 357 unset ( $option['cleanup'] );
214 358 }
@@ -306,9 +450,9 @@
306 450 private function menu_settings() {
307 451 $this->complain_if_sqlite3_unavailable();
308 452
309 453 return apply_filters(
310 - $this->base . 'menu_settings',
454 + 'sqlite_object_cachemenu_settings',
311 455 array(
312 456 'location' => 'options', // Possible settings: options, menu, submenu.
313 457 'parent_slug' => 'options-general.php',
314 458 'page_title' => __( 'SQLite Persistent Object Cache', 'sqlite-object-cache' ),
@@ -406,11 +550,15 @@
406 550 *
407 551 * @return void
408 552 */
409 553 public function register_my_settings() {
554 + $this->parent->sync_apcu_global_to_option( false );
555 + $this->settings = $this->settings_fields();
556 +
410 557 if ( is_array( $this->settings ) ) {
411 558
412 559 /* get the tab chosen by the user ('standard' or 'stats') */
560 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended
413 561 $tab = isset ( $_REQUEST['tab'] ) ? sanitize_key( $_REQUEST['tab'] ) : 'standard';
414 562
415 563 $default_option = array();
416 564 $option_name = $this->base . 'settings';
@@ -445,9 +593,9 @@
445 593 // Add field to page.
446 594 add_settings_field(
447 595 $field['id'],
448 596 $field['label'],
449 - array( $this->parent->admin, 'echo_field' ),
597 + array( $this, 'echo_field' ),
450 598 $this->parent->_token . '_settings',
451 599 $section,
452 600 array(
453 601 'field' => $field,
@@ -472,8 +620,9 @@
472 620 */
473 621 public function settings_section_header( $section ) {
474 622
475 623 $this->support_links();
624 + $this->apcu_admonition();
476 625 $this->versions();
477 626
478 627 if ( array_key_exists( 'description', $this->settings[ $section['id'] ] ) ) {
479 628 echo '<p> ' . esc_html( $this->settings[ $section['id'] ]['description'] ) . '</p>' . PHP_EOL;
@@ -495,10 +644,47 @@
495 644 echo esc_html__( 'To rate this plugin please', 'sqlite-object-cache' ) . ' ';
496 645 echo '<a href="' . esc_url( $reviewUrl ) . '">' . esc_html__( 'click here', 'sqlite-object-cache' ) . '</a>. ';
497 646 echo esc_html__( 'Your feedback helps make it better, faster, and more useful', 'sqlite-object-cache' ) . '.';
498 647 echo '</p>';
648 +
499 649 }
500 650
651 + private function apcu_admonition() {
652 + echo '<p>';
653 + echo esc_html__( 'You can use WP-CLI to configure this plugin. Please type', 'sqlite-object-cache' ) . ' ';
654 + echo '<code>wp help sqlite-object-cache</code> ';
655 + echo esc_html__( 'into your shell for details', 'sqlite-object-cache' ) . '.';
656 + echo '</p>';
657 + $option = get_option( $this->parent->_token . '_settings', array() );
658 + $apcu_active = array_key_exists( 'use_apcu', $option ) && 'on' == $option['use_apcu'];
659 + $apcu_enabled = $this->parent->apcu_extension_is_enabled();
660 + if ( $apcu_enabled && ! $apcu_active ) {
661 + echo '<p>';
662 + echo esc_html__( 'On your site you can use php\'s', 'sqlite-object-cache' ) . ' ';
663 + echo '<a href="https://www.php.net/manual/en/book.apcu.php" target="_blank">' . esc_html__( 'APCu User Cache', 'sqlite-object-cache' ) . '</a> ';
664 + echo esc_html__( 'to improve the performance of this SQLite Object Cache. ', 'sqlite-object-cache' );
665 + echo esc_html__( 'To enable APCu caching please check the "Use APCu" box.', 'sqlite-object-cache' ) . ' ';
666 + echo '</p>';
667 + }
668 + if ( $apcu_enabled && $apcu_active ) {
669 + echo '<p>';
670 + echo esc_html__( 'You are using php\'s', 'sqlite-object-cache' ) . ' ';
671 + echo '<a href="https://www.php.net/manual/en/book.apcu.php" target="_blank">' . esc_html__( 'APCu User Cache', 'sqlite-object-cache' ) . '</a> ';
672 + echo esc_html__( 'to improve the performance of this SQLite Object Cache. ', 'sqlite-object-cache' ) . ' ';
673 + echo esc_html__( 'To disable APCu caching please uncheck the "Use APCu" box.', 'sqlite-object-cache' ) . ' ';
674 + echo '</p>';
675 + }
676 + if ( ! $apcu_enabled ) {
677 + echo '<p>';
678 + echo esc_html__( 'This object cache performs better when used with php\'s', 'sqlite-object-cache' ) . ' ';
679 + echo '<a href="https://www.php.net/manual/en/book.apcu.php" target="_blank">' . esc_html__( 'APCu User Cache', 'sqlite-object-cache' ) . '</a> ';
680 + echo esc_html__( 'extension. ', 'sqlite-object-cache' ) . ' ';
681 + echo esc_html__( 'However, it is not available on your server.', 'sqlite-object-cache' ) . ' ';
682 + echo esc_html__( 'It may be possible for you or your hosting service to install it.', 'sqlite-object-cache' ) . ' ';
683 + echo '</p>';
684 + }
685 + }
686 +
501 687 /**
502 688 * Display versions.
503 689 *
504 690 * @return void
@@ -505,21 +691,37 @@
505 691 * @throws Exception Announce database failure.
506 692 */
507 693 private function versions() {
508 694 global $wp_object_cache;
509 - $igbinary = function_exists( 'igbinary_serialize' ) && function_exists( 'igbinary_unserialize' )
510 - ? __( 'available', 'sqlite-object-cache' )
695 + global $wp_version;
696 + $igbinary = function_exists( 'igbinary_serialize' ) && function_exists( 'igbinary_unserialize' )
697 + ? phpversion( 'igbinary' )
511 698 : __( 'unavailable', 'sqlite-object-cache' );
699 + $force_serialize = defined( 'WP_SQLITE_OBJECT_CACHE_SERIALIZE' ) && WP_SQLITE_OBJECT_CACHE_SERIALIZE;
700 + $igbinary .= $force_serialize ? esc_html__( '(disabled by WP_SQLITE_OBJECT_CACHE_SERIALIZE)', 'sqlite-object-cache' ) : '';
512 701
702 + $apcu_version = $this->parent->apcu_extension_is_enabled()
703 + ? phpversion( "apcu" )
704 + : __( 'unavailable', 'sqlite-object-cache' );
705 +
513 706 if ( method_exists( $wp_object_cache, 'sqlite_get_version' ) ) {
707 + $server_software = 'unk';
708 + // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.ValidatedSanitizedInput.MissingUnslash
709 + if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && is_string( $_SERVER['SERVER_SOFTWARE'] ) ) {
710 + $server_software = $_SERVER['SERVER_SOFTWARE'];
711 + }
712 + // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.ValidatedSanitizedInput.MissingUnslash
514 713 echo '<p>' . esc_html( sprintf(
515 - /* translators: 1: version for sqlite 2: version for php 3: webserver version 4: version for plugin 5: status of igbinary */
516 - __( 'Versions: SQLite: %1$s php: %2$s Server: %3$s Plugin: %4$s igbinary: %5$s.', 'sqlite-object-cache' ),
714 + /* translators: 1: version for sqlite 2: version for php 3: webserver version 4: version for plugin 5: igbinary 6:APCu 7:WordPress */
715 + __( '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' ),
517 716 $wp_object_cache->sqlite_get_version(),
518 717 PHP_VERSION,
519 - $_SERVER['SERVER_SOFTWARE'],
718 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended
719 + $server_software,
520 720 $this->parent->_version,
521 - $igbinary ) ) . '</p>';
721 + $igbinary,
722 + $apcu_version,
723 + $wp_version ) ) . '</p>';
522 724 }
523 725 }
524 726
525 727 /**
@@ -548,9 +750,11 @@
548 750 * @return void
549 751 */
550 752 public function settings_page() {
551 753
754 + $this->enqueue_assets();
552 755 /* get the tab chosen by the user ('standard' by default or 'stats') */
756 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended
553 757 $tab = isset ( $_REQUEST['tab'] ) ? sanitize_key( $_REQUEST['tab'] ) : 'standard';
554 758
555 759 // Build page HTML.
556 760 echo '<div class="wrap" id="' . esc_attr( $this->parent->_token . '_settings' ) . '">' . PHP_EOL;
@@ -580,8 +784,12 @@
580 784 // Output tab.
581 785 echo '<a href="' . esc_url( $tab_link ) . '" class="' . esc_attr( $class ) . '">' . esc_html( $data['title'] ) . '</a>' . PHP_EOL;
582 786 }
583 787
788 + if ( 'stats' === $tab ) {
789 + echo '<a href="' . esc_url( self::$statistics_help_url ) . '" class="nav-tab" target="_blank">' . esc_html__( 'Help', 'sqlite-object-cache' ) . '</a>' . PHP_EOL;
790 + }
791 +
584 792 echo '</h2>' . PHP_EOL;
585 793 }
586 794
587 795 /** @noinspection HtmlUnknownTarget */
@@ -587,10 +795,10 @@
587 795 /** @noinspection HtmlUnknownTarget */
588 796 echo '<form method="post" action="options.php" enctype="multipart/form-data">' . PHP_EOL;
589 797
590 798 // Get settings fields.
591 - settings_fields( $this->parent->_token . '_settings' );
592 - do_settings_sections( $this->parent->_token . '_settings' );
799 + settings_fields( 'sqlite_object_cache_settings' );
800 + do_settings_sections( 'sqlite_object_cache_settings' );
593 801
594 802 echo '<p class="submit">' . PHP_EOL;
595 803 echo '<input type="hidden" name="tab" value="' . esc_attr( $tab ) . '" />' . PHP_EOL;
596 804 echo '<input name="Submit" type="submit" class="button-primary" value="' . esc_attr( $submit_caption ) . '" />' . PHP_EOL;
@@ -633,7 +841,323 @@
633 841 * @return void
634 842 */
635 843 private function exit_maintenance_mode() {
636 844 $maintenanceFileName = ABSPATH . '.maintenance';
637 - unlink( $maintenanceFileName );
845 + wp_delete_file( $maintenanceFileName );
638 846 }
847 +
848 + /**
849 + * Update a variable in the wp-config.php file.
850 + *
851 + * If enabling, check if the variable is defined, and if not, define it.
852 + * Vice versa for disabling.
853 + */
854 + private function update_wp_config_constant( $symbol, $enable ) {
855 +
856 + /**
857 + * Follow WP's logic to locate wp-config file
858 + * @see wp-load.php
859 + */
860 + $conf_file = ABSPATH . 'wp-config.php';
861 + if ( ! file_exists( $conf_file ) ) {
862 + $conf_file = dirname( ABSPATH ) . '/wp-config.php';
863 + }
864 + $config = new SQLite_Object_Cache_File( $conf_file );
865 + // Remove the line `define('WHATEVER', true/false);` first
866 + $removed = $config->remove( $symbol );
867 + $inserted = false;
868 + if ( $enable ) {
869 + $cmd = "define( '" . $symbol . "', true );";
870 + $inserted = $config->insert_before( $cmd );
871 + }
872 + if ( $removed !== $inserted ) {
873 + $config->save();
874 + }
875 + return true;
876 + }
877 +
878 + /**
879 + * Put troubleshooting information into Site Health - Info
880 + *
881 + * @param array $info
882 + *
883 + * @return array
884 + */
885 + public function debug_information( $info ) {
886 + global $wp_object_cache;
887 +
888 + $stanza = array(
889 + 'label' => 'SQLite Object Cache',
890 + 'fields' => array()
891 + );
892 +
893 + try {
894 + $option = get_option( $this->parent->_token . '_settings', array() );
895 +
896 + $stanza['fields']['configuration'] = array(
897 + 'label' => __( 'Configuration', 'sqlite-object-cache' ),
898 + 'value' => $this->flatten( $option ),
899 + );
900 +
901 + } catch ( \Exception $e ) {
902 + /* empty, intentionally */
903 + }
904 +
905 + ob_start();
906 + try {
907 + $this->versions();
908 + $versions = wp_strip_all_tags( ob_get_clean() );
909 + $stanza['fields']['versions'] = array(
910 + 'label' => __( 'Versions', 'sqlite-object-cache' ),
911 + 'value' => $versions,
912 + );
913 + } catch ( \Exception $e ) {
914 + ob_get_clean();
915 + }
916 +
917 + try {
918 + $settings = array();
919 + if ( $this->parent->apcu_extension_is_enabled() ) {
920 + $settings = array_merge( $settings, ini_get_all( 'apcu' ) );
921 + }
922 + $settings = array_merge( $settings, ini_get_all( 'sqlite3' ) );
923 + $settings = array_merge( $settings, $this->inis_get( 'session.save_handler', 'session.auto_start', 'session.lifetime', 'session.gc_maxlifetime' ) );
924 +
925 + $stanza['fields']['settings'] = array(
926 + 'label' => __( 'Settings', 'sqlite-object-cache' ),
927 + 'value' => $this->flatten( $settings ),
928 + );
929 + } catch ( \Exception $e ) {
930 + /* empty, intentionally */
931 + }
932 +
933 + try {
934 + if ( $this->parent->apcu_extension_is_enabled() ) {
935 +
936 + $sizes = array();
937 + $counts = array();
938 + $totalsize = 0;
939 + $totalcount = 0;
940 + foreach ( new APCUIterator( null, APC_ITER_KEY | APC_ITER_MEM_SIZE ) as $item ) {
941 + $salt = '?';
942 + $splits = explode( '|', $item['key'], 2 );
943 + if ( count( $splits ) >= 2 ) {
944 + $salt = $splits[0];
945 + }
946 + if ( ! array_key_exists( $salt, $sizes ) ) {
947 + $sizes[ $salt ] = 0;
948 + $counts[ $salt ] = 0;
949 + }
950 + $sizes[ $salt ] += $item['mem_size'];
951 + $totalsize += $item['mem_size'];
952 + $counts [ $salt ] += 1;
953 + $totalcount += 1;
954 + }
955 +
956 + $apcusalt = trim( property_exists( $wp_object_cache, 'apcusalt' ) && is_string( $wp_object_cache->apcusalt ) ? $wp_object_cache->apcusalt : '', '|' );
957 + $settings = array();
958 + $settings ['salt'] = $apcusalt;
959 + $settings ['total'] = "$totalsize($totalcount)";
960 + $siteno = 1;
961 + foreach ( $sizes as $salt => $val ) {
962 + switch ( $salt ) {
963 + case $apcusalt:
964 + $csalt = 'mine';
965 + break;
966 + case '?':
967 + $csalt = 'no salt';
968 + break;
969 + default:
970 + $csalt = 'site' . $siteno ++;
971 + break;
972 + }
973 + $settings[ $csalt ] = "$val($counts[$salt])";
974 + }
975 +
976 +
977 + $stanza['fields']['apcu_utilization'] = array(
978 + 'label' => __( 'APCu utilization', 'sqlite-object-cache' ),
979 + 'value' => $this->flatten( $settings ),
980 + );
981 +
982 + $apcu_cache_info = array();
983 + $apcu_cache_info = array_merge( $apcu_cache_info, apcu_cache_info( true ) );
984 + $apcu_cache_info = array_merge( $apcu_cache_info, apcu_sma_info( true ) );
985 +
986 + $stanza['fields']['apcu_cache_info'] = array(
987 + 'label' => __( 'APCu info', 'sqlite-object-cache' ),
988 + 'value' => $this->flatten( $apcu_cache_info ),
989 + );
990 +
991 +
992 + }
993 + } catch ( \Exception $e ) {
994 + /* empty, intentionally */
995 + }
996 +
997 + try {
998 + global $wp_object_cache;
999 + if ( method_exists( $wp_object_cache, 'sqlite_sizes' ) ) {
1000 + $stanza['fields']['sqlite_sizes'] = array(
1001 + 'label' => __( 'SQLite utilization', 'sqlite-object-cache' ),
1002 + 'value' => $this->flatten( $wp_object_cache->sqlite_sizes() ),
1003 + );
1004 + }
1005 + } catch ( \Exception $e ) {
1006 + /* empty, intentionally */
1007 + }
1008 +
1009 +
1010 + $info['sqlite-object-cache'] = $stanza;
1011 + return $info;
1012 + }
1013 +
1014 + private function inis_get( ...$list ) {
1015 + $result = array();
1016 + foreach ( $list as $item ) {
1017 + $val = ini_get( $item );
1018 + $result[ $item ] = $val ?: '-missing-';
1019 + }
1020 + return $result;
1021 +
1022 + }
1023 +
1024 + private function flatten( $a ) {
1025 + $result = array();
1026 + foreach ( $a as $k => $v ) {
1027 + if ( is_array( $v ) ) {
1028 + if ( array_key_exists( 'local_value', $v ) ) {
1029 + $v = $v['local_value'];
1030 + } else {
1031 + // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
1032 + $v = var_export( $v, true );
1033 + }
1034 + }
1035 + $result [] = $k . ':' . $v;
1036 + }
1037 + return implode( '; ', $result );
1038 + }
1039 +
1040 + /**
1041 + * Generate HTML for displaying fields.
1042 + *
1043 + * @param mixed $idata Data array.
1044 + * @param object $post Post object.
1045 + */
1046 + public function echo_field( $idata = array(), $post = null ) {
1047 +
1048 + // Get field info.
1049 + if ( isset( $idata['field'] ) ) {
1050 + $field = $idata['field'];
1051 + } else {
1052 + $field = $idata;
1053 + }
1054 +
1055 + $option_name = '';
1056 + if ( isset( $idata['option'] ) ) {
1057 + $option_name = $idata['option'];
1058 + }
1059 +
1060 + /* Get saved data. */
1061 + $field_id = $field['id'];
1062 + $data = null;
1063 +
1064 + /* Data to display, if set */
1065 + $option = get_option( $option_name, array() );
1066 + if ( is_array( $option ) && array_key_exists( $field_id, $option ) ) {
1067 + $data = $option[ $field_id ];
1068 + }
1069 +
1070 + /* the reset element sets the value of the field, overriding whatever is in the option */
1071 + if ( array_key_exists( 'reset', $field ) ) {
1072 + $data = $field['reset'];
1073 + }
1074 +
1075 + /* Show default data if no option saved and default is supplied. */
1076 + if ( null === $data && isset( $field['default'] ) ) {
1077 + $data = $field['default'];
1078 + } elseif ( null === $data ) {
1079 + $data = '';
1080 + }
1081 +
1082 + /* CSS class array */
1083 + $classes = array();
1084 + if ( array_key_exists( 'cssclass', $field ) ) {
1085 + $classes = is_array( $field['cssclass'] ) ? $field['cssclass'] : array( $field['cssclass'] );
1086 + }
1087 +
1088 + echo '<input ';
1089 + echo 'id="' . esc_attr( $field['id'] ) . '" ';
1090 + $this->echo_classes( $classes ) . ' ';
1091 + echo 'name="' . esc_attr( $option_name ) . '[' . esc_attr( $field_id ) . ']" ';
1092 + if ( array_key_exists( 'placeholder', $field ) ) {
1093 + echo 'placeholder="' . esc_attr( $field['placeholder'] ) . '" ';
1094 + }
1095 + switch ( $field['type'] ) {
1096 +
1097 + case 'text':
1098 + case 'url':
1099 + case 'email':
1100 + echo 'type="text" ';
1101 + echo 'value="' . esc_attr( $data ) . '" ';
1102 + break;
1103 +
1104 + case 'number':
1105 + echo 'type="number" ';
1106 + echo 'value="' . esc_attr( $data ) . '" ';
1107 + if ( isset( $field['min'] ) ) {
1108 + echo 'min="' . esc_attr( $field['min'] ) . '" ';
1109 + }
1110 + if ( isset( $field['max'] ) ) {
1111 + echo 'max="' . esc_attr( $field['max'] ) . '" ';
1112 + }
1113 + if ( isset( $field['step'] ) ) {
1114 + echo 'step="' . esc_attr( $field['step'] ) . '" ';
1115 + }
1116 + break;
1117 +
1118 + case 'password':
1119 + case 'hidden':
1120 + echo 'type="' . esc_attr( $field['type'] ) . '" ';
1121 + echo 'value="' . esc_attr( $data ) . '" ';
1122 + break;
1123 +
1124 + case 'checkbox':
1125 + echo 'type="checkbox" ';
1126 + if ( 'on' === $data ) {
1127 + echo 'checked="checked" ';
1128 + }
1129 + break;
1130 + }
1131 + echo '>' . PHP_EOL;
1132 +
1133 + if ( ! $post ) {
1134 + echo '<label for="' . esc_attr( $field['id'] ) . '">' . PHP_EOL;
1135 + }
1136 +
1137 + echo '<span class="description">' . esc_html( $field['description'] ) . '</span>' . PHP_EOL;
1138 +
1139 + if ( ! $post ) {
1140 + echo '</label>' . PHP_EOL;
1141 + }
1142 + }
1143 +
1144 + /**
1145 + * Given an array of CSS class names ['foo', 'bar'] echo class="foo bar".
1146 + *
1147 + * If the array is empty just echo a space.
1148 + *
1149 + * @param array $classes
1150 + *
1151 + * @return void
1152 + */
1153 + private function echo_classes( array $classes ) {
1154 + if ( count( $classes ) > 0 ) {
1155 + echo 'class="';
1156 + echo esc_html( implode( ' ', array_map( 'esc_attr', $classes ) ) );
1157 + echo '"';
1158 + }
1159 + echo ' ';
1160 + }
1161 +
1162 +
639 1163 }