惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Privacy International News Feed
Martin Fowler
Martin Fowler
D
Docker
Y
Y Combinator Blog
云风的 BLOG
云风的 BLOG
U
Unit 42
T
Tailwind CSS Blog
J
Java Code Geeks
G
Google Developers Blog
MongoDB | Blog
MongoDB | Blog
阮一峰的网络日志
阮一峰的网络日志
WordPress大学
WordPress大学
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
美团技术团队
F
Fortinet All Blogs
N
News and Events Feed by Topic
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hacker News - Newest:
Hacker News - Newest: "LLM"
The GitHub Blog
The GitHub Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Recorded Future
Recorded Future
N
Netflix TechBlog - Medium
Google DeepMind News
Google DeepMind News
Hacker News: Ask HN
Hacker News: Ask HN
L
LINUX DO - 最新话题
Microsoft Security Blog
Microsoft Security Blog
N
News and Events Feed by Topic
I
Intezer
TaoSecurity Blog
TaoSecurity Blog
NISL@THU
NISL@THU
小众软件
小众软件
博客园 - 聂微东
博客园 - Franky
有赞技术团队
有赞技术团队
P
Palo Alto Networks Blog
爱范儿
爱范儿
H
Hacker News: Front Page
C
Cyber Attacks, Cyber Crime and Cyber Security
C
Cisco Blogs
P
Proofpoint News Feed
I
InfoQ
Google DeepMind News
Google DeepMind News
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Vercel News
Vercel News
H
Heimdal Security Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
量子位

流动

对口型视频合成方案对比:Wav2Lip、VideoReTalking 与 MuseTalk 2026 音色克隆方案对比:IndexTTS-2、CosyVoice、GPT-SoVITS、Fish Speech、VoxCPM 部署与实测 AI Agent折腾记(OpenClaw / Hermes Agent) 我的2025年 大连之行 回家收麦 六一儿童节爬长城 Golang database/sql 数据库断线自动重连机制解析 Golang默认Http Client导致的cannot assign requested address错误 清明踏春,爬山看海 购入小牛G400T电动车 北京的三月飞雪 wrenAI本地LLM模型部署 天津一日游 2024年终总结 停止使用staticfile.org服务 使用 ImageMagick 自动添加水印,保护图片版权 如何注册一个.sol域名 奥森公园半日游 昌平42公里骑行绿道打卡 十月一日爬慕田峪长城 当Hugo遇上AVIF,优化图片加载 博客被恶意镜像 Github Pages 部署流程解析 搭建个人锻炼页面 你好 Follow 中秋爬山 Google Adsense的审核之旅 让你的IPFS站点持久在线:接入Filebase的Names(IPNS)服务 一次简短的青岛之行 解决 "undeclared name: any (requires version go1.18 or later)" 编译错误 搭建自托管IPFS Gateway服务,替代Cloudflare的IPFS Gateway 302跳转的跨域问题(CORS) GORM增加sqlcommenter特性 源码分析:GORM是如何生成sql的 工银亚洲网银密码重置 加速Cloudflare访问 2023年终总结 2023年12月北京暴雪记录 使用Hugo实现响应式和优化的图片 加速Google Analytics 使用Google Indexing API加速博客收录 在Netlify上部署Twikoo评论系统 利用Github Actions定时抓取微博 北大口腔牙周刮治记录 故乡回忆之旅 解决Golang使用go get安装包后找不到可执行文件的问题 修正Hugo的JSON Feed格式 我的学车之路 将博客部署到星际文件系统(IPFS) 新冠疫情后的第一个春节 第一次清理键盘 2022年终总结 去掉Cloudflare烦人的email-decode.min.js请求 累计布局偏移修复方案改进 —— 自动生成图片宽高 优化博客的累计布局偏移(CLS)问题 将博客部署到Cloudflare Pages 奥林匹克公园向日葵之旅 记第二次洗牙 记录2022年海淀幼升小 Golang解析json的一个问题 疫情下的生活 整理下博客的一些调整 疫情下的五一假期 自己动手,更换thinkpad x1硬盘 二刷百望山 带娃游颐和园 博客架构说明 难得的清明假期 十一年的等待,终于拿到了liudon.com域名 被隔离的一周 mysql中字符串和整型自动转换的问题 一次惊心动魄的Mysql更新操作 如何在北京公积金网站上修改婚姻状况 PHP7.2编译安装后没有php.ini文件的问题 检测网站支持的SSL/TLS协议版本 记一次难忘的手术经历 十一假期经历 Swoft 框架运行分析(五) —— ConsoleProcessor模块分析 Swoft 框架运行分析(四) —— EventProcessor模块分析 一个git submodule update引发的问题 一个Curl的耗时长的问题 Swoft 框架运行分析(二) —— AnnotationProcessor模块分析 Swoft 框架运行分析(一) BCMath 与 科学计数 Flink Could Not Resolve Resourcemanager Address 解决Sublime Text安装包时"There Are No Packages Available for Installation"的报错 关于本站 2019,新开始
Swoft 框架运行分析(三) —— BeanProcessor模块分析
Liudon · 2019-09-02 · via 流动

