Monday, November 10. 2008
Annotations are a really helpful feature in present-day development. An annotation is a special form of syntactic metadata that can be added to source code elements such as classes, methods, properties and parameters. They do not affect the program semantic directly, but can be used by tools and libraries to handle such annotated code in a certain way. Examples for such usage are marking classes or methods as accessible via web services, marking methods as unit test methods, or to define how an object should be persisted.
Annotations are mainly known from the Java programming language, and since Java 5.0 annotations are a language feature. More important, these annotations are accessible at runtime, which allow various new meta-programming approaches where the domain object stays very clean and does not have any references to the handling code, i.e. when persisting domain objects or rendering them into XML.
Today I want to take a look on the state of annotations in the PHP world. PHP itself does not offer such a feature, so we have to take a look at userland implementations. Such implementations are possible using the reflection API introduced with PHP 5. When looking at userland implementations one has to differentiate between specialised and generic implementations.
Specialised implementations allow to use annotations defined by the implementation, and mainly use regular expressions to detect annotations. Furthermore, they do not allow to have user-defined annotations, or make it very hard for the user to define its own without having to change the inner workings of the implementation. Such implementations include the Extended Reflection API which is part of InstantSVC, which mainly focuses on the known doc comment annotations. Another example of such an implementation is PHPUnit which introduces several implementation-specific annotations like @assert and @test. Other implementations are SCA for creating web services, Services_Webservice to create WSDLs and EZPDO to ease object-relational mapping.
While all of these are without doubt very useful, I do not want to focus on them in this article. What is more interesting from my point of view are generic implementations. They allow the user to define its own annotations, provide some generic parser, and give the user access to annotations at runtime. Unfortunately, there is no common standard available. Each implementation introduces its own syntax and features, so beside looking at the available implementations I want to take a look at their specialities.
As the reader might blame me to be biased because I'm the lead developer of Stubbles and Stubbles itself provides a generic implementation, I will work through the list in alphabetical order. If you think I got something wrong in the description of the single implementations or if I missed an implementation or an important detail on a specific implementation do not hesitate to drop a comment below.
Additional information: if an implementation has support for annotations on classes it is most likely it has support for annotations on interfaces as well, as interfaces in PHP are reflected with ReflectionClass just like a normal class.
Addendum is a LGPL licensed generic implementation. It provides an extended reflection API which allows to check for the presence of annotations and to retrieve them. Annotations are intances of the class Annotation provided by Addendum, and it is possible to annotate classes, methods and properties. It offers different kind of annotations:
Single annotations may be restricted to classes, methods, or properties. If no such restriction is set, an annotation can be applied to all three elements. Annotations are written into the doc comment of the respective class, method or property.
The latest release is version 0.3.2 from April 2008. On the Addendum page two reference usages are listed, one of them really impressed me: APDT is a plugin for PDT on Eclipse which delivers support for aspect-oriented development right into PDT, using Addendum for runtime support of annotations.
AttributeReader is a simple class with static methods released under the LGPL license and allows to read YAML-style annotations from classes, methods and properties:
The class requires the pecl syck package which is not part of a default PHP installation and in beta state, which limits the usage areas quite a bit if you do not have the possibility to install pecl extensions yourself. The only release is a zip file available for download from the blog article linked above.
FLOW3 is the framework which serves as the foundation for TYPO3 5.0 but can be used without TYPO3 5.0. GPL-licensed, it provides an enhanced reflection API for access to annotations. As there is no user documentation about annotations available yet I had to look into the FLOW3 code. It is possible that I made mistakes understanding the code, so take the following with a grain of salt.
First, FLOW3 provides access to any tag within the doc comment, so things like @var, @return and so on are available as well as the annotations defined by FLOW3. Exceptions to this rule are @package, @subpackage, @license, @copyright, @author, @version, @const. From what I guessed I think the implementation of a user-defined annotation is very simple - just annotate the class, method or property with the annotation of your choice and it will be accessible at runtime via the FLOW3 reflection classes. FLOW3 annotations only allow single values, and thus seem to me kind of limited. However, for most usecases this is fully satisfying.
FLOW3 is under heavy development and is currently available as SVN checkout only. There is no release date set yet. Additionally it requires PHP 5.3 as it is already implemented using namespaces (no backslashes until now).
PEAR_PHP_Annotation is a PEAR package providing a parser for generic doc comments and Java style annotations. However, the first and last commit was January 2007, and no single release was made until today. Therefore I suspect this to be dead and won't bother you with details.
Sebastian Bermann's implementation
Sebastian Bergmann did an implementation and blogged about it back in 2005, but the links do not work anymore and there is no detail about single aspects of the implementation available.
Stubbles is a framework providing generic annotation functionality, released under the BSD license. Stubbles provides an interface for annotations which user-defined annotations need to implemented, so annotations are objects itself. Access to annotations at runtime is granted via an extended reflection API. One unique selling point of Stubbles is its support for annotations on method and function parameters beside the usual support for annotations on classes, methods, functions and properties. Beside that it provides a bunch of other features like support for different value types (boolean, integer, double, constants and reflection class instances) or restricting annotations to classes or methods or properties. For more details about the annotation features in Stubbles and code examples see the manual. Annotations are written into the doc comment of the respective class, method or property.
While Stubbles itself is a full-blown framework the releases provide a sized-down version containing only the core classes and the extended reflection API which allows to use the annotations feature without the other stuff provided by the framework. The latest release is 0.11.0 from November 3 2008, which is labeled to be in beta status. The first stable release is not expected before January 2009, however the core classes and extended reflection API including the annotation parts are quite stable.
The XP-Framework, released under the BSD license, introduces a different kind of syntax for annotations. Here, annotations start with a # sign, so it looks like this:
Interestingly, it already has support for annotations since its PHP4 versions. This is also why the XP-Framework provides its very own reflection implementation which is loosely based on PHP's reflection API and which can be used to retrieve the annotations at runtime. The retrieved annotations are arrays containing the annotation information. Supported are annotations for classes and methods. More details can be found in the XP-Framework's manual.
The latest release of the XP-Framework is version 5.7.0 from November 4 2008.
As this article showed, annotations made their way into the PHP community. No wonder, since they are a very powerful tool which help to develop clean code and are a base for some of the fancy stuff developers want to have today, like automatic dependency injection and clean domain models which are not cluttered with persistence or any other logic except the domain logic.
Nevertheless, userland implementations suffer from performance, and nearly all of them are not compatible to each other. Furthermore some of them are specifically bound to the framework in which they are implemented, so it is hard to make use of annotations without binding itself to the framework as a whole. Therefore it might be a good idea to provide support for annotations in the language itself, but I doubt it will be done in the near future. But who knows - no one can predict the future, and two years ago I expected we will never have namespaces in PHP...
Btw: the chicken did not cross the road as the core developers could not agree on whether cross_road() should be locale-aware or not...
Display comments as (Linear | Threaded)
Interesting article. I think it would even more useful if you showed examples that showed how the annotations looked next to PHP code, rather than just the annotations in isolation.
Btw, I'm pretty sure Java simply stole the idea of annotations from .NET
IMHO was xdoclet(java) the first popular application for annotations.
Thanks for linking to my project. Btw. there are three real world projects using Addendum. The newest one is a lightweight ORM.
There is one gatcha with this approach, which I used not to long ago to convert PHP classes directly in WSDL documents: Opcode cachers don't cache comments (by default).
So the annotations will work fine when the script is loaded the first time, and stop working after that.
The only solution I've found so far when parsing annotations, is to first check if a opcode caching extension is loaded, and if so, to touch() the script before parsing. I'm sure though that has a performance impact.
I've released my own annotation framework for PHP, and it is now stable - have a look:
It's early days for this project still - although the framework itself is stable and complete, there is still a lot of documentation to be written, and I am planning a library of standard annotations.
There is no place for discussion yet, but you're welcome to submit bug reports, or comment via email.
Syndicate This Blog