Documentation with phpDocumentor: Part 1

Exclusive offer: get 50% off this eBook here
Expert PHP 5 Tools

Expert PHP 5 Tools — Save 50%

Proven enterprise development tools and best practices for designing, coding, testing, and deploying PHP applications

$29.99    $15.00
by Dirk Merkel | March 2010 | MySQL Open Source PHP

In this two-part article by Dirk Merkel, author of Expert PHP 5 Tools, we will take a look at documentation. We will learn to create code-level documentation using phpDocumentor, PHP's entry into the xDoc family of documentation tools and the de facto standard for documenting PHP code. Specifically, we will install phpDocumentor. Next, we will learn the general syntax for DocBlocks and how to run phpDocumentor to generate the documentation.

Code-level documentation

The documentation we will be creating describes the interface of the code more than minute details of the actual implementation. For example, you might document an API that you have developed for the outside world to interact with some insanely important project on which you are working.

Having an API is great, but for other developers to quickly get an overview of the capabilities of the API and being able to crank out working code within a short amount of time is even better. If you are following the proper conventions while writing the code, all you would have to do is run a utility to extract and format the documentation from the code.

Even if you're not inviting the whole world to interact with your software, developers within your own team will benefit from documentation describing some core classes that are being used throughout the project. Just imagine reading your co-worker's code and coming across some undecipherable object instance or method call. Wouldn't it be great to simply pull up the API documentation for that object and read about its uses, properties, and methods? Furthermore, it would be really convenient if the documentation for the whole project were assembled and logically organized in one location. That way, a developer cannot only learn about a specific class, but also about its relationships with other classes. In a way, it would enable the programmer to form a high-level picture of how the different pieces fit together.

Another reason to consider code-level documentation is that source code is easily accessible due to PHP being a scripting language. Unless they choose to open source their code, compiled languages have a much easier time hiding their code. If you ever plan on making your project available for others to download and run on their own server, you are unwittingly inviting a potential critic or collaborator. Since it is rather hard (but not impossible) to hide the source code from a user that can download your project, there is the potential for people to start looking at and changing your code.

Generally speaking, that is a good thing because they might be improving the quality and usefulness of the project and hopefully they will be contributing their improvements back to the user community. In such a case, you will be glad that you stuck to a coding standard and added comments throughout the code. It will make understanding your code much easier and anybody reading the code will come away with the impression that you are indeed a professional.

Great, you say, how do I make sure I always generate such useful documentation when I program? The answer is simple. You need to invest a little time learning the right tool(s). That's the easy part for someone in the technology field where skill sets are being expanded every couple of years anyway. The hard part is to consistently apply that knowledge. Like much else in this book, it is a matter of training yourself to have good habits. Writing API level documentation at the same time as implementing a class or method should become second nature as much as following a coding standard or properly testing your code.

Luckily, there are some tools that can take most of the tedium out of documenting your code. Foremost, modern IDEs (Integrated Development Environments) are very good at extracting some of the needed information automatically. Templates can help you generate documentation tags rather rapidly.

Levels of detail

As you create your documentation, you have to decide how detailed you want to get. I have seen projects where easily half the source code consisted of comments and documentation that produced fantastic developer and end-user documentation. However, that may not be necessary or appropriate for your project. My suggestion is to figure out what level of effort you can reasonably expect of yourself in relation to what would be appropriate for your target audience. After all, it is unlikely that you will start documenting every other line of code if you are not used to adding any documentation at all. On one hand, if your audience is relatively small and sophisticated, you might get away with less documentation. On the other hand, if you are documenting the web services API for a major online service as you are coding it, you probably want to be as precise and explicit as possible. Adding plenty of examples and tutorials might enable even novice developers to start using your API quickly. In that case, your employer's success in the market place is directly tied to the quality and accessibility of the documentation. In this case, the documentation is very much part of the product rather than an afterthought or merely an add-on.

