PHP Annotations Are a Horrible Idea
17 Oct 2012
| Comment on Hacker News (Loading comment count...)
tl;dr: Code comments should never have an effect on application functionality or flow.
UPDATE: It has come to my attention that there is an RFC pending that would add an annotation syntax to PHP (link). I fully support making annotations a language feature. This post specifically criticizes the current implementation of annotations in code comments.
Both the Symfony 2 and Doctrine 2 libraries and components make liberal use of
what have come to be called annotations—special code comments, usually
prefixed with an @ that are actually interpreted by the application and
affect its functionality. For example, in order to use PHP templates for
a controller action with Symfony, one must declare that the returned value
should be rendered with a template in the following way:
/**
* @Template(engine="php")
*/
public function indexAction()
{
...
}
Without the special @Template comment, Symfony does not know where to find
the template file.
Doctrine 2 does the same thing when declaring object properties based on database table columns1:
/**
* @Column(type="string", length=32, unique=true, nullable=false)
*/
protected $username;
This trend needs to die.
Why?
Because code comments should never be necessary for a script to function properly. Code comments are exactly that—comments. Their purpose is to provide commentary on the code, not to provide logic that is critical to the application’s functionality.
1. DX Regressions
Consider debugging code in which several different types of critical application logic are contained within annotations. There are several DX regressions:
- You can’t use any of PHP’s helpful linting capabilities (
php -l). - Try to
var_dump()ordebug_backtrace()an annotation. You simply can’t. - Your IDE can’t link to classes in which an annotation is defined.
- Sometimes, developers set their editors (like vim) to automatically fold comments in order to save screen space. In this case, they may never actually see the annotations that are making your code work.
2. Decreased Readability/Discoverability
A developer who knows PHP very well can spend a couple hours with most applications and frameworks, and figure out the basics. If you can read PHP, and the code is not unreasonable obtuse, you should be up and running pretty quickly. However, annotations do not necessarily make sense to a developer who knows PHP perfectly well, because they are not native to the language. Based on their knowledge of code comments (that, usually, they do not affect the application logic), they would not immediately know to look there when debugging.
3. Reliance on Yet Another Library
If you use annotations, your application is now dependant on a library that
parses annotations (doctrine/common, most likely). I’m sure quite a number of
development hours were/are spent writing and bugfixing that library, all of
which could have been spent on other projects.
4. Icky Feeling
Perhaps it’s just me, but it just feels wrong to put application logic inside comments. They’re a bit magical, which does not lend itself to writing maintainable code.
Alternatives
But most of all, annotations really signal to me a lack of functionality or flexibility in PHP itself—the PHP community is now officially so desperate for better metaprogramming capabilities that it is attempting to do so using code comments.
With a language like Ruby, which allows for class-level function calls, you do not need annotations because you can define configuration in a declarative way, like so:
class BlogPost < ActiveRecord::Base
attr_accessible :name, :body
end
The fact that the language permits you to call functions within a class, but outside of a function definition, allows for extremely elegant configuration. Though I don’t know much about the inner-workings of the Ruby language, I’m sure this has something to do with the fact that Ruby classes can be reopened throughout the compilation lifecycle.
However, it seems like there are equally elegant ways to achieve the same result with PHP using class properties. Consider:
class User
{
public static $mapping = [
'username' => [ 'type' => 'string', 'length' => 32, 'unique' => true ]
];
}
Then Doctrine could grab all of the $mapping properties from model classes, and
wrap them in a nice configuration class so that we don’t have to deal with
dumb arrays later in the lifecycle. And now that PHP has proper lambda
functions and closures, you can even make your definition a bit more dynamic,
as shown above, should the need arise.
Concluding
So annotations suck for a variety of reasons. With PHP, we have elegant alternatives. My vote is that we stop using them, and remove the ability to use them from frameworks and libraries. If you have compelling reasons for using annotations, I would love to know what they are.
-
Fortunately, Doctrine also allows you to declare property metadata using PHP, YAML, or XML, though none of the options is particularly elegant.↩