Using Vim as a PHP IDE

This isn't a blog about why you should use Vim. That's obvious. If it isn't, read Why do those nutheads use Vi? Instead, this a tutorial about using Vim as a fully-fledged IDE, and specifically for PHP projects. Many Vim users would only use it for dipping in and out of the odd file on their computer, or maybe just on a remote server during an SSH session. I'm here to tell you that it can be configured to be more efficient and productive than your favourite IDE.

  1. Why bother?

  2. Starting out with Vim

  3. PHP syntax highlighting

  4. PHP syntax checking/linting

  5. Tags and auto-completion

  6. Codesniffer and mess detector support

  7. PHPUnit support

  8. Xdebug integration

  9. Everything in a neat package

If you're impatient and want to start tinkering straight away, jump to section 9 to install my Vim configuration.

1. Why bother?

I've used a number of IDEs for PHP projects over the past few years: Eclipse, Aptana, Komodo Edit/IDE, Netbeans and PHPStorm. The problem with many of them is speed and resource usage: they eat away at your memory and hog your CPU. Another problem is keyboard shortcuts and commands: it is impossible to do everything using the keyboard, and many things end up being quicker with the mouse, requiring you to constantly swap between keyboard and mouse. This may not sound like a big deal, but spend a few months learning Vim and it will be a big source of frustration.

So, the answer to that question is this: in the long run, you will be a lot more efficient. Jump to exactly the right place in a file in just a couple of key strokes; open a horizontal split window comparing two files with a command as simple as <Ctrl-w> + s; swap between open files by number (the order of opening); and my favourite - view and navigate a directory tree with all the usual Vim functionality.

2. Starting out with Vim

If you've never used Vim before, try the tutorial I mentioned at the top of this post. Also, take a look at the Vim cheat sheet.

The rest of this tutorial assumes that you have a working knowledge of Vim and know how to configure it on at least a basic level (i.e. you know where your vimrc file is, and you know how to install vim scripts). It also assumes that you're running at least version 7.0 of Vim.

If you don't know how to install Vim scripts I'd recommend using Vundle, which will manage all your plugins for you and make installing/removing a breeze.

3. PHP syntax highlighting

Vim understands PHP by itself, but you can get improved highlighting and basic syntax checking with the php.vim plugin. This will add better support for certain PHP keywords like define and static, etc., and will allow you to apply custom colours to these keywords. Also, it puts some basic checks in place for invalid syntax, and will highlight the line if you don't close your brackets, for instance.

4. PHP syntax checking/linting

Most IDEs will run the PHP parser on the file periodically, and warn you of any errors such as invalid syntax and multiple declarations of classes/functions within the same file. One of the beautiful things about Vim is that you can run files through command line programs with total ease, so to check your PHP file you could just run:

:!php -l %

This prints out any error message to the screen. However, we can do better. Vim has a feature called signs, which are used to mark and highlight lines in a file. Wouldn't it be nice if we could take the PHP parser's error message and highlight the right line in the file?

It would be nice. And it is nice, because it's been done.  I have a plugin that does it, called PHP QA tools . Install this, and every time you write a file it will check it for syntax errors and highlight any offending lines in the file. It will also open a quickfix window that displays the error. This window is specially made for listing errors, and you can read about how to use it in the documentation.

PHP lint error in Vim windowPHP syntax errors are highlighted and shown in the quickfix window

The same plugin will be useful for some things that appear later, so read on...

An alternative to my own plugin for syntax checking is syntastic. This has support for many languages, so I use it for everything except PHP.

5. Tags and auto-completion

Code-completion is a feature that most large IDEs provide, along with the ability to jump to class and function definitions within multiple files. All IDEs do this by creating a database of the definitions, and updating this database as you change the code. Many people think that this is a reason not to change to using Vim, as the support isn't there; this isn't true.

Omnicompletion is a feature of Vim 7+, and it works by reading a tag file generated by a command-line program called ctags. This file contains definitions for all the classes, functions and variables in your project. You then use <c-x><c-o> (read that as: Ctrl+x followed by Ctrl+o - that's Vim's way of showing a Ctrl modifier shortcut) to bring up a list of completions when you are in insert mode.

A quick note

Before we go on, I'd just like to say that I don't use omnicompletion. Instead, I use Vim's local keyword completion. There are several reasons for this:

  1. There's a delay as the context for omnicompletion is determined, and the tags are collected together. This is the case in every IDE I've used - it makes logical sense that it as a project gets bigger, the impact on performance of autocompletion gets greater.

  2. Local keyword completion is lightning fast, and in many cases saves me valuable seconds (or at least milliseconds!) in typing longer words. This is even more the case with the SuperTab plugin, which I'll mention soon.

  3. It only works for words defined in the current file, but it encourages me to be a better programmer by using consistent naming methods for functions and variables, for instance.

