Extending phpDocumentor with custom tags · 23 October 2009, 19:36
“How on earth does one add custom tags to phpDocumentor?“ I had to find an answer to that question for a project at work after we decided we’d like our developers to document their webinterface. Makes sense, right? After all, $_GET / $_POST / etc calls are effectively function parameters, too, and likely far more exciting than those declared in the function header.
Turns out it’s not so simple to tweak phpDocumentor like that. I, for one, didn’t manage to do it gracefully entirely without code-duplication, though that’s not to say there’s no way of doing that, just that I didn’t find it.
Note that my solution is based on the tokenizer. I have no idea how adaptable it is if you use the Parser base class only, out of necessary.
The files you’ll need

Obviously, since we’re working with phpDocumentor, we’ll want to have a copy of that somewhere. The 1.4.3 base installation looks like this →
I wanted my related files in the same folder for ease of inclusion, and ideally all in one place. So it’s off to create a folder in your phpDoc install base – just call it phpDocumentorCustom. It’s going to do everything phpDocumentor does (largely by inheritance), so ‘phpDocumentor’ makes sense – and ‘Custom’, well, in my case it was my project’s acronymed codename, but for the purposes of this tutorial I’ll try to stay generic.
Therein, I had:
a. a few files that were direct equivalents to files in ../phpDocumentor/:
- DocBlockTags.inc: contains the custom tag’s class.
- ParserDocBlock.inc: contains storage for custom tag meta-data associated with a docblock.
- phpDocumentorTParser.inc: contains a parser that understands your custom tag.
- phpdoc.inc: phpDoc command-line/web-interface executable.
- Setup.inc.php: setup for phpDoc.
b. a few files that were direct equivalents to files in ../:
- phpdoc: kept around for consistency (level of indirection to phpdoc.inc).
c. purely custom-purpose files:
- makedoc: File that saves typing various parameters after ./phpdoc. You may not want this. Mine contains: ./phpdoc -ct customparam -o CSV:ProjectAcronym -d $1
How to tweak them
phpDoc, as its documentation says, throws a Parser at your documents, then churns the result through an IntermediateParser, only to squash that through the chosen Converter and thusly hack and cough out your results.
We want to change the Parser so it recognises our @customtag as a valid doctag. The Parser itself will happily treat it as a tag regardless of whether you do that or not, but will just route that through defaultTagHandler(), which either only handles a description (as in, all text after the tag will be considered semantically grouped), or you’ll have to override it anyway if you want anything more complex, and differentiate according to the tag name you’re fed. S’better to do that cleanly.
phpDocumentorCustom/phpDocumentorTParser.inc: download

Most of the magic lies in teaching the Parser the tag’s handler. It was the first change I ever did to the system and it was already doing quite a lot just with that change in place. The handler has to store its parsed data somewhere, though – that’s what addCustomTag() does and where our parserDocBlock variation enters the picture:
phpDocumentorCustom/ParserDocBlock.inc: download

parserCustomTag is our custom tag’s abstraction class. Every tag in phpDoc has its own class for internal abstract representation. You don’t have to use one. You could, instead of doing “new parserCustomTag” just write your data into your data storage directly. Again, what I did, I did for consistency.
phpDocumentorCustom/DocBlockTags.inc: download

So far so good. Now you just need your setup to use it.
phpDocumentorCustom/Setup.inc.php: download

I daresay that part’s pretty simple.
phpDocumentorCustom/phpdoc.inc: download

That’s the whole nine yards, Parser-side; though it’s worth noting that your ./phpdoc script will still throw a warning that it doesn’t know the tag, despite our flawless handling. To suppress that error the quick way, make sure to call ./phpdoc with the -ct customtag parameter, e.g. -ct myveryspecialtag.
Afflicting it all with prettiness
Of course, all of that does not fancy documentation output make. In fact, you’ll have no idea any of this is working unless you tack in some debug output to make sure.
If you want to write your own Converter, you should definitely take a look at the Converters HowTo on the official phpDocumentor site. I had to write one because I couldn’t hack the HTMLframesConverter (or, in fact, any of the others proffered by default) into coughing up a clean CSV for me. Yes, my first attempts were very hack-y indeed.
If you just want your stuff to show up in your standard HTML output, sweet! All you have to do is change the .tpl files to your liking – you can find them all the way down in phpDocumentor/Converters/HTML/frames/templates/default/templates. If you want a quick crashcourse in Smarty: section Smarty-tags are loops. Your section name= is your iterator. section name=blah will grant you $blah as you iterator variable within that block. .key stands for the array-key ‘key’ but any variables remain variables (e.g. your iterator will be $iterator) – I assume mostly because methods[$iterator].custom[$customiterator].data is a bit easier to read than methods[$iterator][‘custom’][$customiterator][‘data’], but I didn’t investigate.
“Wait, wait, wait, thanks to you, we’ve got objects in there. :|“ Indeed. You’ll need to change the HTML Converter around a tad. You can find your objects in the docblock you find referenced all over the (base!) Converter $element->docblock, then in ‘custom’ $element->docblock->custom, the property we defined in our ParserDocBlock.Inc. You’ll want to translate that to an array for your template – or at least make sure the template gets your array, if you were using that in the first place, by making sure it’s passed to the Converter class via the $additions parameter that nearly every convert* function has.
If you want to write your own Converter:
The function that uses all the parsed data is called walk(). It’s parameter $pages is what should interest you – I recommend a print_r() / echo var_dump() to get an overview of its complexity, while running your custom phpDocumentor over a small data set. Manipulating those into an output should be easy enough!
— Neike Taika-Tessaro
Comment
| Categories: | development, knowledge, php, server-side |
|---|---|
| Related: | Apache & PHP: Headache with POST / GET · 10 December 2011, 02:43JavaScript and CSS Block Encoding: Broken by design · 1 September 2010, 14:44mediawiki's Special: Recent Changes bug · 30 November 2009, 15:04void and null function 'returns' in PHP · 22 September 2009, 16:53PHP loose type comparison: Arrays · 18 September 2009, 13:30 |