The definitive remote debug and unittest with PHPStorm guide: part 3
This post is part of the guide on how to setup your PHPStorm for remote debugging and unit testing. Click here for the beginning of this guide.
Debugging your web application
Let’s start with probably the most important part of all: debugging your web applications. In this day and age, people still use var_dump() and die() to debug their application. A shame really, knowing that step-debugging through your code is made really easy with PHPStorm. Using var_dump() is very slow, error prone and you only get a small fraction of the context you need in order to debug correctly. And how many times did such a var_dump() hit your production environment?? Truth be told, implementing XDebug does need a little bit of work, but fortunately PHPStorm has made things super easy for us.
Setting up XDebug
First thing we need to do is setup the XDebug extension on the remote machine. We need to install it and most of the time this involves doing a “pecl install xdebug”, but you need to have some additional packages for this (php-dev for instance). But often you can even find a php-xdebug package in your distribution as well. You can find more info on installing XDebug at their website.
Once Xdebug is compiled/installed, we need to configure it. Nowadays, this normally is done inside a separate xdebug.ini file:
; The path to where your extension is installed
xdebug.remote_enable = 1
xdebug.idekey = "PhpStorm1"
xdebug.remote_autostart = 1
xdebug.remote_connect_back = 1
xdebug.remote_port = 9000
xdebug.remote_connect_back is a nice one too, as it tells xdebug that it needs to connect back to the client that is connecting to the website. So, if we connect locally to our remote web server (http://project.debian.virtualbox.local), it automatically figures out who you are (your IP), and uses that IP to find a running debugger. In effect, you don’t need to worry about from where you connect from (this only applies for WEB applications though, we come back later to figure out how we deal with CLI apps).
Xdebug should be fully up and running now. Whenever you browse to a site you should see no issues and everything should work as expected and without any timeouts. If you have a 5-10 second timeout on each page, you might have issues where xdebug tries to connect to a debugger which isn’t listening. I haven’t seen this issues on Linux / OSX , but I’ve seen it can happen on window machines. In that case, you might want to set the xdebug.remote_autostart flag to 0 and start the debugger manually when you need to.
Setting up your PHPStorm
Let’s start with creating a simple project, and open this in PHPStorm.
We can browse the site by just browsing to http://myproject.debian.virtualbox.local. Notice that there is no debug connection at all? As if Xdebug isn’t operating. What we need to do is enable the debugger listener which can be found in the toolbar at the top of PHPStorm.
A green listening icon means that the debugger is listening to incoming connections. At this point, we can return to our browser and hit refresh. PHPStorm will popup a new window notifying you of an (unknown) incoming debug connection.
If we accept the connection, nothing seems to have happened, and this is actually ok: we haven’t told the debugger to do anything. You can configure the debugger to always stop at the start of your application but this will be more annoying that you might think. I normally want the debugger to actually stop at the place where I really want to debug. Which is where our breakpoints come in…
Suppose something goes wrong around line 3, and we want to debug this. We place a breakpoint at this point by clicking at the left gutter which add a red bullet and make the line turn red. Now we have an active breakpoint. Let’s refresh the website again:
There we go: the debugger stopped before executing our line 3, and we can do our debugger thing!
Dealing with path mappings
Things become a bit more complex when you deal with libraries or anything that is outside your public document root. Especially in a composer/PSR0 world, pretty much everything is nicely stored inside reusable components and libraries. Let’s assume the following as a proof of concept:
We have a “library” and notice how this library is outside our “public” webroot? I’ve set a breakpoint in the library code which gets called, but PHPStorm will NOT stop.
The reason for this are path mappings (i told about path mappings in the first chapter). It can find its file inside the public webroot, and if you have other files, those will probably debug properly, but everything outside is unknown to PHPStorm. You must notify PHPStorm about those files, which luckily is easier than it sounds.
Click on the “server configuration button” (the black triangle in the toolbar) and select “edit configurations”.
Open the defaults, find the PHP Remote Debug which will look something similar to this:
Click on the “…” after the <no server>”. Here you get a default server configuration:
Notice that the path mapping is set on the /public directory and points to /wwwroot/myproject/public? In order for PHPStorm to find your files, you must set a path mapping to the lowest directory possible. In our case we could set a path mapping from /Volumes/www/myproject to /wwwroot/myproject, after which all files in that directory (thus including all the libraries are automatically found).
Let’s set the correct path mapping at the lowest directory possible. Normally, one path mapping is more than enough because everything above would map correctly, but in case of very extreme complicated setups, multiple path mappings might make sense.
Save/Apply this path mapping, and refresh the website. If the breakpoint in the library was still there, it will now perfectly break on your breakpoint. Hurrah! Easy peasy remote debugging!
Why the needlessly difficult path mappings?
Remember we are dealing with local files and remote files which all point to the same thing? From PHPStorms perspective, our index.php file is found at /Volumes/www/myproject/public/index.php, from apache (and also xdebug) point of view, this file is found at /wwwroot/myproject/public/index.php. When you set a breakpoint in the library, it tells xdebug to set a breakpoint in /Volumes/www/myproject/library/library.php, a file which Xdebug knows absolutely nothing about. This is why the breakpoint never happened. However, with the correct path mapping, PHPStorm can tell xdebug the correct file: /wwwroot/myproject/library/library.php. The same applies for files and information that xdebug sends to PHPStorm: when it receives /wwwroot/myproject/library/library.php, PHPStorm is now is aware that this file is found locally at /Volumes/www/myproject/library/library.php.
- Breakpoints are remarkably more powerful than most users know. Right click on the red-dot breakpoint, click “more” and you can even set conditions when this breakpoint must be triggered. For instance, only at the 10th time we run this breakpoint, or not until another breakpoint has been hit, or even when a certain evaluation has run (when $s == “foobar”).
- If your breakpoints don’t run, check if your debugger listener is actually enabled. (the phone icon in the toolbar). When turned red, the debugger isn’t listening.
- The frames and variable windows are perfect in seeing what is happening at this exact moment in your app. You have an instant stack-trace inside the frame window
- You can change values on the fly during debugging.
- It’s possible to have multiple project windows open in PHPStorm (which i often do), but if you have one of them listen to debug connections, other windows will be triggered as well.
- Use “php -m” and phpinfo() to quickly figure out if the xdebug extension is loaded in PHP. It should be listed in the output. If not, this means that the xdebug extension has a problem loading (most likely, a wrong path, loaded as a normal extension instead of a zend_extention).