注解

注解是 Hyperf 非常强大的一项功能,可以通过注解的形式减少很多的配置,以及实现很多非常方便的功能。

概念

什么是注解什么是注释?

在解释注解之前我们需要先定义一下 注解注释 的区别:

  • 注释:给程序员看,帮助理解代码,对代码起到解释、说明的作用。
  • 注解:给应用程序看,用于元数据的定义,单独使用时没有任何作用,需配合应用程序对其元数据进行利用才有作用。

注解解析如何实现?

Hyperf 使用了 doctrine/annotations 包来对代码内的注解进行解析,注解必须写在下面示例的标准注释块才能被正确解析,其它格式均不能被正确解析。注释块示例:

  1. /**
  2. * @AnnotationClass()
  3. */

在标准注释块内通过书写 @AnnotationClass() 这样的语法即表明对当前注释块所在位置的对象(类、类方法、类属性)进行了注解的定义, AnnotationClass 对应的是一个 注解类 的类名,可写全类的命名空间,亦可只写类名,但需要在当前类 use 该注解类以确保能够根据命名空间找到正确的注解类。

注解是如何发挥作用的?

我们有说到注解只是元数据的定义,需配合应用程序才能发挥作用,在 Hyperf 里,注解内的数据会被收集到 Hyperf\Di\Annotation\AnnotationCollector 类供应用程序使用,当然根据您的实际情况,也可以收集到您自定义的类去,随后在这些注解本身希望发挥作用的地方对已收集的注解元数据进行读取和利用,以达到期望的功能实现。

忽略某些注解

在一些情况下我们可能希望忽略某些 注解,比如我们在接入一些自动生成文档的工具时,有不少工具都是通过注解的形式去定义文档的相关结构内容的,而这些注解可能并不符合 Hyperf 的使用方式,我们可以通过在 config/autoload/annotations.php 内将相关注解设置为忽略。

  1. return [
  2. 'scan' => [
  3. // ignore_annotations 数组内的注解都会被注解扫描器忽略
  4. 'ignore_annotations' => [
  5. 'mixin',
  6. ],
  7. ],
  8. ];

使用注解

注解一共有 3 种应用对象,分别是 类方法类属性

使用类注解

类注解定义是在 class 关键词上方的注释块内,比如常用的 @Controller@AutoController 就是类注解的使用典范,下面的代码示例则为一个正确使用类注解的示例,表明 @ClassAnnotation 注解应用于 Foo 类。

  1. /**
  2. * @ClassAnnotation()
  3. */
  4. class Foo {}

使用类方法注解

类方法注解定义是在方法上方的注释块内,比如常用的 @RequestMapping 就是类方法注解的使用典范,下面的代码示例则为一个正确使用类方法注解的示例,表明 @MethodAnnotation 注解应用于 Foo::bar() 方法。

  1. class Foo
  2. {
  3. /**
  4. * @MethodAnnotation()
  5. */
  6. public function bar()
  7. {
  8. // some code
  9. }
  10. }

使用类属性注解

类属性注解定义是在属性上方的注释块内,比如常用的 @Value@Inject 就是类属性注解的使用典范,下面的代码示例则为一个正确使用类属性注解的示例,表明 @PropertyAnnotation 注解应用于 Foo 类的 $bar 属性。

  1. class Foo
  2. {
  3. /**
  4. * @PropertyAnnotation()
  5. */
  6. private $bar;
  7. }

注解参数传递

  • 传递主要的单个参数 @DemoAnnotation("value")
  • 传递字符串参数 @DemoAnnotation(key1="value1", key2="value2")
  • 传递数组参数 @DemoAnnotation(key={"value1", "value2"})

自定义注解

创建一个注解类

在任意地方创建注解类,如下代码示例:

  1. namespace App\Annotation;
  2. use Hyperf\Di\Annotation\AbstractAnnotation;
  3. /**
  4. * @Annotation
  5. * @Target({"METHOD","PROPERTY"})
  6. */
  7. class Bar extends AbstractAnnotation
  8. {
  9. // some code
  10. }
  11. /**
  12. * @Annotation
  13. * @Target("CLASS")
  14. */
  15. class Foo extends AbstractAnnotation
  16. {
  17. // some code
  18. }

注意注解类的 @Annotation@Target 注解为全局注解,无需 use

其中 @Target 有如下参数:

  • METHOD 注解允许定义在类方法上
  • PROPERTY 注解允许定义在类属性上
  • CLASS 注解允许定义在类上
  • ALL 注解允许定义在任何地方

我们注意一下在上面的示例代码中,注解类都继承了 Hyperf\Di\Annotation\AbstractAnnotation 抽象类,对于注解类来说,这个不是必须的,但对于 Hyperf 的注解类来说,继承 Hyperf\Di\Annotation\AnnotationInterface 接口类是必须的,那么抽象类在这里的作用是提供极简的定义方式,该抽象类已经为您实现了注解参数自动分配到类属性根据注解使用位置自动按照规则收集到 AnnotationCollector 这样非常便捷的功能。

自定义注解收集器

注解的收集时具体的执行流程也是在注解类内实现的,相关的方法由 Hyperf\Di\Annotation\AnnotationInterface 约束着,该接口类要求了下面 3 个方法的实现,您可以根据自己的需求实现对应的逻辑:

  • public function collectClass(string $className): void; 当注解定义在类时被扫描时会触发该方法
  • public function collectMethod(string $className, ?string $target): void; 当注解定义在类方法时被扫描时会触发该方法
  • public function collectProperty(string $className, ?string $target): void 当注解定义在类属性时被扫描时会触发该方法

利用注解数据

在没有自定义注解收集方法时,默认会将注解的元数据统一收集在 Hyperf\Di\Annotation\AnnotationCollector 类内,通过该类的静态方法可以方便的获取对应的元数据用于逻辑判断或实现。

IDE 注解插件

因为 PHP 并不是原生支持 注解,所以 IDE 不会默认增加注解支持。但我们可以添加第三方插件,来让 IDE 支持 注解

PhpStorm

我们到 Plugins 中搜索 PHP Annotations,就可以找到对应的组件 PHP Annotations。然后安装组件,重启 PhpStorm,就可以愉快的使用注解功能了,主要提供了为注解类增加自动跳转和代码提醒支持,使用注解时自动引用注解对应的命名空间等非常便捷有用的功能。