今天讲一下BeanProcessor模块,先看一下handle方法实现。

/**
    * Handle bean
    *
    * @return bool
    * @throws ReflectionException
    * @throws AnnotationException
    */
public function handle(): bool
{
    if (!$this->application->beforeBean()) {
        return false;
    }

    $handler     = new BeanHandler();
    $definitions = $this->getDefinitions();
    $parsers     = AnnotationRegister::getParsers();
    $annotations = AnnotationRegister::getAnnotations();

    BeanFactory::addDefinitions($definitions);
    BeanFactory::addAnnotations($annotations);
    BeanFactory::addParsers($parsers);
    BeanFactory::setHandler($handler);
    BeanFactory::init();

    /* @var Config $config*/
    $config = BeanFactory::getBean('config');

    CLog::info('config path=%s', $config->getPath());
    CLog::info('config env=%s', $config->getEnv());

    $stats = BeanFactory::getStats();

    CLog::info('Bean is initialized(%s)', SwoftHelper::formatStats($stats));

    return $this->application->afterBean();
}

先通过getDefinitions方法获取所有的Bean定义。

/**
    * Get bean definitions
    *
    * @return array
    */
private function getDefinitions(): array
{
    // Core beans
    $definitions = [];
    $autoLoaders = AnnotationRegister::getAutoLoaders();

    // get disabled loaders by application
    $disabledLoaders = $this->application->getDisabledAutoLoaders();

    foreach ($autoLoaders as $autoLoader) {
        if (!$autoLoader instanceof DefinitionInterface) {
            continue;
        }

        $loaderClass = get_class($autoLoader);

        // If the component is disabled by user.
        if (isset($disabledLoaders[$loaderClass])) {
            CLog::info('Auto loader(%s) is <cyan>disabled</cyan>, skip handle it', $loaderClass);
            continue;
        }

        // If the component is not enabled.
        if ($autoLoader instanceof ComponentInterface && !$autoLoader->isEnable()) {
            continue;
        }

        $definitions = ArrayHelper::merge($definitions, $autoLoader->beans());
    }

    // Bean definitions
    $beanFile = $this->application->getBeanFile();
    $beanFile = alias($beanFile);

    if (!file_exists($beanFile)) {
        throw new InvalidArgumentException(
            sprintf('The bean config file of %s is not exist!', $beanFile)
        );
    }

    $beanDefinitions = require $beanFile;
    $definitions     = ArrayHelper::merge($definitions, $beanDefinitions);

    return $definitions;
}

通过AnnotationRegister::getAutoLoaders()拿到所有的autoloader对象,排除掉非DefinitionInterface对象,通过bean()方法获取定义的Bean信息。

这里以http-server\src\AutoLoader.php为例。

<?php declare(strict_types=1);

namespace Swoft\Http\Server;

