swoft: 1.0.0 源码解读-1

看源码时结合swoft源码,边看边打断点查看打印的信息,参考 //www.greatytc.com/p/c188f73893f1 总结比较容易阅读

启动分析

入口脚本: /bin/swoft

#!/usr/bin/env php
<?php
require_once __DIR__ . '/bootstrap.php';
$console = new \Swoft\Console\Console();
$console->run();
  • 引入bootstrap文件,实例化并运行console实例

启动文件: /bin/bootstrap文件

require_once dirname(__DIR__) . '/vendor/autoload.php';
require_once dirname(__DIR__) . '/config/define.php';

// init the factory of bean
\Swoft\Bean\BeanFactory::init();

/* @var \Swoft\Bootstrap\Boots\Bootable $bootstrap*/
$bootstrap = \Swoft\App::getBean(\Swoft\Bootstrap\Bootstrap::class);
$bootstrap->bootstrap();
  • 加载composer自动加载文件autoload.php
  • 加载定义常量
  • 初始化bean工厂

bean工厂初始化init函数: \Swoft\Bean\BeanFactory::init()

public static function init()
    {
        $properties = self::getProperties();
        self::$container = new Container();
        self::$container->setProperties($properties);
        self::$container->autoloadServerAnnotation();
        $definition = self::getServerDefinition();
        self::$container->addDefinitions($definition);
        self::$container->initBeans();
    }
  • $properties = self::getProperties(); //获取项目配置
private static function getProperties()
    {
        $properties = [];
        $config     = new Config();
        $dir        = App::getAlias('@properties');
        if (is_readable($dir)) {
            $config->load($dir);
            $properties = $config->toArray();
        }
        dd($properties); // 打印测试
        return $properties;
    }
  1. 加载config/properties目录下的所有配置文件并返回配置数组,$properties打印如下:
    properties部分配置
  • self::$container = new Container(); //实例化核心容器
  • self::$container->setProperties($properties); //将配置参数绑定到container的properties属性上
public function setProperties(array $properties)
    {
        $this->properties = $properties;
    }
  • self::$container->autoloadServerAnnotation(); // 绑定扫描空间和绑定组件命名空间、解析注解
public function autoloadServerAnnotation()
    {
        $bootScan = $this->getScanNamespaceFromProperties('bootScan');
        $resource = new ServerAnnotationResource($this->properties);
        $resource->addScanNamespace($bootScan);
        $definitions = $resource->getDefinitions();
        $this->definitions = array_merge($definitions, $this->definitions); // 将解析后的注解绑定到container的definitions属性上
    }
  1. $bootScan = $this->getScanNamespaceFromProperties('bootScan'); // 从配置文件中读取扫描命名空间
private function getScanNamespaceFromProperties(string $name)
    {
        $properties = $this->properties;
        if (!isset($properties[$name]) || !\is_array($properties[$name])) {
            return [];
        }
        dd($properties[$name]); // 打印测试
        return $properties[$name];
    }

bootScan的配置如下
bootScan的配置

打印信息如下:
bootScan打印
  1. resource = new ServerAnnotationResource(this->properties) // 实例化注解资源
    2.1 ServerAnnotationResource构造函数 // 查看是否有自定义组件,有的话,添加到resource的customComponents数组中
    public function __construct(array $properties)
    {
        $this->properties = $properties;
        if (isset($properties['components']['custom']) && is_array($properties['components']['custom'])) {
            $this->customComponents = $properties['components']['custom'];
        }
    }
    
  2. $resource->addScanNamespace($bootScan); // 添加配置文件中指定的需要扫描的命名空间绑定到resource的scanNamespaces属性上
public function addScanNamespace(array $namespaces)
    {
        foreach ($namespaces as $key => $namespace) {
            if (is_string($key)) {
                $this->scanNamespaces[$key] = $namespace;
                continue;
            }
            $nsPath = ComposerHelper::getDirByNamespace($namespace);
            if (!$nsPath) {
                $nsPath = str_replace("\\", "/", $namespace);
                $nsPath = BASE_PATH . "/" . $nsPath;
            }
            $this->scanNamespaces[$namespace] = $nsPath;
        }
        dd($this->scanNamespaces); // 打印调试

        $this->registerNamespace();
    }

打印调试

3.1 $this->registerNamespace(); // 注册命名空间

