Wednesday, June 20th, 2007

How to Debug PHP with Vim and XDebug on Linux

By

Sam Ghods is the Vice President of Technology at Box, where he manages the design and architecture of Box’s application and technology stack.

Here is the scenario: You have multiple developers logged into a Linux server which is running Apache and PHP using Vim to write PHP code. They’re using error_log and echo statements to debug their code. It takes forever, it’s tedious and can result in bugs from forgotten debug statements. You stare enviously at .NET programmers with fancy debuggers (while you snicker knowing that you edit 10x faster with Vim anyways). But still, you know there has to be a better way. There is. Here’s how it works. You’re coding away in vim. You hit F5; Vim waits for a connection from the PHP server. You refresh the PHP page you’re working on. It attempts to contact Vim — connection successful. You are launched into a debugging session right inside Vim. You can step into, over, and out of statements, eval statements, get all variables in context, get and set properties, remove and set breakpoints, all on the fly. Finally, some real programming tools.

The environment: Vim

First, we need to make sure vim is compiled correctly. Type :version in your Vim and check the features section. If you have +python and +signs, you’re good to go. Skip ahead to the next section. Otherwise, you need to compile from source. On Linux, download and untar the source code, and open up src/feature.h. You need to comment out the conditional statements around the +signs feature. In Vim 7.1, it looks like this:

/* * +signs Allow signs to be displayed to the left of text lines. * Adds the ":sign" command. */
#if defined(FEAT_BIG) || defined(FEAT_SUN_WORKSHOP) || defined(FEAT_NETBEANS_INTG) # define 
FEAT_SIGNS # if ((defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && 
defined(HAVE_X11_XPM_H)) || defined(FEAT_GUI_GTK) || (defined(WIN32) && 
defined(FEAT_GUI)) # define FEAT_SIGN_ICONS # endif #endif

You need to comment out both if statements and their respective endif’s, so it looks like this:

/* * +signs Allow signs to be displayed to the left of text lines. * Adds the ":sign" command. */ 
/*#if defined(FEAT_BIG) || defined(FEAT_SUN_WORKSHOP) || defined(FEAT_NETBEANS_INTG)*/ # 
define FEAT_SIGNS /*# if ((defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && 
defined(HAVE_X11_XPM_H)) || defined(FEAT_GUI_GTK) || (defined(WIN32) && 
defined(FEAT_GUI))*/ # define FEAT_SIGN_ICONS /*# endif #endif*/

then cd back to the top source directory and run:

./configure --enable-pythoninterp

If on a 64-bit system or a system with a strange python installation, you may have to add --with-python-config-dir=/usr/lib64/python2.3/config to the configure string. If the configure script can’t find the config directory it will say so in the script output, but the configure won’t explicitly fail — so you have to be sure that all the python stuff is OK. After configuration, do

make && make install

to install your newly python’d vim.

The client: Debugger.vim (and Debugger.py)

Now that vim is ready, download the DBGp client script. Extract the two files (debugger.vim and debugger.py) to your vim plugin directory or your .vim home directory. In Vim 7.1 compiled on Linux, the default load-for-everyone plugin location is /usr/local/share/vim/vim71/plugin/, so you would put both files in that directory. Try to run vim — if you get no errors, everything should be good. If you get an error, double check :version to make sure +python and +signs are there. If they are, post a comment here with the vi error you get. If they aren’t, the vim compilation/installation didn’t work — go back and try it again. Now your debugging client is ready. Now let’s setup the server.

The server (engine): XDebug

Download XDebug. Download the source, compile the .so and add the following lines to your php.ini:

[Zend] zend_extension = /full/path/to/xdebug.so xdebug.remote_enable = 1 xdebug.remote_port = 
9000 xdebug.remote_host = localhost

Open a file in your browser which outputs <?php phpinfo(); ?> to make sure xdebug is loaded.

Now, with your site being example.com, go to http://example.com/index.php?XDEBUG_SESSION_START=1. This will set a cookie in your browser which expires in 1 hour which tells the PHP XDebug module to try to make a connection every time a page loads to a debugging client which is listening on port 9000. The cool thing is that if it can’t make a connection, it just keeps loading the page, so there’s no issue just leaving the cookie on. Now go back to vim and press F5. You should see a message like “waiting for a new connection on port 9000 for 5 seconds…” at the bottom of the screen. You have five seconds to now refresh the PHP page. This will create a connection between the debugger and client. Now you’re debugging. Follow the instructions in the Help window to step into, over and out of code. Press F5 to run the code until a breakpoint (which you can set using :Bp).

But what if I have multiple developers on the same machine?

No problem. Simply set g:debuggerPort in each developer’s .vimrc to get the client listening on a different port. So if you wanted one developer to connect on 9001 instead of the standard 9000, you would add this line to their .vimrc:

let g:debuggerPort = 9001

Getting the server to connect on a different port is a little trickier. You need to set a custom php.ini value (xdebug.remote_port) for each user. It works best if you’re using VirtualHost’s in Apache. Just add the following line to the VirtualHost section of your httpd.conf:

php_value xdebug.remote_port 9001

Now restart Apache and if you use that VirtualHost and that vi user, then they should connect successfully.

That’s about it

Please post any questions or suggestions you may have. I hope this helps a few of you out there who want debugging tools but don’t want to give up Vim editing. Also be sure to post any alternate methods, or any patches or improvements to the remote PHP debugger vim script, and I’ll be sure to incorporate them. [Note: This is the first in a series of posts we'll be doing along the lines of tutorials, tools we've developed, tech commentary, and so on. Please feel free to subscribe, as well as leave any comments, thoughts or suggestions below so we can be sure to improve with each article! Thanks!]

By

See all of Sam's articles.

Tags

PHP VIM

  • stabledog

    Well, 6 years later I think your vim-based php debugger is a wonderful thing. I use it to debug CLI php scripts: http://stackoverflow.com/a/17885410/237059

  • inessence

    I downloaded and compiled xdebug-2.2.3.tgz, and it seemed to go okay. The vim gives the “waiting for a new connection…” message, and I can see xdebug with the phpinfo() command, but if I try to put any of the remote options (e.g., “xdebug.remote_enable = 1 xdebug.remote_port = 9000 xdebug.remote_host = localhost”) into php.ini, then phpinfo() does not display anything about xdebug. This is on a shared host on Bluehost. Do you think that Bluehost is preventing it?

  • wthielen

    And one more year later, I found this, and agree with stabledog. It is wonderful, and I’m going to learn to get used to this method of debugging. Just wish it would exit cleanly, or notify cleanly, when the PHP script has been executed fully, instead of showing me exceptions that the socket is closed with a Python backtrace.