看源码时结合swoft源码,边看边打断点查看打印的信息,参考 https://www.jianshu.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;
}
- 加载
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属性上
}
-
$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打印
-
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']; } }
-
$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;
}
-
$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
网友评论