Documentation with phpDocumentor: Part 2

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 | Open Source PHP

Read Part One of Documentation with phpDocumentor here.

Documentation without DocBlocks

You have probably already noticed that short of some inline comments, the sample project has no DocBlocks, tags, or anything else added by the programmer for the purpose of documenting the code. Nevertheless, there is quite a bit that phpDocumentor can do with uncommented PHP code. If we are in the directory containing the project directory, we can run phpDocumentor and ask to generate documentation for the project like this:

The above command will recursively process all files in the project directory (--directory ./project/), create documentation with a custom title (--title 'Generated Documentation - No DocBlocks'), include a source code listing of each file processed (--sourcecode on), save all documentation to the docs directory (--target ./project/docs), and group everything under a specified package name (--defaultpackagename 'UserAuthentication').

Listing all documentation pages that phpDocumentor generated is impractical, but let's take a look at the outline and at least one of the classes. All we have to do to view the documentation is to open the index.html file in the docs directory where we told phpDocumentor to direct the output with a web browser.

Looking at the above screenshot, we see that phpDocumentor correctly found all the class files. Moreover, it identified Accountable as an interface and found index.php, even though it contains no class definitions. All classes and interfaces are grouped together under the AuthenticationUser package name that was specified from the command line. At the same time, we see some of the shortcomings. There is no further classification or grouping and all components are simply listed under the root level.

Before we move on, let's also take a look at what information phpDocumentor was able to extract from the Users.php file:

It correctly identified the methods of the class, their visibility, and which parameters are required. I think that is a pretty useful start, albeit the description is a bit sparse and we have no idea what methods were actually implemented using the magic __call() method.

Another point to note here is that the class property $accounts does not appear in the documentation at all. That is intended behavior because the property has been declared private. If you want elements with private visibility to appear in your documentation, you will have to add the –pp / --parse private command line option or put this option in a config file.

Documentation with DocBlocks

Of course, this example wouldn't be complete if we didn't proceed to add proper DocBlocks to our code. The following is the exact same code as before, but this time it has been properly marked up with DocBlocks.

