更新hyperf
This commit is contained in:
parent
9f80c71d42
commit
e84acb95b9
@ -13,8 +13,8 @@ export default defineConfig({
|
||||
tipLabel: '提示',
|
||||
warningLabel: '警告',
|
||||
dangerLabel: '危险',
|
||||
infoLabel: '信息',
|
||||
detailsLabel: '详细信息'
|
||||
infoLabel: 'info',
|
||||
detailsLabel: '点我查看'
|
||||
},
|
||||
image: {
|
||||
// 默认禁用图片懒加载
|
||||
|
@ -16,6 +16,10 @@ export default [
|
||||
{ text: '路由寻址', link: hyperf + '路由寻址' },
|
||||
{ text: '中间件', link: hyperf + '中间件' },
|
||||
{ text: '响应', link: hyperf + '响应' },
|
||||
{ text: '附录1 container的get方法', link: hyperf + '附录1 container的get方法' },
|
||||
{ text: '附录2 注解命令获取', link: hyperf + '附录2 注解命令获取' },
|
||||
{ text: '附录3 命令注册Application', link: hyperf + '附录3 命令注册Application' },
|
||||
{ text: '附录4 Application Run方法', link: hyperf + '附录4 Application_Run' },
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
title: 初始化容器类
|
||||
---
|
||||
# 初始化容器类
|
||||
|
||||
本节分析容器类的实例化过程,
|
||||
|
||||
```php
|
||||
@ -28,10 +29,14 @@ return ApplicationContext::setContainer($container);
|
||||
|
||||
```
|
||||
|
||||
::: tip
|
||||
首先,可以看出,先是实例化了`DefinitionSourceFactory`类,然后使用方法的调用方式调用类,会触发该类的`__invoke`方法,最后返回的内容作为`Container`类的构造函数的参数,然后返回实例化后的容器类。
|
||||
:::
|
||||
|
||||
### 生成包含依赖关系和配置的定义源
|
||||
|
||||
查看`DefinitionSourceFactory`工厂类做了哪些工作,
|
||||
|
||||
### DefinitionSourceFactory类
|
||||
先看该类做了那些工作,
|
||||
```php
|
||||
declare(strict_types=1);
|
||||
namespace Hyperf\Di\Definition;
|
||||
@ -43,7 +48,6 @@ class DefinitionSourceFactory
|
||||
{
|
||||
public function __invoke(): DefinitionSource
|
||||
{
|
||||
// 常量不存在,报错
|
||||
if (! defined('BASE_PATH')) {
|
||||
throw new Exception('BASE_PATH is not defined.');
|
||||
}
|
||||
@ -51,10 +55,9 @@ class DefinitionSourceFactory
|
||||
// 该方法上面一节分析过,获取所有的服务提供者返回的配置信息数组
|
||||
$configFromProviders = [];
|
||||
if (class_exists(ProviderConfig::class)) {
|
||||
$configFromProviders = ProviderConfig::load();
|
||||
$configFromProviders = :ProviderConfig:load();
|
||||
}
|
||||
|
||||
// 同理,这里会获取配置文件中的依赖配置信息,如果存在相同的key,则覆盖掉
|
||||
$serverDependencies = $configFromProviders['dependencies'] ?? [];
|
||||
$dependenciesPath = BASE_PATH . '/config/autoload/dependencies.php';
|
||||
if (file_exists($dependenciesPath)) {
|
||||
@ -65,11 +68,69 @@ class DefinitionSourceFactory
|
||||
return new DefinitionSource($serverDependencies);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
只有一个方法,也是会被调用的`__invoke`方法,获取依赖信息,并作为参数传递给`DefinitionSource`用作实例化参数。
|
||||
|
||||
`$serverDependencies`数组具体内容如下,
|
||||
|
||||
:::details
|
||||
|
||||
```php
|
||||
Array
|
||||
(
|
||||
[Psr\SimpleCache\CacheInterface] => Hyperf\Cache\Cache
|
||||
[Hyperf\Contract\ConfigInterface] => Hyperf\Config\ConfigFactory
|
||||
[Hyperf\DbConnection\Pool\PoolFactory] => Hyperf\DbConnection\Pool\PoolFactory
|
||||
[Hyperf\Database\Connectors\ConnectionFactory] => Hyperf\Database\Connectors\ConnectionFactory
|
||||
[Hyperf\Database\ConnectionResolverInterface] => Hyperf\DbConnection\ConnectionResolver
|
||||
[db.connector.mysql] => Hyperf\Database\Connectors\MySqlConnector
|
||||
[Hyperf\Database\Migrations\MigrationRepositoryInterface] => Hyperf\DbConnection\DatabaseMigrationRepositoryFactory
|
||||
[Hyperf\Di\MethodDefinitionCollectorInterface] => Hyperf\Di\MethodDefinitionCollector
|
||||
[Hyperf\Di\ClosureDefinitionCollectorInterface] => Hyperf\Di\ClosureDefinitionCollector
|
||||
[Hyperf\Engine\Contract\Socket\SocketFactoryInterface] => Hyperf\Engine\Socket\SocketFactory
|
||||
[Psr\EventDispatcher\ListenerProviderInterface] => Hyperf\Event\ListenerProviderFactory
|
||||
[Psr\EventDispatcher\EventDispatcherInterface] => Hyperf\Event\EventDispatcherFactory
|
||||
[Hyperf\ExceptionHandler\Formatter\FormatterInterface] => Hyperf\ExceptionHandler\Formatter\DefaultFormatter
|
||||
[League\Flysystem\Filesystem] => Hyperf\Filesystem\FilesystemInvoker
|
||||
[Hyperf\Contract\ApplicationInterface] => Hyperf\Framework\ApplicationFactory
|
||||
[Hyperf\Contract\StdoutLoggerInterface] => Hyperf\Framework\Logger\StdoutLogger
|
||||
[Hyperf\HttpMessage\Server\RequestParserInterface] => Hyperf\HttpMessage\Server\Request\Parser
|
||||
[Hyperf\HttpServer\Contract\RequestInterface] => Hyperf\HttpServer\Request
|
||||
[Hyperf\HttpServer\Contract\ResponseInterface] => Hyperf\HttpServer\Response
|
||||
[Psr\Http\Message\ServerRequestInterface] => Hyperf\HttpServer\Request
|
||||
[Psr\Log\LoggerInterface] => Closure Object
|
||||
(
|
||||
[this] => Hyperf\Logger\ConfigProvider Object
|
||||
(
|
||||
)
|
||||
|
||||
[parameter] => Array
|
||||
(
|
||||
[$container] => <required>
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
[Redis] => Hyperf\Redis\Redis
|
||||
[Symfony\Component\Serializer\Serializer] => Hyperf\Serializer\SerializerFactory
|
||||
[Hyperf\Contract\NormalizerInterface] => Hyperf\Serializer\SimpleNormalizer
|
||||
[Swoole\Server] => Hyperf\Server\SwooleServerFactory
|
||||
[Hyperf\Contract\TranslatorLoaderInterface] => Hyperf\Translation\FileLoaderFactory
|
||||
[Hyperf\Contract\TranslatorInterface] => Hyperf\Translation\TranslatorFactory
|
||||
[Hyperf\Validation\Contract\PresenceVerifierInterface] => Hyperf\Validation\DatabasePresenceVerifierFactory
|
||||
[Hyperf\Validation\Contract\ValidatorFactoryInterface] => Hyperf\Validation\ValidatorFactoryFactory
|
||||
)
|
||||
|
||||
```
|
||||
只有一个方法,也是会被调用的`__invoke`方法。这里返回实例化的`DefinitionSource`对象。
|
||||
### DefinitionSource类
|
||||
|
||||
:::
|
||||
可以看出,基本上是接口绑定对应的实现类。
|
||||
|
||||
#### 将抽象接口或类绑定到其对应的实现类或实例
|
||||
|
||||
> `DefinitionSource类`是管理容器中的绑定关系,即将抽象接口或类绑定到其对应的实现类或实例,来看源码。
|
||||
>
|
||||
```php
|
||||
namespace Hyperf\Di\Definition;
|
||||
|
||||
@ -85,18 +146,18 @@ use function method_exists;
|
||||
|
||||
class DefinitionSource implements DefinitionSourceInterface
|
||||
{
|
||||
protected array $source;
|
||||
protected array $source; // [!code focus]
|
||||
|
||||
public function __construct(array $source)
|
||||
public function __construct(array $source) // [!code focus:5]
|
||||
{
|
||||
// source 是服务提供者配置文件中的依赖配置信息
|
||||
$this->source = $this->normalizeSource($source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the user definition source to a standard definition source.
|
||||
* 将用户定义的源转换为标准源
|
||||
*/
|
||||
protected function normalizeSource(array $source): array
|
||||
protected function normalizeSource(array $source): array // [!code focus:11]
|
||||
{
|
||||
$definitions = [];
|
||||
foreach ($source as $identifier => $definition) {
|
||||
@ -107,8 +168,7 @@ class DefinitionSource implements DefinitionSourceInterface
|
||||
}
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
/**
|
||||
/** // [!code focus:23]
|
||||
* 绑定的内容可以是数组、类名、闭包
|
||||
* @param array|callable|string $definition
|
||||
*/
|
||||
@ -119,12 +179,14 @@ class DefinitionSource implements DefinitionSourceInterface
|
||||
}
|
||||
|
||||
if (is_string($definition) && class_exists($definition)) {
|
||||
// 如果对应实现类存在__invoke方法,则包装为FactoryDefinition
|
||||
if (method_exists($definition, '__invoke')) {
|
||||
return new FactoryDefinition($identifier, $definition, []);
|
||||
}
|
||||
// 普通实现则调用autowire自动装配方法
|
||||
return $this->autowire($identifier, new ObjectDefinition($identifier, $definition));
|
||||
}
|
||||
|
||||
//如果是闭包,也包装为FactoryDefinition类
|
||||
if (is_callable($definition)) {
|
||||
return new FactoryDefinition($identifier, $definition, []);
|
||||
}
|
||||
@ -134,7 +196,7 @@ class DefinitionSource implements DefinitionSourceInterface
|
||||
/**
|
||||
* 根据类名自动完成构造函数的依赖注入
|
||||
*/
|
||||
protected function autowire(string $name, ObjectDefinition $definition = null): ?ObjectDefinition
|
||||
protected function autowire(string $name, ObjectDefinition $definition = null): ?ObjectDefinition // [!code focus:21]
|
||||
{
|
||||
$className = $definition ? $definition->getClassName() : $name;
|
||||
if (! class_exists($className) && ! interface_exists($className)) {
|
||||
@ -143,9 +205,7 @@ class DefinitionSource implements DefinitionSourceInterface
|
||||
|
||||
$definition = $definition ?: new ObjectDefinition($name);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
// 获取类反射,根据类的构造函数,自动注入依赖
|
||||
$class = ReflectionManager::reflectClass($className);
|
||||
$constructor = $class->getConstructor();
|
||||
if ($constructor && $constructor->isPublic()) {
|
||||
@ -158,24 +218,32 @@ class DefinitionSource implements DefinitionSourceInterface
|
||||
}
|
||||
|
||||
```
|
||||
> `normalizeSource`方法会将绑定的内容进行标准化,标准化后的内容是`DefinitionInterface`接口的实现类。(意思就是将绑定的`key`和`value`转换成`DefinitionInterface`接口的实现类)
|
||||
|
||||
最终,将绑定和对应的实现类保存到`$this->source`属性中。
|
||||
::: tip
|
||||
`normalizeSource`方法会将绑定的内容进行标准化,标准化后的内容是`DefinitionInterface`接口的实现类。(意思就是将绑定的`key`和`value`转换成`DefinitionInterface`接口的实现类)
|
||||
:::
|
||||
|
||||
实际上,这里`(new DefinitionSourceFactory())()`执行过后返回的是一个`DefinitionSource`对象,该对象中保存了所有的绑定关系。
|
||||
最终,将绑定和对应的实现类保存到`$this->source`属性中,返回自身实例。
|
||||
|
||||
> 实际上,这里`(new DefinitionSourceFactory())()`执行过后返回的是一个`DefinitionSource`对象,该对象中保存了所有的绑定关系。
|
||||
|
||||
### Container实例化
|
||||
|
||||
容器参数上面已经初始化完成,接下来进行容器的实例化。
|
||||
|
||||
然后继续往下看,
|
||||
```php
|
||||
$container = new Container((new DefinitionSourceFactory())());
|
||||
```
|
||||
> 这里返回`DefinitionSource`对象后,再调用`new Container`方法,传入`DefinitionSource`对象,返回`Container`对象。
|
||||
|
||||
> 参数是`DefinitionSource`对象
|
||||
|
||||
查看`Container`类的构造函数源码,
|
||||
|
||||
```php
|
||||
public function __construct(protected Definition\DefinitionSourceInterface $definitionSource)
|
||||
{
|
||||
$this->definitionResolver = new ResolverDispatcher($this);
|
||||
// Auto-register the container.
|
||||
// 将自身实例放到已解析绑定的数组中
|
||||
$this->resolvedEntries = [
|
||||
self::class => $this,
|
||||
PsrContainerInterface::class => $this,
|
||||
@ -183,12 +251,121 @@ public function __construct(protected Definition\DefinitionSourceInterface $defi
|
||||
];
|
||||
}
|
||||
```
|
||||
总结:
|
||||
|
||||
构造函数所做的事情:
|
||||
|
||||
1. 将传入的`$definitionSource`对象放到`$this->definitionSource`属性中。
|
||||
2. 创建`ResolverDispatcher`解析器,调度不同类型的解析器来处理容器中的定义,它会根据定义的类型(如类、工厂函数等)来选择适当的解析器进行解析
|
||||
3. 注册容器本身的实例
|
||||
::: details
|
||||
|
||||
```php
|
||||
namespace Hyperf\Di\Resolver;
|
||||
|
||||
use Hyperf\Di\Definition\DefinitionInterface;
|
||||
use Hyperf\Di\Definition\FactoryDefinition;
|
||||
use Hyperf\Di\Definition\ObjectDefinition;
|
||||
use Hyperf\Di\Definition\SelfResolvingDefinitionInterface;
|
||||
use Hyperf\Di\Exception\InvalidDefinitionException;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use RuntimeException;
|
||||
|
||||
class ResolverDispatcher implements ResolverInterface
|
||||
{
|
||||
protected ?ObjectResolver $objectResolver = null;
|
||||
|
||||
protected ?FactoryResolver $factoryResolver = null;
|
||||
// 实例化传入容器对象
|
||||
public function __construct(private ContainerInterface $container) // [!code focus:3]
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析definition对应的类实现或执行闭包
|
||||
*
|
||||
* @param DefinitionInterface $definition object that defines how the value should be obtained
|
||||
* @param array $parameters optional parameters to use to build the entry
|
||||
* @return mixed value obtained from the definition
|
||||
* @throws InvalidDefinitionException if the definition cannot be resolved
|
||||
*/
|
||||
public function resolve(DefinitionInterface $definition, array $parameters = []) // [!code focus:15]
|
||||
{
|
||||
if ($definition instanceof SelfResolvingDefinitionInterface) {
|
||||
return $definition->resolve($this->container);
|
||||
}
|
||||
|
||||
$guard = DepthGuard::getInstance();
|
||||
|
||||
return $guard->call(
|
||||
$definition->getName(),
|
||||
fn () => $this->getDefinitionResolver($definition)->resolve($definition, $parameters)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断一个definition能否解析
|
||||
*
|
||||
* @param DefinitionInterface $definition object that defines how the value should be obtained
|
||||
* @param array $parameters optional parameters to use to build the entry
|
||||
*/
|
||||
public function isResolvable(DefinitionInterface $definition, array $parameters = []): bool
|
||||
{
|
||||
if ($definition instanceof SelfResolvingDefinitionInterface) {
|
||||
return $definition->isResolvable($this->container);
|
||||
}
|
||||
|
||||
$guard = DepthGuard::getInstance();
|
||||
|
||||
return $guard->call(
|
||||
$definition->getName(),
|
||||
fn () => $this->getDefinitionResolver($definition)->isResolvable($definition, $parameters)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据definition类型,返回要使用的解析器
|
||||
*
|
||||
* @throws RuntimeException no definition resolver was found for this type of definition
|
||||
*/
|
||||
private function getDefinitionResolver(DefinitionInterface $definition): ResolverInterface // [!code focus:8]
|
||||
{
|
||||
return match (true) {
|
||||
$definition instanceof ObjectDefinition => $this->objectResolver ??= new ObjectResolver($this->container, $this),
|
||||
$definition instanceof FactoryDefinition => $this->factoryResolver ??= new FactoryResolver($this->container, $this),
|
||||
default => throw new RuntimeException('No definition resolver was configured for definition of type ' . get_class($definition)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
:::
|
||||
3. 注册容器本身的实例
|
||||
::: details
|
||||
|
||||
```php
|
||||
$this->resolvedEntries = [
|
||||
self::class => $this,
|
||||
PsrContainerInterface::class => $this,
|
||||
HyperfContainerInterface::class => $this,
|
||||
];
|
||||
```
|
||||
|
||||
:::
|
||||
到此,容器实例化完成。
|
||||
|
||||
### ApplicationContext 类
|
||||
|
||||
```php
|
||||
$container = new Container((new DefinitionSourceFactory())());
|
||||
|
||||
return ApplicationContext::setContainer($container); // [!code focus]
|
||||
```
|
||||
|
||||
最后,将容器实例保存到`ApplicationContext`类的属性中。
|
||||
后续在代码中获取容器类可以使用`ApplicationContext::getContainer()`方法获取容器实例。
|
||||
|
||||
## 总结
|
||||
|
||||
### 总结
|
||||
1. 首先创建一个`DefinitionSourceFactory`工厂实例,并且触发该工厂实例的`__invoke`方法
|
||||
2. `__invoke`方法中,首先加载配置文件包括类映射和依赖配置
|
||||
3. 跟`config`目录下`dependencies.php`文件中的依赖配置进行合并
|
||||
|
1458
src/hyperf/启动服务.md
1458
src/hyperf/启动服务.md
File diff suppressed because it is too large
Load Diff
191
src/hyperf/附录1 container的get方法.md
Normal file
191
src/hyperf/附录1 container的get方法.md
Normal file
@ -0,0 +1,191 @@
|
||||
---
|
||||
title: 附录1 container的get方法
|
||||
---
|
||||
|
||||
分析一下`get`方法源码,
|
||||
|
||||
```php
|
||||
public function get($id)
|
||||
{
|
||||
// 如果解析过,则直接返回
|
||||
if (isset($this->resolvedEntries[$id]) || array_key_exists($id, $this->resolvedEntries)) {
|
||||
return $this->resolvedEntries[$id];
|
||||
}
|
||||
return $this->resolvedEntries[$id] = $this->make($id);
|
||||
}
|
||||
```
|
||||
|
||||
上面可以看出,首次解析不走`if`,查看`make`方法,
|
||||
|
||||
```php
|
||||
public function make(string $name, array $parameters = [])
|
||||
{
|
||||
$definition = $this->getDefinition($name);
|
||||
if (! $definition) {
|
||||
throw new NotFoundException("No entry or class found for '{$name}'");
|
||||
}
|
||||
|
||||
return $this->resolveDefinition($definition, $parameters);
|
||||
}
|
||||
```
|
||||
|
||||
前面绑定阶段,已经将`ApplicationInterface`绑定到`Hyperf\Framework\ApplicationFactory`类上,通过`DefinitionSource`类,将对应实现转换成对应`DefinitionInterface`接口的实现类。
|
||||
|
||||
```php
|
||||
// 调用逻辑
|
||||
public function __construct(array $source)
|
||||
{
|
||||
$this->source = $this->normalizeSource($source);
|
||||
}
|
||||
|
||||
protected function normalizeSource(array $source): array
|
||||
{
|
||||
$definitions = [];
|
||||
foreach ($source as $identifier => $definition) {
|
||||
$normalizedDefinition = $this->normalizeDefinition($identifier, $definition);
|
||||
if (! is_null($normalizedDefinition)) {
|
||||
$definitions[$identifier] = $normalizedDefinition;
|
||||
}
|
||||
}
|
||||
return $definitions;
|
||||
}
|
||||
|
||||
protected function normalizeDefinition(string $identifier, $definition): ?DefinitionInterface
|
||||
{
|
||||
if ($definition instanceof PriorityDefinition) {
|
||||
$definition = $definition->getDefinition();
|
||||
}
|
||||
|
||||
|
||||
if (is_string($definition) && class_exists($definition)) {
|
||||
if (method_exists($definition, '__invoke')) {
|
||||
return new FactoryDefinition($identifier, $definition, []);
|
||||
}
|
||||
return $this->autowire($identifier, new ObjectDefinition($identifier, $definition));
|
||||
}
|
||||
|
||||
if (is_callable($definition)) {
|
||||
return new FactoryDefinition($identifier, $definition, []);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
查看`ApplicationFactory`类,存在`__invoke`方法,返回的是一个`FactoryDefinition`对象。
|
||||
![](image <https://oss.xiaokeaii.top/2024/applicationFactory.png>)
|
||||
|
||||
```php
|
||||
$definition = $this->getDefinition($name);
|
||||
```
|
||||
|
||||
所以这行返回的是一个`FactoryDefinition`对象。打印结果如下,
|
||||
![](image <https://oss.xiaokeaii.top/2024/工厂定义.png> )
|
||||
|
||||
#### resolveDefinition方法
|
||||
|
||||
继续分析,查看`resolveDefinition`方法,
|
||||
> 文件位置: /vendor/hyperf/di/src/Container.php
|
||||
|
||||
```php
|
||||
protected function resolveDefinition(DefinitionInterface $definition, array $parameters = [])
|
||||
{
|
||||
return $this->definitionResolver->resolve($definition, $parameters);
|
||||
}
|
||||
```
|
||||
|
||||
上面可以看到,这里调用了解析调度器`ResolverDispatcher`的`resolve`方法。
|
||||
|
||||
##### resolve方法
|
||||
>
|
||||
> 文件位置:/vendor/hyperf/di/src/Resolver/ResolverDispatcher.php
|
||||
|
||||
```php
|
||||
public function resolve(DefinitionInterface $definition, array $parameters = [])
|
||||
{
|
||||
if ($definition instanceof SelfResolvingDefinitionInterface) {
|
||||
|
||||
return $definition->resolve($this->container);
|
||||
}
|
||||
$guard = DepthGuard::getInstance();
|
||||
|
||||
return $guard->call(
|
||||
$definition->getName(),
|
||||
fn () => $this->getDefinitionResolver($definition)->resolve($definition, $parameters)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
这里`FactoryDefinition`不是`SelfResolvingDefinitionInterface`接口的实现类,所以不走`if`。
|
||||
`$guard`是`DepthGuard`类的实例,调用`call`方法,
|
||||
|
||||
###### call方法
|
||||
>
|
||||
> 文件位置:/vendor/hyperf/di/src/Resolver/DepthGuard.php
|
||||
|
||||
```php
|
||||
public function call(string $name, callable $callable)
|
||||
{
|
||||
try {
|
||||
$this->increment();
|
||||
return $callable();
|
||||
} catch (CircularDependencyException $exception) {
|
||||
$exception->addDefinitionName($name);
|
||||
throw $exception;
|
||||
} finally {
|
||||
$this->decrement();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这里最终执行传入的闭包,回到上面,执行闭包。
|
||||
查看闭包执行源码,
|
||||
> ./vendor/hyperf/di/src/Resolver/ResolverDispatcher.php
|
||||
>
|
||||
##### getDefinitionResolver方法
|
||||
|
||||
```php
|
||||
private function getDefinitionResolver(DefinitionInterface $definition): ResolverInterface
|
||||
{
|
||||
return match (true) {
|
||||
$definition instanceof ObjectDefinition => $this->objectResolver ??= new ObjectResolver($this->container, $this),
|
||||
$definition instanceof FactoryDefinition => $this->factoryResolver ??= new FactoryResolver($this->container, $this),
|
||||
default => throw new RuntimeException('No definition resolver was configured for definition of type ' . get_class($definition)),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
从代码可知,这里会根据`DefinitionInterface`的实现类,将对应解析器赋值给成员变量。
|
||||
这里传进来的是`FactoryDefinition`类,所以会执行第二条。
|
||||
紧接着调用`FactoryResolver`类的`resolve`方法。
|
||||
|
||||
###### resolve方法
|
||||
>
|
||||
> 文件位置:/vendor/hyperf/di/src/Resolver/FactoryResolver.php
|
||||
|
||||
```php
|
||||
public function resolve(DefinitionInterface $definition, array $parameters = [])
|
||||
{
|
||||
$callable = null;
|
||||
try {
|
||||
$callable = $definition->getFactory();
|
||||
if (! method_exists($callable, '__invoke')) {
|
||||
throw new NotCallableException();
|
||||
}
|
||||
if (is_string($callable)) {
|
||||
$callable = $this->container->get($callable);
|
||||
}
|
||||
return $callable($this->container, $parameters);
|
||||
} catch (NotCallableException $e) {
|
||||
// Custom error message to help debugging
|
||||
if (is_string($callable) && class_exists($callable) && method_exists($callable, '__invoke')) {
|
||||
throw new InvalidDefinitionException(sprintf('Entry "%s" cannot be resolved: factory %s. Invokable classes cannot be automatically resolved if autowiring is disabled on the container, you need to enable autowiring or define the entry manually.', $definition->getName(), $e->getMessage()));
|
||||
}
|
||||
|
||||
throw new InvalidDefinitionException(sprintf('Entry "%s" cannot be resolved: factory %s', $definition->getName(), $e->getMessage()));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
分析该方法,`$callable`是工厂的类名,如果传递过来的是字符串,则通过容器解析出对应的类,方法同理,不再赘述。
|
||||
最终调用该工厂类的`__invoke`方法返回解析结果。
|
3
src/hyperf/附录2 注解命令获取.md
Normal file
3
src/hyperf/附录2 注解命令获取.md
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
title: 附录2 注解命令获取
|
||||
---
|
65
src/hyperf/附录3 命令注册Application.md
Normal file
65
src/hyperf/附录3 命令注册Application.md
Normal file
@ -0,0 +1,65 @@
|
||||
---
|
||||
title: 附录3 命令注册Application
|
||||
---
|
||||
|
||||
### 注册命令
|
||||
|
||||
查看`Application`类的`add`方法,
|
||||
|
||||
```php
|
||||
public function add(Command $command)
|
||||
{
|
||||
$this->init();
|
||||
|
||||
$command->setApplication($this);
|
||||
|
||||
if (!$command->isEnabled()) {
|
||||
$command->setApplication(null);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$command instanceof LazyCommand) {
|
||||
// Will throw if the command is not correctly initialized.
|
||||
$command->getDefinition();
|
||||
}
|
||||
|
||||
if (!$command->getName()) {
|
||||
throw new LogicException(sprintf('The command defined in "%s" cannot have an empty name.', get_debug_type($command)));
|
||||
}
|
||||
// 重点看这行,将命令的名称以及对应实现添加到命令属性中
|
||||
$this->commands[$command->getName()] = $command;
|
||||
|
||||
foreach ($command->getAliases() as $alias) {
|
||||
$this->commands[$alias] = $command;
|
||||
}
|
||||
return $command;
|
||||
}
|
||||
```
|
||||
|
||||
打印配置的命令数组,打开第一个`/vendor/hyperf/server/src/Command/StartServer.php`命令类,
|
||||
![](image <https://oss.xiaokeaii.top/2024/start.png>)
|
||||
> 我们可以发现,在父类的构造方法中,会将`name`赋值给该命令类的成员变量,之后通过`getName`方法获取命令的名称
|
||||
|
||||
```php
|
||||
public function __construct(private ContainerInterface $container)
|
||||
{
|
||||
parent::__construct('start');
|
||||
$this->setDescription('Start hyperf servers.');
|
||||
}
|
||||
// StartServer的父类的构造方法
|
||||
public function __construct(?string $name = null)
|
||||
{
|
||||
$this->definition = new InputDefinition();
|
||||
//...
|
||||
if (null !== $name) {
|
||||
$this->setName($name);
|
||||
}
|
||||
//...
|
||||
}
|
||||
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
```
|
408
src/hyperf/附录4 Application_Run.md
Normal file
408
src/hyperf/附录4 Application_Run.md
Normal file
@ -0,0 +1,408 @@
|
||||
---
|
||||
title: 附录4 Application_Run
|
||||
---
|
||||
```php
|
||||
(function () {
|
||||
Hyperf\Di\ClassLoader::init();
|
||||
/** @var Psr\Container\ContainerInterface $container */
|
||||
$container = require BASE_PATH . '/config/container.php';
|
||||
|
||||
$application = $container->get(Hyperf\Contract\ApplicationInterface::class);
|
||||
$application->run(); // [!code focus]
|
||||
})();
|
||||
```
|
||||
|
||||
从上面分析可知,`$application`是一个`Symfony\Component\Console\Application`对象,调用`run`方法。
|
||||
先看源码,
|
||||
|
||||
```php
|
||||
public function run(?InputInterface $input = null, ?OutputInterface $output = null): int
|
||||
{
|
||||
// 设置终端的高度和宽度
|
||||
if (\function_exists('putenv')) {
|
||||
@putenv('LINES='.$this->terminal->getHeight());
|
||||
@putenv('COLUMNS='.$this->terminal->getWidth());
|
||||
}
|
||||
// 初始化输入和输出对象,如果没有提供输入和输出对象,则创建默认
|
||||
$input ??= new ArgvInput();
|
||||
$output ??= new ConsoleOutput();
|
||||
// 渲染异常的闭包函数
|
||||
$renderException = function (\Throwable $e) use ($output) {
|
||||
if ($output instanceof ConsoleOutputInterface) {
|
||||
$this->renderThrowable($e, $output->getErrorOutput());
|
||||
} else {
|
||||
$this->renderThrowable($e, $output);
|
||||
}
|
||||
};
|
||||
if ($phpHandler = set_exception_handler($renderException)) {
|
||||
restore_exception_handler();
|
||||
if (!\is_array($phpHandler) || !$phpHandler[0] instanceof ErrorHandler) {
|
||||
$errorHandler = true;
|
||||
} elseif ($errorHandler = $phpHandler[0]->setExceptionHandler($renderException)) {
|
||||
$phpHandler[0]->setExceptionHandler($errorHandler);
|
||||
}
|
||||
}
|
||||
// 根据输入参数和环境变量配置输入和输出对象
|
||||
$this->configureIO($input, $output);
|
||||
|
||||
try {
|
||||
$exitCode = $this->doRun($input, $output);
|
||||
} catch (\Throwable $e) {
|
||||
if ($e instanceof \Exception && !$this->catchExceptions) {
|
||||
throw $e;
|
||||
}
|
||||
if (!$e instanceof \Exception && !$this->catchErrors) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$renderException($e);
|
||||
|
||||
$exitCode = $e->getCode();
|
||||
if (is_numeric($exitCode)) {
|
||||
$exitCode = (int) $exitCode;
|
||||
if ($exitCode <= 0) {
|
||||
$exitCode = 1;
|
||||
}
|
||||
} else {
|
||||
$exitCode = 1;
|
||||
}
|
||||
} finally {
|
||||
// if the exception handler changed, keep it
|
||||
// otherwise, unregister $renderException
|
||||
if (!$phpHandler) {
|
||||
if (set_exception_handler($renderException) === $renderException) {
|
||||
restore_exception_handler();
|
||||
}
|
||||
restore_exception_handler();
|
||||
} elseif (!$errorHandler) {
|
||||
$finalHandler = $phpHandler[0]->setExceptionHandler(null);
|
||||
if ($finalHandler !== $renderException) {
|
||||
$phpHandler[0]->setExceptionHandler($finalHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->autoExit) {
|
||||
if ($exitCode > 255) {
|
||||
$exitCode = 255;
|
||||
}
|
||||
|
||||
exit($exitCode);
|
||||
}
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
```
|
||||
|
||||
#### doRun方法
|
||||
>
|
||||
> 文件位置:/vendor/symfony/console/Application.php
|
||||
|
||||
```php
|
||||
public function doRun(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// 输出版本信息
|
||||
if (true === $input->hasParameterOption(['--version', '-V'], true)) {
|
||||
$output->writeln($this->getLongVersion());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
// Makes ArgvInput::getFirstArgument() able to distinguish an option from an argument.
|
||||
$input->bind($this->getDefinition());
|
||||
} catch (ExceptionInterface) {
|
||||
// Errors must be ignored, full binding/validation happens later when the command is known.
|
||||
}
|
||||
|
||||
// 获取命令名称,项目启动代码是 php bin/hyperf.php start,
|
||||
// 所以命令名称就是 start
|
||||
$name = $this->getCommandName($input);
|
||||
// 如果命令名称是--help,则显示帮助信息
|
||||
if (true === $input->hasParameterOption(['--help', '-h'], true)) {
|
||||
if (!$name) {
|
||||
$name = 'help';
|
||||
$input = new ArrayInput(['command_name' => $this->defaultCommand]);
|
||||
} else {
|
||||
$this->wantHelps = true;
|
||||
}
|
||||
}
|
||||
// 如果命令名称不存在,则显示默认信息
|
||||
if (!$name) {
|
||||
$name = $this->defaultCommand;
|
||||
$definition = $this->getDefinition();
|
||||
$definition->setArguments(array_merge(
|
||||
$definition->getArguments(),
|
||||
[
|
||||
'command' => new InputArgument('command', InputArgument::OPTIONAL, $definition->getArgument('command')->getDescription(), $name),
|
||||
]
|
||||
));
|
||||
}
|
||||
|
||||
try {
|
||||
$this->runningCommand = null;
|
||||
// 从应用中找到对应的命令
|
||||
$command = $this->find($name);
|
||||
} catch (\Throwable $e) {
|
||||
// 找不到的时候的异常处理
|
||||
if (($e instanceof CommandNotFoundException && !$e instanceof NamespaceNotFoundException) && 1 === \count($alternatives = $e->getAlternatives()) && $input->isInteractive()) {
|
||||
$alternative = $alternatives[0];
|
||||
|
||||
$style = new SymfonyStyle($input, $output);
|
||||
$output->writeln('');
|
||||
$formattedBlock = (new FormatterHelper())->formatBlock(sprintf('Command "%s" is not defined.', $name), 'error', true);
|
||||
$output->writeln($formattedBlock);
|
||||
if (!$style->confirm(sprintf('Do you want to run "%s" instead? ', $alternative), false)) {
|
||||
if (null !== $this->dispatcher) {
|
||||
$event = new ConsoleErrorEvent($input, $output, $e);
|
||||
$this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
|
||||
|
||||
return $event->getExitCode();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$command = $this->find($alternative);
|
||||
} else {
|
||||
if (null !== $this->dispatcher) {
|
||||
$event = new ConsoleErrorEvent($input, $output, $e);
|
||||
$this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
|
||||
|
||||
if (0 === $event->getExitCode()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$e = $event->getError();
|
||||
}
|
||||
|
||||
try {
|
||||
if ($e instanceof CommandNotFoundException && $namespace = $this->findNamespace($name)) {
|
||||
$helper = new DescriptorHelper();
|
||||
$helper->describe($output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output, $this, [
|
||||
'format' => 'txt',
|
||||
'raw_text' => false,
|
||||
'namespace' => $namespace,
|
||||
'short' => false,
|
||||
]);
|
||||
|
||||
return isset($event) ? $event->getExitCode() : 1;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
} catch (NamespaceNotFoundException) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果commend继承LazyCommand类,则属于懒加载命令
|
||||
// 延迟实例化命令对象,以提高应用程序启动速度
|
||||
if ($command instanceof LazyCommand) {
|
||||
$command = $command->getCommand();
|
||||
}
|
||||
// 将运行的命令赋值给runningCommand属性
|
||||
$this->runningCommand = $command;
|
||||
// 执行命令
|
||||
$exitCode = $this->doRunCommand($command, $input, $output);
|
||||
$this->runningCommand = null;
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
```
|
||||
|
||||
##### doRunCommand方法
|
||||
|
||||
这里正式进行命令的执行操作; 我们的启动命令是`start`,我们就以`start`命令为例,该命令对应的类是`StartServer`。
|
||||
|
||||
> 文件位置:/vendor/symfony/console/Application.php
|
||||
|
||||
```php
|
||||
protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// 循环命令的帮助器集合,初始化时未配置,默认是有4个
|
||||
// return new HelperSet([
|
||||
// new FormatterHelper(),
|
||||
// new DebugFormatterHelper(),
|
||||
// new ProcessHelper(),
|
||||
// new QuestionHelper(),
|
||||
// ]);
|
||||
foreach ($command->getHelperSet() as $helper) {
|
||||
if ($helper instanceof InputAwareInterface) {
|
||||
$helper->setInput($input);
|
||||
}
|
||||
}
|
||||
// 获取当前命令是否实现了`SignalableCommandInterface`接口,如果是,获取订阅的信号列表
|
||||
// $commandSignals = $command instanceof SignalableCommandInterface ? $command->getSubscribedSignals() : [];
|
||||
// 检查是否存在需要处理的信号或者事件,$commandSignals初始化时为空,不处理信号
|
||||
// if ($commandSignals || $this->dispatcher && $this->signalsToDispatchEvent) {
|
||||
// if (!$this->signalRegistry) {
|
||||
// throw new RuntimeException('Unable to subscribe to signal events. Make sure that the "pcntl" extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.');
|
||||
// }
|
||||
|
||||
// if (Terminal::hasSttyAvailable()) {
|
||||
// $sttyMode = shell_exec('stty -g');
|
||||
|
||||
// foreach ([\SIGINT, \SIGTERM] as $signal) {
|
||||
// $this->signalRegistry->register($signal, static fn () => shell_exec('stty '.$sttyMode));
|
||||
// }
|
||||
// }
|
||||
|
||||
// if ($this->dispatcher) {
|
||||
// // We register application signals, so that we can dispatch the event
|
||||
// foreach ($this->signalsToDispatchEvent as $signal) {
|
||||
// $event = new ConsoleSignalEvent($command, $input, $output, $signal);
|
||||
|
||||
// $this->signalRegistry->register($signal, function ($signal) use ($event, $command, $commandSignals) {
|
||||
// $this->dispatcher->dispatch($event, ConsoleEvents::SIGNAL);
|
||||
// $exitCode = $event->getExitCode();
|
||||
|
||||
// // If the command is signalable, we call the handleSignal() method
|
||||
// if (\in_array($signal, $commandSignals, true)) {
|
||||
// $exitCode = $command->handleSignal($signal, $exitCode);
|
||||
// // BC layer for Symfony <= 5
|
||||
// if (null === $exitCode) {
|
||||
// trigger_deprecation('symfony/console', '6.3', 'Not returning an exit code from "%s::handleSignal()" is deprecated, return "false" to keep the command running or "0" to exit successfully.', get_debug_type($command));
|
||||
// $exitCode = 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (false !== $exitCode) {
|
||||
// $event = new ConsoleTerminateEvent($command, $event->getInput(), $event->getOutput(), $exitCode, $signal);
|
||||
// $this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE);
|
||||
|
||||
// exit($event->getExitCode());
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// // then we register command signals, but not if already handled after the dispatcher
|
||||
// $commandSignals = array_diff($commandSignals, $this->signalsToDispatchEvent);
|
||||
// }
|
||||
|
||||
// foreach ($commandSignals as $signal) {
|
||||
// $this->signalRegistry->register($signal, function (int $signal) use ($command): void {
|
||||
// $exitCode = $command->handleSignal($signal);
|
||||
// // BC layer for Symfony <= 5
|
||||
// if (null === $exitCode) {
|
||||
// trigger_deprecation('symfony/console', '6.3', 'Not returning an exit code from "%s::handleSignal()" is deprecated, return "false" to keep the command running or "0" to exit successfully.', get_debug_type($command));
|
||||
// $exitCode = 0;
|
||||
// }
|
||||
|
||||
// if (false !== $exitCode) {
|
||||
// exit($exitCode);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
// 进入该方法
|
||||
if (null === $this->dispatcher) {
|
||||
return $command->run($input, $output);
|
||||
}
|
||||
|
||||
// // bind before the console.command event, so the listeners have access to input options/arguments
|
||||
// try {
|
||||
// $command->mergeApplicationDefinition();
|
||||
// $input->bind($command->getDefinition());
|
||||
// } catch (ExceptionInterface) {
|
||||
// // ignore invalid options/arguments for now, to allow the event listeners to customize the InputDefinition
|
||||
// }
|
||||
|
||||
// $event = new ConsoleCommandEvent($command, $input, $output);
|
||||
// $e = null;
|
||||
|
||||
// try {
|
||||
// $this->dispatcher->dispatch($event, ConsoleEvents::COMMAND);
|
||||
|
||||
// if ($event->commandShouldRun()) {
|
||||
// $exitCode = $command->run($input, $output);
|
||||
// } else {
|
||||
// $exitCode = ConsoleCommandEvent::RETURN_CODE_DISABLED;
|
||||
// }
|
||||
// } catch (\Throwable $e) {
|
||||
// $event = new ConsoleErrorEvent($input, $output, $e, $command);
|
||||
// $this->dispatcher->dispatch($event, ConsoleEvents::ERROR);
|
||||
// $e = $event->getError();
|
||||
|
||||
// if (0 === $exitCode = $event->getExitCode()) {
|
||||
// $e = null;
|
||||
// }
|
||||
// }
|
||||
|
||||
// $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode);
|
||||
// $this->dispatcher->dispatch($event, ConsoleEvents::TERMINATE);
|
||||
|
||||
// if (null !== $e) {
|
||||
// throw $e;
|
||||
// }
|
||||
|
||||
// return $event->getExitCode();
|
||||
}
|
||||
|
||||
```
|
||||
查看`StartServer`中,没有`run`方法,查看父类`Command`,
|
||||
> 文件位置: /vendor/symfony/console/Command/Command.php
|
||||
|
||||
```php
|
||||
public function run(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
// add the application arguments and options
|
||||
$this->mergeApplicationDefinition();
|
||||
|
||||
// bind the input against the command specific arguments/options
|
||||
try {
|
||||
$input->bind($this->getDefinition());
|
||||
} catch (ExceptionInterface $e) {
|
||||
if (!$this->ignoreValidationErrors) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$this->initialize($input, $output);
|
||||
|
||||
// if (null !== $this->processTitle) {
|
||||
// if (\function_exists('cli_set_process_title')) {
|
||||
// if (!@cli_set_process_title($this->processTitle)) {
|
||||
// if ('Darwin' === \PHP_OS) {
|
||||
// $output->writeln('<comment>Running "cli_set_process_title" as an unprivileged user is not supported on MacOS.</comment>', OutputInterface::VERBOSITY_VERY_VERBOSE);
|
||||
// } else {
|
||||
// cli_set_process_title($this->processTitle);
|
||||
// }
|
||||
// }
|
||||
// } elseif (\function_exists('setproctitle')) {
|
||||
// setproctitle($this->processTitle);
|
||||
// } elseif (OutputInterface::VERBOSITY_VERY_VERBOSE === $output->getVerbosity()) {
|
||||
// $output->writeln('<comment>Install the proctitle PECL to be able to change the process title.</comment>');
|
||||
// }
|
||||
// }
|
||||
|
||||
// if ($input->isInteractive()) {
|
||||
// $this->interact($input, $output);
|
||||
// }
|
||||
|
||||
// // The command name argument is often omitted when a command is executed directly with its run() method.
|
||||
// // It would fail the validation if we didn't make sure the command argument is present,
|
||||
// // since it's required by the application.
|
||||
// if ($input->hasArgument('command') && null === $input->getArgument('command')) {
|
||||
// $input->setArgument('command', $this->getName());
|
||||
// }
|
||||
|
||||
$input->validate();
|
||||
|
||||
if ($this->code) {
|
||||
$statusCode = ($this->code)($input, $output);
|
||||
} else {
|
||||
// 最终会执行execute方法
|
||||
$statusCode = $this->execute($input, $output);
|
||||
|
||||
if (!\is_int($statusCode)) {
|
||||
throw new \TypeError(sprintf('Return value of "%s::execute()" must be of the type int, "%s" returned.', static::class, get_debug_type($statusCode)));
|
||||
}
|
||||
}
|
||||
|
||||
return is_numeric($statusCode) ? (int) $statusCode : 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
分析上面代码可知,`run`方法最终会调用`execute`方法,继续往下看。
|
Loading…
Reference in New Issue
Block a user