On one end of the spectrum, you can have documentation that pertains to the project as a whole, such as a "README" file. At the next level down, you might have a doc section at the beginning of each file. That way, you can cover the functionality of the file or class without going into too much detail.

Introducing phpDocumentor

phpDocumentor is an Open Source project that has established itself as the dominanot tool for documenting PHP code. Although there are other solutions, phpDocumentor is by far the one you are most likely to encounter in your work–and for good reason. Taking a clue from similar documentation tools that came before it, such as JavaDoc, phpDocumentor offers many features in terms of user interface, formatting, and so on.

PhpDocumentor provides you with a large library of tags and other markup, which you can use to embed comments, documentation, and tutorials in your source code. The phpDoc markup is viewed as comments by PHP when it executes your source file and therefore doesn't interfere with the code's functionality. However, running the phpDocumentor command line executable or using the web-based interface, you can process all your source files, extract the phpDoc related content, and compile it into functional documentation. There is no need to look through the source files because phpDocumentor assembles the documentation into nicely looking HTML pages, text files, PDFs, or CHMs.

Although phpDocumentor supports procedural programming and PHP4, the focus in this article will be on using it to document applications developed with object-oriented design in mind. Specifically, we will be looking at how to properly document interfaces, classes, properties, and methods. For details on how to document some of the PHP4 elements that don't typically occur in PHP5's object-oriented implementation, please consult the phpDocumentor online manual: http://manual.phpdoc.org/

Installing phpDocumentor

There are two ways of installing phpDocumentor. The preferred way is to use the PEAR repository. Typing pear install PhpDocumentor from the command line will take care of downloading, extracting, and installing phpDocumentor for you. The pear utility is typically included in any recent standard distribution of PHP. However, if for some reason you need to install it first, you can download it from the PEAR site: http://pear.php.net/

Before we proceed with the installation, there is one important setting to consider. Traditionally, phpDocumentor has been run from the command line, however, more recent versions come with a rather functional web-based interface. If you want pear to install the web UI into a sub-directory of your web server's document root directory, you will first have to set pear's data_dir variable to the absolute path to that directory. In my case, I created a local site from which I can access various applications installed by pear. That directory is /Users/dirk/Sites/phpdoc. From the terminal, you would see the following if you tell pear where to install the web portion and proceed to install phpDocumentor.

As part of the installation, the pear utility created a directory for phpDocumentor's web interface. Here is the listing of the contents of that directory:

The other option for installing phpDocumentor is to download an archive from the project's SourceForge.net space. After that, it is just a matter of extracting the archive and making sure that the main phpdoc executable is in your path so that you can launch it from anywhere without having to type the absolute path. You will also have to manually move the corresponding directory to your server's document root directory to take advantage of the web-based interface.

DocBlocks

Let's start by taking a look at the syntax and usage of phpDocumentor.The basic unit of phpDoc documentation is a DocBlock. All DocBocks take the following format:

/**
* Short description
*
* Long description that can span as many lines as you wish.
* You can add as much detail information and examples in this
* section as you deem appropriate. You can even <i>markup</i>
* this content or use inline tags like this:
* {@tutorial Project/AboutInlineTags.proc}
*
* @tag1
* @tag2 value2 more text
* ... more tags ...
*/

A DocBlock is the basic container of phpDocumentor markup within PHP source code. It can contain three different element groups: short description, long description, and tags–all of which are optional.

The first line of a DocBlock has only three characters, namely "/**". Similarly, the last line will only have these three characters: " */ ". All lines in between will start with " * ".

Short and long descriptions

An empty line or a period at the end of the line terminates short descriptions. In contrast, long descriptions can go on for as many lines as necessary. Both types of descriptions allow certain markup to be used: <b>, <br>, <code>, <i>, <kbd>, <li>, <ol>, <p>, <pre>, <samp>, <ul>, <var>. The effect of these markup tags is borrowed directly from HTML. Depending on the output converter being used, each tag can be rendered in differe nt ways.

Tags