public function registerNamespace()
    {
        $swoftDir      = dirname(__FILE__, 5); // 地址:/vendor/swoft
        $componentDirs = scandir($swoftDir); // /vendor/swoft下所有的目录
        foreach ($componentDirs as $component) {
            if ($component == '.' || $component == '..') { // 过滤./和../目录
                continue;
            }

            $componentDir = $swoftDir . DS . $component; // /vendor/swoft/对应组件目录/src
            $componentCommandDir = $componentDir . DS . 'src'; ///vendor/swoft/对应组件目录
            if (! is_dir($componentCommandDir)) {
                continue;
            }

            $ns = ComponentHelper::getComponentNamespace($component, $componentDir); // 获取组件命名空间
            $this->componentNamespaces[] = $ns; // 将组件命名空间绑定到resource的$componentNamespaces属性上

            // console component
            if ($component == $this->consoleName) { // console组件添加到容器的scanNamespaces属性上
                $this->scanNamespaces[$ns] = $componentCommandDir;
                continue;
            }

            foreach ($this->serverScan as $dir) { 
                // 下面是swoft组件中需要扫描的目录, 如果在组件中出现下面三个任意目录,则将该组件的命名空间绑定到resource的scanNamespaces属性上
                // $serverScan = [
                 //   'Command',
                  //   'Bootstrap',
                   //   'Aop',
                // ];
                $scanDir = $componentCommandDir . DS . $dir;
                if (!is_dir($scanDir)) {
                    continue;
                }

                $scanNs                        = $ns . "\\" . $dir;
                $this->scanNamespaces[$scanNs] = $scanDir;
            }
        }
        // 注册服务命名空间
        $this->registerServerNamespace();
    }

3.2 $this->registerServerNamespace(); // 注册服务命名空间

public function registerServerNamespace()
    {
        // 循环自定义组件,刚才实例化ServerAnnotationResource对象时,构造函数中对自定义组件进行了检测
        foreach ($this->customComponents as $ns => $componentDir) {
            if (is_int($ns)) {
                $ns = $componentDir;
                // 根据自定义组件的命名空间获取组件所在的目录
                $componentDir = ComposerHelper::getDirByNamespace($ns);
                $ns = rtrim($ns, "\\");
                $componentDir = rtrim($componentDir, "/");
            }

            // 将自定义组件绑定到resource的componentNamespaces的属性上
            $this->componentNamespaces[] = $ns;
            // 根据组件命名空间获取别名
            $componentDir = alias($componentDir);
            // 将自定义组件需要扫描的目录绑定到resource的scanNamespaces属性上
            foreach ($this->serverScan as $dir) {
                $scanDir = $componentDir . DS . $dir;
                if (!is_dir($scanDir)) {
                    continue;
                }

                $scanNs = $ns . "\\" . $dir;
                $this->scanNamespaces[$scanNs] = $scanDir;
            }
        }
    }

3.3 alias方法介绍

function alias(string $alias): string
    {
        return \Swoft\App::getAlias($alias);
    }
// 如果$aliase为空或者不以@符开头的,直接返回aliase,默认self::$aliases = ['@swoft' => __DIR__,],swoft只定义了一个别名叫@swoft,但当我们传@swoft/db的时候,也可以返回db组件所在的目录绝对路径
public static function getAlias(string $alias): string
    {
        // empty OR not an alias
        if (!$alias || $alias[0] !== '@') {
            return $alias;
        }
        if (isset(self::$aliases[$alias])) {
            return self::$aliases[$alias];
        }
        list($root) = \explode('/', $alias, 2);
        if (!isset(self::$aliases[$root])) {
            throw new \InvalidArgumentException('The set root alias does not exist,alias=' . $root);
        }
        $rootPath  = self::$aliases[$root];
        $aliasPath = \str_replace($root, '', $alias);
        return $rootPath . $aliasPath;
    }
  1. $definitions = $resource->getDefinitions(); // 解析需要扫描的命名空间,并将注解的信息封装成$objectDefinition对象绑定到resource的definitions属性上
public function getDefinitions()
    {
        $classNames     = $this->registerLoaderAndScanBean();
        $fileClassNames = $this->scanFilePhpClass();// 扫描额外的文件
        $classNames     = array_merge($classNames, $fileClassNames);
        foreach ($classNames as $className) {
            $this->parseBeanAnnotations($className);
        }
        $this->parseAnnotationsData();
        return $this->definitions;
    }

4.1 $classNames = $this->registerLoaderAndScanBean(); // 获取所有扫描空间下php文件

protected function registerLoaderAndScanBean()
    {
        $phpClass = [];
        // 前面我们提到过我们将scanNamespaces和componentNamespaces绑定到resource对象上
        foreach ($this->scanNamespaces as $namespace => $dir) {
            // 此处用到了# [doctrine](https://github.com/doctrine)/**[annotations](https://github.com/doctrine/annotations)** 这个第三方扩展

            // 注册自动加载器回调。回调接受该类作为第一个和唯一的参数,如果找到并包含相应的文件,则必须返回true。
            AnnotationRegistry::registerLoader(function ($class) {
                if (class_exists($class) || interface_exists($class)) {
                    return true;
                }

                return false;
            });
            $scanClass = $this->scanPhpFile($dir, $namespace);
            $phpClass  = array_merge($phpClass, $scanClass);
        }

        return array_unique($phpClass);
    }

4.1.1 $scanClass = $this->scanPhpFile($dir, $namespace); //获取该目录下所有的php文件

