A Guide to WordPress Development Mode and Debug Constants for Plugin Authors

Illuminated Mechanical Keyboard
Illuminated Mechanical Keyboard – License Adobe Stock

Releasing with WordPress 6.3 is a new developer feature called Development Mode. It’s a new PHP constant with a few helper functions that give context to the type of development you are doing.

This adds to the myriad of ways plugin developers can use WordPress for debugging purposes.

Within this tutorial, I’ll cover the existing ways developers can debug WordPress and why Development Mode is a welcome addition to the WordPress codebase.

The WP_DEBUG constant and its uses

Debug Deprecation Notice Shown With WP_DEBUG Enabled
Debug Deprecation Notice Shown With WP_DEBUG Enabled

As a plugin developer, it’s pretty much a defacto requirement that you should develop your plugins (or at least test them) with WP_DEBUG enabled. There might be a PHP deprecation notice or a _doing_it_wrong note that a core function is being accessed incorrectly. These errors don’t usually reveal themselves on their own unless you’ve been hit with a fatal (critical) error, and by then, it could be too late. It’s important to stay on top of deprecations and notices, as they will often point to areas in the code that need to be improved in the short and long term.

Turning on WP_DEBUG is as simple as declaring a PHP constant.

The WP_DEBUG constant should be declared in the wp-config.php file. If enabled, it’ll look like this:

define( 'WP_DEBUG', true );Code language: PHP (php)

As a plugin developer, WP_DEBUG is a useful constant for determining if the user would like extra information in regard to an error. You can simply check for the constant and take action if WP_DEBUG mode is enabled. Here’s an example snippet that checks for WP_DEBUG.

<?php
// $post variable defined above (assume variable is passed via function parameter).
$post = '';

// Check if $post is a WP_Post object. If not, throw error.
if ( ! is_a( $post, 'WP_Post' ) ) {
	if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
		// $post is not a WP_Post object, so don't use it.
		_doing_it_wrong( 'DLXPlugins/WP_DEBUG', 'A non-post object was used for the post parameter.', '1.0.0' );
	}
}

Code language: PHP (php)

With WP_DEBUG enabled, you might run into issues with headers being sent early or REST/Ajax calls not completing correctly due to unforeseen output. To prevent this output from interfering with program execution, it’s recommended to log these notices to file instead.

By defining constant WP_DEBUG_LOG, you can log the errors to a file. You can do this by setting WP_DEBUG_LOG to true in the wp-config.php file.

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );Code language: JavaScript (javascript)

By setting WP_DEBUG_LOG to true, the errors will be stored in the wp-content folder under debug.log.

.
└── wordpress-install/
    ├── wp-config.php
    ├── wp-admin
    ├── wp-includes
    └── wp-content/
        └── debug.logCode language: AsciiDoc (asciidoc)

Since debug logs can contain sensitive information, you may not want these stored in a public location. You can modify WP_DEBUG_LOG to point to a file not in the public path.

For example, this attempts to store a file named debug.log one directory beneath the root:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', dirname( __FILE__, 2 ) . '/tmp/debug.log' );
define( 'WP_DEBUG_DISPLAY', false );
Code language: PHP (php)

Fortunately, you can disable error printing to the screen rather easily using the WP_DEBUG_DISPLAY constant in the wp-config.php file:

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );Code language: PHP (php)

If you’re still getting notices to screen, you may need to tweak your php.ini file and set the error_reporting or use ini_set.

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
error_reporting(0);
@ini_set('display_errors', 0);Code language: JavaScript (javascript)

If you’re okay with having debug.log in your wp-content folder and are using a public domain, I recommend the following .htaccess rule to prevent public viewing of the file.

<Files debug.log>
    Order deny,allow
    Deny from all
</Files>Code language: Apache (apache)

For NGINX, you’d place this in your nginx.conf file:

location ~ /debug.log {
    deny all;
}

SCRIPT_DEBUG and its uses

Very much worth mentioning is SCRIPT_DEBUG. This is such a useful constant. When you set it to true, WordPress will serve unminified versions of most of its scripts and styles. From what I’ve seen, for the most part, a lot of plugin and theme authors use it, too, particularly when it comes to version numbers when registering and cache-busting assets.

You’d place SCRIPT_DEBUG in the wp-config.php file:

define( 'SCRIPT_DEBUG', true );Code language: PHP (php)

Here’s an example of serving an unminified version of a JavaScript file when SCRIPT_DEBUG is true.

<?php

