PHP的Annotations

QQ20160723-0@2x

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的类型啊,大小啊什么的。

filter

class 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

参考
PHP Annotations: They exist! by Rafael Dohms at the PHP Benelux Conference 2013

PHP的Annotations
Tags:         

2 thoughts on “PHP的Annotations

  • 07/23/2016 at 下午10:02
    Permalink

    写的不错

    Reply
  • 06/05/2017 at 下午3:48
    Permalink

    我觉得官方一直很矛盾,注释如果添加了对代码约束那还是注释吗?框架的支持是很自然的,因为使用框架必须遵循它的规则。

    Reply

发表评论

电子邮件地址不会被公开。 必填项已用*标注