protected function scanPhpFile(string $dir, string $namespace)
    {
        if (!is_dir($dir)) {
            return [];
        }
        // 获取dir下目录和文件的一个迭代对象 参考:http://php.net/manual/zh/class.recursivedirectoryiterator.php
        $iterator = new \RecursiveDirectoryIterator($dir);
        // 递归获取该迭代对象下所有的文件和目录
        $files    = new \RecursiveIteratorIterator($iterator);

        $phpFiles = [];
        foreach ($files as $file) {
            // 获取文件类型
            $fileType = pathinfo($file, PATHINFO_EXTENSION);
            if ($fileType != 'php') {
                continue;
            }

            $replaces = ['', '\\', '', ''];
            $searches = [$dir, '/', '.php', '.PHP'];

            // 将真实的文件地址按照对应参数进行的进行替换
            // 比如:/var/www/swoft/vendor/composer/../../app/Boot/SwoftListener.php 替换完成后会变成 \SwoftListener
            $file       = str_replace($searches, $replaces, $file);
            $phpFiles[] = $namespace . $file;
        }

        return $phpFiles;
    }

4.2 $fileClassNames = $this->scanFilePhpClass(); // 获取默认的需要扫描的php文件

protected function scanFilePhpClass()
    {
        $phpClass = [];
        // 此时resource上的scanFiles默认是空的
        foreach ($this->scanFiles as $ns => $files) {
            foreach ($files as $file) {
                $pathInfo = pathinfo($file);
                if (!isset($pathInfo['filename'])) {
                    continue;
                }
                $phpClass[] = $ns . "\\" . $pathInfo['filename'];
            }
        }

        return $phpClass;
    }

4.3 $this->parseBeanAnnotations($className); // 解析扫描得到的php文件的注解,将相应的注解绑定到resource的annotation属性上

public function parseBeanAnnotations(string $className)
    {
        //  判断类、接口是否存在
        if (!class_exists($className) && !interface_exists($className)) {
            return null;
        }

        // 注解解析器
        // 实例化一个注解解析器
        $reader           = new AnnotationReader();
        // 添加需要忽略的注解
        $reader           = $this->addIgnoredNames($reader);
        // 获取一个反射类对象实例
        $reflectionClass  = new \ReflectionClass($className);
        // 获取类所有注解
        $classAnnotations = $reader->getClassAnnotations($reflectionClass);

        // 没有类注解不解析其它注解
        if (empty($classAnnotations)) {
            return;
        }

        foreach ($classAnnotations as $classAnnotation) {
            // 绑定注解类的class属性到resource上的annotations属性上
            $this->annotations[$className]['class'][get_class($classAnnotation)] = $classAnnotation;
        }

        // 解析属性
        // 获取反射类的属性列表
        $properties = $reflectionClass->getProperties();
        foreach ($properties as $property) {
            if ($property->isStatic()) {
                continue;
            }
            $propertyName        = $property->getName();
             // 如果发现你的注解没有被获取到,可以查看是否被全局忽略,doctrine/annotations默认已经添加了很多,详见:https://github.com/doctrine/annotations/blob/master/lib/Doctrine/Annotations/AnnotationReader.php
            $propertyAnnotations = $reader->getPropertyAnnotations($property);
            foreach ($propertyAnnotations as $propertyAnnotation) {
                // 绑定注解类的property属性到resource的annotation属性上
                $this->annotations[$className]['property'][$propertyName][get_class($propertyAnnotation)] = $propertyAnnotation;
            }
        }

        // 解析方法
        $publicMethods = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC);
        foreach ($publicMethods as $method) {
            if ($method->isStatic()) {
                continue;
            }

            $methodName = $method->getName();

            // 解析方法注解
            $methodAnnotations = $reader->getMethodAnnotations($method);

            foreach ($methodAnnotations as $methodAnnotation) {
                // 绑定注解类的method属性到resource的annotation属性上
                $this->annotations[$className]['method'][$methodName][get_class($methodAnnotation)][] = $methodAnnotation;
            }
        }
    }

4.3.1 $reader = $this->addIgnoredNames($reader); // 添加忽略的注解

protected function addIgnoredNames(AnnotationReader $reader)
    {
      // protected $ignoredNames
      // = [
      //    'Usage',
      //    'Options',
      //    'Arguments',
      //    'Example',
      //   ];
      // 默认reader会忽略上述注解
        foreach ($this->ignoredNames as $name) {
            $reader->addGlobalIgnoredName($name);
        }

        return $reader;
    }

4.4 $this->parseAnnotationsData(); // 解析注解数据

public function parseAnnotationsData()
    {
        // 对resource上绑定的注解循环进行处理
        foreach ($this->annotations as $className => $annotation) {
            $classAnnotations = $annotation['class'];
            $this->parseClassAnnotations($className, $annotation, $classAnnotations);
        }
    }

4.4.1 $this->parseClassAnnotations($className, $annotation, $classAnnotations); // 解析注解,对于注解涉及到wapper、parser、collect等操作,具体含义请看后面介绍

