I did not know what XDebug was six months ago.
I had assumed that, because I’d never known a PHP developer to use a debugger, there was no such tool, or that if there was, it would be annoyingly difficult to use.
I won’t lie and say that XDebug will cut the debugging time of every issue you have in half. I won’t pretend that I use it every single day. But it is a tool that, if you really want to be a better programmer, you need in your tool belt. And it’s a tool I didn’t even know existed, because no one I knew used it.
Why You Should Use XDebug
Let me explain the claim I made: “if you really want to be a better programmer,” you need to be using XDebug.
XDebug is a (the) “Debugger and Profiler Tool for PHP“. It also supposedly “provides code coverage functionality for use with PHPUnit.” It does a number of nice extra things, like fancying up your
var_dump. But all I’m talking about here is using XDebug’s debugger functionality (fingers crossed, I’ll get to talk about the profiling aspect one of these days here).
When I first set up XDebug, I wasn’t certain how useful it would be. After all, PHP developers much my senior were not using it. A quick run through of XDebug with my Laravel application left me unimpressed; though it did a better job of showing me which class had actually called my bad method, XDebug did not give me as sudden and crystal-clear an understanding of the path my code was taking through all the factories and getters as I’d been hoping for.
But XDebug will make your debugging faster, safer, and more informative. Instead of having to decide where to write your var_dumps() and dies(), and having to refresh the page each time you realized you didn’t var_dump() in quite the right place, you can just set a couple of breakpoints and step over and through the code as you need. You can decide, on the spot, which variables you want to be watching. You’re less likely to accidentally leave some of that debugging code behind — and you’re less likely to accidentally change things for the worse while trying to debug an issue.
XDebug is especially nice for debugging issues that are occurring in loops, or other pieces of code that are called multiple times in a request. You can watch all the variables you want, and keep playing over the loops until you find something that looks funky. I’ve found this to be especially useful when my code is doing some processing over multiple items, and the data on one of those items (it’s never the first one) seems to be messing something up.
Really, there are all kinds of particular cases where XDebug really shines. In general, the more levels of difficulty added to a debugging problem, the more of a life saver XDebug becomes. And the more you use it in your day-to-day life, the more comfortable you’ll be pulling it out in those really difficult situations.
But more importantly: using XDebug, even once, will make you much more aware of the life cycle of a request to your app. Using XDebug regularly can help you write better code. Instead of just writing a line of code and rerunning the script to see if you’re getting the final result you were expecting, you can easily keep an eye on exactly what those variables are doing. Is the same calculation being done over and over and over again, when maybe it should just be saved somewhere after the first calculation? Is something else also calling your getter in this request, and you didn’t realize it?
You become much more aware of these sorts of things when you’re using XDebug. You become a better programmer.
What is XDebug, and how does it work?
XDebug is a PHP Zend extension. You obtain the appropriate .so file (or .dll file, if you’re using Windows) and add it as a “zend_extension” in your PHP configuration file(s). You can configure it just like all your other PHP configurations in that file or an included configuration file, and you’ll be able to check its configuration in the output of
phpinfo(). When the extension is successfully included, you’ll also see XDebug listed in the command line output of
When configured correctly, XDebug and your IDE will talk to each other on the configured port. In the setup I talk about here, your IDE starts listening on the configured port when you “turn on” debugging. It waits for XDebug to connect, which will not happen until a request is made to your PHP server. After that request has been made, spinning up XDebug, XDebug will connect to the configured port. Assuming everything is configured correctly and the two can now “talk” to each other, the IDE will then tell XDebug where our specified breakpoints are and on what exceptions to break. Those are things you select in your IDE’s debugger UX, and you can see them communicated after each successful connection in your xdebug.log file, assuming you configure XDebug with a log file.
If the breakpoint conditions specified by the IDE are met in this request, XDebug will pause the execution of the script and, at this point, the IDE and XDebug can communicate back and forth to tell you useful things about the stack trace, what the variables in scope are, and to respond to your requests to step over or into following portions of the code. As far as I’ve been able to tell, all of this communication is logged in xdebug’s log file. You can also get your IDE to give you some logging on all of this, but so far I’ve only found VSCode’s logging useful for telling whether or not the connection was made.
Setting up XDebug
These are the general steps for setting up XDebug, whatever the specifics of your environment are.
1. Obtain the appropriate XDebug extension for your PHP install
2. Configure your PHP install to use the extension
3. Configure when and how XDebug tries to connect to the “remote” debugger
4. Obtain the appropriate extension/plugin/package for your XDebug and your IDE, if necessary
5. Configure your IDE to listen at the same port you told XDebug to connect to
6. Configure your IDE to use the correct path mappings
There are some environments that make this all incredibly easy. If you’ve got a single PHP version, installed and managed by a good package manager, and on the same machine as your IDE, the package manager and IDE can take care of nearly the entire thing for you.
For example, if you’re running PHP on OSX and you’re managing that install via Homebrew, you can simply run
brew install php71-xdebug #or php56, whatever you see for you PHP version in brew list
That single line will get the appropriate XDebug extension and create a PHP configuration file that will include XDebug for you.
Or if you’re running CentOS and using Yum, you should be able to run
sudo yum install php-pecl-xdebug #this installed, among other things, php-pecl-xdebug-2.2.7-1.el7.x86_64, which it knows is compatible with my version of PHP
Following both of those one-liners, I could then see XDebug in the output of
php -v, though you’ll generally have to restart your web server (if you’re not going to be debugging a command line script) to have XDebug available for requests that go through your web server.
If we stick with the simple OSX example, you can just add three lines to the configuration file. Mine is located at /usr/local/etc/php/7.1/conf.d/ext-xdebug.ini and you can see how little is in it below. You don’t really have to add the remote_log line, but I certainly wouldn’t start off without it.
zend_extension="/usr/local/opt/php71-xdebug/xdebug.so" # this line was already there
xdebug.remote_enable = 1
xdebug.remote_autostart = 1
xdebug.remote_log = "/[path_where_I_log_things]/xdebug.log"
I didn’t even have to add a line specifying my port or host; the defaults are 9000 and localhost, respectively.
Then I went to my IDE, which happens to be Visual Studio Code, and installed the PHP XDebug extension by Felix Becker. The extension is available on Windows and OSX, and came up as the top thing for me when I searched XDebug in the extension marketplace. VSCode creates a new launch.json file for each project you debug in; when you first go to turn on debugging it’ll ask you what kind of debugging you want to do. You select “PHP,” and the config file that it’ll autogenerate will look something like this:
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"name": "Listen for XDebug",
"name": "Launch currently open script",
You have to choose which configuration option you want to use (I never use “launch currently open script” as I’m generally debugging whole applications, rather than individual script files), but both of them work immediately. That’s it!
Your particular IDE will have its own quirks about the debugging process, but VSCode’s was similar enough to Chrome’s that I didn’t have to look anything up. Nonetheless, they seem to have useful instructions on using breakpoints, etc, over here. I know plenty of other IDEs have similar instructions.
For all the other scenarios
Of course, it’s not going to be that easy for a lot of people.
I now know many of my coworkers were not using XDebug because they had been unable to set it up. Setting up XDebug can provide some difficulty, and I think a large part of that difficulty actually stems from all the quick-and-dirty tutorials on getting up and running with XDebug out there. We developers work in many different environments, and XDebug needs to be configured to match those environments. Some environments certainly make things more difficult.
So at some point here I’m going to start sharing some of the tips and tricks I’ve been using for debugging other’s debug setups. Unfortunately, compiling them all into one blog post doesn’t seem like it’s going to be easily done; there are, quite simply, too many things to bring together all at once. Therefore I’ll be including links to XDebug-related posts below as they come along.
If you have any questions, feel free to email me.