Debugging PHP in Vim using VimDebugger
Remote Debugging PHP from vim
I’ve recently been using PHP a lot. I’ve also been using VIM a lot. So, it makes sense that I would start using the PHP remote debugger, right? Well, the problem is (and don’t take this the wrong way), the php remote debugger plugin for vim was written in 2004, and there are a few things missing from it. So, being the overly-easily-amused developer I am, I embarked on a quest to fix it.
DBGp python library
It didn’t take me long to realize that the python classes (vimscript doesn’t do sockets, so you have to use something like python) provided with the old plugin was not extensible enough for what I needed. I started re-factoring it, but fortunately before I got too far I found a DBGp python library written by ActiveState. I had to do some hacking to get it all working together — vim doesn’t deal with import statements in python well — but it turned out to be perfect for my needs.
Here is the README from the github repo:
VimDebugger is a dbgp client plugin for vim for debugging php applications with xdebug. It is currently in early stages, but is already much more advanced than many other options.
VimDebugger utilizes the ActiveState DBGp library. VimDebugger borrows code liberally from the remote PHP debugger vim plugin by Seung Woo Shin.
At present, VimDebugger.py must be saved as $VIMRUNTIME.”/plugin/VimDebugger.py” in order to work correctly. I hope to allow more flexible placement of this file in a future release. VimDebugger.vim should generally be placed in the same directory.
VimDebugger will listen on localhost:9000. This could be changed in VimDebugger.vim, but currently is not easily configurable through your .vimrc. You need to have xdebug already configured to connect to that port.
The following commands are added by the debugger for all your remote debugging needs:
- :DbgRun – Starts the listener (listens for 10 seconds) if your script is not already attached. If it is, continues (will run to the end or until the next breakpoint)
- :DbgDetach – Detaches the remote debugger and shuts down the listener
- :DbgToggleBreakpoint – Toggles a breakpoint on the current line of the current file
- :DbgStepInto – Steps into the next function or include
- :DbgStepOver – Steps over the next function or include
- :DbgStepOut – Steps out to the next step up in the stack
The debugger does not automatically bind any hotkeys, but leaves that to you to do in your own .vimrc. I often use Visual Studio, so I set my key bindings up in a similar way:
map <F11> :DbgStepInto<CR> map <F10> :DbgStepOver<CR> map <S-F11> :DbgStepOut<CR> map <F5> :DbgRun<CR> map <S-F5> :DbgDetach<CR> map <F8> :DbgToggleBreakpoint<CR>
Currently, there are no functions for adding your own watch items, but that is planned for one of the next releases. For now, the watch window will automatically refresh every step with the current context. The Watch window utilizes vim code folding on multi-line entries, so if you have an object, be sure to expand it by double clicking, hitting enter, or using the vim code folding keyboard commands to see the introspection at work. Only three levels are returned, to keep the response from overwhelming the debugger.
For objects, a list of the methods available on that object will be returned with their visibility. Also, a list of properties will be returned with their visibility and their value.
For arrays, a list of values (up to three nested levels) will be returned.
The stack window updates every frame. To go to another part of the stack, simply go to the line in question and double click or press enter.
How well does it work? Well, let me show you some screenshots, and then you decide.
On the left, you see the code window; that looks almost identical to the old vim plugin. That’s one thing it did very well, and I couldn’t improve on it; so I shamelessly stole a lot of the code, instead. On the bottom right you see the first of the improvements I added. Instead of just displaying a list of stack entries and requiring someone to type “:Up” and “:Dn” to traverse the stack, the list is formatted and set up so that you can double click or press enter on the line you want, and it will take you there in the left pane.
The real improvements can be seen in the watch window, on the top right. You may notice that these are folded; there are three variables in the local scope, and as you can see they are all instances of objects. Well, often when I’m debugging I need to know what kind of object that is! Here it tells you. That’s not even the best part, though; expand the fold, and you’ll see the following:
The number of methods and properties! Expand methods:
I’ve truncated it for brevity, but trust me: the method names are all there. Let’s look at properties.
You’ll notice again; all of the properties, with their values, and… more folds? Open one of them.
As you can see, array values are also sent. For practical reasons, there is a depth limit of 3 when fetching watch variables, but that is configurable. The main challenge is getting the data back from PHP; I have the max data size set at 64KB, so that is the limit to how much can be sent. We could probably raise that limit, but that has other consequences.
First, you may want to read this blog post to see how to set up xdebug. Instead of using the vim plugin they link to, though, get mine from the github repo. Put VimDebugger.py and VimDebugger.vim in your ~/.vim/plugins dir, and add the key bindings to your .vimrc. Let me know how it goes!