Debug in WordPress

In development, you need to be able to see where the error is when something suddenly breaks. WordPress has a special debug mode for this. In this post, let's take it apart and see what this WP_DEBUG constant is.

Why do we need "debug mode"?

Let's say you changed the functions.php file of the theme and the site stopped working. In its place is a white screen - nothing is clear. "Debug" will help to understand, it will show you the error and tell you in which line of what file it is.

"Debug" displays not only the errors that cause the site to stop working, but also the notes and warnings. Notes can be created by PHP itself (for example, when a variable is used incorrectly) or by PHP script code (for example, WP creates such notes if the site uses a deprecated WordPress function, or an deprecated function parameter).

There are two options for debug mode:

  1. WP_DEBUG_DISPLAY - Display errors on the screeen.
  2. WP_DEBUG_LOG - Write errors to log file.

The debug mode itself is controlled by WP_DEBUG constant.

All three constants can take true or false values.

By default, the debug constants have these values:

  • WP_DEBUG = false (true when 'development' === wp_get_environment_type())
  • WP_DEBUG_DISPLAY = true
  • WP_DEBUG_LOG = false

All constants can be defined in the wp-config.php file or defined by wp_initial_constants(), which is triggered early on WordPress loading.

WP_DEBUG_DISPLAY and WP_DEBUG_LOG are activated only if the WP_DEBUG constant is enabled.

Enabling WP_DEBUG does not change the value of the other constants. That is, with WP_DEBUG=true WP_DEBUG_DISPLAY and WP_DEBUG_LOG will retain their default values and PHP display and error logging settings will be based on those values.

The wp_debug_mode() function sets the PHP settings based on the values of those constants.

Error display is always disabled for AJAX requests, you can only see errors there through the log file. This is set in wp_debug_mode():

if ( defined( 'XMLRPC_REQUEST' ) || defined( 'REST_REQUEST' ) || ( defined( 'WP_INSTALLING' ) && WP_INSTALLING ) || wp_doing_ajax() ) {
	@ini_set( 'display_errors', 0 );
}

How to enable error display in an AJAX request, see article about AJAX.

Use wp_get_environment_type(), to control the development environment.

It is important to disable "debugging" on the working (production) site.

How to enable "debug" (display errors in WordPress)

Basic enabling

Open the wp-config.php file at the root of your site and change false to true on the line:

define( `WP_DEBUG`, true ); // false - turn off error display

With this option errors and noties will be displayed, but nothing will be logged in log file.

Enable and configure debugging

The code below, will enable the recording of errors, warnings and noties in the wp-content/debug.log file and disable the display of notes and warnings on the screen, so they do not get in the way when browsing the site.

define( 'WP_DEBUG', true );          // enable debug mode
define( 'WP_DEBUG_LOG', true );      // true - logging errors into /wp-content/debug.log file
define( 'WP_DEBUG_DISPLAY', false ); // false - turns off displaying errors on the screen
define( 'SCRIPT_DEBUG', true );      // use full versions of JS and CSS engine files

Insert this code anywhere in the wp-config.php file, but before the line:

require_once( ABSPATH . 'wp-settings.php' );

Thanks to the code above, error logging to a file has been enabled. This can be used to write the contents of the variables to log file:

error_log( $value );               // Scalar values
error_log( print_r( $value, 1) );  // Any data

Dynamically turn on/off errors display

This code helps to quickly enable the WP_DEBUG constant, which on a working site should be turned off. The code also allows you to enable error logging in the /wp-content/debug.log file, and disable error display on the screen.

Why this code is handy? Let's say we made the site and it works, but we periodically fix it's code. While editing of course appear different errors, including fatal ones. To see what the cause, we need to enable debug, to do this, open wp-config.php and change the constant, after completion it is necessary to return everything back. This is inconvenient, it's more convenient to enable debug via URL and see errors when needed.

Enabling errors is saved in the cookie of your site so it will works only for you.

To get things working, replace line define( WP_DEBUG, false ); in file wp-config.php with this code:

GitHub
<?php