Even so, the tags files are extremely useful even if not using omnicompletion, as they are used for jumping to definitions quickly. I'll explain how to set-up the environment and start generating tag files.

How to generate Ctags

You'll want to use exuberant-ctags (http://ctags.sourceforge.net/), which has support for over 40 programming languages, including PHP, of course. Installation is a doddle on Ubuntu (sudo apt-get install exuberant-ctags), otherwise follow the installation instructions on the website for your OS. After installation you should be able to run ctags-exuberant from the command line. Go to the top-level directory of a project containing PHP files and run:

ctags-exuberant -f php.tags --languages=PHP -R

This will create a file named php.tags, which contains a summary of all the definitions of PHP constructs in your project. You may need to tweak the parameters in this command: you may want to exclude certain files (e.g. build artefacts, documentation, etc.), or exclude whole languages like JavaScript from being parsed (--languages=+PHP,-JavaScript). When you're happy with your tags file, you need to tell Vim to use it:

:set tags=~/path/to/php.tags

You can use more than one tag file at a time: I generate a tag file for my entire PHP include library (i.e. PEAR stuff), a tag file for the framework I'm using and then a tag file for each project, and set all three to be used with a comma separated list.

Now that you've set up your tags, try it out. Open up a new PHP file, set the tag file and start typing the name of a class that you know exists. After a few characters type <c-x><c-o> (insert mode) and the omnicompletion will kick in. Then, on the completed class name in command mode, type <c-]>. You will be taken to the class definition, or given a list of definitions if it matches more than one.

Another neat feature is jumping to a tag from the Vim command line. For example, if you know you need to get to the file containing the class MyLovelyClass, just type:

:tag MyLovelyClass

And hit return. You will be taken straight to it. This is almost always quicker than trying to navigate a file tree to find the file you want. Also, this supports tab completion, so you could just type :tag My<tab> and you would get a list of options.

If you're going to use omnicompletion, definitely install the phpcomplete plugin, as this adds better context awareness for omnicompletion in PHP. I'd also recommend installing SuperTab, and adding this to your vimrc:

let g:SuperTabDefaultCompletionType = ""

Now you just press Tab to trigger omnicompletion.

Automating tag generation

Without a plugin to manage your tags, you will have to manually update the tag files every time you make changes. As a result, I created a very simple tag manager plugin, taggatron. This allows you to specify the languages for which you want to generate tags, and then it watches for changes and automatically and incrementally updates the tag files. You might like to give this a try if you plan on using tags heavily.

6. Codesniffer and mess detector support

If you're not familiar with these tools then you may want to skip this section, as you will only want this support if you already use them. Support for these comes bundled in the PHP QA tools plugin (view on github), which you will already have installed if you're following each step of this tutorial. The plugin parses their output, puts the violations into a quickfix window and puts line markers in the file (uses the marker S> *for codesniffer and *M> for mess detector violations). Nice and simple.

PHP mess detector and codesniffer support in VimCodesniffer and mess detector signs are shown in the margin, and messages in the quickfix window

Everyone who uses these programs has their own set of configuration rules, and happily this plugin allows you to change the configuration to suit. You can set various options in your vimrc file:

CODESNIFFER

" Pass arguments to phpcs binary
let g:phpqa_codesniffer_args = "--standard=Zend"
" Another example
let g:phpqa_codesniffer_args = "--standard=/path/to/xml/file.xml --tab-width=4"

" PHP codesniffer binary (default = phpcs)
let g:phpqa_codesniffer_cmd='/path/to/phpcs'

" Run codesniffer on save (default = 1)
let g:phpqa_codesniffer_autorun = 0

Mess detector

let g:phpqa_messdetector_ruleset = "/path/to/phpmd.xml"

" PHP mess detector binary (default = phpmd)
let g:phpqa_messdetector_cmd='/path/to/phpmd'

" Run mess detector on save (default = 1)
let g:phpqa_messdetector_autorun = 0

7. PHPUnit support

If you use PHPUnit then you can run tests from the Vim command window with another one of my plugins: PHPUnitQF (view on github). This parses the output from PHPUnit tests, and any failures or errors are put into a quickfix window, allowing you to easily jump to the tests that weren't successful.

To run the tests, just execute:

:Test

Where is passed straight to the phpunit command. For example, if you would normally run phpunit MyClassTest from a command line, you run :Test MyClassTest from Vim. If you have a standard set of arguments that you pass to phpunit (e.g. a configuration file), you can set a variable in your vimrc file. Here are the available configuration options:

" Arguments to pass to the phpunit binary
let g:phpunit_args = "--configuration /path/to/config"

" Location of phpunit
let g:phpunit_cmd = "/usr/bin/mytest"

