Annotation是什么?
好吧,名字确实有点陌生,但是直白一点Annotation就是每天与我们经常打交道的注释。 何为注释?一种独特的有说明性的注解,在代码中就是注释咯。Annotation是相关代码的元数据(metadata:用来描述数据的数据)。有些编程语言中它仅仅是一种描述性的东西,并不会直接影响我们的代码运行情况,比如PHP;而在另外一些编程语言中,则会产生影响,比如Java、C#等。 但是说白了,今天讨论的Annotation没什么高大上的,它还是注释。但我想今天讨论了Annotation之后,你心中的注释的范围或者注释的功能将会变得不同。
先预个热,假如我们之前谈到的注释叫Comment的话,那么Annotation将是Comment的超集,也就是说Annotation = {Comment, DocBlock, …}。 你可能发现了DocBlock这样的概念。
DocBlock是什么,和传统的Comment有什么区别?
Comment:// this is a comment
/* this is a multiline comment */
Docblock:/**
* this is a docblock
*/
还有一个区别:Comment将会被Opcache忽略,而Docblock将会被Opcache缓存。
Annotation有什么好处?
姑且说我们已经可以写得一屏好注释(Comment)了,所以我们今天将只讨论Annotation的DocBlock部分。
先来一个Annotation的例子,前面说到Comment本身无法为我们的代码带来什么好处,而是会带来一些提高可读性的好处,如下:class Foo
{
/**
* @var integer
* @range(0, 100)
*/
public $bar;
}告诉我们$bar期望是一个整数
告诉我们$bar期望的范围是0-100
除了告诉我们之外,也告诉IDE Foo实例的$bar属性是整数
基本的Annotation例子就是这样,而且我敢保证这样的例子你也并不陌生,标准的Annotation就是类似于以 @ 开头的,比如@var @param等,它们的语法格式为:@var {type} {description}
@param {type} {$name} {description}
如果是PHP程序员的话,对@var会比较陌生,因为IDE经常会为我们自动生成@param这样的Annotation。
PHPDocument?
看到这样的语法,如果接触过PHPDocument或者Javadoc的同学可能会相对会熟悉一些,比如我们可以通过PHPDocument生成代码的文档,而且可以在线浏览。而如果我们今天只讨论这种文档级别的内容或许就没什么意思了,看一下下面这个例子。public class Customer
{
[Required]
[StringLength(50)]
public string Prename(String name){}
看到上面的代码,我们猜猜Prename()函数上面的[Required]和[StringLength(50)]有什么作用?实际上这个可以理解为C#的Annotation,而这两行就是用于控制name参数一定要存在并且长度不超过50个字符。那么是否真的可以控制?或者说如果真的超过50个字符后程序会有什么表现呢?难道还能抛出异常还是什么呢?
把上面的代码翻译成PHP版本的,大概是下面这个样子:class Customer
{
/**
* required
* NameLength(50)
*/
public function Prename($name){}
然而,然而,然而,这Annotation是写在/** */里面的,从学任何一门编程语言的第一天起就知道,这是不会执行的,对吧?那Annotation貌似好像并没有什么卵用。
PHP官方对Annotation的支持程度?
2000:PHPDocument创立
2005:PHP5.1的Reflection支持getDoccomments()<?php
/**
* * A test class
* *
* * @param foo bar
* * @return baz
* */
class TestClass { }
$rc = new ReflectionClass('TestClass');
var_dump($rc->getDocComment());
?>
2008:Doctrine2 也是PHP的Annotation引擎
2010:PHP Rfc讨论了关于Annotations内容rfc/annotations,But没有通过投票。
2011:PHP Rfc讨论了DocBlockrfc/annotations-in-docblock
2013:再一次讨论
没下文了。。。
是的,实际上真的没有下文了,不过如果今天的讨论到此为止,那我猜你得想打死我~然而PHP官方确实没有对此进行过强有力的支持,但即便如此,有许多优秀的框架/库还是花费了不懈的努力去实现并享受着Annotation带来的好处。
Annotation的实际项目Symfony
Doctrine
PHPUnit
ZendFramework
…
Annotation可以用来做什么?
测试
PHPUnit的一个例子,通过为测试方法定义一些Annotation,比如此例子中定义InvalidArgumentException参数指定异常实例。class DataTest extends PHPUnit_Framework_TestCase
{
/**
* @dataProvider provider
*
* @expectedException InvalidArgumentException
* @expectedExceptionMessage Right Message
*/
public function testAdd($a, $b, $c)
{
/* Test code */
}
钩子/回调/参数验证/**
* @Route("/myaction/{id}", name="myaction")
* @Method("POST")
*
* @Template("MyBundle:MyController:my.html.twig") *
* @param int $id *
* @return array
*/
public function myAction($id)
{
/* Controller Logic */
return array('data' => $data);
}
看上面这个Symfony的例子,通过定义为myAction定义了@Route @Method @Template等Annotation,不仅对myAction()一目了然,并且Symfony通过解析@Template则可以做一些类似于回调的东西,可以在框架层次将内容解析到模板中,此过程大概类似于如下
index.php路由到myAction -> $rc->getDocComment()得到myAction()的Annotation -> 解析Annotation得到myAction()的模板 -> 框架得到myAction()返回的数据 -> 将数据渲染到模板
过程其实还是很清晰的,同时在此过程中也可以很容易的做一些参数验证的工作,比如$id的类型啊,大小啊什么的。
filterclass Foo
{
/**
* @My\Annotation("name", nullable=true)
*/
public funciton myAction($name)
{
从上面的例子上看,实现参数过滤当然也是可能的啦,而在Symfony2中还存在了这样一个bundle以达到通过Annotatios功能的rdohms/DMS。
综上
PHP对Annotation的支持一直表现的很拘谨,不过好在在反射中还对此做出了点贡献,不然今天的PHP届Annotation真心不太好玩。不过感谢那么多大牛和前辈将Annotation吸收到了非常有名的框架中,让更多的PHPer知道。
当然Annotation也不是一个特别流行的语言特性,目前貌似只有c#和Java存在,虽然Annotation有比较多的用途,但是具体使用还是要综合考量。
延伸阅读
如果读者感兴趣,可以研究下Doctrine的Annotation Parser