private function parseClassAnnotations(string $className, array $annotation, array $classAnnotations)
    {
        // 当注解依赖多个类循环处理
        foreach ($classAnnotations as $classAnnotation) {
            // 获取注解对象的类名 e.g. Swoft\Bean\Annotation\ServerListener
            $annotationClassName = get_class($classAnnotation);
            $classNameTmp        = str_replace('\\', '/', $annotationClassName);
            // 返回路径中的文件名部分 e.g. ServerListener
            $classFileName       = basename($classNameTmp);
            // 返回路径中的目录部分 e.g. Swoft/Bean
            $beanNamespaceTmp    = dirname($classNameTmp, 2);
             // 路径转换成命名空间 e.g. Swoft/Bean
            $beanNamespace       = str_replace('/', '\\', $beanNamespaceTmp);

            // 获取注解包装类名称 e.g. "Swoft\Bean\Wrapper\ServerListenerWrapper"
            $annotationWrapperClassName = "{$beanNamespace}\\Wrapper\\{$classFileName}Wrapper";

            if (!class_exists($annotationWrapperClassName)) {
                continue;
            }

             // 作用:注解的wapper,并将resource对象传递到wapper中
            /* @var WrapperInterface $wrapper */
            $wrapper = new $annotationWrapperClassName($this);

            // wrapper extend
            // 遍历组件命名空间,对组件注解wapper进行扩展
            foreach ($this->componentNamespaces as $componentNamespace) {
                $annotationWrapperExtendClassName = "{$componentNamespace}\\Bean\\Wrapper\\Extend\\{$classFileName}Extend";
                if (!class_exists($annotationWrapperExtendClassName)) {
                    continue;
                }
                $extend = new $annotationWrapperExtendClassName();
                // 将扩展的wapper绑定到wapper的extends属性上
                $wrapper->addExtends($extend);
            }

            $objectDefinitionAry = $wrapper->doWrapper($className, $annotation);
            if ($objectDefinitionAry != null) {
                list($beanName, $objectDefinition) = $objectDefinitionAry;
                // 将解析后的注解类绑定到resource的definitions属性上
                $this->definitions[$beanName] = $objectDefinition;
            }
        }
    }
    public function addExtends(WrapperExtendInterface $extend)
    {
        $extendClass = get_class($extend);
        $this->extends[$extendClass] = $extend;
    }    
    public function doWrapper(string $className, array $annotations)
    {
        $reflectionClass = new \ReflectionClass($className);

        // 解析类注解,通过注解类获取到对应的parser解析器,触发collector操作,返回关于该bean的一些状态信息,如ServerListenerParser中返回:[$beanName, $scope, '']。 $beanName 是 声明@Bean注解的类名称,$scope是返回是否是单例
        $beanDefinition = $this->parseClassAnnotations($className, $annotations['class']);

        // 没配置注入bean注解
        if (empty($beanDefinition) && !$reflectionClass->isInterface()) {
            // 解析属性 获取反射类的属性
            $properties = $reflectionClass->getProperties();

            // 解析注解属性 获取反射类所有属性属性的注解
            $propertyAnnotations = $annotations['property']??[];
            $this->parseProperties($propertyAnnotations, $properties, $className);

            // 解析方法
            $publicMethods = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC);
            $methodAnnotations = $annotations['method'] ??[];

            $this->parseMethods($methodAnnotations, $className, $publicMethods);

            return null;
        }


        // parser bean annotation
        list($beanName, $scope, $ref) = $beanDefinition;

        // 初始化对象 将解析的bean的数据实例化为一个ObjectDefinition对象
        $objectDefinition = new ObjectDefinition();
        $objectDefinition->setName($beanName);
        $objectDefinition->setClassName($className);
        $objectDefinition->setScope($scope);
        $objectDefinition->setRef($ref);

        if (!$reflectionClass->isInterface()) {
            // 解析属性
            $properties = $reflectionClass->getProperties();

            // 解析属性
            $propertyAnnotations = $annotations['property']??[];
            $propertyInjections = $this->parseProperties($propertyAnnotations, $properties, $className);
            // 将解析后的注解属性返回值绑定到ObjectDefinition对象的propertyInjections属性上
            $objectDefinition->setPropertyInjections($propertyInjections);
        }

        // 解析方法
        $publicMethods = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC);
        $methodAnnotations = $annotations['method'] ??[];
        $this->parseMethods($methodAnnotations, $className, $publicMethods);

        return [$beanName, $objectDefinition];
    }
      public function parseClassAnnotations(string $className, array $annotations)
        {
         // 判断是否能够解析类注解
        if (!$this->isParseClass($annotations)) {
            return null;
        }

        $beanData = null;
        foreach ($annotations as $annotation) {
            $annotationClass = get_class($annotation);
             // 判断该注解是否是wapper中含有的注解
            if (!in_array($annotationClass, $this->getClassAnnotations())) {
                continue;
            }

            // annotation parser 获取该注解类对应的注解解析器
            $annotationParser = $this->getAnnotationParser($annotation);
            if ($annotationParser == null) {
                continue;
            }
            // 获取注解解析后的数据, 在parser的过程中,触发collect操作
            $annotationData = $annotationParser->parser($className, $annotation);
            if ($annotationData != null) {
                $beanData = $annotationData;
            }
        }

        return $beanData;
    }

    private function isParseClass(array $annotations): bool
    {
        // 如果允许解析类注解或者允许解析
        return $this->isParseClassAnnotations($annotations) || $this->isParseExtendAnnotations($annotations, 1);
    }
    private function getClassAnnotations(): array
    {
        // protected $classAnnotations = [  ServerListener::class ]; 由具体的wapper类引入注解类
        return array_merge($this->classAnnotations, $this->getExtendAnnotations(1));
    }
    // 获取扩展的注解类
    private function getExtendAnnotations(int $type = 1): array
    {
        $annotations = [];
        foreach ($this->extends as $extend) {
            if ($type == 1) {
                $extendAnnoation = $extend->getClassAnnotations();
            } elseif ($type == 2) {
                $extendAnnoation = $extend->getPropertyAnnotations();
            } else {
                $extendAnnoation = $extend->getMethodAnnotations();
            }
            $annotations = array_merge($annotations, $extendAnnoation);
        }

        return $annotations;
    }
    private function getAnnotationParser($objectAnnotation)
    {
        $annotationClassName = get_class($objectAnnotation);
        $classNameTmp = str_replace('\\', '/', $annotationClassName);
        $className = basename($classNameTmp);
        $namespaceDir = dirname($classNameTmp, 2);
        $namespace = str_replace('/', '\\', $namespaceDir);

        // 解析器类名 根据注解类获取到的命名空间+固定的命名空间+类名称获取注解解析器名称
        $annotationParserClassName = "{$namespace}\\Parser\\{$className}Parser";
        if (!class_exists($annotationParserClassName)) {
            return null;
        }

        $annotationParser = new $annotationParserClassName($this->annotationResource);
        return $annotationParser;
    }
    // 解析类属性
    private function parseProperties(array $propertyAnnotations, array $properties, string $className)
    {
        $propertyInjections = [];

        /* @var \ReflectionProperty $property */
        foreach ($properties as $property) {
            if ($property->isStatic()) {
                continue;
            }
            $propertyName = $property->getName();
            if (!isset($propertyAnnotations[$propertyName]) || !$this->isParseProperty($propertyAnnotations[$propertyName])) {
                continue;
            }

             // 通过反射类获取该属性的值
            $object = new $className();
            $property->setAccessible(true);
            $propertyValue = $property->getValue($object);

            list($injectProperty, $isRef) = $this->parsePropertyAnnotations($propertyAnnotations, $className, $propertyName, $propertyValue);
            if ($injectProperty == null) {
                continue;
            }

            $propertyInjection = new PropertyInjection($propertyName, $injectProperty, (bool)$isRef);
            $propertyInjections[$propertyName] = $propertyInjection;
        }

        return $propertyInjections;
    }
    private function parsePropertyAnnotations(array $propertyAnnotations, string $className, string $propertyName, $propertyValue)
    {
        $isRef = false;
        $injectProperty = "";

        // 没有任何注解
        if (empty($propertyAnnotations) || !isset($propertyAnnotations[$propertyName])
            || !$this->isParseProperty($propertyAnnotations[$propertyName])
        ) {
            return [null, false];
        }

        // 属性注解解析
        foreach ($propertyAnnotations[$propertyName] as $propertyAnnotation) {
            $annotationClass = get_class($propertyAnnotation);
            // 判断属性注解是否属于定义的注解
            if (!in_array($annotationClass, $this->getPropertyAnnotations())) {
                continue;
            }

            // 解析器 【使用具体的解析器(如ValueParser,ReferenceParser等)解析注入元信息】
            $annotationParser = $this->getAnnotationParser($propertyAnnotation);
            if ($annotationParser === null) {
                $injectProperty = null;
                $isRef = false;
                continue;
            }
            // 返回注入的属性值$injectProperty和isRef,isRef 决定属性需要注入一个Bean还是一个基本类型值
            list($injectProperty, $isRef) = $annotationParser->parser($className, $propertyAnnotation, $propertyName, "", $propertyValue);
        }

        return [$injectProperty, $isRef];
    }
    public function setPropertyInjections($propertyInjections)
    {
        $this->propertyInjections = $propertyInjections;
    }
    private function parseMethods(array $methodAnnotations, string $className, array $publicMethods)
    {
        // 循环解析
        foreach ($publicMethods as $method) {
            /* @var \ReflectionMethod $method*/
            if ($method->isStatic()) {
                continue;
            }

            /* @var \ReflectionClass $declaredClass*/
            $declaredClass = $method->getDeclaringClass();
            $declaredName = $declaredClass->getName();

            // 不是当前类方法
            if ($declaredName != $className) {
                continue;
            }
            $this->parseMethodAnnotations($className, $method, $methodAnnotations);
        }
    }
     private function parseMethodAnnotations(string $className, \ReflectionMethod $method, array $methodAnnotations)
    {
        // 方法没有注解解析
        $methodName = $method->getName();
        $isWithoutMethodAnnotation = empty($methodAnnotations) || !isset($methodAnnotations[$methodName]);
        if ($isWithoutMethodAnnotation || !$this->isParseMethod($methodAnnotations[$methodName])) {
            $this->parseMethodWithoutAnnotation($className, $methodName);
            return;
        }

        // 循环方法注解解析
        foreach ($methodAnnotations[$methodName] as $methodAnnotationAry) {
            foreach ($methodAnnotationAry as $methodAnnotation) {
                if (!$this->inMethodAnnotations($methodAnnotation)) {
                    continue;
                }

                // 解析器解析
                $annotationParser = $this->getAnnotationParser($methodAnnotation);
                if ($annotationParser == null) {
                    $this->parseMethodWithoutAnnotation($className, $methodName);
                    continue;
                }
                // 同类注解一致
                $annotationParser->parser($className, $methodAnnotation, "", $methodName);
            }
        }
    }
    private function parseMethodWithoutAnnotation(string $className, string $methodName)
    {
        // 该解析器默认不管方法有没有注解都对所有swoft组件的collector进行了收集,这块在swoft1.0比较绕,swoft2.0会重写该逻辑,只针对有注解的进行搜集
        $parser = new MethodWithoutAnnotationParser($this->annotationResource);
        $parser->parser($className, null, "", $methodName);
    }
    public function parser(string $className, $objectAnnotation = null, string $propertyName = "", string $methodName = "", $propertyValue = null)
    {
        $swoftDir      = dirname(__FILE__, 5);
        $componentDirs = scandir($swoftDir);
        foreach ($componentDirs as $component) {
            if ($component == '.' || $component == '..') {
                continue;
            }

            $componentCommandDir = $swoftDir . DS . $component . DS . 'src/Bean/Collector';
            if (!is_dir($componentCommandDir)) {
                continue;
            }

            $componentNs = ComponentHelper::getComponentNs($component);
            $collectNs = "Swoft{$componentNs}\\Bean\\Collector";
            $collectorFiles = scandir($componentCommandDir);
            foreach ($collectorFiles as $collectorFile){
                $pathInfo = pathinfo($collectorFile);
                if(!isset($pathInfo['filename'])){
                    continue;
                }
                $fileName = $pathInfo['filename'];
                $collectClassName = $collectNs.'\\'.$fileName;
                if(!class_exists($collectClassName)){
                    continue;
                }

                /* @var \Swoft\Bean\CollectorInterface $collector */
                $collector = new $collectClassName();
                $collector->collect($className, $objectAnnotation, $propertyName, $methodName, $propertyValue);
            }
        }

        return null;
    }
  • $definition = self::getServerDefinition(); // 获取类型为server 的bean对象,返回该bean对象的定义 如:return [ 'commandRoute' => [ 'class' =>HandlerMapping::class,]];
