专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
程序猿  ·  “未来 3 年内,Python 在 AI ... ·  3 天前  
程序员的那些事  ·  惊!小偷“零元购”后竟向 DeepSeek ... ·  2 天前  
待字闺中  ·  DeepSeek 爆火带来的大变化 ·  1 周前  
程序员的那些事  ·  李彦宏自曝开源真相:从骂“智商税”到送出“史 ... ·  4 天前  
51好读  ›  专栏  ›  SegmentFault思否

手把手走入注解——注解收集

SegmentFault思否  · 公众号  · 程序员  · 2020-01-20 11:45

正文

本文转载于思否社区专栏:Grace development

作者:LoyaltyLu





前言



随着 Swoole 的不断的迭代,相应一些 Swoole 的协程框架也逐渐进入了大家的视野,比如:Hyperf、Swoft 等;常驻内存的实现让 PHP 性能比传统 PHP-FPM 模式的框架有质的提升,依据 Swoole 开源的框架都提供了全面的开发组件,看过或使用过 Hyperf、Swoft 框架的小伙伴应该都知道,这些框架当中有类似 SpringCloud 框架灵活的注解,本文就以一个简单的 demo 实现一个注解的实现,方便大家更快速的了解注解。




什么是注解



注解的定义是:附加在数据/代码上的元数据 (metadata)

框架可以基于这些元信息为代码提供各种额外功能,本质上注解就是理解注解只是配置的另一种展现方式。




注解如何工作的



注解是如何在代码里面被识别,又是如何被调用的呢?带着疑问咱们一起来看代码。

GitHub:
https://github.com/LoyaltyLu/annotation_demo

这是我写的一个 demo,里面包含了注解以及实现容器的一个简单的示例方便大家参看,再看文章前建议大家先看下这两个文档:

Doctrine Annotations:
https://www.doctrine-project.org/projects/doctrine-annotations/en/1.6/index.html
PHP 反射:
https://www.php.net/manual/zh/book.reflection.php




代码解析



注解收集

带大家通过 demo 简单了解下注解是如何被收集的。

类注解收集

index.php




$loader = require __DIR__ . "/vendor/autoload.php";

Core\Application::init($loader);

......

var_dump(\Core\Route::dispatch('/index/test'));
首先在 index.php 中使用 composer 自动加载传入封装好的 Applicatio n 一个处理器类;调用 init 方法初始化。

Core\Application.php



namespace Core;

use Annotation\Parser\RequestMappingParser;

use Doctrine\Common\Annotations\AnnotationRegistry;

use \Doctrine\Common\Annotations;

/**

* 相当于一个处理器,做一些初始化工作

* Class Application

*

* @package Core

*/


class Application

{

public static $beans = [];

public static function init($loader)

{

AnnotationRegistry::registerLoader([$loader, 'loadClass']);

self::loadAnnotationRoute();

self::loadAnnotationBean();

}

......

}
这里引用了 doctrine/annotations 包,更多参考 Doctrine Annotations

init() 方法首先自动加载,然后调用静态方法 self::loadAnnotationRoute();
注意这里同时调用了 self::loadAnnotationBean(); 方法这是模拟容器的一个方法咱们先看注解的实现逻辑

Core\Application.php


......

public static function loadAnnotationRoute()

{

//自动加载注解类(规则)到组件当中

$reader = new Annotations\AnnotationReader();

//这里采用手动实例化类、可以利用 glob() 遍历文件

$obj = new \App\Http\Controller\HomeController();

$re = new \ReflectionClass($obj);

//获取类注解

$class_annos = $reader->getClassAnnotations($re);

foreach ($class_annos as $class_anno) {

$routePrefix = $class_anno->getPrefix();

//通过反射得到所有的方法

$refMethods = $re->getMethods();

foreach ($refMethods as $method) {

$methodAnnos = $reader->getMethodAnnotations($method);

foreach ($methodAnnos as $methodAnno) {

$routePath = $methodAnno->getRoute();

//把某个逻辑放到在某个解析类当中处理逻辑

//$re->newInstance();反射实例化

(new RequestMappingParser())->parse($routePrefix,$routePath, $re->newInstance(), $method->name);
}
}

}

......
如何定制规则可 翻阅 文档
https://www.doctrine-project.org/projects/doctrine-annotations/en/1.6/custom.html#custom-annotation-classes

规则存放在: ./Annotation/Mapping/ 目录中;

AnnotationReader 类中的获取类注解的方法 getClassAnnotations($re) 方法需要一个反射类,更多反射请参考 PHP 反射;

手动实例化类:
$obj = new \App\Http\Controller\HomeController();

然后获取反射类:
$re = new \ReflectionClass($obj);

因为是 demo 就没有做过于复杂,这里大家可以继续完善

调用 getClassAnnotations($re) 方法获取类所有注解

$class_annos = $reader->getClassAnnotations($re);

打印 $class_annos 的结果如下:


array(1) {

[0]=>

object(Annotation\Mapping\Controller)#15 (1) {

["prefix":"Annotation\Mapping\Controller":private]=>

string(6) "/index"

}

}
这个结果是获取到的哪里的参数呢?接下来分别看下实例化的 HomeController 和定义的注解规则类 Annotation\Mapping\Controller;

Annotation\Mapping\Controller.php


declare(strict_types=1);

namespace Annotation\Mapping;

use Doctrine\Common\Annotations\Annotation\Attribute;

use Doctrine\Common\Annotations\Annotation\Attributes;

use Doctrine\Common\Annotations\Annotation\Required;

use Doctrine\Common\Annotations\Annotation\Target;

/**

* Class Controller

* @Annotation

* @Target("CLASS")

* @Attributes({

* @Attribute("prefix", type="string"),

* })

* @since 2.0

*/


final class Controller

{

private $prefix = '';

public function __construct(array $values)

{

if (isset($values['value'])) {
$this->prefix = $values['value'];
}

if (isset($values['prefix'])) {
$this->prefix = $values['prefix'];
}
}

public function getPrefix(): string

{

return $this->prefix;
}

}
为了节省篇幅这里删除了一部分没用的代码和注释,大家可以去 GitHub 中查看:
https://github.com/LoyaltyLu/annotation_demo

注解类中的类注解是设置规则的地方:


/**
* Class Controller
* @Annotation
* @Target("CLASS")
* @Attributes({
* @Attribute("prefix", type="string"),
* })
* @since 2.0
*/
@Target 指示种类元件,其注释类型是适用的。然后,你可以定义一个或多个目标:

  • CLASS 允许在类的 docblock
  • PROPERTY 允许在属性的 docblock
  • METHOD 允许在该方法的 docblock
  • ALL 允许类,属性和方法的 docblock
  • ANNOTATION 允许其他注释里面

更多介绍还请大家移步 Doctrine Annotations:
https://www.doctrine-project.org/projects/doctrine-annotations/en/1.6/custom.html#custom-annotation-classes

HomeController.php


declare(strict_types=1);

namespace App\Http\Controller






请到「今天看啥」查看全文