swoft2.0源码剖析01-注解器
前言
本人认为学习一个框架正确的方式是先看框架的文档,再进入实战动手写代码写一些demo,然后就是看框架的源码,深入了解框架是怎么启动运行的,整个框架的流程最好用debug跑一遍,看一下有哪些可以值得学习和扩展的地方,比如框架源码中用到的一些设计模式,思考为什么用这个设计模式;比如框架源码中有一些可扩展的方法,是文档中没有提及到的;比如想要写框架的扩展库,必须要深入了解框架源码和运行流程。
下面主要是对swoft这个框架注解器组件方面做一个流程剖析。
swoft注解
注解(Annotations) 是Swoft里面很多重要功能的基础。特别是AOP,IoC容器、事件监听的基础。
注解的定义是: "附加在数据/代码上的元数据(metadata)"
swoft框架可以基于这些元数据为代码提供各种额外功能。
注解 VS 注释
一般而言,在编程届中注解是一种和注释平行的概念。
- 注释提供对可执行代码的说明,单纯用于开发人员阅读,不影响代码的执行;
- 而注解往往充当着对代码的声明和配置的作用,为可执行代码提供机器可用的额外信息,在特定的环境下会影响程序的执行;
php注解与swoft注解
目前PHP没有对注解的官方实现,主流的PHP框架中使用的注解都是借用T_DOC_COMMENT型注释块(/**型注释*/)
中的@Tag,定义自己的注解机制。
Swoft没有重新造轮子,搞一个新的的注解方案,而是选择使用Doctrine的注解引擎
Doctrine的注解方案也是基于T_DOC_COMMENT型注释的,Doctrine使用反射获取代码的T_DOC_COMMENT型注释,并将注释中的特定类型@Tag映射到对应注解类
如果使用
在过源码流程之前,先说一下作为swoft核心之一的注解器是怎么使用的,就像我们日常开发写注解一样,只需在类、方法或成员变量上方按规则添加注解即可,如定义一个监听器:
namespace SwoftTest\Event;
use Swoft\Event\Annotation\Mapping\Listener;
use Swoft\Event\EventHandlerInterface;
use Swoft\Event\EventInterface;
/**
* Class SendMessageListener
*
* @Listener("order.created")
*/
class SendMessageListener implements EventHandlerInterface
{
/**
* @param EventInterface $event
*/
public function handle(EventInterface $event): void
{
$pos = __METHOD__;
echo "handle the event '{$event->getName()}' on the: $pos\n";
}
}
和laravel必须要写事件监听类绑定不一样,swoft只需要在类注解写上@Listener这个注解就能将这个类定义为一个监听器,并绑定到具体的事件。
order.created是这个监听类绑定的具体事件,任何地方调用Swoft::trigger("order.created")
创建订单事件能触发到这个发送消息的监听器,执行handle()方法。
实现注解
那么怎么使得这些注解附有意义呢?这时候要引出强大的ReflectionClass反射类,通过反射类的getDocComment()方法能获取某个类的类注释,方法和属性的注释。
/**
* Gets doc comment
* @link https://php.net/manual/en/reflectionproperty.getdoccomment.php
* @return string|bool The doc comment if it exists, otherwise <b>FALSE</b>
* @since 5.1.0
*/
public function getDocComment () {}
不妨设想一下,程序在开始运行的时候(php bin/swoft http start)应该是把全部文件都扫描一遍,通过反射获取类注解,如果类注解为Swoft\Event\Annotation\Mapping\Listener
就把该类(SwoftTest\Event\SendMessageListener
)和对应的事件名("order.created")绑定到事件管理器,然后触发事件就能找到对应的监听器。其实就是1、先识别注解 ==> 再到2、让注解起作用两个步骤。
下面来看一下程序启动的运行流程,可以一边看代码一边看文章,看一下注解是怎么起作用的。
先看命令启动的入口文件 bin/swoft
// Bootstrap
require_once __DIR__ . '/bootstrap.php';
Swoole\Coroutine::set([
'max_coroutine' => 300000,
]);
// 启动App
(new \App\Application())->run();
主要就是程序的入口,实例化Application,并执行run()方法,我们知道Application是继承与SwoftApplication,Application没有构造方法,自然就会调用父类SwoftApplication的构造方法,父类的构造方法如下:
/**
* Class constructor.
*
* @param array $config
*/
public function __construct(array $config = [])
{
......
// Init application
$this->init();
......
}
前半部分主要是程序初始化的一些校验,日志器的初始化,省略掉,主要是$this->init()
初始化方法
protected function init(): void
{
......
$processors = $this->processors();
$this->processor = new ApplicationProcessor($this);
$this->processor->addFirstProcessor(...$processors);
}
......
/**
* swoft 六大处理器
* @return ProcessorInterface[]
*/
protected function processors(): array
{
return [
new EnvProcessor($this), // 处理环境变量
new ConfigProcessor($this), // 处理配置
new AnnotationProcessor($this), // 处理注解(本文章重点)
new BeanProcessor($this), // 处理容器对象
new EventProcessor($this), // 处理事件
new ConsoleProcessor($this), // 处理命令
];
}
初始化里面主要是把swoft六大处理器添加到应用程序的主处理器ApplicationProcessor中,本文章主要解读的是AnnotationProcessor注解处理器,通过这个处理器实现对类注解的解析。添加处理器后,Application初始化结束。下面再来调用的run()方法。
/**
* Run application
*/
public function run(): void
{
try {
if (!$this->beforeRun()) {
return;
}
$this->processor->handle();
} catch (Throwable $e) {
......
}
}
run()方法主要执行了应用程序的主处理器ApplicationProcessor的handle()方法,handle()方法把前面添加的六大处理器的handle()方法都执行了一遍(代码省略),下面主要看AnnotationProcessor注解处理器中的handle()方法。
/**
* Handle annotation
*
* @return bool
* @throws Exception
*/
public function handle(): bool
{
......
$app = $this->application;
AnnotationRegister::load([
'inPhar' => IN_PHAR,
'basePath' => $app->getBasePath(),
'notifyHandler' => [$this, 'notifyHandle'],
'disabledAutoLoaders' => $app->getDisabledAutoLoaders(),
'disabledPsr4Prefixes' => $app->getDisabledPsr4Prefixes(),
]);
......
}
注解处理器handle()方法中调用了load方法,AnnotationRegister::load()
方法调用了AnnotationResource实例化对象的load()方法,AnnotationResource类用途就是收集整合注解资源的,load方法的主要作用是:
- 查找目录中的AutoLoader.php文件,如果没有的话就不解析(所以swoft扩展库的src目录必须有AutoLoader.php文件,否则注解器等功能不能生效)。
- 解析每个目录下每个文件并收集带有解析的类或属性方法。
/**
* 遍历查找目录中的AutoLoader.php文件,如果没有就不解析,如果有就根据这个文件指定的目录文件解析
*
* @throws AnnotationException
* @throws ReflectionException
*/
public function load(): void
{
// 获取composer里面autoload的psr-4映射目录,包括了app目录和vendor第三方库的目录
$prefixDirsPsr4 = $this->classLoader->getPrefixesPsr4();
foreach ($prefixDirsPsr4 as $ns => $paths) {
......
// 循环每个目录,查找Autoloader.php文件
foreach ($paths as $path) {
$loaderFile = $this->getAnnotationClassLoaderFile($path);
.......
$loaderClass = $this->getAnnotationLoaderClassName($ns);
$isEnabled = true;
$autoLoader = new $loaderClass();
.......
// 如果Autoloader类没有允许加载,则不能加载注解,通过isEnable()控制
if (isset($this->disabledAutoLoaders[$loaderClass]) || !$autoLoader->isEnable()) {
$isEnabled = false;
$this->notify('disabledLoader', $loaderFile);
} else {
AnnotationRegister::addAutoLoaderFile($loaderFile);
$this->notify('addLoaderClass', $loaderClass);
// 加载并收集注解类(核心)
$this->loadAnnotation($autoLoader);
}
// Storage autoLoader instance to register
AnnotationRegister::addAutoLoader($ns, $autoLoader, $isEnabled);
}
}
}
load()方法已经完成了Autoloader.php文件的发现,这就找到了允许被swoft解析注解的目录,有了目录,接下来就是遍历目录中的每个文件,收集并解析注解,这一核心流程交给了$this->loadAnnotation($autoLoader)
方法去实现。
/**
* 循环解析目录下每个文件的注解
*
* @param LoaderInterface $loader
*
* @throws AnnotationException
* @throws ReflectionException
*/
private function loadAnnotation(LoaderInterface $loader): void
{
// 获取Autoloader类中设置的目录
$nsPaths = $loader->getPrefixDirs();
foreach ($nsPaths as $ns => $path) {
// 迭代生成目录下文件迭代器,然后遍历每个文件
$iterator = DirectoryHelper::recursiveIterator($path);
foreach ($iterator as $splFileInfo) {
......
$suffix = sprintf('.%s', $this->loaderClassSuffix);
$pathName = str_replace([$path, '/', $suffix], ['', '\\', ''], $filePath);
$className = sprintf('%s%s', $ns, $pathName);
// 解析某个类,查看某个类有没有类注解,方法注解,属性注解等。
$this->parseAnnotation($ns, $className);
}
}
}
/**
* 解析某个类的注解
*
* @param string $namespace
* @param string $className
*
* @throws AnnotationException
* @throws ReflectionException
*/
private function parseAnnotation(string $namespace, string $className): void
{
// **核心**:实例化某类的ReflectionClass类,比如说上面的SwoftTest\Event\SendMessageListener类
$reflectionClass = new ReflectionClass($className);
// Fix ignore abstract
if ($reflectionClass->isAbstract()) {
return;
}
// 根据反射类ReflectionClass解析某个类并查找某个类注解,返回了某个类整合完的注解(数组形式)
$oneClassAnnotation = $this->parseOneClassAnnotation($reflectionClass);
// 如果某个类整合完的注解不为空,就注册到AnnotationRegister类中
if (!empty($oneClassAnnotation)) {
AnnotationRegister::registerAnnotation($namespace, $className, $oneClassAnnotation);
}
}
核心流程:实例化了某类的ReflectionClass类,比如说上面的SwoftTest\Event\SendMessageListener
类,然后把反射类传递给parseOneClassAnnotation()方法去处理。
/**
* 解析某个类的注解
*
* @param ReflectionClass $reflectionClass
*
* @return array
* @throws AnnotationException
* @throws ReflectionException
*/
private function parseOneClassAnnotation(ReflectionClass $reflectionClass): array
{
// Annotation reader 注解阅读器
$reader = new AnnotationReader();
$className = $reflectionClass->getName();
$oneClassAnnotation = [];
// $reader获取类注解
$classAnnotations = $reader->getClassAnnotations($reflectionClass);
// Register annotation parser 注册注解的解析器,这里的解析器不是getDocComment(),而是把上面例子中监听器和时间绑定的解析器。通常在src\Annotation\Parser目录中。
foreach ($classAnnotations as $classAnnotation) {
if ($classAnnotation instanceof AnnotationParser) {
// * 如果是AnnotationParser解析类,则把该类注册到AnnotationRegister类的$parsers(解析器)属性中,这个解析类后文作用重大,用来让注解真正起作用的,一个注解类对应一个解析类
$this->registerParser($className, $classAnnotation);
return [];
}
}
// Class annotation
if (!empty($classAnnotations)) {
$oneClassAnnotation['annotation'] = $classAnnotations;
$oneClassAnnotation['reflection'] = $reflectionClass;
}
// 获取类属性 => 遍历类属性 => 获取属性注解
$reflectionProperties = $reflectionClass->getProperties();
foreach ($reflectionProperties as $reflectionProperty) {
$propertyName = $reflectionProperty->getName();
// $reader获取属性注解
$propertyAnnotations = $reader->getPropertyAnnotations($reflectionProperty);
if (!empty($propertyAnnotations)) {
$oneClassAnnotation['properties'][$propertyName]['annotation'] = $propertyAnnotations;
$oneClassAnnotation['properties'][$propertyName]['reflection'] = $reflectionProperty;
}
}
// 获取类方法 => 遍历类方法 => 获取方法注解
$reflectionMethods = $reflectionClass->getMethods();
foreach ($reflectionMethods as $reflectionMethod) {
$methodName = $reflectionMethod->getName();
// $reader获取方法注解
$methodAnnotations = $reader->getMethodAnnotations($reflectionMethod);
if (!empty($methodAnnotations)) {
$oneClassAnnotation['methods'][$methodName]['annotation'] = $methodAnnotations;
$oneClassAnnotation['methods'][$methodName]['reflection'] = $reflectionMethod;
}
}
$parentReflectionClass = $reflectionClass->getParentClass();
if ($parentReflectionClass !== false) {
$parentClassAnnotation = $this->parseOneClassAnnotation($parentReflectionClass);
if (!empty($parentClassAnnotation)) {
$oneClassAnnotation['parent'] = $parentClassAnnotation;
}
}
return $oneClassAnnotation;
}
AnnotationParser解析类,与注解类一一对应,有Swoft\Event\Annotation\Mapping\Listener
注解类就有对应的Swoft\Event\Annotation\Parser\ListenerParser
解析类,一般都存放在app或者库src目录下的Annotation目录中。
$reader = new AnnotationReader()
注解阅读器是引用了Doctrine注解引擎这个包里面的类。
parseOneClassAnnotation()方法解析某个类的注解,并根据类、属性、方法整合到了$oneClassAnnotation数组中,并返回。AnnotationReader类能解析(类、属性、方法)注解,下面看看这个类是怎么通过getClassAnnotations()方法获取注解的。
/**
* {@inheritDoc}
*/
public function getClassAnnotations(ReflectionClass $class)
{
$this->parser->setTarget(Target::TARGET_CLASS);
$this->parser->setImports($this->getClassImports($class));
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName());
}
使用ReflectionClass的getDocComment()方法获取类注释,再传递给this->parser->parse()`方法根据@关键字把注解格式化为数组形式,数组里面是具体的注解类实例化对象例如:
/**
* Class SendMessageListener
*
* @since 2.0
*
* @Listener(DbEvent::MODEL_SAVED)
*/
class SendMessageListener{
......
}
经过parser()处理后变成如下形式,由于只有一个注解@Listener,所以数组只有一个元素。
array(1) {
[0]=>
object(Swoft\Event\Annotation\Mapping\Listener)#1479 (2) {
["event":"Swoft\Event\Annotation\Mapping\Listener":private]=>
string(17) "swoft.model.saved"
["priority":"Swoft\Event\Annotation\Mapping\Listener":private]=>
int(0)
}
}
同样的方式获取属性的注解,方法的注解,并组装成oneClassAnnotation注册到然后通过AnnotationRegister类的$annotations属性中。
整个目录文件扫描的流程完成后,最终会把所有的注解(格式化后 => 注解类实例化对象)添加在AnnotationRegister类的$annotations属性中。
/**
* Class AnnotationRegister
*
* @since 2.0.0
*/
final class AnnotationRegister
{
/**
* @var array
*
* @example
* [
* // 命名空间 SwoftTest\Event
* 'loadNamespace' => [
* // 类名,例子中的SwoftTest\Event\SendMessageListener
* 'className' => [
* // 类注解
* 'annotation' => [
* // 例子中的Swoft\Event\Annotation\Mapping\Listener注解类对象
* new ClassAnnotation(),
* new ClassAnnotation(),
* new ClassAnnotation(),
* ]
* 'reflection' => new ReflectionClass(),
* // 属性注解
* 'properties' => [
* 'propertyName' => [
* 'annotation' => [
* new PropertyAnnotation(),
* new PropertyAnnotation(),
* new PropertyAnnotation(),
* ]
* 'reflection' => new ReflectionProperty(),
* ]
* ],
* // 方法注解
* 'methods' => [
* 'methodName' => [
* 'annotation' => [
* new MethodAnnotation(),
* new MethodAnnotation(),
* new MethodAnnotation(),
* ]
* 'reflection' => new ReflectionFunctionAbstract(),
* ]
* ]
* ]
* ]
* ]
*/
private static $annotations = [];
}
一般来说,每个不同的注解类都会有不同的属性,比如Swoft\Event\Annotation\Mapping\Listener
注解类就保存了事件名和优先级属性,而Swoft\Bean\Annotation\Mapping\Bean
这个注解类就保存了名称、作用域、别名等属性,这些属性时在解析类中获取的,解析类的作用下文说。
至此,AnnotationProcessor任务完成。但是有个疑问的是,上面的流程只是把注解格式化出来(步骤1识别注解),具体怎么样让注解起作用好像是还没有涉及到,那得继续看下一个处理器BeanProcessor的handle()方法了。
先说一下BeanProcessor处理器的功能,这个处理器的功能如下:
- Bean 就是一个类的一个对象实例。 容器Container就是一个巨大的工厂,用于存放和管理 Bean 生命周期。所以这个处理器是用来生成Bean并放入容器中的。
- 把app目录下的bean.php文件的数组和AutoLoader的beans()方法返回的数组合并再实例化后放入容器Container中。
- 把用了
@Bean注解
的类实例化后放入Container中,所以必须要让注解起作用后才能进行Bean的实例化。
/**
* Class BeanProcessor
*
* @since 2.0
*/
class BeanProcessor extends Processor
{
/**
* Handle bean
*
* @return bool
* @throws ReflectionException
* @throws AnnotationException
*/
public function handle(): bool
{
......
$handler = new BeanHandler();
// 获取bean.php文件的数组和AutoLoader的beans()方法返回的数组
$definitions = $this->getDefinitions();
// 获取$parsers(解析类)
$parsers = AnnotationRegister::getParsers();
// 获取$annotations(所有格式化后注解的结果,注解类)
$annotations = AnnotationRegister::getAnnotations();
// 把上面获取到的都添加到BeanFactory的属性中
BeanFactory::addDefinitions($definitions);
BeanFactory::addAnnotations($annotations);
BeanFactory::addParsers($parsers);
BeanFactory::setHandler($handler);
// Bean工厂初始化
BeanFactory::init();
......
}
......
}
/**
* Class BeanFactory
*
* @since 2.0
*/
class BeanFactory
{
/**
* Init bean container
*
* @return void
* @throws AnnotationException
* @throws ReflectionException
*/
public static function init(): void
{
// 调用容器的初始化方法
Container::getInstance()->init();
}
......
}
/**
* Class Container
*/
class Container implements ContainerInterface
{
/**
* Init
*
* @throws AnnotationException
* @throws ReflectionException
*/
public function init(): void
{
// 解析注解
$this->parseAnnotations();
// 解析Bean对象
$this->parseDefinitions();
// 实例化Bean对象
$this->initializeBeans();
}
}
我们重点看$this->parseAnnotations()
这个方法,这个方法是解析注解类的,让注解起作用的(步骤2),下面的是解析和实例化Bean,和注解类怎么起作用的无关,就不理它了。至于为什么把解析注解类的流程放在BeanProcessor处理器上而不放在AnnotationProcessor处理器上,可能是解析类处理完要返回一个结果提供给Bean,为什么这么做,猜想是要兼容很多解析类有关吧,具体我也不太明白,继续看代码。
/**
* Class Container
*/
class Container implements ContainerInterface
{
/**
* Parse annotations
*
* @throws AnnotationException
*/
private function parseAnnotations(): void
{
$annotationParser = new AnnotationObjParser(
$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases
);
// 实例化AnnotationObjParser对象,用这个对象去解析注解类
$annotationData = $annotationParser->parseAnnotations($this->annotations, $this->parsers);
[$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases] = $annotationData;
}
}
/**
* Class AnnotationParser
*
* @since 2.0
*/
class AnnotationObjParser extends ObjectParser
{
/**
* Parse annotations
*
* @param array $annotations
* @param array $parsers
*
* @return array
* @throws AnnotationException
*/
public function parseAnnotations(array $annotations, array $parsers): array
{
$this->parsers = $parsers;
$this->annotations = $annotations;
foreach ($this->annotations as $loadNameSpace => $classes) {
foreach ($classes as $className => $classOneAnnotations) {
$this->parseOneClassAnnotations($className, $classOneAnnotations);
}
}
return [$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases];
}
}
AnnotationObjParser类的parseAnnotations方法循环了所有的注解className(SwoftTest\Event\SendMessageListener
)和这个类的所有注解类$classOneAnnotations (包括类注解,方法注解,属性注解)作为参数传递给了这个方法。
/**
* 解析某类的所有注解
*
* @param string $className
* @param array $classOneAnnotations
*
* @throws AnnotationException
*/
private function parseOneClassAnnotations(string $className, array $classOneAnnotations): void
{
......
// Parse class annotations
$classAnnotations = $classOneAnnotations['annotation'];
$reflectionClass = $classOneAnnotations['reflection'];
$classAry = [
$className,
$reflectionClass,
$classAnnotations
];
// 解析类注解
$objectDefinition = $this->parseClassAnnotations($classAry);
// 解析属性注解
$propertyInjects = [];
$propertyAllAnnotations = $classOneAnnotations['properties'] ?? [];
foreach ($propertyAllAnnotations as $propertyName => $propertyOneAnnotations) {
$proAnnotations = $propertyOneAnnotations['annotation'] ?? [];
$propertyInject = $this->parsePropertyAnnotations($classAry, $propertyName, $proAnnotations);
if ($propertyInject) {
$propertyInjects[$propertyName] = $propertyInject;
}
}
// 解析方法注解
$methodInjects = [];
$methodAllAnnotations = $classOneAnnotations['methods'] ?? [];
foreach ($methodAllAnnotations as $methodName => $methodOneAnnotations) {
$methodAnnotations = $methodOneAnnotations['annotation'] ?? [];
$methodInject = $this->parseMethodAnnotations($classAry, $methodName, $methodAnnotations);
if ($methodInject) {
$methodInjects[$methodName] = $methodInject;
}
}
......
}
我们只看怎么解析类注解$this->parseClassAnnotations($classAry)
。
/**
* @param array $classAry
*
* @return ObjectDefinition|null
*/
private function parseClassAnnotations(array $classAry): ?ObjectDefinition
{
[, , $classAnnotations] = $classAry;
$objectDefinition = null;
foreach ($classAnnotations as $annotation) {
$annotationClass = get_class($annotation);
if (!isset($this->parsers[$annotationClass])) {
continue;
}
// 去解析类数组里面根据注解类名找到对应的解析类名(前面说了注解类和解析类一一对应的)
$parserClassName = $this->parsers[$annotationClass];
// 根据解析类名获取示例化后的解析类(例子中是Swoft\Event\Annotation\Parser\ListenerParser)
$annotationParser = $this->getAnnotationParser($classAry, $parserClassName);
// 调用解析类的parse()
$data = $annotationParser->parse(Parser::TYPE_CLASS, $annotation);
......
}
return $objectDefinition;
}
调用解析类的parse()方法,例子中就是调用Swoft\Event\Annotation\Parser\ListenerParser
这个解析类的parse()方法,这个方法就是实现了步骤2让注解起作用,具体看一下parse()方法。
class ListenerParser extends Parser
{
/**
* @param int $type
* @param Listener $annotation
*
* @return array
* @throws AnnotationException (注解类,例子中的Swoft\Event\Annotation\Mapping\Listener)
*/
public function parse(int $type, $annotation): array
{
if ($type !== self::TYPE_CLASS) {
throw new AnnotationException('`@Listener` must be defined on class!');
}
// 注册监听器,key为为SwoftTest\Event\SendMessageListener,值为["order.created" => 0]
ListenerRegister::addListener($this->className, [
// event name => listener priority
$annotation->getEvent() => $annotation->getPriority()
]);
return [$this->className, $this->className, Bean::SINGLETON, ''];
}
}
ListenerParser类的parse()方法把注解类(Swoft\Event\Annotation\Mapping\Listener
)实例化对象中保存的事件名和优先级注册到了ListenerRegister类的$listeners属性中,从而使得SwoftTest\Event\SendMessageListener和"order.created"绑定了关系,后续程序中触发了order.created事件就能找到对应的监听器了。
通过识别注解 ==> 到解析注解这么一个流程,类注解成功绑定了事件和监听器了。
自定义注解
swoft框架的注解流程讲解完了,如果要自定义一个注解怎么做应该也清晰多了
主要是编写注解类和解析类:
注解类写在App\Annotation\Mapping目录,比如编写要编写一个门面Facades的注解类App\Annotation\Mapping\FacadesAnnotation
类,让类变成门面类(不了解门面的可以看一下laravel门面的文档)。
namespace App\Annotation\Mapping;
use Doctrine\Common\Annotations\Annotation\Attribute;
use Doctrine\Common\Annotations\Annotation\Attributes;
/**
* Class Facades
*
* @since 2.0
*
* @Annotation //声明这是一个注解类
* @Target("CLASS") //声明这个注解只可用在class级别的注释中
* @Attributes({
* @Attribute("alias",type="string")
* })
*/
class Facades
{
/**
* @var string
*/
private $alias = '';
/**
* StringType constructor.
*
* @param array $values
*/
public function __construct(array $values)
{
if (isset($values['value'])) {
$this->message = $values['value'];
}
if (isset($values['alias'])) {
$this->alias = $values['alias'];
}
}
/**
* @return string
*/
public function getAlias(): string
{
return $this->alias;
}
}
在程序任何有Autoload.php文件的目录下的类都能在类注解上按格式写上自定义的注解类(记得要use 注解类,phpstorm有注解的插件可以直接引入)比如:
use App\Annotation\Mapping\Facades
/**
* Class Calculate
*
* @Facades()
*/
class Calculate{
/**
* 执行
*
* @return mixed
* @throws Exception
*/
public function execute()
{
......
}
}
解析类写在App\Annotation\Parser目录下,编写App\Annotation\Parser\FacadesParser
类:
<?php declare(strict_types=1);
/**
* This file is part of Swoft.
*
* @link https://swoft.org
* @document https://swoft.org/docs
* @contact group@swoft.org
* @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE
*/
namespace App\Annotation\Parser;
use ReflectionException;
use Swoft\Annotation\Annotation\Mapping\AnnotationParser;
use Swoft\Annotation\Annotation\Parser\Parser;
use App\Annotation\Mapping\Facades;
use Swoft\Validator\Exception\ValidatorException;
use Swoft\Validator\ValidatorRegister;
/**
* Class FacadesParser
*
* @AnnotationParser(annotation=Facades::class) // 参数值写上注解类
*/
class FacadesParser extends Parser
{
/**
* @param int $type
* @param object $annotationObject
*
* @return array
* @throws ReflectionException
* @throws ValidatorException
*/
public function parse(int $type, $annotationObject): array
{
// 可以获取到目标类Calculate,用$this->className获取
// 可以获取到注解类对象$annotationObject
// 这里是把目标类Calculate怎么变成门面的流程,我也没有实现,有兴趣的可以自己写一个
return [];
}
}
以上就是Swoft注解器的源码流程剖析。
网友评论