use function bean;
use function dirname;
use ReflectionException;
use Swoft\Bean\Exception\ContainerException;
use Swoft\Helper\ComposerJSON;
use Swoft\Http\Message\ContentType;
use Swoft\Http\Message\Response;
use Swoft\Http\Server\Formatter\HtmlResponseFormatter;
use Swoft\Http\Server\Formatter\JsonResponseFormatter;
use Swoft\Http\Server\Formatter\XmlResponseFormatter;
use Swoft\Http\Server\Parser\JsonRequestParser;
use Swoft\Http\Server\Parser\XmlRequestParser;
use Swoft\Http\Server\Swoole\RequestListener;
use Swoft\Server\SwooleEvent;
use Swoft\SwoftComponent;

/**
 * Class AutoLoader
 *
 * @since 2.0
 */
class AutoLoader extends SwoftComponent
{
    /**
     * Metadata information for the component.
     *
     * @return array
     * @see ComponentInterface::getMetadata()
     */
    public function metadata(): array
    {
        $jsonFile = dirname(__DIR__) . '/composer.json';

        return ComposerJSON::open($jsonFile)->getMetadata();
    }

    /**
     * Get namespace and dirs
     *
     * @return array
     */
    public function getPrefixDirs(): array
    {
        return [
            __NAMESPACE__ => __DIR__,
        ];
    }

    /**
     * @return array
     * @throws ReflectionException
     * @throws ContainerException
     */
    public function beans(): array
    {
        return [
            'httpRequest'     => [
                'parsers' => [
                    ContentType::XML  => bean(XmlRequestParser::class),
                    ContentType::JSON => bean(JsonRequestParser::class),
                ]
            ],
            'httpResponse'    => [
                'format'     => Response::FORMAT_JSON,
                'formatters' => [
                    Response::FORMAT_HTML => bean(HtmlResponseFormatter::class),
                    Response::FORMAT_JSON => bean(JsonResponseFormatter::class),
                    Response::FORMAT_XML  => bean(XmlResponseFormatter::class),
                ]
            ],
            'acceptFormatter' => [
                'formats' => [
                    ContentType::JSON => Response::FORMAT_JSON,
                    ContentType::HTML => Response::FORMAT_HTML,
                    ContentType::XML  => Response::FORMAT_XML,
                ]
            ],
            'httpServer'      => [
                'on' => [
                    SwooleEvent::REQUEST => bean(RequestListener::class)
                ]
            ],
            'httpRouter'      => [
                'name'            => 'swoft-http-router',
                // config
                'ignoreLastSlash' => true,
                'tmpCacheNumber'  => 500,
            ],
        ];
    }
}

可以看到,这里通过beans()定义了httpRequesthttpResponseacceptFormatterhttpServerhttpRouter四个Bean对象。

回到上面getDefinitions方法。

$definitions = ArrayHelper::merge($definitions, $autoLoader->beans());

然后将Bean信息添加到definitions对象上。

之后通过$beanFile = $this->application->getBeanFile();获取bean配置文件。

$beanDefinitions = require $beanFile;
$definitions     = ArrayHelper::merge($definitions, $beanDefinitions);

加载配置文件,然后将Bean信息添加到definitions对象上。

可以看到Bean有两种定义方式:通过AutoLoader和配置文件,与swoft官方文档里的说明一致。

回到handle方法。

$parsers     = AnnotationRegister::getParsers();
$annotations = AnnotationRegister::getAnnotations();

还记得上一篇文章最后提到的AnnotationRegister类的annotationsparsers两个属性吗?这里通过getParsersgetAnnotations获取这两个属性。

BeanFactory::addDefinitions($definitions);
BeanFactory::addAnnotations($annotations);
BeanFactory::addParsers($parsers);
BeanFactory::setHandler($handler);
BeanFactory::init();

向BeanFatory注册信息。

/**
    * Init
    *
    * @return void
    * @throws AnnotationException
    * @throws ReflectionException
    */
