Documenting PHP with Doxygen: The Pros and Cons

February 1, 2012

It's been a few years, now, since I gave up using PHPDocumentor to document my PHP projects. I switched to [Doxygen](http://www.stack.nl/~dimitri/doxygen), an automated documentation tool that supports a wide variety of languages, including PHP. While PHPDocumentor enjoys broad support in the PHP community, Doxygen, too, is well entrenched. (Drupal uses it.) I recently began a new project from scratch, and it gave me an opportunity to once again turn a hard gaze upon Doxygen. After some careful reflection on my experiences developing this new medium-sized library and documenting it with Doxygen, here are what I see as Doxygen's strong and weak points when it comes to PHP API documentation. ## The Pros ### Speed! Proofing documentation is enough of a pain as it is. But waiting a minutes for phpdoc to run was just downright aggravating. I often found myself kicking off a process and then going to do something else, and in the process forgetting all about my documentation tasks. It was a colossal waste of time. Doxygen is blazingly fast. In my current setup, it generates graphics (class diagrams) along with the main documentation, and still does this in just a few seconds. Usually, regenerating documentation is so quick that I can't hit my browser's reload button before the generation has finished. ### Autolinking One of the most time-saving features in Doxygen is its autolinking support. As it analyzes comments, it looks for specific patterns -- camelCasing, parens(), and name::spaces to name a few -- and attempts to determine whether those strings are actually references to existing classes, functions, namespaces, variables, etc. If it determines that they are, it automatically generates a link the the appropriate generated documentation.

This is a fantastic feature, saving developers the time it would otherwise take to make this relation explicit.

While Doxygen sometimes gets false positives (and, occasionally, misses what seems to be an obvious case), this feature saves me copious amounts of time. I'm willing to accept a few misses.

Rich Command (Tag) Library

Doxygen has an astounding number of available commands. You're not limited to things like @param and @author. It has support for sytax-highlighted code blocks (@code and @endcode), subgrouping (@group, @ingroup, and so on), callouts (@todo, @attention, @remark, @bug), and extra stand-alone documentation that is not bound to a piece of source (@mainpage, @page, and so on). If you have a few hours to kill, you can peruse the entire list here: http://www.stack.nl/~dimitri/doxygen/commands.html.

In addition to this command goodness, Doxygen supports a subset of HTML tags, too. And this includes not just bold, italics, and fixed fonts, but also tables and images.

The Source Should Be as Readable as the Generated Docs

I like HTML, PDF, and man page documentation. I use these frequently. But when I'm actively working on a piece of code, it's far more likely that I will read the documentation in the source, rather than in an external web browser. So in my mind it is important that the source code be readable

How many times do you find yourself writing lists -- ordered or unordered -- in your documentation? I do it quite frequently. And I don't want to have to write HTML tags for my lists. I want theses lists to be just as easy to read in the source.

Doxygen makes writing lists as easy as it is in Markdown:

- this
- will
- be
- a 
- bullet
- list

and

-# this
-# will
-# be
-# a
-# numbered
-# list

Advanced Support for Objects and Classes

So much PHP is now done using its bolted-on object and class structure. Whether or not this feels bolted on in the language itself (and it does), it should feel natural in the documentation.

Doxygen accomplishes this with some great features:

  • When a class inherits from another class or interface, documentation is inherited too. No need to explain the same method twice, even if you did override a superclass method.
  • Classes can be navigated (in generated documentation) by their class hierarchy, by class members, by namespace, and alphabetically by name.
  • Methods are sorted into categories -- constructors/destructors, public, private, protected, static, and so on. Properties and constants are sorted thus, too.
  • AND Doxygen can generate graphical glass diagrams. I know, it's eye candy. But beautiful functional eye candy.

Elegant UI Out-of-the-Box

Okay, I know this is kinda whiny, but I hate ugly documentation. I want API documentation to be attractive and I want findability to be top priority.

Doxygen has a very nice default theme (you can change it, of course). The colors are attractive, the fonts are clear, there aren't numerous cases of markup overflow... it's nice. Customizing colorscheme, logo, and so on is very easy -- it's done via configuration parameters. As I understand it, if you would like to write a full set of HTML templates to replace the defaults, you can do this too.

But findability is clearly the top priority, as it should be.

  • There's a built-in JavaScript-based search engine.
  • Navigation is done with a JavaScript-enabled tree structure
  • There are no less than eight different paths to navigate into the documentation, ranging from by-file to full alphabetical index.

The Cons

The Dastardly Backslash

