How to Use Xdebug for Advanced PHP Debugging

#

You could just debug your PHP code using dump debugging functions such as error_log, print, and var_dump, and let’s be honest, we’ve all done it a lot! While helpful sometimes, they often just aren’t enough and can actually slow you down while developing. There must be a better way, surely?!

Enter Xdebug, the rather awesome step debugging and profiling tool for PHP.

In this post, I’m going to take you through my Xdebug journey, and show you how to use it to make your life easier. You can thank me later (in the comments) 🙂

If you’re the type of person who prefers to learn things visually, Jonathan, our developer educator, has made a short video detailing how to use Xdebug with PHPStorm.

How Xdebug Changed My Life

A few years ago I didn’t know debuggers existed, had never heard of Xdebug, and was quite happy using a function I’d cobbled together pinched from Stack Overflow in all my WordPress sites for debugging:

if ( ! function_exists( '_log' ) ) {
    function _log( $message ) {
        if ( WP_DEBUG === true ) {
            if ( is_array( $message ) || is_object( $message ) ) {
                error_log( print_r( $message, true ) );
            } else {
                error_log( $message );
            }
        }
    }
}

It was all I thought I needed until someone introduced me to Xdebug, and my life changed. Excuse the hyperbole, but it happened!

When you are dump debugging using functions like error_log or var_dump, you only output the variables you define, which might not be what you need. For example, while troubleshooting some code, you add the function somewhere to debug a variable. You refresh your page, so the debug line is executed, and then check your error log to look at the output. If this doesn’t give you any insight into the cause of the problem at hand, then you need to go back and add more debug lines to the code. Rinse and repeat.

Xdebug allows you to break during code execution and inspect all the variables in scope during a request. What this means is you have everything you need to troubleshoot during only one iteration of this cycle. You save a tremendous amount of time when tracking down issues, and your development workflow is more efficient.

Troubleshooting++ with Xdebug

Xdebug really shines when trying to troubleshoot a problem whose cause is utterly unknown to you. If you know your function is broken, logging lines inside it is great and works well. But strange problem-causing behavior could be coming from anywhere in a project, like a plugin or WordPress core itself. How the hell do you track that down?

By using breakpoints, you can pause the code execution at any point, which means the best thing to do is to break early and follow the code execution through with Xdebug (more on that later) until you spot something gone awry. To save time, you can progressively move breakpoints further along in the execution until you get closer to the potential issue, instead of breaking on code already analyzed as not the cause of the problem. When you find the issue, you can change the values of variables on the fly after they are assigned, so you can test potential fixes while debugging, all during the same request.

Debug Driven Development

Another great use of Xdebug is using it proactively during development instead of reacting to existing code issues. Think of this like Test-Driven Development (TDD) but using the debugger first instead of writing tests. I’m not even sure if Debug Driven Development (DDD) is a widely used term, but for me, I’ve used Xdebug to help me write new code in a couple of ways:

  1. Inspect existing arrays, objects, and class instances to find the data available to me in code, so I can use it in new code I write
  2. Immediately debug a newly-written piece of code to test it is working as expected

This isn’t something I do all the time, but Xdebug gives me this insight when I need it.

Installing Xdebug

Hopefully the benefits I’ve detailed have got you wanting to use Xdebug and you’re ready for some installation steps. I’ve put together a list of the most common local environments with some handy links to getting Xdebug installed on them:

Matt Stauffer also recently compiled a list of popular configurations available, so if I’ve missed it, you should find something for your local environment there.

Do you work with a different local environment and need some guidance? Let us know in the comments.

Xdebug Version 3

In November 2020, Xdebug 3 was released. Besides massive performance improvements and PHP 8 support, the most significant change was how you turn on Xdebug functionality through the new mode setting. In addition to making it easier to enable different types of debugging modes, it also changes how you configure Xdebug in your php.ini in the first place.

The online tutorial you’re following to install and configure Xdebug for your local development environment will determine which settings you need to configure. Fortunately, an Xdebug help doc on upgrading from version 2 to version 3 exists to guide you.

As a quick example, if you have version 2 installed, you might be instructed to configure your php.ini settings to look something like this:

[xdebug]
zend_extension="/path/to/xdebug/extension/xdebug.so"
xdebug.remote_enable=1
xdebug.remote_host=127.0.0.1
xdebug.remote_port="9000"