/**
 * Dynamically enable/disable the display of PHP errors in WordPress.
 *
 * Installation:
 * replace line 'define( WP_DEBUG, false );' in 'wp-config.php' file with this code.
 *
 * Enabling debug mode:
 * NOTE: Strongly recommended to changing the 'debug' word to something more unique!
 * add the 'debug' query parameter to the URL. Examples:
 * https://site.com/?debug - default enabling of WP_DEBUG constant
 * https://site.com/?debug=1 - logging of errors into file 'DOCUMENT_ROOT/../php-errors-{HOST}.log'.
 * https://site.com/?debug=2 - linking uncompressed scripts and saving all SQL queries to $wpdb->queries
 * https://site.com/?debug=3 - saving all SQL queries in $wpdb->queries
 * https://site.com/?debug=4 - disable displaying errors (enabled by default)
 * https://site.com/?debug=14 - combining
 *
 * Disabling debug mode:
 * https://site.com/?debug=anything
 *
 * @author Kama (http://wp-kama.ru)
 * ver 2.5
 */
// IMPORTANT: change from `debug` to your unique key!
kama_define_wp_debug( 'debug' );

function kama_define_wp_debug( $key ){

	$val = isset( $_GET[ $key ] ) ? ( $_GET[ $key ] ?: 'yes' ) : false;

	// set/delete cookie
	if( $val !== false ){

		$cookie = preg_match( '/^(yes|[1234])$/', $val ) ? $val : null;

		$host = str_replace( 'www.', '', $_SERVER['HTTP_HOST'] );

		// cirilic domains: .сайт, .онлайн, .дети, .ком, .орг, .рус, .укр, .москва, .испытание, .бг
		false !== strpos( $host, 'xn--' )
			? preg_match( '~xn--[^.]+.xn--[^.]+$~', $host, $mm )
			: preg_match( '~[a-z0-9][a-z0-9-]{1,63}.[a-z.]{2,6}$~', $host, $mm );

		$host = $mm[0];

		$_COOKIE[ $key ] = $cookie;
		setcookie( $key, $cookie, time() + ( $cookie ? 3600 * 24 * 365 : -3600 ), '/', ".$host" );
	}

	// enable the debug based on the cookie
	if( ! empty( $_COOKIE[ $key ] ) ){

		define( 'WP_DEBUG', true );

		$set = array_flip(
			preg_split( '/(\d)/', $_COOKIE[ $key ], -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY )
		);

		isset( $set[1] ) && define( 'WP_DEBUG_LOG', dirname( $_SERVER['DOCUMENT_ROOT'] ) . "/php-errors-{$_SERVER['HTTP_HOST']}.log" );
		isset( $set[2] ) && define( 'SCRIPT_DEBUG', true );
		isset( $set[3] ) && define( 'SAVEQUERIES', true );
		isset( $set[4] ) && define( 'WP_DEBUG_DISPLAY', false );
	}
	else {
		define( 'WP_DEBUG', false );
	}
}

Now, to enable WP_DEBUG mode, you need to add ?debugquery parameter to any URL, for example:

  • http://example.com/?debug - enables debug.
  • http://example.com/?debug=1 - forced output to screen if such output is disabled.
  • http://example.com/?debug=2 - logging to file.

For security reason, strongly recommended change the ?debug parameter, specify something rare and known only to you.

When enabling error logging (?debug=2) the site parent folder (DOCUMENT_ROOT/..) must be writable, otherwise PHP will not be able to create the log file. Or you can manually create a log file DOCUMENT_ROOT/../php-errors-example.com.log. It must have write permissions (CHMOD 660 or higher).

WP_DEBUG

WP_DEBUG - is a PHP constant (a global constant - only defined once). The value of this constant enables or disables error display in PHP. It is also used in various places in WordPress code to show or suppress errors where necessary.

WP_DEBUG should be defined in the wp-config.php file.

// disable debug (default)
define( 'WP_DEBUG', false );
// or
define( 'WP_DEBUG', 0 );

// enable debug
define( 'WP_DEBUG', true );
// or
define( 'WP_DEBUG', 1 );

Note: do not set false in quotes - 'false'. In this case the debug will be enabled because the value of string 'false' is logical true.

PHP errors, warnings and notices

There are different levels of errors in PHP. Without going into details, the significance levels are:

  1. errors - a serious error which leads to a halt of the script. Aborts PHP.
  2. warnings - not an error, but a warning about something. PHP does not interrupt.
  3. notices - not an error, but a note about something. PHP does not interrupt. Notes may indicate possible bugs in the code. Fixing them will usually make the code more stable.