Tags are keywords known to phpDocumentor. Each tag can be followed by a number of optional arguments, such as data type, description, or URL. For phpDocumentor to recognize a tag, it has to be preceded by the @ character. Some examples of common tags are:

/**
* @package ForeignLanguageParser
* @author Dirk Merkel dirk@waferthin.com
* @link http://www.waferthin.com Check out my site
*/
class Translate
{
}

In addition to the above "standard" tags, phpDocumentor recognizes "inline" tags, which adhere to the same syntax, with the only notable difference that they are enclosed by curly brackets. Inline tags occur inline with short and long descriptions like this:

/**
* There is not enough space here to explain the value and usefulness
* of this class, but luckily there is an extensive tutorial available
* for you: {@tutorial ForeignLanguageParser/Tran slate.cls}
*/

DocBlock templates

It often happens that the same tags apply to multiple successive elements. For example, you might group all private property declarations at the beginning of a class. In that case, it would be quite repetitive to list the same, or nearly the same DocBlocks, over and over again. Luckily, we can take advantage of DocBlock templates, which allow us to define DocBlock sections that will be added to the DocBlock of any element between a designated start and end point.

DocBlock templates look just like regular DocBlocks with the difference that the first line consists of /**#@+ instead of /**. The tags in the template will be added to all subsequent DocBlocks until phpDocumenter encounters the ending letter sequence /**#@-*/.

The following two code fragments will produce the same documentation. First, here is the version containing only standard DocBlocks:

<?php
class WisdomDispenser
{
/**
* @access protected
* @var string
*/
private $firstSaying = 'Obey the golden rule.';
/**
* @access protected
* @var string
*/
private $secondSaying = 'Get in or get out.';
/**
* @access protected
* @var string
* @author Albert Einstein <masterof@relativity.org>
*/
private $thirdSaying = 'Everything is relative';
}
?>

And here is the fragment that will produce the same documentation using a more concise notation by taking advantage of DocBlock templates:

<?php
class WisdomDispenser
{
/**#@+
* @access protected
* @var string
*/
private $firstSaying = 'Obey the golden rule.';
private $secondSaying = 'Get in or get out.';
/**
* @author Albert Einstein <masterof@relativity.org>
*/
private $thirdSaying = 'Everything is relative';
/**#@-*/
}
?>
Expert PHP 5 Tools Proven enterprise development tools and best practices for designing, coding, testing, and deploying PHP applications
Published: March 2010
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

Tutorials

DocBlocks are targeted at developers. phpDocumetor generates beautiful documentation, but even if you are looking at the code itself, DocBlocks are very valuable to a programmer. In contrast, tutorials are often targeted at end-users or present extended examples and instructions to developers, which often require a little more handholding than even a DocBlock's long description can accommodate.

Naming conventions and how to reference tutorials

Tutorials are typically organized in their own directory that mimics the package structure you designed using the @package and @subpackage tags. Each tutorial is a self-contained file that will be referenced from DocBlocks using the @tutorial, {@tutorial}, or {@link} tags.

For example, perhaps you have used the @package tag to group various files and classes together under a package named "WebServices." Furthermore, let's assume you have created multiple sub-packages using the @subpackage tag, one if which might be called "Authentication." If you wanted to write a tutorial on how to consume the authentication web service and use it to log into your system, you would employ the following directory structure and file naming convention:

WebServices/
`-- Authentication
`-- Login.cls

The corresponding DocBlock preceding the Login class might then look something like this:

/**
* Login class for web services authentication
*
* This class provides various methods that are exposed
* by the web services layer of the package. This class
* and its methods can be used to obtain a token from
* the authentication system that will be required during
* subsequent API calls. For more detail on how to call
* the authentication system from you PHP code, take a
* look at our tutorial:
* {@tutorial WebServices/Authentication/Login.cls}.
*
* @package WebServices
* @subpackage Authentication
* @tutorial WebServices/Authentication/Login.cls
*/
class Login
{
// lots of useful methods here!
}