private static function getServerDefinition(): array
    {
        // 获取@console别名的路径
        $file             = App::getAlias('@console');
        $configDefinition = [];
        if (\is_readable($file)) {
            $configDefinition = require_once $file; // 获取/swoft/config/beans/console.php配置文件内容
        }
        // BootBeanCollector::TYPE_SERVER = 'server'
        // 获取核心bean
        $coreBeans  = self::getCoreBean(BootBeanCollector::TYPE_SERVER);
        return ArrayHelper::merge($coreBeans, $configDefinition);
    }
    private static function getCoreBean(string $type): array
    {
        $collector = BootBeanCollector::getCollector(); // 获取启动时的collector的beans数组,使用了@BootBean注解的bean,存放到该bootBeanCollector的beans数组中
        // dd($collector); 详见图5-1
        if (!isset($collector[$type])) {
            return [];
        }

        $coreBeans = [];
        /** @var array $bootBeans */
        $bootBeans = $collector[$type];
        foreach ($bootBeans as $beanName) {
            /* @var \Swoft\Core\BootBeanInterface $bootBean */
            $bootBean  = App::getBean($beanName);  // 获取默认启动的bean对象,启动过程中根据是否是单例对象会被绑定到container的singletonEntries属性上
            $beans     = $bootBean->beans();
            $coreBeans = ArrayHelper::merge($coreBeans, $beans);
        }

        return $coreBeans;
    }
    // APP::getBean()
    public static function getBean(string $name)
    {
        return ApplicationContext::getBean($name);
    }
    // ApplicationContext::getBean()
    public static function getBean(string $name)
    {
        return BeanFactory::getBean($name);
    }
    // BeanFactory::getBean()
    public static function getBean(string $name)
    {
        return self::$container->get($name);
    }
    public function get(string $name)
    {
        // 如果单例实体singletonEntries中已经存在该bean,那么直接返回该对象
        if (isset($this->singletonEntries[$name])) {
            return $this->singletonEntries[$name];
        }

        // 未定义
        if (!isset($this->definitions[$name])) {
            throw new \InvalidArgumentException(sprintf('Bean %s not exist', $name));
        }

        /* @var ObjectDefinition $objectDefinition */
        $objectDefinition = $this->definitions[$name]; // 前面在autoloadServerAnnotation方法将resource上的definitions属性绑定到了container的definitions属性上

        return $this->set($name, $objectDefinition);
    }
    private function set(string $name, ObjectDefinition $objectDefinition)
    {
        // bean创建信息
        $scope             = $objectDefinition->getScope();  // 获取scope值
        $className         = $objectDefinition->getClassName(); // 获取类名
        $propertyInjects   = $objectDefinition->getPropertyInjections(); // 获取注入的属性
        $constructorInject = $objectDefinition->getConstructorInjection(); // 获取注入的构造属性

        if ($refBeanName = $objectDefinition->getRef()) { // 判断是否有引用,根据refBeanName寻找到最终实现类【ref属性重定向依赖查找,一般用于在Interface这种需要具体实现类的Bean上,用于指定实际使用的实现类】
            return $this->get($refBeanName);
        }

        // 构造函数
        // 构造器注入的逻辑看了代码没有具体代码有调用过setConstructorInjection方法,swoft应该没有实现构造器注入的逻辑
        $constructorParameters = [];
        if ($constructorInject !== null) {
            $constructorParameters = $this->injectConstructor($constructorInject);
        }

        $reflectionClass = new \ReflectionClass($className);
        $properties      = $reflectionClass->getProperties();

        // new实例 $this->initMethod = ‘init’
        $isExeMethod = $reflectionClass->hasMethod($this->initMethod);
        // 生成一个实例对象
        $object      = $this->newBeanInstance($reflectionClass, $constructorParameters);

        // 属性注入
        $this->injectProperties($object, $properties, $propertyInjects);

        // 执行初始化方法
        if ($isExeMethod) {
            $object->{$this->initMethod}();
        }

        if (!$object instanceof AopInterface) {
            $object = $this->proxyBean($name, $className, $object); // 生成一个代理对象,通过完全clone一个类的代码重新实例一个新的对象
        }

        // 单例处理
        if ($scope === Scope::SINGLETON) {
            $this->singletonEntries[$name] = $object;
        }

        return $object;
    }
    private function injectConstructor(MethodInjection $constructorInject): array
    {
        $constructorParameters = [];

        /* @var ArgsInjection $parameter */
        foreach ($constructorInject->getParameters() as $parameter) {
            $argValue = $parameter->getValue(); 
            if (\is_array($argValue)) {// 对于注入的数组值要根据ref区分是否是引用类型或基本类型值
                $constructorParameters[] = $this->injectArrayArgs($argValue);
                continue;
            }
            if ($parameter->isRef()) {
                $constructorParameters[] = $this->get($parameter->getValue());
                continue;
            }
            $constructorParameters[] = $parameter->getValue();
        }

        return $constructorParameters;
    }
    private function newBeanInstance(\ReflectionClass $reflectionClass, array $constructorParameters)
    {
        if ($reflectionClass->hasMethod('__construct')) {
            return $reflectionClass->newInstanceArgs($constructorParameters);
        }

        return $reflectionClass->newInstance();
    }
    private function injectProperties($object, array $properties, $propertyInjects)
    {
        foreach ($properties as $property) {
            if ($property->isStatic()) {
                continue;
            }

            $propertyName = $property->getName();
            if (!isset($propertyInjects[$propertyName])) {
                continue;
            }

            // 设置可用
            if (!$property->isPublic()) {
                $property->setAccessible(true);
            }

            /* @var PropertyInjection $propertyInject */
            $propertyInject = $propertyInjects[$propertyName];
            $injectProperty = $propertyInject->getValue(); // 获取注入的值

            // 属性是数组 循环数组里面的值,分析值类型是引用类型还是基本类型
            if (\is_array($injectProperty)) {
                $injectProperty = $this->injectArrayArgs($injectProperty);
            }

            // 属性是bean引用【或者说是一个引用bean对象】
            if ($propertyInject->isRef()) {
                $injectProperty = $this->get($injectProperty);
            }

            if ($injectProperty !== null) { // 如果不为null,直接注入该值
                $property->setValue($object, $injectProperty);
            }
        }
    }
    private function injectArrayArgs(array $injectProperty): array
    {
        $injectAry = [];
        foreach ($injectProperty as $key => $property) {
            // 递归循环注入
            if (\is_array($property)) {
                $injectAry[$key] = $this->injectArrayArgs($property);
                continue;
            }

            // 参数注入
            if ($property instanceof ArgsInjection) {
                $propertyValue = $property->getValue();
                if ($property->isRef()) {
                    $injectAry[$key] = $this->get($propertyValue);
                    continue;
                }
                $injectAry[$key] = $propertyValue;
            }
        }

        if (empty($injectAry)) {
            $injectAry = $injectProperty;
        }

        return $injectAry;
    }
    private function proxyBean(string $name, string $className, $object)
    {
        /* @var Aop $aop */
        $aop = App::getBean(Aop::class); // 获取aop bean对象

        $rc  = new \ReflectionClass($className);
        $rms = $rc->getMethods();
        foreach ($rms as $rm) {
            $method      = $rm->getName();
            $annotations = Collector::$methodAnnotations[$className][$method] ?? []; // 获取方法注解
            $annotations = array_unique($annotations);
            $aop->match($name, $className, $method, $annotations); // 
        }

        $handler = new AopHandler($object);

        return Proxy::newProxyInstance(\get_class($object), $handler);
    }
    public function match(string $beanName, string $class, string $method, array $annotations)
    {
        foreach ($this->aspects as $aspectClass => $aspect) {
            if (!isset($aspect['point'], $aspect['advice'])) {
                continue;
            }

            // Include
            $pointBeanInclude = $aspect['point']['bean']['include'] ?? [];
            $pointAnnotationInclude = $aspect['point']['annotation']['include'] ?? [];
            $pointExecutionInclude = $aspect['point']['execution']['include'] ?? [];

            // Exclude
            $pointBeanExclude = $aspect['point']['bean']['exclude'] ?? [];
            $pointAnnotationExclude = $aspect['point']['annotation']['exclude'] ?? [];
            $pointExecutionExclude = $aspect['point']['execution']['exclude'] ?? [];

            $includeMath = $this->matchBeanAndAnnotation([$beanName], $pointBeanInclude) || $this->matchBeanAndAnnotation($annotations, $pointAnnotationInclude) || $this->matchExecution($class, $method, $pointExecutionInclude);

            $excludeMath = $this->matchBeanAndAnnotation([$beanName], $pointBeanExclude) || $this->matchBeanAndAnnotation($annotations, $pointAnnotationExclude) || $this->matchExecution($class, $method, $pointExecutionExclude);

            if ($includeMath && ! $excludeMath) {
                $this->map[$class][$method][] = $aspect['advice'];
            }
        }
    }
    public static function newProxyInstance(string $className, HandlerInterface $handler)
    {
        $reflectionClass = new \ReflectionClass($className);
        $reflectionMethods = $reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED);

        // Proxy property
        $id = \uniqid('', false);
        $proxyClassName = \basename(str_replace("\\", '/', $className)); // 返回文件名
        $proxyClassName = $proxyClassName . '_' . $id;
        $handlerPropertyName = '__handler_' . $id;

        // Base class template
        $template = "class $proxyClassName extends $className {
            private \$$handlerPropertyName;
            public function __construct(\$handler)
            {
                \$this->{$handlerPropertyName} = \$handler;
            }
        ";

        // Methods
        $template .= self::getMethodsTemplate($reflectionMethods, $handlerPropertyName);
        $template .= '}';

        eval($template);
        $newRc = new \ReflectionClass($proxyClassName);

        return $newRc->newInstance($handler); // 返回一个新的实例对象
    }
    private static function getMethodsTemplate(array $reflectionMethods, string $handlerPropertyName): string
    {
        $template = '';
        foreach ($reflectionMethods as $reflectionMethod) {
            $methodName = $reflectionMethod->getName();

            // not to override method
            if ($reflectionMethod->isConstructor() || $reflectionMethod->isStatic()) {
                continue;
            }
            
            // define override methodBody, 最终通过aop的execute进行执行
            $methodBody = "{
                return \$this->{$handlerPropertyName}->invoke('{$methodName}', func_get_args());
            }
            ";
            
            // the template of parameter
            $template .= " public function $methodName (";
            $template .= self::getParameterTemplate($reflectionMethod);
            $template .= ' ) ';

            // the template of return type
            $reflectionMethodReturn = $reflectionMethod->getReturnType();
            if ($reflectionMethodReturn !== null) {
                $returnType = $reflectionMethodReturn->__toString();
                $returnType = $returnType === 'self' ? $reflectionMethod->getDeclaringClass()->getName() : $returnType;
                $template .= " : $returnType";
                // if returnType is void
                if ($returnType === 'void') {
                    $methodBody = str_replace('return ', '', $methodBody);
                }
            }

            // append methodBody
            $template .= $methodBody;
        }

        return $template; // 返回代理对象 执行方法的模板string
    }
    public function invoke($method, $parameters)
    {
        /* @var Aop $aop */
        $aop = \bean(Aop::class);

        return $aop->execute($this->target, $method, $parameters);
    }
    public function execute($target, string $method, array $params)
    {
        $class = \get_class($target);

        // If doesn't have any advices, then execute the origin method
        if (!isset($this->map[$class][$method]) || empty($this->map[$class][$method])) {
            return $target->$method(...$params);
        }

        // Apply advices's functionality
        $advices = $this->map[$class][$method];
        return $this->doAdvice($target, $method, $params, $advices);
    }
图5-1
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,194评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,058评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,780评论 0 346
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,388评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,430评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,764评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,907评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,679评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,122评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,459评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,605评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,270评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,867评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,734评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,961评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,297评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,472评论 2 348

推荐阅读更多精彩内容