File project/classes/Accountable.php:
<?php
/**
* @author Dirk Merkel <dirk@waferthin.com>
* @package WebServices
* @subpackage Authentication
* @copyright Waferthin Web Works LLC
* @license http://www.gnu.org/copyleft/gpl.html Freely available
under GPL
*/
/**
* <i>Accountable</i> interface for authentication
*
* Any class that handles user authentication <b>must</b>
* implement this interface. It makes it almost
* trivial to check whether a user is currently
* logged in or not.
*
* @package WebServices
* @subpackage Authentication
* @author Dirk Merkel <dirk@waferthin.com>
* @version 0.2
* @since r12
*/
interface Accountable
{
const AUTHENTICATION_ERR_MSG = 'There is no user account
associated with the current session. Try logging in fist.';
/**
* Did the current user log in?
* * This method simply answers the question
* "Did the current user log in?"
*
* @access public
* @return bool
*/
public function isLoggedIn();
/**
* Returns user account info
*
* This method is used to retrieve the account corresponding
* to a given login. <b>Note:</b> it is not required that
* the user be currently logged in.
*
* @access public
* @param string $user user name of the account
* @return Account
*/
public function getAccount($user = '');
}
?>
File project/classes/Authentication.php:
<?php
/**
* @author Dirk Merkel <dirk@waferthin.com>
* @package WebServices
* @subpackage Authentication
* @copyright Waferthin Web Works LLC
* @license http://www.gnu.org/copyleft/gpl.html Freely available
under GPL
*/
/**
* <i>Authentication</i> handles user account info and login actions
*
* This is an abstract class that serves as a blueprint
* for classes implementing authentication using
* different account validation schemes.
*
* @see Authentication_HardcodedAccounts
* @author Dirk Merkel <dirk@waferthin.com>
* @package WebServices
* @subpackage Authentication
* @version 0.5
* @since r5
*/
abstract class Authentication implements Accountable
{
/**
* Reference to Account object of currently
* logged in user.
*
* @access private
* @var Account
*/
private $account = null;
/**
* Returns account object if valid.
*
* @see Accountable::getAccount()
* @access public
* @param string $user user account login
* @return Account user account
*/
public function getAccount($user = '')
{
if ($this->account !== null) {
return $this->account;
} else {
return AUTHENTICATION_ERR_MSG;
}
}

/**
* isLoggedIn method
*
* Says whether the current user has provided
* valid login credentials.
*
* @see Accountable::isLoggedIn()
* @access public
* @return boolean
*/
public function isLoggedIn()
{
return ($this->account !== null);
}

/**
* login method
*
* Abstract method that must be implemented when
* sub-classing this class.
*
* @access public
* @return boolean
*/
abstract public function login($user, $password);
}
?>
File project/classes/Authentication/HardcodedAccounts.php:
<?php
/**
* @author Dirk Merkel <dirk@waferthin.com>
* @package WebServices
* @subpackage Authentication
* @copyright Waferthin Web Works LLC
* @license http://www.gnu.org/copyleft/gpl.html Freely available
under GPL
*/
/**
* <i>Authentication_HardcodedAccounts</i> class
* * This class implements the login method needed to handle
* actual user authentication. It extends <i>Authentication</i>
* and implements the <i>Accountable</i> interface.
*
* @package WebServices
* @subpackage Authentication
* @see Authentication
* @author Dirk Merkel <dirk@waferthin.com>
* @version 0.6
* @since r14
*/
class Authentication_HardcodedAccounts extends Authentication
{
/**
* Referece to <i>Users</i> object
* @access private
* @var Users
*/
private $users;
/**
* Authentication_HardcodedAccounts constructor
*
* Instantiates a new {@link Users} object and stores a reference
* in the {@link users} property.
*
* @see Users
* @access public
* @return void
*/
public function __construct()
{
$this->users = new Users();
}

/**
* login method
*
* Uses the reference {@link Users} class to handle
* user validation.
*
* @see Users
* @todo Decide which validate method to user instead of both
* @access public
* @param string $user account user name
* @param string $password account password
* @return boolean
*/
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);
}
}
}
?>
File project/classes/Users.php:
<?php
/**
* @author Dirk Merkel <dirk@waferthin.com>
* @package WebServices
* @subpackage Accounts
* @copyright Waferthin Web Works LLC
* @license http://www.gnu.org/copyleft/gpl.html Freely available
under GPL
*/
/**
* <i>Users</i> class
*
* This class contains a hard-coded list of user accounts
* and the corresponding passwords. This is merely a development
* stub and should be implemented with some sort of permanent
* storage and security.
*
* @package WebServices
* @subpackage Accounts
* @see Authentication
* @see Authentication_HardcodedAccounts
* @author Dirk Merkel <dirk@waferthin.com>
* @version 0.6
* @since r15
*/
class Users
{
/**
* hard-coded user accounts
*
* @access private
* @static
* @var array $accounts user name => password mapping
*/
private static $accounts = array('dirk' => 'myPass',
'albert' => 'einstein');

/**
* static validate method
*
* Given a user name and password, this method decides
* whether the user has a valid account and whether
* he/she supplied the correct password.
*
* @see Authentication_HardcodedAccounts::login()
* @access public
* @static
* @param string $user account user name
* @param string $password account password
* @return boolean
*/
public static function validate($user, $password)
{
return self::$accounts[$user] == $password;
}

/**
* magic __call method
*
* This method only implements a magic validate method
* where the second part of the method name is the user's
* account name.
*
* @see Authentication_HardcodedAccounts::login()
* @see validate()
* @access public
* @method boolean validate<user>() validate<user>(string
$password) validate a user
* @staticvar array $accounts used to validate users & passwords
*/
public function __call($name, $arguments)
{
if (preg_match("/^validate(.*)$/", $name, $matches) &&
count($arguments) > 0) {
return self::validate($matches[1], $arguments[0]);
}
}
}
?>
File project/index.php:
<?php
/**
* Bootstrap file
*
* This is the form handler for the login application.
* It expects a user name and password via _POST. If
*
* @author Dirk Merkel <dirk@waferthin.com>
* @package WebServices
* @copyright Waferthin Web Works LLC
* @license http://www.gnu.org/copyleft/gpl.html Freely available
under GPL
* @version 0.7
* @since r2
*/
/**
* required class files and interfaces
*/
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";
}
}
?>

