2015 Oct 09

Over the years I've stumbled upon code that's had the need to really be examined at a deep level in order to understand what it's trying to do. Sometimes it's not enough to just read through the code to try and understand what's happening, or start killing the script to dump variables with print_r or var_dump (more on that later).

The best way to debug PHP code is similar to how you would a compiled language (C++ or Java). It is with a PHP extension called Xdebug. Xdebug on the server works together with your IDE or code editor over a protocol called DBGP (I'm guessing that stands for Debug Protocol). Here is a quick explanation of what is going on with DBGP with a visual. Just know that the server and your editor are connected and you can watch the process flow while seeing current state information passed down as the code executes. If you want more info, you can nerd out on DBGP here.

While using an xdebug capable editor or IDE, you're able to set breakpoints, step into, over, and out of code and watch variables and the call stack change as the request makes its way through the code. These are all familiar concepts you'd normally see while debugging a compiled language, or even Javascript using the debug tools in the browser.

That all being said, this isn't something you should incorporate into your everyday coding. In my mind it's nice to know it's there when I need it but too heavy to use as a normal tool while developing. Assuming for a second you disagree, I'll also argue that 95% of any modern PHP being written shouldn't warrant immediate debugging at the level of xdebug. These days we use frameworks and common tools to keep all of the complicated stuff off in a black box and out of sight. Things like authentication, data migration, MVC routing, etc. are all usually built into CMS's or web application frameworks and are known to work. The things that are left which us developers create usually follow a pattern or convention (again based on a pre-defined tool) so that from project to project you can spot a folder structure or file naming similarites. Having this, we then know what's going on without having to disect any given lifecycle request for the app to see how it's working. Some real life examples would be if someone showed you a website built using a common CMS like Drupal or Wordpress, or a web application built using Laravel or Symfony. If you already knew something about how things are built using those tools (or even if you didn't), you can glean how it's built pretty quickly as there are key places where things go. This being in complete contrast with something built from scratch where every convention was soley up to the previous developer. In that case documentation and maybe a walk through with xdebug is in the cards.

Here I'll give my steps used to setup Xdebug whilst using a Vagrant VM. In previous years I'd just used Xdebug with XAMPP locally as xdebug came with the xampp install and it was all local, thus being easy to setup. I remember trying to debug "remotely" and not having much luck, but having done it recently it's not that much different.

The first step is deciding on which IDE or editor you want to use to debug. For now, I still err on the side of using IDE's for this type of thing as that's true to their nature (netbeans or eclipse for example). See here for a complete list of xdebug clients. I also have a list of resources at the end for how to setup with different types of editors.

I've chosen the Netbeans IDE for PHP as it's what I've used in the past and I like its interface as it's not too busy. 

Once that decision has been made we can install and configure xdebug on the server. For my setup I've got a Vagrant VM with CentOS on it running Nginx and PHP-FPM. If you run Apache and mod_php it's the same config. The only thing we actually need to do is put a config section in the php.ini file at the bottom. I want to also touch on the settings below as they are generally the ones you'll find other people using and in different combinations. This is one of the harder parts I've found about using Xdebug, trying to find out which settings I actually need form a consolidated source.

[xdebug]
zend_extension=/path/to/xdebug.so(.dll)
xdebug.remote_enable=On
xdebug.remote_handler="dbgp"
xdebug.remote_port=9000
xdebug.remote_autostart = 1
xdebug.idekey = "netbeans-xdebug"
xdebug.remote_host="YOUR.IP.GOES.HERE"
xdebug.remote_connect_back=on
  •  zend_extension: Typically you don't need this. If you've installed xdebug with a package manager (which we'll do in a second), it should have put the .so file in the right spot. If you're on windows I'm guessing you're probably just using some version of *AMP and can usually just go into the exising .ini file and uncomment the xdebug section. If there isn't already a config block then you can enter the settings and find the .dll with the rest of the extensions and enter the correct path to it.
  •  xdebug.remote_enable: This is useful for when you want to debug remotely, something we're going to do. You'll notice some params take '1' and others take 'On'. I've found they both work but I think the modern approach is to use 1 or 0.
  •  xdebug.remote_handler: You can leave this out. Based on the docs for remote_handler dbgp is the only supported version after Xdebug 2.1 anyway
  •  xdebug.remote_port: Defaults to port 9000. I've used this to set a different port as sometimes you might get a message from Netbeans asking "Port 9000 in use, use another port?"
  •  xdebug.remote_autostart: I haven't ever used this but it can be used to allow every request to the server to look for a debug session. Normally when you start a session, you or your editor will pass in a query string to indicate that you want to start debugging. It looks something like ?XDEBUG_SESSION_START=netbeans-xdebug.
  •  xdebug.idekey: You can leave this out. The editor environment will usually control what this gets set to.
  •  xdebug.remote_host: The IP of your machine. Don't set this as the next setting will trump it.
  •  xdebug.remote_connect_back: If enabled, the xdebug.remote_host setting is ignored and Xdebug will try to connect to the client that made the HTTP request.

 So in short all you really need for remote debugging is: 

[xdebug]
xdebug.remote_enable=on
xdebug.remote_connect_back=on
xdebug.remote_port=9000

For debugging locally these settings are probably even still more than you need as the remote settings are moot at that point.

The next step is installing the PHP extension. There's a cool tool available called the "xdebug wizard" where you can paste in the output of your phpinfo page and it'll tell you how to configure it based on what it finds. I've used it in the past but not anymore as package managers on linux make things easy. I thought I'd mention it for anyone still using the many variations of *AMP out there (XAMPP, WAMP, MAMP, AMPPS). 

For debian based distros:
sudo apt-get install php5-dev
sudo pecl install xdebug

For red-hat based distros:
sudo yum install php-pear php-devel
sudo pecl install xdebug

The EPEL and REMI repos (which I'm using) contain xdebug for php. The package name is php-pecl-xdebug, so you can just yum install php-pecl-xdebug if you're using those repos already.

Once that's done, you need to restart the process for PHP (PHP-FPM or if using mod_php, restart Apache). You should then see the new xdebug section in your info file like the image below:

The xdebug config display on the phpinfo page

Now back in Netbeans, we need to configure our project. Under Tools, Options, go to the PHP page and click the Debugging tab. Change the port to be the one you set in your php.ini file. Also, if you haven't set any breakpoints, click "Stop at First Line". Then Under File, Project Properties, "Sources" should be highlighted already. You'll probably alreay have the project and source folders setup from when you created the project but the important setting here is the Web Root path. Normally it hasn't needed to be set but this is something that's stumped me while trying to debug a laravel app. Netbeans needed an overall folder structure for the project, but needed to also know where the entry point to the app was. So in this case I needed to set the webroot to "Public", which is where a laravel doc root is setup inside the overall project.

Xdebug config within Netbeans

Under "Run Configuration", set the project URL to the same address you use in your browser. On this page there's also an advanced button. In there, you can set browser behaviour to open every time, to ask every time, or to not open a browser for debugging at all. I'll mention the path mapping setting just because it's sometimes needed for remote debugging. I haven't needed to employ it though. I won't go into it here but more info about path mapping is available elsewhere on the web.

That's all the config we need. You should be able to start debugging and see the first line of code highlighted in green, or where you've set a breakpoint. One important part is that you need to make sure the IDE is part of the whitelist in your firewall. Typically on windows as an example when you hit run for the first time you'll see the familiar prompt asking you if you will allow access for this program, similar to skype or any other app trying to open a port.

The degug bar before debugging:
xdebug bar before testing has started

...and after it's been started:
xdebug bar while testing

All the changes we've just made in Netbeans are similar enough to how other tools do it. I've included links below for how to set this up in other popular editors.

Conclusion

At the start I mentioned I'd say more about print_r and var_dump. I want to point out that these are still valid to use day-to-day. Most of the time to figure out how something works I will just read the code. Then if running into trouble while running it, I'll jump in and start using die() and print_r to quickly see what's happening. In Atom i have a snippet saved for such occasions. If while in a PHP file I can type "pr", hit tab, and the following appears with the variable part highlighted ready to change:

echo '<pre>';
print_r($variable);
echo '</pre>';

Resources

xdebug for atom 

This is currently an alpha release, and still in active development. (as of this writing). I tried it briefly but gave up quickly as it wasn't showing any signs of working.
Atom PHP Debugging Package

xdebug for sublime

Debugging with Xdebug and Sublime Text 3
Xdebug and Sublime Text
Download the Xdebug package for sublime

How to setup for PHP storm

xdebug for PHPstorm