Essentially, the directory structure reflects your package and sub-package names. The names of tutorial files match the name of the element being documented in the case of package and class-level documentation. The file extension indicates the PHP element being documented: tutorials for a package have file extension .pkg, class tutorial end in .cls, and procedure-level tutorials end in .proc.

PackageName/
`-- SubpackageName
|-- ClassTutorialName.cls
|-- PackageTutorialName.pkg
`-- Proced ureTutorialName.proc

DocBook syntax

Rather than coming up with their own format, the developers of phpDocumentor relied on an established format for technical documentation: DocBook. DocBook is an XML-based markup language originally developed to document software and hardware. However, it has since been applied to all kinds of documentation, including tutorials.

Rather than to go into the details of DocBook syntax, which is beyond the scope of this article, I want to present a basic outline of a DocBook tutorial that continues our theme of writing a tutorial for a web services authentication call. This rudimentary DocBook document has been adapted from the phpDocumentor manual and can be used as a starting point for your own tutorials.

<refentry id="{@id}"> 
<refnamediv>
<refname>Web Services Authentication Tutorial</refname>
<refpurpose>How to use the authentication web service to
obtain a security token that will be required for
subsequent web services requests.
</refpurpose>
</refnamediv>
<refsynopsisdiv>
<author>
Dirk Merkel
<authorblurb>{@link mailto:dirk@waferthin.com Dirk
Merkel}</authorblurb>
</author>
</refsynopsisdiv>
{@toc}
<refsect1 id="{@id intro}">
<title>Web Services Authentication Tutorial</title>
<para>Pay attention because this is how you will have to
implement authentication to access our web service
</para>
</refsect1>
</refentry>

One important thing to take away from this example is that DocBook tutorials can contain certain inline phpDocumentor tags. This is very important because it allows tutorials to tie into the rest of the project's documentation. The following inline tags can be used in DocBook tutorials:

{@link}
{@tutorial}
{@id}
{@toc}

Documenting a project

To really illustrate the usefulness of good documentation, nothing takes the place of actually seeing a complete example. Now that we have covered the basic syntax and you have some idea of the available tags, let's see what phpDocumentor can do. For the purpose of this example, I have created a small project. I have tried to cram as many different object-oriented features in as possible. This way, there will be plenty for us to document and explore the different phpDoc tags at our disposal.

The purpose of this example project is to provide user authentication. Given a username and password, this code tries to verify that a corresponding valid account exists. In an effort to keep things at a manageable size, I have taken the liberty to simplify much of the functionality.

Here is a hierarchical outline of the files in the sample project:

project/
|-- classes
| |-- Accountable.php
| |-- Authentication
| | `-- HardcodedAccounts.php
| |-- Authentication.php
| `-- Users.php
`-- index.php

The classes directory contains all our interfaces and class definitions. The index.php file handles all necessary includes, creates some objects, and serves as a form handler. It essentially ties everything together. Next, let's take a look at each of the files.

File project/classes/Accountable.php:
<?php
interface Accountable
{
const AUTHENTICATION_ERR_MSG = 'There is no user account
associated with the current session. Try logging in fist.';
public function isLoggedIn();
public function getAccount($user = '');
}
?>

The Accountable interface defines a constant and two methods that will have to be implemented by any that implement the interface.

File project/classes/Authentication.php: 
<?php
abstract class Authentication implements Accountable
{
private $account = null;

public function getAccount($user = '')
{
if ($this->account !== null) {
return $this->account;
} else {
return AUTHENTICATION_ERR_MSG;
}
}

public function isLoggedIn()
{
return ($this->account !== null);
}
abstract public function login($user, $password);
}
?>

Authentication is a class that implements the Accountable interface. It provides concrete implementations of the two methods dictated by the interfaces. However, since it also declares an abstract method, the class itself is abstract. Authentication serves as a blueprint for any class providing authentication services. Any child class will have to implement the login() method.

File project/classes/Authentication/HardcodedAccounts.php:
<?php
class Authentication_HardcodedAccounts extends Authentication
{
private $users;
public function __construct()
{
$this->users = new Users();
}

public function login($user, $password)
{
if (empty($user) || empty($password)) {
return false;
} else {
// both validation methods should work ...
// user static method to validate account
$firstValidation = Users::validate($user, $password);
// use magic method validate<username>($password)
$userLoginFunction = 'validate' . $user;
$secondValidation = $this->users-
>$userLoginFunction($password);
return ($firstValidation && $secondValidation);
}
}
}
?>

Class Authentication_HardcodedAccounts extends abstract class Authentication and provides the required implementation of method login(). To actually validate whether a given username and password correspond to an account, it delegates the work to a User class, which we will see in the next listing.

One thing to note is that login() calls two different methods of the User object to validate the username and password. There is absolutely no reason for doing so other than showing two different ways of passing the required parameters. The first is a call to a static method, which passes both username and password as method arguments. The second is a call to magic method login<user>($password), which passes the username as part of the method name and the corresponding password as an argument.

File project/classes/Users.php:
<?php
class Users
{
private static $accounts = array('dirk' => 'myPass',
'albert' => 'einstein');

public static function validate($user, $password)
{
return self::$accounts[$user] == $password;
}

public function __call($name, $arguments)
{
if (preg_match("/^validate(.*)$/", $name, $matches) &&
count($arguments) > 0) {
return self::validate($matches[1], $arguments[0]);
}
}
}
?>

Class Users has a hard-coded list of users and their passwords stored in a private array property. I sure hope you're not thinking of implementing this in production, but it does keep things nice and simple for our example.

Users also provides the two account validation methods we saw being called from the Authentication_HardcodedAccounts class. validate<user>() is implemented with the help of the __call() magic method.

File project/index.php:
<?php
require_once('classes/Accountable.php');
require_once('classes/Authentication.php');
require_once('classes/Users.php');
require_once('classes/Authentication/HardcodedAccounts.php');
$authenticator = new Authentication_HardcodedAccounts();
// uncomment for testing
$_POST['user'] = 'dirk';
$_POST['password'] = 'myPass';
if (isset($_POST['user']) && isset($_POST['password'])) {
$loginSucceeded = $authenticator->login($_POST['user'],
$_POST['password']);
if ($loginSucceeded === true) {
echo "Congrats - you're in!\n";
} else {
echo "Uh-uh - try again!\n";
}
}
?>

Lastly, we have index.php, which ties everything together. After including the necessary class files, it creates an instance of Authentication_HardcodedAccounts, and uses it to validate the username and password that were presumably posted from a web form.

>> Continue Reading Documentation with phpDocumentor: Part 2

 

[ 1 | 2 ]

If you have read this article you may be interested to view :

Expert PHP 5 Tools Proven enterprise development tools and best practices for designing, coding, testing, and deploying PHP applications
Published: March 2010
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

About the Author :


Dirk Merkel

Dirk Merkel is the owner of Waferthin Web Works LLC. In his spare time, he likes to ruin perfectly good open-source projects by submitting unsolicited patches. He also writes about Web development. He lives in San Diego with his lovely wife and two wonderful daughters. Dirk can be reached at  dirk@waferthin.com.

Books From Packt


Drupal E-commerce with Ubercart 2.x
Drupal E-commerce with Ubercart 2.x

Drupal 6 Attachment Views
Drupal 6 Attachment Views

Magento 1.3: PHP Developer's Guide
Magento 1.3: PHP Developer's Guide

PHP 5 E-commerce Development
PHP 5 E-commerce Development

jQuery 1.4 Reference Guide
jQuery 1.4 Reference Guide

iReport 3.7
iReport 3.7

Grok 1.0 Web Development
Grok 1.0 Web Development

Python Testing: Beginner's Guide
Python Testing: Beginner's Guide


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software