All hail Xdebug and lets let var dump die

Posted December 30, 2016

How many times have you been working in Magento or any other php application and hit an error, exception or something not quite right? For me a lot. I’ve been that developer that debugs by fire and throws echo var_dumps and dies around like a western gunslinger. It’s easy, provides quick feedback cycles but lets be honest its lazy, in efficient, rarely provides all of the data you need to solve the problem on the first try and its not something you want to boast about by the coffee machine.

Before we look at the future lets take a trip into the past and well present for some people. What can go wrong and what are we missing by adding these “debug” statements to our code?

Well to start with how many times have you dropped a var_dump($someObject);die(); and either been presented with a huge array of objects that make no sense and out comes “ctrl + f” to hunt down the variable you want, or even worse that white screen and out comes more debugging to find out that the object we wanted to dump was too large and we ran out of memory.

Even worse if you are dropping in die statements in many different classes to try and work out the path of execution saving each one how do you remember to not commit one of those dies that didn’t actually execute on that request? Trust me I’ve been there and had to un commit someones die statement that made it into live.

So whats the “better” way of debugging a application? Well welcome Xdebug + PHPStorm. Imagine inside of the IDE we can set a breakpoint, a fancy die and reload our web page. Magically the IDE has stopped execution and we can see the state of the application at that exact path. We can see the variables and we can step through the code and see exactly what class and method is called next and so on and so forth.

Now in Vagrant I actually had this working without issue, but as you know I am a huge Docker fan and I hit issues with getting Xdebug setup working inside of a Docker environment with Magento 2.

Getting Xdebug and Docker and PHPStorm working.

Right lets dig into what I actually had to do in order to get Xdebug working with a docker container.

I am assuming here that you already have a working docker setup, All of my docker images can be found here. Also take a look at my post on docker and docker-sync for an insight into how I work.

I use a apache:php7 docker image and because I wanted to add Xdebug into this image. I think its also possible to have xdebug as a container as well but for now I went simple. I created a new Dockerfile that inherits from the main apache image. The image already contains all of the inner workings and packages that Magento 2 requires so all I actually need to do is get Xdebug installed inside of docker. So for that I add the following snippet:

# Xdebug stuff
RUN apt-get update && apt-get upgrade -y && apt-get autoremove -y \
    && apt-get install -y git curl \
    && pecl install xdebug \
    && rm -rf /tmp/pear \
    && echo "zend_extension=$(find /usr/local/lib/php/extensions/ -name xdebug.so)\n"
       >> /usr/local/etc/php/conf.d/xdebug.ini

Github link for Dockerfile github

Lets break down what this is doing. Well its pretty simple and if you have had to install Xdebug on a Linux box before you will be used to this. I install all of the linux packages required and tell docker using docker-php-ext-configure to configure php with some more packages. I then finish up with installing Xdebug via PECL (Soon to be stopped) and finish with enabling some default settings in Xdebug.ini file. More will be added to this file later.

Building Images.

So we now have a modified image and our docker-compose file points to the folder now and not the image. We do this via build: ../../ directive and not the image that we would have had.

What I like to do is run docker build app app being the name of the container that we just modified. What this does is well builds the docker image for us and you will see each of the commands running until finally we get success. What this step tells me is that we have a running image now with Xdebug installed and ready to use.

Configure Xdebug to work.

Now I need to configure some settings to get Xdebug working and talking back to the host machine. To do this I already have a php.ini file that gets copied over to the container so I modify this file with the following:

[Xdebug]
;remember to change this to your local ip address
xdebug.remote_host=10.254.254.254
xdebug.remote_autostart=1
xdebug.idekey = PHPSTORM
xdebug.default_enable = 0
xdebug.remote_enable = 1
xdebug.remote_connect_back = 0
xdebug.profiler_enable = 1

Gtihub link for PHP ini

The most important line here is the first line. It has to be the IP address of your host machine. Found via ifconfig in terminal. I ignored this line many times and read guides that say auto_start etc will do this automatically but I could not get Xdebug talking to PHPStorm without this line.

The rest of the settings are pretty stock just enables profiler, connections and IDE Key etc.

Networking.

The final step where docker in concerned was to create a new network alias. It seems that there is a problem with docker that stops docker networks talking to the host machine properly. The docker containers can talk to each other but because of subnet or other network layer communication back to the host machine does not always work. To resolve this we run: sudo ifconfig en0 alias 10.254.254.254 255.255.255.0 This creates the alias for us and will allow docker to work in both directions meaning that Xdebug and PHPStorm can talk to each other.

Now for how to configure PHPStorm.

Now the easy part and if you have had Xdebug and PHPStorm working in the past this is just the same. To start with open up Tools -> DBGProxy and select configure. We need to enter in our: IDEKey, Host and Port number:

Next up we want to configure the “Server” and we do this via Preferences -> Language & Frameworks -> PHP -> Servers.

This time we enter a Name, Host, Port debugger and set up the map so that in my case app maps to /var/www/html inside of the docker container:

Time to debug.

Now with all of the settings saved and docker containers running we can create a breakpoint in our code.

We do this by clicking in the gutter of any open editor. When applied we see as per above the red dot and highlighted line. What does this actually mean? Well when the PHP interpreter runs with Xdebug enabled it knows because of PHPStorm posting into the container to stop execution at that exact point.

Now comes the clever part. I have the chrome plugin installed that allows be to be selective when I want to debug a request. This means that I can reload pages etc without PHPStorm kicking into action and debugging PHP but I can also toggle chrome with the plugin to start debugging when I want. I do this by selecting “debug” from the chrome bug icon:

We also need to set PHPStorm to listen for connections, We do this via the PHPStorm toolbar icon: once this is listening we can reload our page in chrome and we should see that the page looks to be “reloading” for a long time. Head back into PHPStorm and we should now have the debug panel active:

This is where we can start to inspect the “state” of our PHP application. In this example Magento 2. We can step through using the icons on the left and what this does is allow Magento 2 to run the next step of code and break again. This time we will see what class and method is called.

Demo Time

Happy Debugging.

Hopefully by now you have Docker + PHPStorm + Magento 2 + Xdebug all working together in peace and harmony. There are loads of great videos and other resources on using Xdebug on youtube. So I won’t cover stepping through etc. Yet I hope you get from this article a helpful guide on getting Xdebug working in Docker, and if you were like me and debugging via die then I hope you see that there are better ways and will at least try to embrace this way of working to speed up and improve the value of your debugging.

I would also love to hear from you in the comments, If you use Xdebug and Docker do you use it in its own container? Was this guide helpful? did I miss anything? Also how do you approach debugging ?

You may also find these related posts interesting: Developer Book Club A new look and a cleanup of content plus good bye github pages. Docker and Docker Sync Patching Magento 2 vendor directory