" Temporary file used by the plugin
" (shouldn't need to change unless runing on Windows)
let g:phpunit_tmpfile = "/my/new/tmp/file"

You can use any test wrapper whose output is the same format as PHPUnit. For instance, I sometimes use the CakePHP framework which has a wrapper around the PHPUnit command, so I just change g:phpunit_cmd as shown above.

You can also view the full output of the last test run with:

:TestOutput

This opens the output in a split window.

8. Xdebug integration

That's right. You can use Vim as a debugger client with Xdebug, so it throws the same punches as the big boy IDEs. In fact, I think that the Xdebug plugin for Vim allows more flexibility and functionality than any other implementation I've found. It's certainly easier to tweak and modify it. It's also great out of the box, and you can get it up and running within a few minutes.

UPDATE: I no longer use this plugin, as I now actively maintain what I consider to be a far superior plugin, Vdebug. It has support for PHP, Python, Ruby and more, so check out my blog post on it or just go straight to the Github repo. For a description on setting up the older plugin, read on.

This plugin has a bit of a legacy: it started here, a script by Seung Woo Shin, and has been modified and forked like mad. It goes without saying that I have my own version of it, which you can get from the github repository.

Note: by all means use another version if you wish, but I have added quite a few features that I use all the time, so they might be useful to you too. Look at the README in the repository for a list of changes.

After installing the plugin you will need to set up Xdebug on your machine. To do this, follow step 1 and (optionally) 4 and 5 on my Xdebug, LAMP and Netbeans tutorial.

Now you can set breakpoints in your code with F1 or:

:Bp

Press F5 to make Vim try and connect to the debugger (on port 9000 by default), where it waits for 30 seconds before timing out. During the 30 seconds, if you load the webpage or run the script then the session will start. One of my changes is to open the session in a new tab, to not disrupt the window configuration in your current tab, so you will see a new tab and a few windows open up. It will look something like this:

Snapshot of a Xdebug session in VimWindows from left, going clockwise: source code pane, watch window, current stack trace, command window

The left window shows the source code and typically the current execution point. The watch window is one of the key features, as it shows the result of getting the context (i.e. all the variables and their values at the current execution point) and eval results.

To start stepping through the program line-by-line, press F3. The -> marker will move as the program moves on. Execution will pause at any breakpoints that you've set up. To step into a function on the current execution line press F2, and F4 to step back out.

To get the current context, press F11 - the watch window will show all variables and their values. You can also evaluate expressions, by typing:

e

(The Leader key is usually \ or ,) You can then type a PHP expression (e.g. a variable, or any valid expression like sqrt(100)), press enter, and the result will be printed in the watch window.

When you've finished, press F6 to end the session and close all the windows.

That's enough to get you started, but you can read the README on the Github repository for more information.

9. Everything in a neat package

Maintaining a Vim configuration is a tricky business. Sometimes you make changes that you think will be helpful, find it annoying after a couple of weeks, and you want to revert back to an old version. That's exactly why I keep mine under version control - you can view it here.

If you want to use this configuration, there are a couple of things you'll need to install. You'll need exuberant-ctags, as talked about in section 5. I'd also recommend installing the Anonymous Pro font if you're using GVim, as it's the best code font I've come across.

To install, clone the repository to ~/.vim, and initialize the submodule. Then, install the Vundle bundles:

git clone git://github.com/joonty/myvim.git ~/.vim --recurse-submodules
vim +BundleInstall +qall

What's in the package? Along with everything mentioned in this tutorial, it has the following plugins:

  • NERDTree: shows the directory tree, and allows for navigation and modification. Completely essential in my eyes.

  • Fugitive: the definitive Git plugin.

  • Ctrl-P: super-fast file finding and opening (used to be Command-T).

  • EasyMotion: quickly jump to any word or character in a file.

  • Vim-Sauce: a very simple project manager

  • Syntastic: syntax checking for multiple languages

  • MiniBufExpl: (Mini buffer explorer) shows the open buffers in a kind of tab format, allowing you to easily manage multiple files. I had serious performance issues with this plugin, so I no longer use it.

  • Tag List:  show definition summaries of classes, functions, variables, etc. for all open files. I stopped using this plugin, as I found I could do everything with tags and Ctrl-P.

I swapped from Command-T to Ctrl-P, as Command-T requires you to build a library from source. Plus, Ctrl-P has some great additional features.

Here's a word of warning: this is my configuration, and it's likely to change on a fairly regular basis. I don't guarantee that it will work on your system, and I certainly don't guarantee that you'll like it. Feel free to chop and change it as you please.

The Final Word

Hopefully this has been helpful. If you have any suggestions for things I should add to this list then just add a comment.

May the Vim be with you.

← Previous post: Vim with PHP code sniffer, mess detector and code coverage Next post: Enable PHP PCNTL extension with MAMP →
comments powered by Disqus