Als PHP/ASP/.NET programmeur heb je het waarschijnlijk al vaker meegemaakt: Je vindt een mooie class die precies kan wat jij wilt. Maar hoe krijg je hem aan de praat? Je gaat op zoek naar documentatie over de class en vindt een redelijk stuk over de class. Maar wat blijkt: de documentatie is van een oudere versie van de class. Voorbeelden kloppen niet meer, er zijn methoden bij gekomen en de parameters van methoden zijn veranderd.
Uiteraard kom je daar pas na een paar uur achter.

Documentatie
Het bijhouden van documentatie is een lastig werkje en wordt door veel programmeurs niet als ‘leuk’ ervaren. De documentatie die in de class verwerkt wordt is vaak nog wel redelijk van kwaliteit, maar een goed voorbeeld en een volledige overzicht van alle documentatie ontbreekt vaak.
Gelukkig bestaat er zoiets als een automatische-documentatie-generator. phpDocumentor is daar een voorbeeld van . phpdocumentor leest het commentaar dat programmeurs bij de classes en methods hebben gezet, indexeert alle methods en genereert een html-bestand met documentatie.
Drie nadelen:
- Je moet phpDocumentor hebben geïnstalleerd en geconfigureerd
- Je moet de generator regelmatig laten ‘draaien’ (evt. via cronjob – maar dat is niet voor iedereen bekend)
- Je documentatie komt ‘ergens’ op een webpagina te staan. De ‘link’ met de class is vaak weg.
De oplossing lag klaar bij Linux
Ik was al een tijdje op zoek naar een oplossing voor dit probleem. Op een gegeven moment was ik in de Linux command-line met Subversion bezig en vond ik het antwoord:
Als je in Linux iets niet snapt, doe je ‘–help’ waarna je het antwoord krijgt. Bv.:
dev:~# svn add --help
geeft:
dev:~# svn add --help add: Put files and directories under version control, scheduling them for addition to repository. They will be added in next commit. usage: add PATH... Valid options: -N [--non-recursive] : operate on single directory only -q [--quiet] : print as little as possible
.. en zo zou dat bij PHP ook zo moeten werken! De Help()-functie is geboren.
De Help()-functie is geboren
In elke php class zou een help-functie toegevoegd moeten worden. Dan zou het als volgt werken:
$o = new Utils_Email(); // als je iets even niet weet, roep je de Help-functie aan $o->Help();
Vervolgens refresh je je browser en deze geeft als output in je scherm:
/**
* Class for sending e-mail messages.
*
* $oEmail = new Utils_Email();
* $oEmail->SetRecipient('info@test.nl', 'Occhio Web Development');
* $oEmail->SetSender('oebe@test.nl', 'Oebe Rombout');
* $oEmail->SetSubject('end my subscription');
* $oEmail->SetBody('I would like to end my subscription etc...');
* $oEmail->Send();
*
* @copyright 2010 Occhio bv
* @author Oebe Rombout
* @version $Rev 300$
*/
=== Public methods from class Utils_Email ===
/**
* Set the receiver of the e-mail
*
* Example of usage:
* $oEmail->SetRecipient("oebe@test.nl");
* $oEmail->SetRecipient("oebe@test.nl", "Rombout, Oebe");
*
* @param string $email E-mail address of the recipient
* @param string $fullName Fullname of the recipient (optional)
* @return void
*/
SetRecipient($email, $fullName)
/**
* Set the subject of the e-mail
*
* @param string $value Subject of the e-mail
* @return void
*/
SetSubject($value)
etc

Hoe werkt het? Reflection-class
PHP heeft sinds enkele jaren de Reflection-class (phpDocumentor maakt daar ook gebruik van). Hiermee kan je o.a. een class ‘onderzoeken’. Zo kan je van een class opvragen welke methods hij heeft, maar kan je ook het commentaar opvragen.
De output van de Help-functie hoef je dus niet zelf te schrijven. Het wordt gegenereerd uit het commentaar dat je al bij de class en methods gezet hebt. Geen dubbel werk dus.
Daarbij kan je nog allemaal handige dingen toevoegen:
- als het een database-wrapper betreft: genereer een lijst met de veldnamen en hun datatype (scheelt je kijken in phpMyAdmin)
- Geef codevoorbeelden
- Toon constanten uit class
Op deze manier creëer je een class die tevens zichzelf kan ‘uitleggen’. Code en documentatie op één plek; bij Linux werkt het al jaren.
Als je nog meer goede ideeën voor deze Help-class hebt, mail ze me dan.
Code voorbeelden
De method die je in elke class (of superclass daarvan) moet toevoegen:
public function Help()
{
$file = __FILE__;
$class = __CLASS__;
$className = get_class($this);
// get comment of class
$reflectClass = new ReflectionClass($class);
$classDocComment = $reflectClass->getDocComment();
$help = <<<EOHTML
Help-functie van de {$class}-class in {$file}.
<pre>
{$classDocComment}
=== Public methods from class {$className} ===
EOHTML;
echo $help;
// Read public methods from class
Odc_System_Utils_Reflection::GetPublicMethods($this);
exit;
}
De class die een lijst met public methods genereerd:
/**
* @filesource
* @package Utils
*/
/**
* Show handy data of Classes
*
* Example:
public function Help()
{
$file = __FILE__;
$class = __CLASS__;
$className = get_class($this);
$help = <<<EOHTML
Help-functie van de {$class}-class.
EOHTML;
$methodsList = implode("\n- " , get_class_methods($this));
// get comment of GenericObject-class
$reflectClass = new ReflectionClass(__CLASS__);
$goComment = $reflectClass->getDocComment();
$help .= <<<EOHTML
<pre>
$goComment
=== Public methods read from class {$className} ===
EOHTML;
echo $help;
Odc_System_Reflection::GetPublicMethods($this);
exit;
}
* @author Oebe Rombout
* @copyright 2009 Occhio Design
* @package Utils
*/
class Odc_System_Utils_Reflection
{
/**
* Get names of the public methods of this class, to display in Help()
*/
function GetPublicMethods($oThis) {
// Init the return array
$returnArray = array();
// Iterate through each method in the class
foreach (get_class_methods($oThis) as $method) {
// Get a reflection object for the class method
$reflect = new ReflectionMethod($oThis, $method);
// For private, use isPrivate(). For protected, use isProtected()
// See the Reflection API documentation for more definitions
if($reflect->isPublic()) {
// Skip deprecated methods
if (strpos($reflect->getDocComment(), '@deprecated')) {
continue;
}
//$reflect->export($oThis, $method);
echo str_replace("\t", "", $reflect->getDocComment());
// make list of parameters
$aParameters = array();
foreach ($reflect->getParameters() as $oReflectionParameter) {
$aParameters[] = '$' . $oReflectionParameter->getName();
}
$parameters = implode(', ', $aParameters);
echo "\n" . $method . "($parameters)\n\n";
// The method is one we're looking for, push it onto the return array
array_push($returnArray,$method);
}
}
// return the array to the caller
return $returnArray;
}
public function Help()
{
$file = __FILE__;
$class = __CLASS__;
$className = get_class(self);
$help = <<<EOHTML
Help-functie van de {$class}-class.
EOHTML;
$methodsList = implode("\n- " , get_class_methods($this));
// get comment of GenericObject-class
$reflectClass = new ReflectionClass(__CLASS__);
$goComment = $reflectClass->getDocComment();
$help .= <<<EOHTML
<pre>
$goComment
=== Public methods read from class {$className} ===
EOHTML;
echo $help;
Odc_System_Reflection::GetPublicMethods(self);
exit;
}
}