Whereas if you have version 3 installed, you would need to configure it like this:

[xdebug]
zend_extension="/path/to/xdebug/extension/xdebug.so"
xdebug.mode=debug
xdebug.client_host=127.0.0.1
xdebug.client_port="9003"

In version 3, you can also enable different Xdebug modes by specifying them in a comma-separated list.

xdebug.mode=develop,trace

For this article, I’m using Xdebug 3, with the mode set to debug, but we’ll also cover any differences for version 2.

PhpStorm Integration

News flash: I love PhpStorm! And guess what? PhpStorm has an awesome integration with Xdebug and their help docs include a complete set up guide.

If you don’t use PHPStorm, most other Integrated Development Environments (IDEs) like Visual Studio Code and editors like Sublime Text can be configured to use Xdebug.

Start Debugging

Once you’ve installed Xdebug and configured PhpStorm, you can start interactively debugging your code. This is done by setting breakpoints at certain lines of your code and telling PhpStorm to listen for incoming connections. When PHP executes a line that has a breakpoint on it, Xdebug will step in and pause execution, allowing you to gain valuable insight into all that is happening during execution:

PhpStorm Xdebug debugging panel

The sidebar of the debug panel has various buttons for controlling the execution of the code. Here they are top-down as they appear in the screenshot:

  • Resume Program – continue with the PHP execution
  • Pause Program – not available during debugging
  • Stop – halt the execution
  • View Breakpoints – bring up a window of all the breakpoints set in all files of the project
  • Mute Breakpoints – deactivate the breakpoints during execution (good for just finishing the request without breaking any further)
  • Settings – tweak the debugger display
  • Pin Tab – always show the Debug panel

Next to the Debugger, Console, and Output tabs, the top bar of the panel controls how the debugger traverses the code so you can inspect different parts of the codebase:

  • Show Execution Point – jumps back to where the program is broken
  • Step Over – execute and move to the next line of the file
  • Step Into – if the next line has one or more functions, move the debugger into those to step through
  • Force Step Into – step into a function that is marked as skipped
  • Step Out – move the debugger out of the current function back to the function that called it
  • Run to Cursor – execute all the way to the line the cursor is on
  • Evaluate Expression – execute PHP while the debugger is running (think of this like Chrome’s JS console)
  • Show user-defined constants – toggles the display for any PHP constants your code has defined
  • Show Values Addresses – toggles the display of the memory address of objects
  • Show Empty Superglobals Variables – toggles the display of any empty superglobals
  • Add Method to Skip List – mark method to be skipped next time

Most of these buttons also have keyboard shortcuts, but the most frequently used are:

  • F9 – Resume Program
  • F8 – Step Over
  • F7 – Step Into

Breakpoints are added by clicking in the left-hand gutter of the code, along the line you want at which you want to break. PhpStorm also allows you to set conditional breakpoints, where you add PHP logic to control when a breakpoint actually fires:

PhpStorm add conditional breakpoint

When troubleshooting, it is often helpful to inspect and watch a variable’s value all the way through the execution of a request to see when it changes. PhpStorm allows you to add variables to a list that it watches and displays in a separate “Watches” panel, separate from all the data in the main “Variables” panel. You can watch a variable by either right-clicking the variable in the file during debugging and selecting Add to Watches or doing the same from the variable in the “Variables” panel:

PhpStorm add variable to watch list

Watches are not limited to variables, however. You can even add expressions or functions to your watch list. This is extremely useful when you’re working with a CMS like WordPress, which makes use of many helper functions like site_url() or get_post(). By adding the full function call to your watch list, and you’ll always be able to see what the result of that function will be.

Stack Trace

In Xdebug version 2, enabling Xdebug would enable the extended stack trace for any errors, notices, or warnings written to the PHP log. With the release of version 3, this was changed so that it needs to be explicitly enabled by setting the Xdebug mode to develop.

Because I rely on wp-content/debug.log or the PHP error_log to alert me to issues, but I use PhpStorm to see the stack trace, I don’t enable the extended (and very noisy) stack trace. When I used version 2 of Xdebug I disabled the stack trace in the log by putting this in my wp-config.php file:

if ( function_exists( 'xdebug_disable' ) ) {
    xdebug_disable();
}

Advanced Use

