本文转载于思否社区专栏:Grace development
作者:LoyaltyLu
随着 Swoole 的不断的迭代,相应一些 Swoole 的协程框架也逐渐进入了大家的视野,比如:Hyperf、Swoft 等;常驻内存的实现让 PHP 性能比传统 PHP-FPM 模式的框架有质的提升,依据 Swoole 开源的框架都提供了全面的开发组件,看过或使用过 Hyperf、Swoft 框架的小伙伴应该都知道,这些框架当中有类似 SpringCloud 框架灵活的注解,本文就以一个简单的 demo 实现一个注解的实现,方便大家更快速的了解注解。
注解的定义是:附加在数据/代码上的元数据
(metadata)
。
框架可以基于这些元信息为代码提供各种额外功能,本质上注解就是理解注解只是配置的另一种展现方式。
注解是如何在代码里面被识别,又是如何被调用的呢?带着疑问咱们一起来看代码。
https://github.com/LoyaltyLu/annotation_demo
这是我写的一个 demo,里面包含了注解以及实现容器的一个简单的示例方便大家参看,再看文章前建议大家先看下这两个文档:
https://www.doctrine-project.org/projects/doctrine-annotations/en/1.6/index.html
https://www.php.net/manual/zh/book.reflection.php
注解收集
带大家通过 demo 简单了解下注解是如何被收集的。
类注解收集
$loader = require __DIR__ . "/vendor/autoload.php";
Core\Application::init($loader);
......
var_dump(\Core\Route::dispatch('/index/test'));
首先在
index.php
中使用
composer
自动加载传入封装好的
Applicatio
n
一个处理器类;调用
init
方法初始化。
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(); 方法这是模拟容器的一个方法咱们先看注解的实现逻辑
......
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);
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 指示种类元件,其注释类型是适用的。然后,你可以定义一个或多个目标:
更多介绍还请大家移步 Doctrine Annotations:
https://www.doctrine-project.org/projects/doctrine-annotations/en/1.6/custom.html#custom-annotation-classes
declare(strict_types=1);
namespace App\Http\Controller