PluginProbe
WP Debugging / trunk
WP Debugging vtrunk
2.12.6 2.12.5 trunk 2.10.0 2.10.1 2.10.2 2.11.0 2.11.1 2.11.10 2.11.11 2.11.12 2.11.13 2.11.14 2.11.15 2.11.16 2.11.17 2.11.18 2.11.19 2.11.2 2.11.20 2.11.21 2.11.22 2.11.23 2.11.24 2.11.3 All 59 releases
wp-debugging / src / Settings.php

Settings.php in WP Debugging trunk, at src/Settings.php

475 lines 13.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WP Debugging
4 *
5 * @package wp-debugging
6 * @author Andy Fragen
7 * @license MIT
8 */
9
10 namespace Fragen\WP_Debugging;
11
12 // Exit if called directly.
13 if ( ! defined( 'WPINC' ) ) {
14 die;
15 }
16
17 /**
18 * Class Settings
19 */
20 class Settings {
21 /**
22 * Hold plugin options.
23 *
24 * @var array
25 */
26 protected static $options;
27
28 /**
29 * Hold `wp-config.php` file path.
30 *
31 * @var string
32 */
33 protected static $config_path;
34
35 /**
36 * Holds pre-defined constants for `wp-config.php`.
37 *
38 * @var array
39 */
40 protected $defined_constants;
41
42 /**
43 * Holds config args for WPConfigTransformer.
44 *
45 * @var array
46 */
47 protected static $config_args;
48
49 /**
50 * Constructor.
51 *
52 * @param array $options Plugin options.
53 * @param string $config_path Path to config file.
54 * @param array $defined_constants Pre-defined constant group.
55 * @return void
56 */
57 public function __construct( $options, $config_path, $defined_constants ) {
58 self::$options = $options;
59 self::$config_path = $config_path;
60 $this->defined_constants = $defined_constants;
61 self::$config_args = [ 'normalize' => true ];
62
63 if ( false === strpos( file_get_contents( self::$config_path ), "/* That's all, stop editing!" ) ) {
64 if ( 1 === preg_match( '@\$table_prefix(.*;)@', file_get_contents( self::$config_path ), $matches ) ) {
65 self::$config_args = array_merge(
66 self::$config_args,
67 [
68 'anchor' => "$matches[0]",
69 'placement' => 'after',
70 ]
71 );
72 }
73 }
74 }
75
76 /**
77 * Load hooks for settings.
78 *
79 * @return self
80 */
81 public function load_hooks() {
82 add_action( 'admin_init', [ $this, 'add_settings' ] );
83 add_action( is_multisite() ? 'network_admin_menu' : 'admin_menu', [ $this, 'add_plugin_menu' ] );
84 add_action( 'network_admin_edit_wp_debugging', [ $this, 'update_settings' ] );
85 add_action( 'admin_init', [ $this, 'update_settings' ] );
86 add_filter(
87 is_multisite()
88 ? 'network_admin_plugin_action_links_wp-debugging/wp-debugging.php'
89 : 'plugin_action_links_wp-debugging/wp-debugging.php',
90 [ $this, 'plugin_action_links' ]
91 );
92
93 return $this;
94 }
95
96 /**
97 * Add plugin menu.
98 *
99 * @return void
100 */
101 public function add_plugin_menu() {
102 $parent = is_multisite() ? 'settings.php' : 'tools.php';
103 $capability = is_multisite() ? 'manage_network_options' : 'manage_options';
104
105 add_submenu_page(
106 $parent,
107 esc_html__( 'WP Debugging', 'wp-debugging' ),
108 esc_html_x( 'WP Debugging', 'Menu item', 'wp-debugging' ),
109 $capability,
110 'wp-debugging',
111 [ $this, 'create_settings_page' ]
112 );
113 }
114
115 /**
116 * Update settings on save.
117 *
118 * @return void
119 */
120 public function update_settings() {
121 // Exit if improper privileges.
122 if ( ! current_user_can( 'manage_options' )
123 || ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['_wpnonce'] ) ), 'wp_debugging-options' ) )
124 ) {
125 return;
126 }
127
128 if ( isset( $_POST['option_page'] ) &&
129 'wp_debugging' === $_POST['option_page']
130 ) {
131 $options = isset( $_POST['wp-debugging'] )
132 ? array_map( 'sanitize_text_field', wp_unslash( $_POST['wp-debugging'] ) )
133 : [];
134
135 $options = $this->sanitize( $options );
136 $this->update_constants( self::$options, $options );
137 $filtered_options = array_filter(
138 self::$options,
139 static function ( $e ) {
140 return '1' !== $e;
141 }
142 );
143 $options = array_merge( $filtered_options, $options );
144 update_site_option( 'wp_debugging', (array) $options );
145 $this->redirect_on_save();
146 }
147 }
148
149 /**
150 * Update constants in wp-config.php.
151 *
152 * @param array $old Current value of self::$options.
153 * @param mixed $updated New value of $options.
154 * @return void
155 */
156 private function update_constants( $old, $updated ) {
157 $remove = array_diff_assoc( $old, $updated );
158 $add = array_diff_assoc( $updated, $old );
159
160 if ( ! empty( $add ) ) {
161 $this->add_constants( $add );
162 }
163 if ( ! empty( $remove ) ) {
164 $this->remove_constants( $remove );
165 }
166 }
167
168 /**
169 * Add constants to wp-config.php file.
170 *
171 * @uses https://github.com/wp-cli/wp-config-transformer
172 *
173 * @param array $add Constants to add to wp-config.php.
174 * @return array $added Array of added constants.
175 */
176 public function add_constants( $add ) {
177 $added = [];
178 try {
179 $config_transformer = new \WPConfigTransformer( self::$config_path );
180 foreach ( $add as $constant => $config ) {
181 $value = 'wp_debug_display' === $constant ? 'false' : 'true';
182 $value = isset( $config['value'] ) ? $config['value'] : $value;
183 $raw = isset( $config['raw'] ) ? $config['raw'] : true;
184 self::$config_args = array_merge( self::$config_args, [ 'raw' => $raw ] );
185 $config_transformer->update( 'constant', strtoupper( $constant ), $value, self::$config_args );
186 $added[ $constant ] = $value;
187 }
188
189 return $added;
190 } catch ( \Exception $e ) {
191 $messsage = 'Caught Exception: \Fragen\WP_Debugging\Settings::add_constants() - ' . $e->getMessage();
192 wp_die( esc_html( $messsage ) );
193 }
194 }
195
196 /**
197 * Process user defined constants added via filter.
198 *
199 * @return void
200 */
201 public function process_filter_constants() {
202 /**
203 * Filter to add user define constants.
204 *
205 * @since 2.5.0
206 *
207 * @param array Array of added constants.
208 * The format for adding constants is to return an array of arrays.
209 * Each array will have the constant as the key with an array of configuration data.
210 * array(
211 * 'my_constant' =>
212 * array(
213 * 'value' => $value, @type string
214 * 'raw' => $raw, // Optional. @type bool $raw is a boolean where false will return the value in quotes and true will return the raw value. Default is true.
215 * ),
216 * )
217 * Default is an empty array.
218 */
219 $filter_constants = apply_filters( 'wp_debugging_add_constants', [] );
220 $remove_user_defined = array_diff( self::$options, array_flip( $this->defined_constants ) );
221
222 // Remove and re-add user defined constants. Clean up for when filter removed or changed.
223 if ( ! empty( $remove_user_defined ) ) {
224 $this->remove_constants( $remove_user_defined );
225 }
226 $added_constants = $this->add_constants( $filter_constants );
227
228 $options = array_diff( self::$options, $remove_user_defined );
229 self::$options = array_merge( $options, $added_constants );
230 update_site_option( 'wp_debugging', (array) self::$options );
231 }
232
233 /**
234 * Remove constants from wp-config.php file.
235 *
236 * @uses https://github.com/wp-cli/wp-config-transformer
237 *
238 * @param array $remove Constants to remove from wp-config.php.
239 * @return void
240 */
241 public function remove_constants( $remove ) {
242 try {
243 $config_transformer = new \WPConfigTransformer( self::$config_path );
244 foreach ( array_keys( $remove ) as $constant ) {
245 $config_transformer->remove( 'constant', strtoupper( $constant ) );
246 }
247 } catch ( \Exception $e ) {
248 $messsage = 'Caught Exception: \Fragen\WP_Debugging\Settings::remove_constants() - ' . $e->getMessage();
249 wp_die( esc_html( $messsage ) );
250 }
251 }
252
253 /**
254 * Redirect back to settings page on save.
255 *
256 * @return void
257 */
258 private function redirect_on_save() {
259 $update = false;
260
261 // phpcs:disable WordPress.Security.NonceVerification.Missing
262 if ( ( isset( $_POST['action'] ) && 'update' === $_POST['action'] ) &&
263 ( isset( $_POST['option_page'] ) && 'wp_debugging' === $_POST['option_page'] )
264 ) {
265 $update = true;
266 }
267 // phpcs:enable
268
269 $redirect_url = is_multisite() ? network_admin_url( 'settings.php' ) : admin_url( 'tools.php' );
270
271 if ( $update ) {
272 $location = add_query_arg(
273 [
274 'page' => 'wp-debugging',
275 'updated' => $update,
276 ],
277 $redirect_url
278 );
279 wp_safe_redirect( $location );
280 exit;
281 }
282 }
283
284 /**
285 * Add notice when settings are saved.
286 *
287 * @return void
288 */
289 private function saved_settings_notice() {
290 // phpcs:disable WordPress.Security.NonceVerification.Recommended
291 if ( ( isset( $_GET['updated'] ) && '1' === $_GET['updated'] ) ||
292 ( isset( $_GET['settings-updated'] ) && '1' === $_GET['settings-updated'] )
293 ) {
294 echo '<div class="updated"><p>';
295 esc_html_e( 'Saved.', 'wp-debugging' );
296 echo '</p></div>';
297 }
298 // phpcs:enable
299 }
300
301 /**
302 * Register settings.
303 *
304 * @return void
305 */
306 public function add_settings() {
307 register_setting(
308 'wp_debugging',
309 'wp_debugging',
310 [ $this, 'sanitize' ]
311 );
312
313 add_settings_section(
314 'wp_debugging',
315 esc_html__( 'Debugging Constants', 'wp-debugging' ),
316 [ $this, 'print_settings_section' ],
317 'wp_debugging'
318 );
319
320 add_settings_field(
321 'wp_debug',
322 null,
323 [ $this, 'checkbox_setting' ],
324 'wp_debugging',
325 'wp_debugging',
326 [
327 'id' => 'wp_debug',
328 'title' => esc_html__( 'Set WP_DEBUG to true.', 'wp-debugging' ),
329 ]
330 );
331
332 add_settings_field(
333 'wp_debug_display',
334 null,
335 [ $this, 'checkbox_setting' ],
336 'wp_debugging',
337 'wp_debugging',
338 [
339 'id' => 'wp_debug_display',
340 'title' => esc_html__( 'Set WP_DEBUG_DISPLAY to false, default is true.', 'wp-debugging' ),
341 ]
342 );
343
344 add_settings_field(
345 'wp_disable_fatal_error_handler',
346 null,
347 [ $this, 'checkbox_setting' ],
348 'wp_debugging',
349 'wp_debugging',
350 [
351 'id' => 'wp_disable_fatal_error_handler',
352 'title' => esc_html__( 'Set WP_DISABLE_FATAL_ERROR_HANDLER to true.', 'wp-debugging' ),
353 'class' => version_compare( get_bloginfo( 'version' ), '5.2', '>=' ) ? '' : 'hidden',
354 ]
355 );
356 }
357
358 /**
359 * Print settings section information.
360 *
361 * @return void
362 */
363 public function print_settings_section() {
364 esc_html_e( 'The following constants are set with plugin activation and removed with plugin deactivation.', 'wp-debugging' );
365 $this->print_constants();
366 esc_html_e( 'Select the debugging constants.', 'wp-debugging' );
367 }
368
369 /**
370 * Print current constants.
371 *
372 * @return void
373 */
374 private function print_constants() {
375 $added_constants = apply_filters( 'wp_debugging_add_constants', [] );
376 $additional_constants = [];
377 if ( $added_constants ) {
378 foreach ( $added_constants as $constant => $config ) {
379 $additional_constants[ $constant ] = $config['value'];
380 }
381 }
382
383 // Strip user defined constants from $constants array.
384 $constants = [ 'wp_debug_log', 'script_debug', 'savequeries' ];
385 $constants = array_merge( array_keys( self::$options ), $constants );
386 $constants = array_diff( $constants, array_keys( $additional_constants ) );
387
388 echo '<pre>';
389 foreach ( $constants as $constant ) {
390 $value = 'wp_debug_display' === $constant ? 'false' : 'true';
391 $constant = strtoupper( $constant );
392 echo wp_kses_post( "<code>define( '{$constant}', {$value} );</code><br>" );
393 }
394 foreach ( $additional_constants as $constant => $value ) {
395 $value = in_array( $value, [ 'true', 'false' ], true ) ? $value : "'$value'";
396 $constant = strtoupper( $constant );
397 echo wp_kses_post( "<code>define( '{$constant}', {$value} );</code><br>" );
398 }
399 echo '</pre>';
400 }
401
402 /**
403 * Create settings page.
404 *
405 * @return void
406 */
407 public function create_settings_page() {
408 $this->saved_settings_notice();
409 $action = is_multisite() ? 'edit.php?action=wp-debugging' : 'options.php'; ?>
410 <div class="wrap">
411 <h1><?php esc_html_e( 'WP Debugging', 'wp-debugging' ); ?></h1>
412 <div class="updated fade">
413 <p><?php echo wp_kses_post( __( '<strong>Please note:</strong> Your <code>wp-config.php</code> file must be writable by the filesystem. Any errors will result in a PHP Exception being thrown. Debug constants as documented in <a href="https://wordpress.org/documentation/article/debugging-in-wordpress/">Debugging in WordPress</a>.', 'wp-debugging' ) ); ?></p>
414 </div>
415 <div>
416 <form method="post" action="<?php echo esc_attr( $action ); ?>">
417 <?php settings_fields( 'wp_debugging' ); ?>
418 <?php do_settings_sections( 'wp_debugging' ); ?>
419 <?php submit_button(); ?>
420 </form>
421 </div>
422 <?php
423 }
424
425 /**
426 * Sanitize save settings.
427 *
428 * @param array $input Input.
429 * @return array $new_input Sanitized output.
430 */
431 public function sanitize( $input ) {
432 $new_input = [];
433 if ( ! is_array( $input ) ) {
434 $new_input = sanitize_text_field( $input );
435 } else {
436 foreach ( array_keys( (array) $input ) as $id ) {
437 $new_input[ sanitize_text_field( $id ) ] = sanitize_text_field( $input[ $id ] );
438 }
439 }
440
441 return $new_input;
442 }
443
444 /**
445 * Get the settings option array and print one of its values.
446 *
447 * @param array $args 'id' and 'title'.
448 */
449 public function checkbox_setting( $args ) {
450 $checked = isset( self::$options[ $args['id'] ] ) ? self::$options[ $args['id'] ] : null;
451 ?>
452 <style> .form-table th { display:none; } </style>
453 <label for="<?php echo esc_attr( $args['id'] ); ?>">
454 <input type="checkbox" name="wp-debugging[<?php echo esc_attr( $args['id'] ); ?>]" value="1" <?php checked( '1', $checked ); ?> >
455 <?php echo esc_html( $args['title'] ); ?>
456 </label>
457 <?php
458 }
459
460 /**
461 * Add setting link to plugin page.
462 * Applied to the list of links to display on the plugins page (beside the activate/deactivate links).
463 *
464 * @param array $links Plugin links on plugins.php.
465 *
466 * @return array
467 */
468 public function plugin_action_links( $links ) {
469 $settings_page = is_multisite() ? 'settings.php' : 'tools.php';
470 $link = [ '<a href="' . esc_url( network_admin_url( $settings_page ) ) . '?page=wp-debugging">' . esc_html__( 'Settings', 'wp-debugging' ) . '</a>' ];
471
472 return array_merge( $link, $links );
473 }
474 }
475