Since none of the functionality of the code has changed, we can skip that discussion here. What has changed, however, is that we have added DocBlocks for each file, class, interface, method, and property. Whereas the version of the project without documentation had a total of 113 lines of code, the new version including DocBlocks has 327 lines. The number of lines almost tripled! But don't be intimidated. Creating DocBlocks doesn't take nearly as much time as coding. Once you are used to the syntax, it becomes second nature. My estimate is that documenting takes about 10 to 20 percent of the time it takes to code. Moreover, there are tools to really speed things up and help you with the syntax, such as a properly configured code editor or IDE.

Now let's see how phpDocumentor fared with the revised version of the project. Here is the index page:

This time, the heading shows that we are looking at the Web Services package. Furthermore, the classes and interfaces have been grouped by sub-packages in the left-hand index column. Next, here is the documentation page for the Users class:

As you can see, this documentation page is quite a bit more informative than the earlier version. For starters, it has a description of what the class does. Similarly, both methods have a description. All the tags and their content are listed and there are helpful links to other parts of the documentation. And, from the method tag we can actually tell that the magic method __call() was used to implement a method of the form validate<user>($password). That is quite an improvement, I would say!

To really appreciate how much more informative and practical the documentation has become by adding DocBlocks, you really need to run through this example yourself and browse through the resulting documentation.

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:

phpDocumentor options

There are a good number of options when running phpDocumentor that affect everything from where it looks for the code to parse, how to parse, what kind of output to generate, and how to format it. All the options are available from both the command line and the web-based interface.

Command line reference

Here is a listing of all command line options supported by phpDocumentor 1.4.2, which is the version that was used while writing this article. The descriptions are taken directly from the output produced by running phpdoc --help from the command line and it is only reproduced here for the readers' convenience.

Short option

Long option

Description

-f

--filename

Name of file(s) to parse ',' file1,file2. Can contain complete path and * ? wildcards.

-d

--directory

Name of a directory(s) to parse directory1,directory2

-ed

--examplesdir

Full path of the directory to look for example files from @example tags

-tb

--templatebase

Base location of all templates for this parse.

-t

--target

Path where to save the generated files

-i

--ignore

File(s) that will be ignored, multiple separated by ','. Wildcards * and ? are ok

-is

--ignoresymlinks

Ignore symlinks to other files or directories, default is off

-it

--ignore-tags

Tags to ignore for this parse. @package, @subpackage, @access and @ignore may not be ignored.

-dh

--hidden

Set equal to on (-dh on) to descend into hidden directories (directories starting with '.'), default is off

-q

--quiet

Do not display parsing/conversion messages.

Useful for cron jobs on/off, default off

-ue

--undocumentedments

Control whether or not warnings will be shown for undocumented elements. Useful for identifying classes and methods that haven't yet been documented on/off default off

-ti

--title

Title of generated documentation, default is 'Generated Documentation'

-h

--help

Show this help message

-c

--useconfig

Use a Config file in the users/ subdirectory for all command-line options

-pp

--parseprivate

Parse @internal and elements marked private with @access. Use on/off, default off

-po

--packageoutput

Output documentation only for selected packages. Use a comma-delimited list

-dn

--defaultpackagename

Name to use for the default package. If not specified, uses 'default'

-dc

--defaultcategoryname

Name to use for the default category. If not specified, uses 'default'

-o

--output

Output information to use separated by ','. Format: output:converter:templatedir like "HTML:frames:phpedit"

-cp

--converterparams

Dynamic parameters for a converter, separate values with commas

-ct

--customtags