public static function init(): void
{
    Container::getInstance()->init();
}

...

/**
    * Add definitions
    *
    * @param array $definitions
    *
    * @return void
    */
public static function addDefinitions(array $definitions): void
{
    Container::getInstance()->addDefinitions($definitions);
}

/**
    * Add annotations
    *
    * @param array $annotations
    *
    * @return void
    */
public static function addAnnotations(array $annotations): void
{
    Container::getInstance()->addAnnotations($annotations);
}

/**
    * Add annotation parsers
    *
    * @param array $annotationParsers
    *
    * @return void
    */
public static function addParsers(array $annotationParsers): void
{
    Container::getInstance()->addParsers($annotationParsers);
}

/**
    * Set bean handler
    *
    * @param HandlerInterface $handler
    */
public static function setHandler(HandlerInterface $handler): void
{
    Container::getInstance()->setHandler($handler);
}

这里可以看到所有的方法,最终都调用的是Swoft\Bean\Container类。

/**
    * Add definitions
    *
    * @param array $definitions
    *
    * @return void
    */
public function addDefinitions(array $definitions): void
{
    $this->definitions = ArrayHelper::merge($this->definitions, $definitions);
}

/**
    * Add annotations
    *
    * @param array $annotations
    *
    * @return void
    */
public function addAnnotations(array $annotations): void
{
    $this->annotations = ArrayHelper::merge($this->annotations, $annotations);
}

/**
    * Add annotation parsers
    *
    * @param array $annotationParsers
    *
    * @return void
    */
public function addParsers(array $annotationParsers): void
{
    $this->parsers = ArrayHelper::merge($this->parsers, $annotationParsers);
}


/**
    * @param HandlerInterface $handler
    */
public function setHandler(HandlerInterface $handler): void
{
    $this->handler = $handler;
}

这四个方法就是注册属性,接下来是重头戏init方法。

/**
    * Init
    *
    * @throws AnnotationException
    * @throws ReflectionException
    */
public function init(): void
{
    // Parse annotations
    $this->parseAnnotations();

    // Parse definitions
    $this->parseDefinitions();

    // Init beans
    $this->initializeBeans();
}

先看parseAnnotations方法,从代码注释上也可以看出大概,解析注解,接下来我们看下具体是如何实现的。

/**
    * Parse annotations
    *
    * @throws AnnotationException
    */
private function parseAnnotations(): void
{
    $annotationParser = new AnnotationObjParser(
        $this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases
    );
    $annotationData   = $annotationParser->parseAnnotations($this->annotations, $this->parsers);

    [$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases] = $annotationData;
}

声明了一个AnnotationObjParser对象,调用了parseAnnotations方法。

/**
    * 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];
}

这里遍历所有的annotation类,循环调用parseOneClassAnnotations进行解析。

/**
    * Parse class all annotations
    *
    * @param string $className
    * @param array  $classOneAnnotations
    *
    * @throws AnnotationException
    */