add_action( 'wp_enqueue_scripts', 'dlx_enqueue_scripts' );
/**
 * Enqueue scripts.
 */
function dlx_enqueue_scripts() {
	// Serve unminified if SCRIPT_DEBUG is true.
	$suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
	$script_file = 'dlx-example' . $suffix . '.js';
	$script_path = plugin_dir_url( __FILE__ ) . 'js/' . $script_file;
	wp_enqueue_script(
		'dlx-example',
		$script_path,
		array(),
		'1.0.0',
		true
	);
}
Code language: PHP (php)

This, of course, assumes a directory structure as follows:

.
└── dlx-example/
    └── js/
        ├── dlx-example.js
        └── dlx-example.min.jsCode language: AsciiDoc (asciidoc)

Using SCRIPT_DEBUG is a lifesaver when debugging styles and JavaScript errors. It is a widely used constant and most reputable plugin and theme authors honor it. Chances are, if you have WP_DEBUG enabled, you’ll also want SCRIPT_DEBUG set as well.

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', dirname( __FILE__ ) . '/wp-content/mydebug.log' );
define( 'WP_DEBUG_DISPLAY', false );
define( 'SCRIPT_DEBUG', true );Code language: PHP (php)

Plugin break: Query Monitor

If you’re doing any local WordPress development, you need Query Monitor. It’s totally free, and it makes debugging and troubleshooting WordPress or PHP-related errors much easier.

For more debugging plugins, please check out these 18 free debugging plugins, written by Jeff Starr of Digging into WordPress.

WP_LOCAL_DEV and its uses

By defining WP_LOCAL_DEV to true in the wp-config.php file, you can indicate that you are using WordPress locally.

WP_LOCAL_DEV is a more controversial constant and isn’t even officially documented. There’s also a Trac ticket to remove it from site health. I felt I would mention it here for the completeness of this tutorial since it technically is in use.

WP_ENVIRONMENT_TYPE and its uses

When you’re developing sites, chances are you’re working on a local or development environment. When showing sites to clients, you’re typically on a production or staging environment.

Fortunately, with WordPress, you can tell which environment a user is using by making use of the WP_ENVIRONMENT_TYPE PHP constant.

WP_ENVIRONMENT_TYPE can be four values:

  • local
  • development
  • staging
  • production

It would be defined in wp-config.php:

// wp-config.php file.
define( 'WP_ENVIRONMENT_TYPE', 'local' );Code language: PHP (php)

Plugin authors can use function wp_get_environment_type to get the environment type.

<?php
/**
 * Load library based on environment type.
 *
 * @package DLXPlugins
 */

switch ( wp_get_environment_type() ) {
	case 'local':
	case 'development':
		// Load experimental library.
		break;
	case 'staging':
	case 'production':
		// Load stable library.
		break;
}Code language: PHP (php)

Here’s another example that is designed as an mu-plugin. This checks to see if a plugin is active and includes a file directly if on production. I’ve done this in the past when we needed to develop a WordPress plugin, but on production, we include it as an mu-plugin.

<?php

/**
 * Check if the plugin is active.
 *
 * @param string $path Path to the plugin file (e.g., akisimet/akismet.php).
 *
 * @return bool True if the plugin is active, false otherwise.
 */
function mu_dlx_block_assist_is_activated( $path ) {

	// Gets all active plugins on the current site.
	$active_plugins = get_option( 'active_plugins', array() );
	if ( in_array( $path, $active_plugins, true ) ) {
		return true;
	}
	return false;
}

// Check if core plugin is active, if not, load mu-plugin.
if ( ! mu_dlx_block_assist_is_activated( 'my-plugin/my-plugin.php' ) ) {
	if ( 'production' === wp_get_environment_type() ) {
		// Load the mu-plugin if it exists.
		if ( file_exists( WP_CONTENT_DIR . '/mu-plugins/my-mu-plugin/my-plugin.php' ) ) {
			require_once WP_CONTENT_DIR . '/mu-plugins/my-mu-plugin/my-plugin.php';
		}
	}
}Code language: PHP (php)

WP_ENVIRONMENT_TYPE and WP_DEBUG

WP_ENVIRONMENT_TYPE and WP_DEBUG can go hand-in-hand. For example, if you set WP_ENVIRONMENT_TYPE to development, WP_DEBUG will be set to true for you. Here’s a sample of the core code in the linked example:

