美文网首页
swoft2.0源码剖析01-注解器

swoft2.0源码剖析01-注解器

作者: 疯狂小嘉哥 | 来源:发表于2020-02-11 21:10 被阅读0次

    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,`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::registerAnnotation()`方法,将类名(例中的`SwoftTest\Event\SendMessageListener`)、命名空间、和生成的注解数组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方法循环了所有的注解annotations,调用了parseOneClassannotations(),并且把类名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注解器的源码流程剖析。

    相关文章

      网友评论

          本文标题:swoft2.0源码剖析01-注解器

          本文链接:https://www.haomeiwen.com/subject/lgdffhtx.html