更新hyperf

This commit is contained in:
想打瞌睡 2024-07-10 18:42:33 +09:00
parent 9f80c71d42
commit e84acb95b9
8 changed files with 1800 additions and 654 deletions

View File

@ -13,8 +13,8 @@ export default defineConfig({
tipLabel: '提示',
warningLabel: '警告',
dangerLabel: '危险',
infoLabel: '信息',
detailsLabel: '详细信息'
infoLabel: 'info',
detailsLabel: '点我查看'
},
image: {
// 默认禁用图片懒加载

View File

@ -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' },
]
},
{

View File

@ -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,24 +168,25 @@ class DefinitionSource implements DefinitionSourceInterface
}
return $definitions;
}
/**
/** // [!code focus:23]
* 绑定的内容可以是数组、类名、闭包
* @param array|callable|string $definition
*/
protected function normalizeDefinition(string $identifier, $definition): ?DefinitionInterface
protected function normalizeDefinition(string $identifier, $definition): ?DefinitionInterface
{
if ($definition instanceof PriorityDefinition) {
$definition = $definition->getDefinition();
}
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,15 +251,124 @@ 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`文件中的依赖配置进行合并
4. 创建`DefinitionSource`对象,传入依赖配置信息,负责管理容器中的绑定关系和依赖注入的定义
5. 实例化容器对象,将`DefinitionSource`对象传入
6. 构造函数中实例化`ResolverDispatcher`调度器作为容器类的属性,并注册容器本身的实例
6. 构造函数中实例化`ResolverDispatcher`调度器作为容器类的属性,并注册容器本身的实例

File diff suppressed because it is too large Load Diff

View 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`方法返回解析结果。

View File

@ -0,0 +1,3 @@
---
title: 附录2 注解命令获取
---

View 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;
}
```

View 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`方法,继续往下看。