"Debug mode" includes all levels of errors. This is not like PHP's usual behavior: usually only errors at the 'errors' level are included, and warnings and notices are not shown. See error_reporting() for more details.

Deprecated functions, hooks and deprecated function parameters

WP_DEBUG also includes internal WordPress noties. WordPress has special functions like _deprecated_function(), which show a notices level error when an deprecated function or hook or parameter of a hook, function, etc. is used. These notices warn that the WP function is considered deprecated and should be replaced because it can be removed at any time. Such notes most often suggest an alternative function to replace it.

WP_DEBUG_DISPLAY

Default: true.

Another WP_DEBUG component, which controls the display of errors.

IMPORTANT: it depends on WP_DEBUG - the logic of this parameter works only if WP_DEBUG = true. Otherwise, the global PHP option display_errors is used as the value of WP_DEBUG_DISPLAY.

If you specify in wp-config.php:

  • define( 'WP_DEBUG_DISPLAY', true ) - (by default) WP will display errors.

  • define( 'WP_DEBUG_DISPLAY', false ) - The errors will not be displayed. This is needed when errors are written to a log file (see WP_DEBUG_LOG) and you can see them later.

  • define( 'WP_DEBUG_DISPLAY', null ) - WP will not specify a value for PHP option display_errors at all, i.e. it will use the global PHP (server) setting.

Error display is always turned off for REST, AJAX or XML-RPC requests. Such code ini_set('display_errors', 0 ) works for those requests forcibly. The value of WP_DEBUG_DISPLAY constant does not change!

WP_DEBUG_LOG

Default: false.

Another debug component. Enables error logging to file /wp-content/debug.log. Depends on WP_DEBUG - works only if WP_DEBUG = true.

Writing errors in the log file can be useful when you want catch errors, which does not display anything on the screen. For example, when AJAX request or when testing CRON or REST.

``php
define( 'WP_DEBUG_LOG', true ); // true - log errors to /wp-content/debug.log

#### Changing the address of the error log file

##### Via WP

Since WordPress 5.1, in ``WP_DEBUG_LOG`` you can specify the log file path:

```php
define( 'WP_DEBUG_LOG', '/srv/path/to/custom/log/location/errors.log' );
Via PHP

To change the file name, add the following line as early as possible, such as in MU plugins:

ini_set( 'error_log', WP_CONTENT_DIR . '/hidden-debug.log' );

This line cannot be added to wp-config.php - it's too early!

  • The end folder in the specified path must exist and be writable.
  • The file inside may not exist, it will be created as soon as the first error occurs.

SAVEQUERIES

Default: undefined.

Related to debug constant. When enabled, all SQL queries will be saved in the $wpdb->queries property as an array. In this array you can see all SQL queries and if necessary find the right one and make sure it is correct, etc.

In addition to the query itself, also recorded data about how much time the query took and what function it was called.

// save SQL queries and their data in `$wpdb->queries`
define( 'SAVEQUERIES', true );

Important! this option requires additional memory and PHP operations. Therefore, for performance reasons, this constant should be used only while site development.

SCRIPT_DEBUG

Default: false.

A debug-related constant. Controls which JS and CSS files to use: compressed or full. When enabled, WordPress will use uncompressed versions (dev versions) of JS and CSS files. The default is to use min versions of the files. This is necessary for testing when modifying embedded js or css files.

// use full versions of WP `.css` and `.js` files
define( 'SCRIPT_DEBUG', true );

How does WP_DEBUG work?

After setting the debug constants in wp-config.php we go to the site. And when generating the page, at the very beginning of WordPress loading -- see wp-settings.php -- wp_debug_mode() function is triggered. It uses PHP functions to set how and what error levels to show, whether or not to create a log file, etc.

WP_DEBUG does not work?

Sometimes you may have a situation when you enable WP_DEBUG in wp-config, but the error is still not visible. This behavior can occur when somewhere after the error display settings by WordPress itself, these settings are changed. For example in MU plugin, a regular plugin or in the theme, the errors are turned off by reinstalling the ini directives PHP, something like this code:

error_reporting( 0 ); // turn off error messages
ini_set( 'display_errors', 0 ); // disables displaying errors

In this case WP's debug settings get interrupted and it stops working.

To solve this, it is best to find where the settings are changed and delete such lines, so that from now on you can work only with WP_DEBUG constant.

As another solution, you can try to re-build the error output settings by specifying them again:

error_reporting( E_ALL ); // enable error messages
ini_set( 'display_errors', 1 ); // show errors on the screen

WP functions for debugging

  • [wp_debug_backtrace_summary()]() — ERROR: not_found
  • [wp_get_environment_type()]() — ERROR: not_found

System data

For debugging, WP has a class WP_Debug_Data. For example, using the following method we can get a bunch of data about the system:

require_once ABSPATH . '/wp-admin/includes/class-wp-debug-data.php';
require_once ABSPATH . '/wp-admin/includes/update.php';
require_once ABSPATH . '/wp-admin/includes/misc.php';

$data = WP_Debug_Data::debug_data();

print_r( $data );

We get a big data set:

Array
(
	[wp-core] => Array
		(
			[label] => WordPress
			[fields] => Array
				(
					[version] => array( data )

					[site_language] => array( data )

					[user_language] => array( data )

					[timezone] => array( data )

					[home_url] => array( data )

					[site_url] => array( data )

					[permalink] => array( data )

					[https_status] => array( data )

					[multisite] => array( data )

					[user_registration] => array( data )

					[blog_public] => array( data )

					[default_comment_status] => array( data )

					[environment_type] => array( data )

					[user_count] => array( data )

					[dotorg_communication] => array( data )

				)

		)

	[wp-paths-sizes] => Array
		(
			[label] => Directories and Sizes
			[fields] => Array
				(
					[wordpress_path] => array( data )

					[wordpress_size] => array( data )

					[uploads_path] => array( data )

					[uploads_size] => array( data )

					[themes_path] => array( data )

					[themes_size] => array( data )

					[plugins_path] => array( data )

					[plugins_size] => array( data )

					[database_size] => array( data )

					[total_size] => array( data )

				)

		)

	[wp-dropins] => Array
		(
			[label] => Drop-ins
			[show_count] => 1
			[description] => Drop-ins are single files, found in the /public_html/assets directory, that replace or enhance WordPress features in ways that are not possible for traditional plugins.
			[fields] => Array
				(
					[maintenance.php] => array( data )

					[object-cache.php] => array( data )

				)

		)

	[wp-active-theme] => Array
		(
			[label] => Active Theme
			[fields] => Array
				(
					[name] => array( data )

					[version] => array( data )

					[author] => array( data )

					[author_website] => array( data )

					[parent_theme] => array( data )

					[theme_features] => array( data )

					[theme_path] => array( data )

					[auto_update] => array( data )

				)

		)

	[wp-parent-theme] => Array
		(
			[label] => Parent Theme
			[fields] => Array( data )
		)

	[wp-themes-inactive] => Array
		(
			[label] => Inactive Themes
			[show_count] => 1
			[fields] => Array
				(
					[Dummy] => array( data )

				)

		)

	[wp-mu-plugins] => Array
		(
			[label] => Must Use Plugins
			[show_count] => 1
			[fields] => Array
				(
					[disable-plugins-in-front.php] => array( data )

					[main.php] => array( data )

					[not_support_browsers_redirect.php] => array( data )

					[POMOdoro Translation Cache] => array( data )

					[protect-malicious-queries.php] => array( data )

					[Rus to Lat] => array( data )

				)

		)

	[wp-plugins-active] => Array
		(
			[label] => Active Plugins
			[show_count] => 1
			[fields] => Array
				(
					[AJAX Simply] => array( data )

					[Democracy Poll] => array( data )

					[Disable Emojis (GDPR friendly)] => array( data )

					[Display Active Plugins First] => array( data )

					[Kama Breadcrumbs] => array( data )

					[Kama Postviews] => array( data )

					[Kama SpamBlock] => array( data )

					[Kama Thumbnail Pro] => array( data )

					[Redis Object Cache] => array( data )

				)

		)

	[wp-plugins-inactive] => Array
		(
			[label] => Inactive Plugins
			[show_count] => 1
			[fields] => Array
				(
					[404 Error Monitor] => array( data )

					[Category Order and Taxonomy Terms Order] => array( data )

					[Contact Form 7] => array( data )

					[Kama Thumbnail] => array( data )

					[Query Monitor] => array( data )

					[Query Monitor Extend] => array( data )

					[Right Now Reloaded] => array( data )

					[Three Column Screen Layout] => array( data )

					[TinyPNG - JPEG, PNG & WebP image compression] => array( data )

					[User Role Editor] => array( data )

					[Widget Logic] => array( data )

					[WooCommerce] => array( data )

					[WordPress Sphinx Search Plugin] => array( data )

					[WP Crontrol] => array( data )

					[WP Super Cache] => array( data )

					[Yoast SEO] => array( data )

				)

		)

	[wp-media] => Array
		(
			[label] => Media Handling
			[fields] => Array
				(
					[image_editor] => array( data )

					[imagick_module_version] => array( data )

					[imagemagick_version] => array( data )

					[imagick_version] => array( data )

					[file_uploads] => array( data )

					[post_max_size] => array( data )

					[upload_max_filesize] => array( data )

					[max_effective_size] => array( data )

					[max_file_uploads] => array( data )

					[imagick_limits] => Array ( data )

					[imagemagick_file_formats] => Array( JPEG, JPG, MOV, MP4, MPEG, MPG, PNG, PNG24, WBMP, WEBP, WMV ... )

					[gd_version] => array( data )

					[gd_formats] => array( data )

					[ghostscript_version] => array( data )

				)

		)

	[wp-server] => Array
		(
			[label] => Server
			[description] => The options shown below relate to your server setup. If changes are required, you may need your web host’s assistance.
			[fields] => Array
				(
					[server_architecture] => array( data )

					[httpd_software] => array( data )

					[php_version] => array( data )

					[php_sapi] => array( data )

					[max_input_variables] => array( data )

					[time_limit] => array( data )

					[memory_limit] => array( data )

					[max_input_time] => array( data )

					[upload_max_filesize] => array( data )

					[php_post_max_size] => array( data )

					[curl_version] => array( data )

					[suhosin] => array( data )

					[imagick_availability] => array( data )

					[pretty_permalinks] => array( data )

				)

		)

	[wp-database] => Array
		(
			[label] => Database
			[fields] => Array
				(
					[extension] => array( data )

					[server_version] => array( data )

					[client_version] => array( data )

					[database_user] => array( data )

					[database_host] => array( data )

					[database_name] => array( data )

					[database_prefix] => array( data )

					[database_charset] => array( data )

					[database_collate] => array( data )

				)

		)

	[wp-constants] => Array
		(
			[label] => WordPress Constants
			[description] => These settings alter where and how parts of WordPress are loaded.
			[fields] => Array
				(
					[ABSPATH] => array( data )

					[WP_HOME] => array( data )

					[WP_SITEURL] => array( data )

					[WP_CONTENT_DIR] => array( data )

					[WP_PLUGIN_DIR] => array( data )

					[WP_MEMORY_LIMIT] => array( data )

					[WP_MAX_MEMORY_LIMIT] => array( data )

					[WP_DEBUG] => array( data )

					[WP_DEBUG_DISPLAY] => array( data )

					[WP_DEBUG_LOG] => array( data )

					[SCRIPT_DEBUG] => array( data )

					[WP_CACHE] => array( data )

					[CONCATENATE_SCRIPTS] => array( data )

					[COMPRESS_SCRIPTS] => array( data )

					[COMPRESS_CSS] => array( data )

					[WP_LOCAL_DEV] => array( data )

					[DB_CHARSET] => array( data )

					[DB_COLLATE] => Array
						(
						)

				)

		)

	[wp-filesystem] => Array
		(
			[label] => Filesystem Permissions
			[description] => Shows whether WordPress is able to write to the directories it needs access to.
			[fields] => Array
				(
					[wordpress] => array( data )

					[wp-content] => array( data )

					[uploads] => array( data )

					[plugins] => array( data )

					[themes] => array( data )

					[mu-plugins] => array( data )

				)

		)

)

Plugins for debugging and profiling in WordPress

There are several plugins in the WP catalog that extend the "debugging" capabilities and provide additional information to identify code weaknesses. Popular ones are:

  • Query Monitor - displays a bunch of useful information about the current page query. How much time was spent, how many SQL queries, what queries, how long each query took, how much memory was spent, what hooks were used, etc.

  • Query Monitor Addons