One of the worst decisions I think the PHP community has ever made is adopting the backslash () character as a namespace separator. Already used for escape sequences and Windows paths, backslash has become a source of frustration in more than one aspect of my PHP coding (for starters, my IDE keeps thinking I'm trying to escape stuff). I abhor using it.

But it gets worse! Doxygen, too, assigns special meaning to the backslash: Doxygen recognizes it as initiating a command. Yes, either @ or \\ can be used to declare a command. @param and \\param are semantically equivalent to Doxygen. And this is the root of much documentation confusion. For consider any case where you want to reference a PHP namespace in your documentation.

You write:

<?php
/**
 * @see \Foo\Bar
 */
?>

And doxgen sees:

<?php
/**
 * @see @Foo @Bar
 */
?>

This not only causes Doxygen to emit errors, but it also munges up the output. The generated documentation will display something like "See: Foo" or sometimes just "See:".

The simple solution to the backslash problem

Doxygen does have a simple solution. It abstracts the concept of namespacing at a fairly high level, so you can use alternate namespacing separators instead of the backslash. Both :: (double-colon) and # (hash) seem to work in this capacity. Thus I have now developed the habit of documenting namespace references like this:

<?php
/**
 * @see Foo::Bar
 */
?>

PHP Ambiguity

Let's be honest, PHP isn't exactly a first-class citizen in the Doxygen world, and the language's ambiguities sometimes keep it from working in exactly the expected way.

The place where this really shows is in the way @param and @return are processed. Sometimes PHP developers include typing informat in these directives, and sometimes they don't.

Here is documentation with type information:

<?php
/**
 * @param string $foo
 *  An input string.
 * @return string
 *  An output string.
 */
?>

Here is the same documentation without:

<?php
/**
 * @param $foo
 *  An input string.
 * @return
 *  An output string.
 */
?>

From a parsing perspective, the @return tag is problematic, for there are no clear lexical markers indicating whether there is type information. (Some of us conventionally use a newline, but this is not a standard).

To solve problems like this, Doxygen adds extra commands. For example, for typed return information in PHP, you should use @retval instead of @return.

<?php
/**
 * @param string $foo
 *  An input string.
 * @retval string
 *  An output string.
 */
?>

Doxygen will correctly parse @retval in such a way that it preserves the type info.

A related issue is that Doxygen limits its types to the PHP primitives, including resource, object, and array. But it doesn't supported setting namespaced class names as the return type.

Conclusion

Doxygen is feature rich. We didn't even cover some of its advanced options, like generating PDF files or man pages. Nor did we look at its ability to provide supplemental documentation. But it should be clear that it is an amazing tool for API documentation generation.

It has its glitches and drawbacks, but in my mind these are outweighed by its benefits.

(Re)Mapping Command-Shift-Arrow Keys in Janus/MacVIM

January 26, 2012

Users of the Janus VIM suite may notice a change in the newest version of Janus. In previous versions, one could navigate around split panes (including to and from the file browser) by holding down COMMAND-SHIFT and then typing one of the arrow keys. This feature has been removed from the latest versions...

Atoum: A Different Unit Testing Framework for PHP

January 25, 2012

For a long time, it has seemed that when it comes to unit testing in PHP, there is only one game in town: PHPUnit. And in all fairness, it's popularity is justified. It is a good framework, and can generally handle standard use cases.

But recently I found Atoum. Not being one to start small, I began...

Janus VIM on Linux Mint

January 19, 2012

[Linux Mint](http://www.linuxmint.com/) claims to be the second most popular Linux distribution after Ubuntu. And it is largely based on Ubuntu/Debian. While there are many subtle differences, though, the main one is that Mint supports a broader range of desktop environments -- most notably, Gnome 3. [Janus](https://github.com/carlhuda/janus) is a package of tools that turn vanilla [VIM](http://www.vim.org/) into a powerful development environment. It is targeted at GUI versions (gvim and MacVim), and it comes with a large assortment of VIM plugins and scripts. This short blog shows how to install Janus on a newly installed Linux Mint desktop. It assumes that you can run commands using `sudo` in a terminal. ## Install gvim By default, Mint ships with a minimal distribution of VIM. The first thing we want to do is update it to the GUI version. If you are running Mint with Gnome, you should install the Gnome version of VIM: $ sudo apt-get install vim-gnome You will also need several other tools, all of which can be installed in one long command. Some of these tools are used by Janus to build its environment. Many of them are also required by the plugins Janus provides. $ sudo apt-get install git rake ctags ack pep curl (Note that while `curl` isn't listed as a dependency on the Janus site, it is required.) Next, we will run the Janus installer: $ curl -Lo- http://bit.ly/janus-bootstrap | bash This fetches a remote script and executes it as the local user. As this command executes, it will download and configure a wide variety of VIM plugins. This will take some time. When this is finished, you should be able to launch gvim and have all of the Janus goodies: $ gvim ## Quick Tips Here are a few things you should know to get started with Janus: - You can open the file browser (NERDTree) by typing `\n`. (You can close it by typing that again). - Use `CTRL-ww` (that's holding down CTRL and hitting `w` twice) to cycle through frames on your current window. For example, with NERDTree open, you can switch back and forth between your edit window and NERDTree using this command. - Keep in mind that you have THREE different copy/paste buffers in Linux + Janus + VIM: The usual CTRL-C/CTRL-V buffer, the X-windows mouse highlighting buffer, and the VIM yank buffer. This can come in very handy. - Typing `:h quickref` will open a temporary buffer with a vast compendium of VIM commands. I wouldn't call it quick, but I would call it thorough. - *Edit:* And to switch themes, choose `Themes` from the menu. I'm not a big fan of the default. Here's an example of an alternative:

Git drupal:// URLs for projects and sandboxes

January 10, 2012

In a previous post, I showed an example of cloning a module that included this command: git clone drupal://drupalcs. But I neglected to explain how this worked. I'm not sure where I picked this up (it was probably from Sam Boyer), but by adding a few lines to your ~/.gitconfig makes checking out Drupal...

Syntax Checking for Drupal in VIM

January 10, 2012

Vim (VI Improved) is a powerful text editor that comes standard on most versions of Linux, OS X, BSD, and other UNIXes. With thousands of add-ons, console and GUI versions, and a fully scriptable environment, you can transform a humble text editor into a powerful development tool. In fact, there are...

Building a Custom Drupal Image for Vagrant

December 6, 2011

Not too long ago, I posted a blog entry about 5 reasons for using a virtual machine for Drupal development. At the suggestion of some commentors, I have started looking into using Vagrant to manage my VMs. There is an excellent Drupal Vagrant project that provides a great starting point. This article...

Configure iTerm2 To Act Like Visor

November 14, 2011

iTerm2 is a replacement for the built-in Mac terminal app. It provides many terminal features you may be used to from other systems (ahem, Linux), as well as some unique features and tools. When it comes to OS X integration, iTerm2 has some amazing capabilities. You can even configure it to emulate Visor. Here I explain how to configure iTerm2 to slide down from the top of the screen like a Quake-style Visor. You can get an idea of what this looks like from the image on the left (click on it for a full-sized version). The borderless terminal window slides down from the top when focused, and then slides back up when not used. Never again will you need to hunt through your open windows looking for the terminal. By mapping the visor to a hotkey, it becomes easy to quickly access a shell wherever you are without even taking your hands off the keyboard. And then, just as easily, the terminal can be dismissed. This article explains how to set up a visor-like terminal and then map a hotkey. You can set up iTerm2 to do this in only three steps. ## Step 1: Get iTerm 2 Hooray for the obvious! You will need to get [iTerm 2](http://www.iterm2.com/#/section/home) from the website. It is free (as in beer). Install it according to the directions. It's just a typical Mac app. ## Step 2: Create a Profile Launch iTerm2 and click on `Profiles -> Open Profiles` and then click the `Edit Profiles` button in the lower left. On the *Edit Profiles* dialog, you can add a new profile using the `+` icon in the lower left corner. On the first screen all you need to do is give the new profile a name. I name mine *Visor* because it works like a visor. Next, click on the `Window` tab. On this tab there are several settings you will want to make:
  • Rows: This will tell iTerm2 how tall your terminal should be. Trial and error seems to be about the right method for determining this.
  • Style: Choosing Top of the screen will cause the visor to scroll down from the top of the screen.
  • Space: Assuming you use multiple spaces (desktops), you probably want Visor to be available on all of them. Choose All spaces to get this behavior. If you choose Current space, then the visor will appear only on your current space.

That will get you set up with the basics. When you are done, you should be able to launch your Visor profile, and have it display similarly to the screenshot at the top of this article.

There's just one more step: Binding the hotkey.

Step 3: Binding a global hotkey

We have created a visor-like terminal window, but we need a convenient way to access it. I prefer to do this by mapping a global hotkey to iTerm2. Any time I press F7, my visor window slides down.

To set this up, got to iTerm's preferences and go to Keys.

As you can see from this screenshot, I have added the F7 hotkey. You can choose any hotkey arrangement you prefer.

At this point you should have a working Visor-like iTerm2 configuration. You might need to restart the iTerm2 program to get it to pick up all of the changes. But that is all there is to it.

Is Size Code's Worst Enemy?

October 29, 2011

The Drupal codebase upon which I work is now over a million lines of code (excluding whitespace and comments). It sounds impressive. But the reality of the matter is that the combination of lots of code and the Drupal way of doing things makes it not impressive, but a maintenance nightmare. Nobody...

Good PHP: Coding Standards and Why You Should Follow Them

October 29, 2011

In hindsight, I'm surprised how long it took me to develop a strong appreciation of code formatting standards. It's not that I haven't followed them all along (most IDEs and editors do the lion's share of that for you). What surprises me is that I never really appreciated the value of following them...