Custom tags, will be recognized and put in tags[] instead of unknowntags[]

-s

--sourcecode

Generate highlighted sourcecode for every parsed file (PHP 4.3.0+ only) on/off, default off

-j

--javadocdesc

JavaDoc-compliant description parsing. Use on/off, default off (more flexibility)

-p

--pear

Parse a PEAR-style repository (package is directory, _members are @access private) on/off, default off

-ric

--readmeinstallchangelog

Specify custom filenames to parse like README, INSTALL, or CHANGELOG files

Config files

Rather than having to specify all options you want on the command line each time you run generate documentation, phpDocumentor supports config files. You can put all your command line switches in one config file and then conveniently tell the phpdoc executable to use the config file. Here is a config file that has all the same options that we used in the above example:

;; phpDocumentor parse configuration file
;;
;; interface will automatically generate a list of .ini files that can
be used.
;;
;; project_phpdoc_config.ini is used to generate the documentation
;; for the sample project in the chapter on phpDocumentor
;;
;; Copyright 2009, Dirk Merkel <dirk@waferthin.com>

[Parse Data]
;; title of all the documentation
;; legal values: any string
title = Generated Documentation - No DocBlocks

;; comma-separated list of directories to parse
;; legal values: directory paths separated by commas
directory = /Users/dirk/php/project

;; where should the documentation be written?
;; legal values: a legal path
target = /Users/dirk/php/project/docs

;; turn this option on if you want highlighted source code for every
file
;; legal values: on/off
sourcecode = on

To use the config file from the command line, simply type: phpdoc –c /Users/dirk/php/project/project_phpdoc_config.ini

For additional examples of user-defined config files, consult the user/ directory of your phpDocumentor installation. There is also a system-wide default config file called phpDocumentor.ini that can be found in the top level of the phpDocumentor installation directory. It contains some additional options that are not accessible from the command line, such as toggling debug mode, which file extensions to parse, and so on.

Browser-based interface

You may remember from the installation, phpDocumentor includes a browser-based interface that gives you the same options as the command line. However, you get the added convenience of a tabbed interface that lets you easily explore the available options without having to consult the help section. Of course, you can also select your custom config file from this interface just as you did from the command line. Here is an example of the browser-based interface while processing the documentation for our sample project:

One of the 'gotchas' of working with the browser-based interface is that you must have all permissions properly set up. For example, if your project is residing in a directory outside your web server's DocumentRoot, phpDocumentor might not be able to read the files. Also, if the template you are using to format the output comes with one or more stylesheets that need to be copied to the output directory, your web server and/or PHP will need the necessary permissions.

Summary

If the example in this article did not sell you on the benefits of documenting your code using the phpDoc syntax, you only need to take a look at the API documentation of some of the biggest PHP projects out there, such as Zend Framework and Pear. There is a reason that this method of documenting source code has been around for over ten years. Programmers quickly get the big picture of how the various components come together. Moreover, it also allows them to drill down to the granular level of parameters, return values, and so on.

If you are not in the habit of commenting your code, I suggest you start slowly. Rather than documenting every single element, start with file and class-level DocBlocks. I suspect that you will quickly get used to creating documentation at the same time as code – especially when you see the results in the form of detailed and useful documentation. Hopefully, you will feel encouraged enough to start documenting more elements and produce documentation that any programmer that might come across your code can benefit from.

[ 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

jQuery UI 1.7: The User Interface Library for jQuery
jQuery UI 1.7: The User Interface Library for jQuery

Drupal 6 Performance Tips
Drupal 6 Performance Tips

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

NetBeans Platform 6.8 Developer's Guide
NetBeans Platform 6.8 Developer's Guide

OpenX Ad Server: Beginner's Guide
OpenX Ad Server: Beginner's Guide

MooTools 1.2 Beginner's Guide
MooTools 1.2 Beginner's Guide

Django 1.0 Website Development
Django 1.0 Website Development


Your rating: None Average: 5 (1 vote)

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
f
n
S
Z
k
f
Enter the code without spaces and pay attention to upper/lower case.
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