Mastered the basics? Ready for more? Here are some advanced ways you can use Xdebug to improve your development workflows.

Profiling

When you really need to investigate performance issues with your code or website, one of the best tools we’ve found is Blackfire, which we’ve written about before. But Xdebug also profiles for you and PhpStorm can interpret the results, which is great as it means you never have to leave PhpStorm! 😂 To enable the profiler, edit your php.ini file to add or uncomment these lines in the Xdebug section:

Version 3

xdebug.mode = profile
xdebug.output_dir = "/path/to/desired/profiler/output/directory"

Version 2

xdebug.profiler_enable=1
xdebug.profiler_output_dir="/path/to/desired/profiler/output/directory"

Then Xdebug will create a profile log file in the output_dir or profiler_output_dir you specify for each execution of your code. Remember to disable the profiler when you’re done, so you don’t fill your hard drive with log files! Alternatively, set the output directory to the /tmp directory for your OS. These logs can then be analyzed in PhpStorm by navigating to Tools > Analyze Xdebug Profiler Snapshot and selecting the log file. The visualization isn’t as in-depth as Blackfire or using something like KCachegrind, but it is still is a great way to analyze bottlenecks in your code:

Analyzing Xdebug profile log in PhpStorm

Remote Debugging via SSH

You can troubleshoot bugs only if you can recreate them on your development environment. Of course, when it comes to running a website, we recommend having your development environment as close to production as possible, and you debug on a near-as-possible copy of the production database. Still, sometimes bugs and oddities will only happen on production. No one wants to start messing around on a live site, editing files to add logging lines. Don’t worry, PhpStorm and Xdebug have you covered! You can install Xdebug on a remote server and debug the code execution locally using Xdebug and PhpStorm. This feature is a lifesaver and doesn’t require much to set up. Make sure to turn off Xdebug when you are finished, so your site’s performance isn’t impacted by Xdebug unnecessarily.

Is Xdebug the Only Way to Debug PHP for WordPress?

The short answer is ‘no’, the long answer is ‘it depends’. While Xdebug is the recommended way to enable step debugging for PHP, you may want to consider other options. One which crossed my path recently is Ray, from the folks at Spatie. Ray is a paid application that runs on Mac, Windows or Linux, and connects to your PHP project. This is done either through a composer package or a WordPress plugin if you’re debugging for WordPress.

Once you have everything set up, you can use the ray() function call, which is very similar to JavaScript’s console.log() method, to send any arbitrary data to the Ray application. You can send it simple strings, variables (including arrays and objects), and even multiple data combinations.

ray('Hello world');

ray('My variable', $variable);

ray('array', $array, 'object', $object);

Ray sends this data to the Ray application using a similar process as you might do when making a cURL request to a JSON HTTP API.

There are some advantages to using Ray over Xdebug. Because Ray will only transmit data from your code to the Ray application when the application is running, it won’t have as much effect on your system performance as Xdebug might. Xdebug 3 is quite a performance improvement over Xdebug 2, and you can turn off debugging via the browser extension when you need to. It is, however, still installed and active on your local PHP environment, so you can only really turn it off entirely by disabling it in your php.ini.

Ray also allows you to do more advanced things like the ability to pause code execution, remote debugging via SSH, and the ability to capture any queries being executed during a PHP request. Xdebug does quite a bit more and is free, but Ray is something to consider as an option for debugging, especially if you’re still relying on dump debugging during development.

Supporting Xdebug

The person behind Xdebug is Derick Rethans. He is the sole developer of Xdebug and relies on sponsors to fund his ongoing work on maintenance and updates to Xdebug. Here at Delicious Brains, we’ve been a Patreon supporter of his since February 2019. If you or your company finds Xdebug useful, why not sponsor him through his Patreon or via GitHub?

Conclusion

I hope this has been a good introduction to how powerful and valuable using a debugger like Xdebug is for your development workflow, as well as a possible alternative if you find it too complex. Xdebug is a huge time saver, and I couldn’t imagine developing without it in my toolbox. Do you use Xdebug, Ray, or another debugger? Have I missed any excellent features, or do you have any tips? Let me know in the comments below.

About the Author

Iain Poulson

Iain is a product manager based in the south of England. He also runs multiple WordPress products. He helps people buy and sell WordPress businesses and writes a monthly newsletter about WordPress trends.