// Add define( 'WP_DEBUG', true ); to wp-config.php to enable display of notices during development.
if ( ! defined( 'WP_DEBUG' ) ) {
	if ( 'development' === wp_get_environment_type() ) {
		define( 'WP_DEBUG', true );
	} else {
		define( 'WP_DEBUG', false );
	}
}Code language: PHP (php)

As you can see, if development is the environment type, WP_DEBUG is automatically set to true (if not defined).

What is the difference between local and development?

The differences between staging and production are obvious. Staging should look and behave like production, but it’s a sign-off point for clients. Production is well, production. But what about local vs. development?

If you develop plugins locally like me, chances are your local and development environments are in the same place. However, when working on client sites, a development environment is typically hosted and is used for quality assurance and testing items that require a publicly accessible URL (such as device testing).

In this context, a “local” environment would be the environment on the developer’s computer. A “development” environment would be some type of hosted environment that can be used to troubleshoot and test live URLs.

Mika Epstein explains:

Mark Jaquith elaborates:

An example is a caching plugin that connects to Cloudflare. It would make sense to enable Cloudflare cache on a hosted development environment but not on a “local” environment. Other plugins like Jetpack that require a live URL for some services to work would also be able to make use of “local” to indicate that the plugin shouldn’t try to communicate with its hosted services.

For clarity, if you are developing locally and may not be connected to the net or want to indicate offline behavior, use local as your environment type. Otherwise, development should get you by most of the time.

Setting environment variables

For most cases, you can get by setting environment variables using wp-config.php. If you’d like to go the .env route, I recommend this great article on setting environment variables using WordPress.

WP_ENVIRONMENT_TYPE vs. getenv

PHP function getenv can also get environment variables, but using WP_ENVIRONMENT_TYPE is preferred here since getenv is not always available or preferred.

WordPress function wp_get_environment_type is also a wrapper for getenv as well (shown below), so if you are getting an environment type, it’s best to use the core function.

if ( function_exists( 'getenv' ) ) {
	$has_env = getenv( 'WP_ENVIRONMENT_TYPE' );
	if ( false !== $has_env ) {
		$current_env = $has_env;
	}
}

// Fetch the environment from a constant, this overrides the global system variable.
if ( defined( 'WP_ENVIRONMENT_TYPE' ) && WP_ENVIRONMENT_TYPE ) {
	$current_env = WP_ENVIRONMENT_TYPE;
}Code language: PHP (php)

WP_DEVELOPMENT_MODE and its uses

This brings us to the latest constant coming in WordPress 6.3, which is WP_DEVELOPMENT_MODE. Let’s see what this constant provides and how we can use it going forward.

Current valid modes are:

  • core
  • plugin
  • theme
  • all

It would be defined in the wp-config.php file:

define( 'WP_ENVIRONMENT_TYPE', 'development' );
define( 'WP_DEVELOPMENT_MODE', 'plugin' );Code language: PHP (php)

Why is there a need for WP_DEVELOPMENT_MODE?

The need arose from themes and the theme.json file. In order to keep things flexible in block themes, theme.json is used to set all kinds of settings and variables. Reading this in every load and configuring and setting options would slow things down a lot, so this file is only read in periodically. In other words, it is cached.

If you’re developing in theme.json, you’d have to clear your cache every time you make a change to the file. In order to get around this, you can currently use WP_DEBUG to indicate that you’d like this cache cleared every time.

The current workaround is to use WP_DEBUG. In function wp_get_global_stylesheet, there is currently this variable that determines if it returns a cached result or not:

$can_use_cached = empty( $types ) && ! WP_DEBUG;Code language: PHP (php)

Here’s the thing: WP_DEBUG shouldn’t be used to change behavior. Having something enabled or disabled is an incorrect usage. Environment type is problematic here as well. You’d definitely want things cached on staging and production, but is it a valid assumption that caching should be disabled on development and local as well?

Should WP_DEBUG be combined with WP_ENVIRONMENT_TYPE to clear the theme cache? For example?

if ( defined( 'WP_DEBUG' ) && WP_DEBUG && 'local' === wp_get_environment_type() ) { ... }Code language: PHP (php)

This assumes that theme development takes place when WP_DEBUG is enabled and “local” is the environment type. But what if you’re doing plugin development? You don’t need the theme.json cache cleared every time. That slows down your environment further.

By declaring a development mode, you can explicitly tell WordPress, “Yes, I am in development mode. Yes, I’m developing a theme.”

The above cache code could be rewritten a bit more explicit:

$can_use_cached = empty( $types ) && ! wp_is_development_mode( 'theme' );Code language: PHP (php)