private function parseOneClassAnnotations(string $className, array $classOneAnnotations): void
{
    // Check class annotation tag
    if (!isset($classOneAnnotations['annotation'])) {
        throw new AnnotationException(
            sprintf('Property or method(%s) with `@xxx` must be define class annotation', $className)
        );
    }

    // Parse class annotations
    $classAnnotations = $classOneAnnotations['annotation'];
    $reflectionClass  = $classOneAnnotations['reflection'];

    $classAry = [
        $className,
        $reflectionClass,
        $classAnnotations
    ];

    $objectDefinition = $this->parseClassAnnotations($classAry);

    // Parse property annotations
    $propertyInjects        = [];
    $propertyAllAnnotations = $classOneAnnotations['properties'] ?? [];
    foreach ($propertyAllAnnotations as $propertyName => $propertyOneAnnotations) {
        $proAnnotations = $propertyOneAnnotations['annotation'] ?? [];
        $propertyInject = $this->parsePropertyAnnotations($classAry, $propertyName, $proAnnotations);
        if ($propertyInject) {
            $propertyInjects[$propertyName] = $propertyInject;
        }
    }

    // Parse method annotations
    $methodInjects        = [];
    $methodAllAnnotations = $classOneAnnotations['methods'] ?? [];
    foreach ($methodAllAnnotations as $methodName => $methodOneAnnotations) {
        $methodAnnotations = $methodOneAnnotations['annotation'] ?? [];

        $methodInject = $this->parseMethodAnnotations($classAry, $methodName, $methodAnnotations);
        if ($methodInject) {
            $methodInjects[$methodName] = $methodInject;
        }
    }

    if (!$objectDefinition) {
        return;
    }

    if (!empty($propertyInjects)) {
        $objectDefinition->setPropertyInjections($propertyInjects);
    }

    if (!empty($methodInjects)) {
        $objectDefinition->setMethodInjections($methodInjects);
    }

    // Object definition and class name
    $name         = $objectDefinition->getName();
    $aliase       = $objectDefinition->getAlias();
    $classNames   = $this->classNames[$className] ?? [];
    $classNames[] = $name;

    $this->classNames[$className]   = array_unique($classNames);
    $this->objectDefinitions[$name] = $objectDefinition;

    if (!empty($aliase)) {
        $this->aliases[$aliase] = $name;
    }
}

这里可以看到分别有类注解、属性注解和方法注解三类。

对应官方文档的注解说明

/**
    * @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];
        $annotationParser = $this->getAnnotationParser($classAry, $parserClassName);

        $data = $annotationParser->parse(Parser::TYPE_CLASS, $annotation);
        if (empty($data)) {
            continue;
        }

        if (count($data) !== 4) {
            throw new InvalidArgumentException(sprintf('%s annotation parse must be 4 size', $annotationClass));
        }

        [$name, $className, $scope, $alias] = $data;
        $name = empty($name) ? $className : $name;

        if (empty($className)) {
            throw new InvalidArgumentException(sprintf('%s with class name can not be empty', $annotationClass));
        }

        // Multiple coverage
        $objectDefinition = new ObjectDefinition($name, $className, $scope, $alias);
    }

    return $objectDefinition;
}

类注解,这里会调用对应解析类的parse方法。

这里以websocket-server\src\Annotation\Mapping\WsModule.phpwebsocket-server\src\Annotation\Parser\WsModuleParser.php为例。

<?php declare(strict_types=1);

namespace Swoft\WebSocket\Server\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;
use Swoft\WebSocket\Server\MessageParser\RawTextParser;

/**
 * Class WebSocket - mark an websocket module handler class
 *
 * @since 2.0
 *
 * @Annotation
 * @Target("CLASS")
 * @Attributes(
 *     @Attribute("name", type="string"),
 *     @Attribute("path", type="string"),
 *     @Attribute("controllers", type="array"),
 *     @Attribute("messageParser", type="string"),
 * )
 */
final class WsModule
{
    /**
     * Websocket route path.(it must unique in a application)
     *
     * @var string
     * @Required()
     */
    private $path = '/';

    /**
     * Module name.
     *
     * @var string
     */
    private $name = '';

    /**
     * Routing path params binding. eg. {"id"="\d+"}
     *
     * @var array
     */
    private $params = [];

    /**
     * Message controllers of the module
     *
     * @var string[]
     */
    private $controllers = [];

    /**
     * Message parser class for the module
     *
     * @var string
     */
    private $messageParser = RawTextParser::class;

    /**
     * Default message command. Format 'controller.action'
     *
     * @var string
     */
    private $defaultCommand = 'home.index';

    /**
     * Default message opcode for response. please see WEBSOCKET_OPCODE_*
     *
     * @var int
     */
    private $defaultOpcode = 0;