This makes it a bit more clear that cache isn’t used when in the “theme” development mode.

For adding more modes, it is being discusssed in Trac.

As far as WP_DEBUG and WP_DEVELOPMENT_MODE together, you could use these two complementary. The following will throw a fatal error if WP_DEBUG is on and development mode is in “plugin” mode:

<?php

function dlx_get_certain_post( $post_id ) {
	// Get the post.
	$post = get_post( $post_id );

	// If no post, return debug notice.
	if ( ! $post ) {
		if ( dlx_can_throw_fatal_error() ) {
			throw new \Exception( __( 'No post found.', 'dlx' ) );
		} else {
			return new \WP_Error( 'no_post', __( 'No post found.', 'dlx' ) );
		}
	}

	// do more stuff...
}

/**
 * Check if we can throw a fatal error.
 *
 * @return bool
 */
function dlx_can_throw_fatal_error() {
	if ( defined( 'WP_DEBUG' ) && WP_DEBUG && wp_is_development_mode( 'plugin' ) ) {
		return true;
	};
	return false;
}

//dlx_get_certain_post( 0 );Code language: PHP (php)

wp_is_development_mode function

In order to determine if WordPress is in development mode, you can use the new function wp_is_development_mode.

/**
 * Checks whether the site is in the given development mode.
 *
 * @since 6.3.0
 *
 * @param string $mode Development mode to check for. Either 'core', 'plugin', 'theme', or 'all'.
 * @return bool True if the given mode is covered by the current development mode, false otherwise.
 */
function wp_is_development_mode( $mode ) {
	$current_mode = wp_get_development_mode();
	if ( empty( $current_mode ) ) {
		return false;
	}

	// Return true if the current mode encompasses all modes.
	if ( 'all' === $current_mode ) {
		return true;
	}

	// Return true if the current mode is the given mode.
	return $mode === $current_mode;
}Code language: PHP (php)

The function takes in the development mode (‘core,’ ‘plugin,’ ‘theme,’ or ‘all’) and returns true if in development mode, but false if not.

wp_get_development_mode function

Function wp_get_development_mode will return the type of mode WordPress is in.

/**
 * Retrieves the current development mode.
 *
 * The development mode affects how certain parts of the WordPress application behave,
 * which is relevant when developing for WordPress.
 *
 * Development mode can be set via the `WP_DEVELOPMENT_MODE` constant in `wp-config.php`.
 * Possible values are 'core', 'plugin', 'theme', 'all', or an empty string to disable
 * development mode. 'all' is a special value to signify that all three development modes
 * ('core', 'plugin', and 'theme') are enabled.
 *
 * Development mode is considered separately from `WP_DEBUG` and wp_get_environment_type().
 * It does not affect debugging output, but rather functional nuances in WordPress.
 *
 * This function retrieves the currently set development mode value. To check whether
 * a specific development mode is enabled, use wp_is_development_mode().
 *
 * @since 6.3.0
 *
 * @return string The current development mode.
 */
function wp_get_development_mode() {Code language: PHP (php)

It can return ‘core,’ ‘plugin,’ ‘theme,’ ‘all,’ or an empty string. An empty string should signify that WordPress is not in development mode.

Let’s take a plugin license check as an example. I’d like to ping my licensing API, but if I’m in local or development mode and developing a plugin, I don’t want to ping my licensing server. Here’s an example function I could call to prevent license server calls:

<?php
/**
 * Check license status.
 *
 * @package DLXPlugins
 */

function should_check_plugin_license() {

	// Ignore license checks on local/development environment and plugin development mode.
	$environment_type = wp_get_environment_type();
	if ( in_array( $environment_type, array( 'local', 'development' ), true ) && wp_is_development_mode( 'plugin' ) ) {
		return false;
	}
	return true;
}Code language: PHP (php)

I’m using the new WordPress function wp_is_development_mode to check if I’m in the plugin development mode.

Conclusion

Within this article, I went over the various debug features that come with WordPress. I also went over the differences between environment types and development modes.

Do you think others will find the new development mode useful? Please let me know in the comments.

Ronald Huereca
By: Ronald Huereca
Published On: on July 22, 2023

Ronald Huereca founded DLX Plugins in 2022 with the goal of providing deluxe plugins available for download. Find out more about DLX Plugins, check out some tutorials, and check out our plugins.

Leave Your Valuable Feedback

Your email address will not be published. Required fields are marked *

Shopping Cart
  • Your cart is empty.
Scroll to Top