    /**
     * Class constructor.
     *
     * @param array $values
     */
    public function __construct(array $values)
    {
        if (isset($values['value'])) {
            $this->path = (string)$values['value'];
        } elseif (isset($values['path'])) {
            $this->path = (string)$values['path'];
        }

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

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

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

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

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

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

    /**
     * @return string
     */
    public function getPath(): string
    {
        return $this->path;
    }

    /**
     * @return string
     */
    public function getMessageParser(): string
    {
        return $this->messageParser;
    }

    /**
     * @return string
     */
    public function getDefaultCommand(): string
    {
        return $this->defaultCommand;
    }

    /**
     * @return string
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @return string[]
     */
    public function getControllers(): array
    {
        return $this->controllers;
    }

    /**
     * @return array
     */
    public function getParams(): array
    {
        return $this->params;
    }

    /**
     * @return int
     */
    public function getDefaultOpcode(): int
    {
        return $this->defaultOpcode;
    }
}

WsModule声明了一个类注解。

<?php declare(strict_types=1);

namespace Swoft\WebSocket\Server\Annotation\Parser;

use Swoft\Annotation\Annotation\Mapping\AnnotationParser;
use Swoft\Annotation\Annotation\Parser\Parser;
use Swoft\Annotation\Exception\AnnotationException;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Stdlib\Helper\Str;
use Swoft\WebSocket\Server\Annotation\Mapping\WsModule;
use Swoft\WebSocket\Server\MessageParser\RawTextParser;
use Swoft\WebSocket\Server\Router\RouteRegister;

/**
 * Class WebSocketParser
 *
 * @since 2.0
 *
 * @AnnotationParser(WsModule::class)
 */
class WsModuleParser extends Parser
{
    /**
     * Parse object
     *
     * @param int      $type Class or Method or Property
     * @param WsModule $ann  Annotation object
     *
     * @return array
     * Return empty array is nothing to do!
     * When class type return [$beanName, $className, $scope, $alias, $size] is to inject bean
     * When property type return [$propertyValue, $isRef] is to reference value
     * @throws AnnotationException
     */
    public function parse(int $type, $ann): array
    {
        if ($type !== self::TYPE_CLASS) {
            throw new AnnotationException('`@WsModule` must be defined on class!');
        }

        $class = $this->className;

        RouteRegister::bindModule($class, [
            'path'           => $ann->getPath() ?: Str::getClassName($class, 'Module'),
            'name'           => $ann->getName(),
            'params'         => $ann->getParams(),
            'class'          => $class,
            'eventMethods'   => [],
            'controllers'    => $ann->getControllers(),
            'messageParser'  => $ann->getMessageParser() ?: RawTextParser::class,
            'defaultOpcode' => $ann->getDefaultOpcode(),
            'defaultCommand' => $ann->getDefaultCommand(),
        ]);

        return [$class, $class, Bean::SINGLETON, ''];
    }
}

按上一篇文章说明,这里WsModuleParser会被标记为注解类WsModule的注解解析类。

解析注解的时候,会调用WsModuleParserparse方法,这里通过RouteRegister::bindModule做了一些路由操作,这里后续再讲,这里不做深入介绍。

属性和方法注解,也是类似的,parseAnnotations方法就讲完了。

回到Container类的init方法,接下来调用了parseDefinitions方法。

/**
    * Parse definitions
    */
private function parseDefinitions(): void
{
    $annotationParser = new DefinitionObjParser(
        $this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases
    );

    // Collect info
    $definitionData = $annotationParser->parseDefinitions();
    [$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases] = $definitionData;
}

声明了一个DefinitionObjParser对象,调用了parseDefinitions方法。

/**
    * Parse definitions
    *
    * @return array
    */
public function parseDefinitions(): array
{
    foreach ($this->definitions as $beanName => $definition) {
        if (isset($this->objectDefinitions[$beanName])) {
            $objectDefinition = $this->objectDefinitions[$beanName];
            $this->resetObjectDefinition($beanName, $objectDefinition, $definition);
            continue;
        }

        $this->createObjectDefinition($beanName, $definition);
    }

    return [$this->definitions, $this->objectDefinitions, $this->classNames, $this->aliases];
}

遍历所有的Bean对象,调用createObjectDefinition方法。

/**
    * Create object definition for definition
    *
    * @param string $beanName
    * @param array  $definition
    */
private function createObjectDefinition(string $beanName, array $definition): void
{
    $className = $definition['class'] ?? '';
    if (empty($className)) {
        throw new InvalidArgumentException(sprintf('%s key for definition must be defined class', $beanName));
    }

    $objDefinition = new ObjectDefinition($beanName, $className);
    $objDefinition = $this->updateObjectDefinitionByDefinition($objDefinition, $definition);

    $classNames   = $this->classNames[$className] ?? [];
    $classNames[] = $beanName;

    $this->classNames[$className]       = array_unique($classNames);
    $this->objectDefinitions[$beanName] = $objDefinition;
}

声明了ObjectDefinition对象,调用了updateObjectDefinitionByDefinition方法。

/**
    * Update definition
    *
    * @param ObjectDefinition $objDfn
    * @param array            $definition
    *
    * @return ObjectDefinition
    */
private function updateObjectDefinitionByDefinition(ObjectDefinition $objDfn, array $definition): ObjectDefinition
{
    [$constructInject, $propertyInjects, $option] = $this->parseDefinition($definition);

    // Set construct inject
    if (!empty($constructInject)) {
        $objDfn->setConstructorInjection($constructInject);
    }

    // Set property inject
    foreach ($propertyInjects as $propertyName => $propertyInject) {
        $objDfn->setPropertyInjection($propertyName, $propertyInject);
    }

    $scopes = [
        Bean::SINGLETON,
        Bean::PROTOTYPE,
        Bean::REQUEST,
    ];

    $scope = $option['scope'] ?? '';
    $alias = $option['alias'] ?? '';

    if (!empty($scope) && !in_array($scope, $scopes, true)) {
        throw new InvalidArgumentException('Scope for definition is not undefined');
    }

    // Update scope
    if (!empty($scope)) {
        $objDfn->setScope($scope);
    }

    // Update alias
    if (!empty($alias)) {
        $objDfn->setAlias($alias);

        $objAlias = $objDfn->getAlias();
        unset($this->aliases[$objAlias]);

        $this->aliases[$alias] = $objDfn->getName();
    }

    return $objDfn;
}

这里调用了parseDefinition方法进行解析。

/**
    * Parse definition
    *
    * @param array $definition
    *
    * @return array
    */
private function parseDefinition(array $definition): array
{
    // Remove class key
    unset($definition['class']);

    // Parse construct
    $constructArgs = $definition[0] ?? [];
    if (!is_array($constructArgs)) {
        throw new InvalidArgumentException('Construct args for definition must be array');
    }

    // Parse construct args
    $argInjects = [];
    foreach ($constructArgs as $arg) {
        [$argValue, $argIsRef] = $this->getValueByRef($arg);

        $argInjects[] = new ArgsInjection($argValue, $argIsRef);
    }

    // Set construct inject
    $constructInject = null;
    if (!empty($argInjects)) {
        $constructInject = new MethodInjection('__construct', $argInjects);
    }

    // Remove construct definition
    unset($definition[0]);

    // Parse definition option
    $option = $definition['__option'] ?? [];
    if (!is_array($option)) {
        throw new InvalidArgumentException('__option for definition must be array');
    }

    // Remove `__option`
    unset($definition['__option']);

    // Parse definition properties
    $propertyInjects = [];
    foreach ($definition as $propertyName => $propertyValue) {
        if (!is_string($propertyName)) {
            throw new InvalidArgumentException('Property key from definition must be string');
        }

        [$proValue, $proIsRef] = $this->getValueByRef($propertyValue);

        // Parse property for array
        if (is_array($proValue)) {
            $proValue = $this->parseArrayProperty($proValue);
        }

        $propertyInject = new PropertyInjection($propertyName, $proValue, $proIsRef);

        $propertyInjects[$propertyName] = $propertyInject;
    }

    return [$constructInject, $propertyInjects, $option];
}

解析__construct方法和传参,解析属性信息。

回到updateObjectDefinitionByDefinition方法,将__construct和类属性信息注册到ObjectDefinition对象上,到这里parseDefinitions方法执行完毕。

回到Container类的init方法,接下来调用了initializeBeans方法。

/**
    * Initialize beans
    *
    * @throws InvalidArgumentException
    * @throws ReflectionException
    */
private function initializeBeans(): void
{
    /* @var ObjectDefinition $objectDefinition */
    foreach ($this->objectDefinitions as $beanName => $objectDefinition) {
        $scope = $objectDefinition->getScope();
        // Exclude request
        if ($scope === Bean::REQUEST) {
            $this->requestDefinitions[$beanName] = $objectDefinition;
            unset($this->objectDefinitions[$beanName]);
            continue;
        }

        // Exclude session
        if ($scope === Bean::SESSION) {
            $this->sessionDefinitions[$beanName] = $objectDefinition;
            unset($this->objectDefinitions[$beanName]);
            continue;
        }

        // New bean
        $this->newBean($beanName);
    }
}

对于scope不为Bean::REQUESTBean::SESSION的,调用newBean方法。

/**
    * Initialize beans
    *
    * @param string $beanName
    * @param string $id
    *
    * @return object
    * @throws ReflectionException
    */
private function newBean(string $beanName, string $id = '')
{
    // First, check bean whether has been create.
    if (isset($this->singletonPool[$beanName]) || isset($this->prototypePool[$beanName])) {
        return $this->get($beanName);
    }

    // Get object definition
    $objectDefinition = $this->getNewObjectDefinition($beanName);

    $scope     = $objectDefinition->getScope();
    $alias     = $objectDefinition->getAlias();
    $className = $objectDefinition->getClassName();

    // Cache reflection class info
    Reflections::cache($className);

    // Before initialize bean
    $this->beforeInit($beanName, $className, $objectDefinition);

    $constructArgs   = [];
    $constructInject = $objectDefinition->getConstructorInjection();
    if ($constructInject !== null) {
        $constructArgs = $this->getConstructParams($constructInject, $id);
    }

    $propertyInjects = $objectDefinition->getPropertyInjections();

    // Proxy class
    if ($this->handler) {
        $className = $this->handler->classProxy($className);
    }

    $reflectionClass = new ReflectionClass($className);
    $reflectObject   = $this->newInstance($reflectionClass, $constructArgs);

    // Inject properties values
    $this->newProperty($reflectObject, $reflectionClass, $propertyInjects, $id);

    // Alias
    if (!empty($alias)) {
        $this->aliases[$alias] = $beanName;
    }

    // Call init method if exist
    if ($reflectionClass->hasMethod(self::INIT_METHOD)) {
        $reflectObject->{self::INIT_METHOD}();
    }

    return $this->setNewBean($beanName, $scope, $reflectObject, $id);
}

通过反射实例化Bean对应的类,注册对应的属性。

如果类存在self::INIT_METHOD方法,执行此方法。

/**
    * @param string $beanName
    * @param string $scope
    * @param object $object
    * @param string $id
    *
    * @return object
    */
private function setNewBean(string $beanName, string $scope, $object, string $id = '')
{
    switch ($scope) {
        case Bean::SINGLETON: // Singleton
            $this->singletonPool[$beanName] = $object;
            break;
        case Bean::PROTOTYPE:
            $this->prototypePool[$beanName] = $object;
            // Clone it
            $object = clone $object;
            break;
        case Bean::REQUEST:
            $this->requestPool[$id][$beanName] = $object;
            break;
        case Bean::SESSION:
            $this->sessionPool[$id][$beanName] = $object;
            break;
    }

    return $object;
}

setNewBean方法,根据对应的scope信息,将实例化后的反射类注册到对应的类属性上。

到这里BeanProcessor类就执行完了。