更新hyperf

This commit is contained in:
想打瞌睡 2024-07-09 18:33:02 +09:00
parent a5ae1550d5
commit b053c3481a
11 changed files with 443 additions and 351 deletions

View File

@ -6,7 +6,7 @@ export default defineConfig({
title: "源码阅读", title: "源码阅读",
description: "记录源码阅读记录", description: "记录源码阅读记录",
srcDir: 'src', srcDir: 'src',
lastUpdated: true, // lastUpdated: true,
outDir: '/usr/src/app/dist', outDir: '/usr/src/app/dist',
markdown: { markdown: {
container: { container: {
@ -36,7 +36,7 @@ export default defineConfig({
search: { search: {
provider: 'local' provider: 'local'
}, },
lastUpdatedText: "最近更新时间", // lastUpdatedText: "最近更新时间",
docFooter: { prev: '上一篇', next: '下一篇' }, docFooter: { prev: '上一篇', next: '下一篇' },
footer: { footer: {
message: 'Released under the MIT License.', message: 'Released under the MIT License.',

View File

@ -9,13 +9,13 @@ export default [
items: [ items: [
{ text: '阅读版本', link: hyperf + '阅读版本' }, { text: '阅读版本', link: hyperf + '阅读版本' },
{ text: '入口文件', link: hyperf + '入口文件' }, { text: '入口文件', link: hyperf + '入口文件' },
{ text: 'init', link: hyperf + 'init' }, { text: '初始化依赖注入 (DI) 容器', link: hyperf + '初始化依赖注入 (DI) 容器' },
{ text: 'container', link: hyperf + 'container' }, { text: 'container', link: hyperf + 'container' },
{ text: '中间件', link: hyperf + '中间件' },
{ text: '启动服务', link: hyperf + '启动服务' }, { text: '启动服务', link: hyperf + '启动服务' },
{ text: '响应', link: hyperf + '响应' },
{ text: '请求', link: hyperf + '请求' }, { text: '请求', link: hyperf + '请求' },
{ text: '路由寻址', link: hyperf + '路由寻址' }, { text: '路由寻址', link: hyperf + '路由寻址' },
{ text: '中间件', link: hyperf + '中间件' },
{ text: '响应', link: hyperf + '响应' },
] ]
}, },
{ {
@ -23,20 +23,25 @@ export default [
text: 'Laravel', text: 'Laravel',
collapsed: true, collapsed: true,
items: [ items: [
{ text: 'index入口文件', link: laravel + 'index入口文件' },
{ text: 'laravel配置', link: laravel + 'laravel配置' }, { text: 'laravel配置', link: laravel + 'laravel配置' },
{ text: '处理请求', link: laravel + '处理请求' }, { text: 'index入口文件', link: laravel + 'index入口文件' },
{ text: '实例化容器', link: laravel + '实例化容器' },
{ text: '实现自动加载', link: laravel + '实现自动加载' }, { text: '实现自动加载', link: laravel + '实现自动加载' },
{ text: '容器', link: laravel + '容器' }, { text: '实例化容器', link: laravel + '实例化容器' },
{ text: '服务提供者', link: laravel + '服务提供者' }, { text: '设置基础目录', link: laravel + '设置基础目录' },
{ text: '注册基础绑定', link: laravel + '注册基础绑定' }, { text: '注册基础绑定', link: laravel + '注册基础绑定' },
{ text: '注册服务提供者', link: laravel + '注册服务提供者' }, { text: '注册服务提供者', link: laravel + '注册服务提供者' },
{ text: '注册核心别名类', link: laravel + '注册核心别名类' },
{ text: '解析HTTP内核', link: laravel + '解析HTTP内核' },
{ text: '处理请求', link: laravel + '处理请求' },
{ text: '解析路由', link: laravel + '解析路由' },
{ text: '容器类', link: laravel + '容器类' },
{ text: '渲染页面', link: laravel + '渲染页面' }, { text: '渲染页面', link: laravel + '渲染页面' },
{ text: '终止', link: laravel + '终止' }, { text: '终止', link: laravel + '终止' },
{ text: '解析HTTP内核', link: laravel + '解析HTTP内核' }, { text: '服务提供者', link: laravel + '服务提供者' },
{ text: '解析路由', link: laravel + '解析路由' }, { text: 'facades', link: laravel + 'facades' },
{ text: '设置基础目录', link: laravel + '设置基础目录' }, { text: '用户认证', link: laravel + 'auth' },
{ text: 'Eloquent查询构造器_1', link: laravel + 'Eloquent查询构造器_1' },
{ text: 'Eloquent查询构造器_2', link: laravel + 'Eloquent查询构造器_2' },
] ]
}, },
{ {

View File

@ -1,8 +1,4 @@
--- ---
layout: wiki
wiki: hyperf #项目名
date: 2024-03-22 16:56
categories: [Hyperf]
title: 初始化容器类 title: 初始化容器类
--- ---
@ -182,7 +178,7 @@ public function __construct(protected Definition\DefinitionSourceInterface $defi
2. 创建`ResolverDispatcher`解析器,调度不同类型的解析器来处理容器中的定义,它会根据定义的类型(如类、工厂函数等)来选择适当的解析器进行解析 2. 创建`ResolverDispatcher`解析器,调度不同类型的解析器来处理容器中的定义,它会根据定义的类型(如类、工厂函数等)来选择适当的解析器进行解析
3. 注册容器本身的实例 3. 注册容器本身的实例
{%quot el:h3 总结%} ### 总结
1. 首先创建一个`DefinitionSourceFactory`工厂实例,并且触发该工厂实例的`__invoke`方法 1. 首先创建一个`DefinitionSourceFactory`工厂实例,并且触发该工厂实例的`__invoke`方法
2. `__invoke`方法中,首先加载配置文件包括类映射和依赖配置 2. `__invoke`方法中,首先加载配置文件包括类映射和依赖配置
3. 跟`config`目录下`dependencies.php`文件中的依赖配置进行合并 3. 跟`config`目录下`dependencies.php`文件中的依赖配置进行合并

View File

@ -1,301 +0,0 @@
---
title: init方法
---
这节分析这一行代码的作用
```php
Hyperf\Di\ClassLoader::init();
```
`init`方法有三个可选参数,默认是不传参数进来
```php
public static function init(?string $proxyFileDirPath = null, ?string $configDir = null, ?ScanHandlerInterface $handler = null): void
{
// 代理文件目录
if (! $proxyFileDirPath) {
$proxyFileDirPath = BASE_PATH . '/runtime/container/proxy/';
}
// 配置文件目录
if (! $configDir) {
$configDir = BASE_PATH . '/config/';
}
// 扫描器,用来扫描类文件并生成类映射
// 利用 PHP 的 pcntl 扩展提供的功能来进行并发扫描,以加快扫描速度
if (! $handler) {
$handler = new PcntlScanHandler();
}
// 获取composer的自动加载器实例
$composerLoader = Composer::getLoader();
// 这就没啥说的, 如果.env文件存在就加载.env文件
// 使用vlucas/phpdotenv库
if (file_exists(BASE_PATH . '/.env')) {
static::loadDotenv();
}
// Scan by ScanConfig to generate the reflection class map
$config = ScanConfig::instance($configDir);
$composerLoader->addClassMap($config->getClassMap());
$scanner = new Scanner($config, $handler);
$composerLoader->addClassMap(
$scanner->scan($composerLoader->getClassMap(), $proxyFileDirPath)
);
// Initialize Lazy Loader. This will prepend LazyLoader to the top of autoload queue.
LazyLoader::bootstrap($configDir);
}
protected static function loadDotenv(): void
{
$repository = RepositoryBuilder::createWithNoAdapters()
->addAdapter(Adapter\PutenvAdapter::class)
->immutable()
->make();
Dotenv::create($repository, [BASE_PATH])->load();
}
```
### instance方法
看这行,`instance`方法创建一个`ScanConfig`实例,用于加载并解析目录下的配置文件,并生成类映射等扫描相关的配置信息。不理解可以往下看,
```php
$config = ScanConfig::instance($configDir);
```
进入该方法,
```php
public static function instance(string $configDir): self
{
// 如果该实例存在,则直接返回该实例
if (self::$instance) {
return self::$instance;
}
$configDir = rtrim($configDir, '/');
[$config, $serverDependencies, $cacheable] = static::initConfigByFile($configDir);
// 这里返回该实例
return self::$instance = new self(
$cacheable,
$configDir,
$config['paths'] ?? [],
$serverDependencies ?? [],
$config['ignore_annotations'] ?? [],
$config['global_imports'] ?? [],
$config['collectors'] ?? [],
$config['class_map'] ?? []
);
}
```
#### initConfigByFile方法
配置的获取在该方法中,
```php
private static function initConfigByFile(string $configDir): array
{
$config = [];
$configFromProviders = [];
$cacheable = false;
// 该class是hyperf config包中带的
if (class_exists(ProviderConfig::class)) {
$configFromProviders = ProviderConfig::load();
}
//...
}
```
继续往下看,`ProviderConfig::load()`进入该方法,
```php
public static function load(): array
{
// 如果未赋值,则进行赋值
if (! static::$providerConfigs) {
// 该行是从composer.json中获取配置信息并返回数组
// 这里会获得所有的服务提供者信息
$providers = Composer::getMergedExtra('hyperf')['config'] ?? [];
static::$providerConfigs = static::loadProviders($providers);
}
return static::$providerConfigs;
}
```
这里用到了[ConfigProvider 机制](https://hyperf.wiki/3.1/#/zh-cn/component-guide/configprovider),参考官网讲解。
`Composer::getMergedExtra`获取`composer.json`文件中的配置信息,内容如下
![](https://oss.xiaokeaii.top/2024/composer::getMergedExtra.png)
打印获取的内容如下,是所有服务提供者的数组
![](https://oss.xiaokeaii.top/2024/provider.png)
接下来查看`loadProviders`方法
```php
protected static function loadProviders(array $providers): array
{
$providerConfigs = [];
// 循环所有服务提供者
foreach ($providers as $provider) {
// 如果是字符串且存在该类且该类存在__invoke方法
if (is_string($provider) && class_exists($provider) && method_exists($provider, '__invoke')) {
// 当通过方法调用类时会触发__invoke方法
// 所以(new $provider())()相当于执行服务提供者的__invoke方法
$providerConfigs[] = (new $provider())();
}
}
return static::merge(...$providerConfigs);
}
```
我们以`hyperf/config`包为例,打开目录下`ConfigProvider`类,打开`__invoke`方法,会发现,这里返回的是一个配置数组,代码如下,
```php
class ConfigProvider
{
public function __invoke(): array
{
return [
'dependencies' => [
ConfigInterface::class => ConfigFactory::class,
],
'aspects' => [
ValueAspect::class,
],
'listeners' => [
RegisterPropertyHandlerListener::class,
],
];
}
}
```
最终生成的`$providerConfigs`数组格式如下,每个服务提供者都会生成一个数组。
```php
array(22) {
[0]=>
array(5) {
["dependencies"]=>
array(1) {
["Psr\SimpleCache\CacheInterface"]=>
string(18) "Hyperf\Cache\Cache"
}
["listeners"]=>
array(1) {
[0]=>
string(36) "Hyperf\Cache\Listener\DeleteListener"
}
["annotations"]=>
array(1) {
["scan"]=>
array(1) {
["collectors"]=>
array(1) {
[0]=>
string(35) "Hyperf\Cache\CacheListenerCollector"
}
}
}
["aspects"]=>
array(5) {
[0]=>
string(35) "Hyperf\Cache\Aspect\CacheableAspect"
[1]=>
string(36) "Hyperf\Cache\Aspect\CacheAheadAspect"
[2]=>
string(36) "Hyperf\Cache\Aspect\CacheEvictAspect"
[3]=>
string(34) "Hyperf\Cache\Aspect\CachePutAspect"
[4]=>
string(35) "Hyperf\Cache\Aspect\FailCacheAspect"
}
["publish"]=>
array(1) {
[0]=>
array(4) {
["id"]=>
string(6) "config"
["description"]=>
string(21) "The config for cache."
["source"]=>
string(74) "/data/project/hyperf-skeleton/vendor/hyperf/cache/src/../publish/cache.php"
["destination"]=>
string(55) "/data/project/hyperf-skeleton/config/autoload/cache.php"
}
}
}
//...
}
```
最后再看`return static::merge(...$providerConfigs);`这一行,查看`static::merge`方法,该方法是将所有服务提供者的配置数组进行合并,最终返回一个数组。
```php
protected static function merge(...$arrays): array
{
if (empty($arrays)) {
return [];
}
// 递归地合并一个或多个数组,
// 如果输入的数组中有相同的字符串键名,则这些值会被合并到一个数组中去
$result = array_merge_recursive(...$arrays);
// 如果存在dependencies key则对依赖进行合并
if (isset($result['dependencies'])) {
$result['dependencies'] = [];
foreach ($arrays as $item) {
foreach ($item['dependencies'] ?? [] as $key => $value) {
$depend = $result['dependencies'][$key] ?? null;
if (! $depend instanceof PriorityDefinition) {
$result['dependencies'][$key] = $value;
continue;
}
if ($value instanceof PriorityDefinition) {
$depend->merge($value);
}
}
}
}
// 返回合并后的配置数组
return $result;
}
```
以上,`ProviderConfig::load()`方法走完。
往下看以下是将config目录下配置与上面获取的配置进行合并存在重复则配置文件优先
```php
$serverDependencies = $configFromProviders['dependencies'] ?? [];
if (file_exists($configDir . '/autoload/dependencies.php')) {
$definitions = include $configDir . '/autoload/dependencies.php';
$serverDependencies = array_replace($serverDependencies, $definitions ?? []);
}
$config = static::allocateConfigValue($configFromProviders['annotations'] ?? [], $config);
// 这里是注解相关的配置,回来在分析
if (file_exists($configDir . '/autoload/annotations.php')) {
$annotations = include $configDir . '/autoload/annotations.php';
$config = static::allocateConfigValue($annotations, $config);
}
// Load the config/config.php and merge the config
if (file_exists($configDir . '/config.php')) {
$configContent = include $configDir . '/config.php';
$appEnv = $configContent['app_env'] ?? 'dev';
$cacheable = value($configContent['scan_cacheable'] ?? $appEnv === 'prod');
if (isset($configContent['annotations'])) {
$config = static::allocateConfigValue($configContent['annotations'], $config);
}
}
return [$config, $serverDependencies, $cacheable];
```
`instance`方法的三个返回值都已经清楚,接下来返回自身实例
```php
return self::$instance = new self(
$cacheable,
$configDir,
$config['paths'] ?? [],
$serverDependencies ?? [],
$config['ignore_annotations'] ?? [],
$config['global_imports'] ?? [],
$config['collectors'] ?? [],
$config['class_map'] ?? []
);
```
现在,`config`文件配置已经得到,
```php
$config = ScanConfig::instance($configDir);
```
分析下一行,
```php
// 将提供的类映射添加到 Composer 的自动加载器中
$composerLoader->addClassMap($config->getClassMap());
```
由上面配置文件可知,默认情况下,`class_map`为空。

View File

@ -1,8 +1,4 @@
--- ---
layout: wiki
wiki: hyperf #项目名
date: 2024-03-28 16:56
categories: [Hyperf]
title: 中间件 title: 中间件
--- ---

View File

@ -19,10 +19,10 @@ require BASE_PATH . '/vendor/autoload.php';
// 同上,如果未定义,则定义该常量SWOOLE_HOOK_FLAGS // 同上,如果未定义,则定义该常量SWOOLE_HOOK_FLAGS
! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', Hyperf\Engine\DefaultOption::hookFlags()); ! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', Hyperf\Engine\DefaultOption::hookFlags());
// 自调用的匿名函数,它创建了自己的作用域并保持全局命名空间的清洁。 // 自调用的匿名函数,它创建了自己的作用域并保持全局命名空间的清洁。 // [!code focus:20]
(function () { (function () {
Hyperf\Di\ClassLoader::init(); Hyperf\Di\ClassLoader::init();
/** @var Psr\Container\ContainerInterface $container */ /** @var Psr\Container\ContainerInterface $container */
$container = require BASE_PATH . '/config/container.php'; $container = require BASE_PATH . '/config/container.php';

View File

@ -0,0 +1,412 @@
---
title: 初始化依赖注入 (DI) 容器
---
## 初始化依赖注入 (DI) 容器
```php
(function () {
Hyperf\Di\ClassLoader::init(); // [!code focus]
/** @var Psr\Container\ContainerInterface $container */
$container = require BASE_PATH . '/config/container.php';
/**
* @var \Symfony\Component\Console\Application $application
*/
$application = $container->get(Hyperf\Contract\ApplicationInterface::class);
$application->run();
})();
```
`init`方法有三个可选参数,默认是不传参数进来
```php
public static function init(?string $proxyFileDirPath = null, ?string $configDir = null, ?ScanHandlerInterface $handler = null): void
{
// 代理文件目录
if (! $proxyFileDirPath) {
$proxyFileDirPath = BASE_PATH . '/runtime/container/proxy/';
}
// 配置文件目录
if (! $configDir) {
$configDir = BASE_PATH . '/config/';
}
// 扫描器,用来扫描类文件并生成类映射
// 利用 PHP 的 pcntl 扩展提供的功能来进行并发扫描,以加快扫描速度
if (! $handler) {
$handler = new PcntlScanHandler();
}
// 获取composer的自动加载器实例
$composerLoader = Composer::getLoader();
// 加载.env文件 // [!code focus:17]
// 使用的是vlucas/phpdotenv库
if (file_exists(BASE_PATH . '/.env')) {
static::loadDotenv();
}
// 通过 ScanConfig 扫描并生成反射类映射
$config = ScanConfig::instance($configDir);
$composerLoader->addClassMap($config->getClassMap());
$scanner = new Scanner($config, $handler);
$composerLoader->addClassMap(
$scanner->scan($composerLoader->getClassMap(), $proxyFileDirPath)
);
// Initialize Lazy Loader. This will prepend LazyLoader to the top of autoload queue.
LazyLoader::bootstrap($configDir);
}
protected static function loadDotenv(): void
{
$repository = RepositoryBuilder::createWithNoAdapters()
->addAdapter(Adapter\PutenvAdapter::class)
->immutable()
->make();
Dotenv::create($repository, [BASE_PATH])->load();
}
```
### 读取配置目录
看这行,`instance`方法创建一个`ScanConfig`实例,用于加载并解析目录下的配置文件,并生成类映射等扫描相关的配置信息。
```php
$config = ScanConfig::instance($configDir);
public static function instance(string $configDir): self // [!code focus:20]
{
// 如果该实例存在,则直接返回该实例
if (self::$instance) {
return self::$instance;
}
$configDir = rtrim($configDir, '/');
[$config, $serverDependencies, $cacheable] = static::initConfigByFile($configDir);
// 这里返回该实例
return self::$instance = new self(
$cacheable,
$configDir,
$config['paths'] ?? [],
$serverDependencies ?? [],
$config['ignore_annotations'] ?? [],
$config['global_imports'] ?? [],
$config['collectors'] ?? [],
$config['class_map'] ?? []
);
}
```
从`initConfigByFile`方法中,在指定目录解析出对应的`$config`、`$serverDependencies` 和 `$cacheable`三个变量。该方法具体实现如下,
```php
private static function initConfigByFile(string $configDir): array
{
$config = [];
$configFromProviders = [];
$cacheable = false;
if (class_exists(ProviderConfig::class)) { // [!code warning:3]
$configFromProviders = ProviderConfig::load();
}
$serverDependencies = $configFromProviders['dependencies'] ?? [];
if (file_exists($configDir . '/autoload/dependencies.php')) {
$definitions = include $configDir . '/autoload/dependencies.php';
$serverDependencies = array_replace($serverDependencies, $definitions ?? []);
}
$config = static::allocateConfigValue($configFromProviders['annotations'] ?? [], $config);
// Load the config/autoload/annotations.php and merge the config
if (file_exists($configDir . '/autoload/annotations.php')) {
$annotations = include $configDir . '/autoload/annotations.php';
$config = static::allocateConfigValue($annotations, $config);
}
// Load the config/config.php and merge the config
if (file_exists($configDir . '/config.php')) {
$configContent = include $configDir . '/config.php';
$appEnv = $configContent['app_env'] ?? 'dev';
$cacheable = value($configContent['scan_cacheable'] ?? $appEnv === 'prod');
if (isset($configContent['annotations'])) {
$config = static::allocateConfigValue($configContent['annotations'], $config);
}
}
return [$config, $serverDependencies, $cacheable];
}
```
#### 获取项目配置信息
使用`ProviderConfig::load()`方法,获取项目配置信息,该方法实现如下,
```php
namespace Hyperf\Config;
use Hyperf\Di\Definition\PriorityDefinition;
use Hyperf\Support\Composer;
use function class_exists;
use function is_string;
use function method_exists;
/**
* Provider config allow the components set the configs to application.
*/
class ProviderConfig // [!code focus:4]
{
// 项目配置数组
private static array $providerConfigs = [];
/**
* 从组件中加载和合并所有的服务提供者
*/
public static function load(): array // [!code focus:8]
{
if (! static::$providerConfigs) {
$providers = Composer::getMergedExtra('hyperf')['config'] ?? [];
static::$providerConfigs = static::loadProviders($providers);
}
return static::$providerConfigs;
}
public static function clear(): void
{
static::$providerConfigs = [];
}
protected static function loadProviders(array $providers): array // [!code focus:11]
{
$providerConfigs = [];
foreach ($providers as $provider) {
if (is_string($provider) && class_exists($provider) && method_exists($provider, '__invoke')) {
$providerConfigs[] = (new $provider())();
}
}
return static::merge(...$providerConfigs);
}
protected static function merge(...$arrays): array
{
if (empty($arrays)) {
return [];
}
$result = array_merge_recursive(...$arrays);
if (isset($result['dependencies'])) {
$result['dependencies'] = [];
foreach ($arrays as $item) {
foreach ($item['dependencies'] ?? [] as $key => $value) {
$depend = $result['dependencies'][$key] ?? null;
if (! $depend instanceof PriorityDefinition) {
$result['dependencies'][$key] = $value;
continue;
}
if ($value instanceof PriorityDefinition) {
$depend->merge($value);
}
}
}
}
return $result;
}
}
```
这里用到了[ConfigProvider 机制](https://hyperf.wiki/3.1/#/zh-cn/component-guide/configprovider),参考官网讲解。
简单来说,`Hyperf`会读取每个组件包`composer.json`中指定的信息,作为数组返回。`Composer::getMergedExtra`获取`composer.json`文件中的配置信息
,打印获取的内容如下
![](https://oss.xiaokeaii.top/2024/provider.png)
接下来查看`loadProviders`方法,
```php
protected static function loadProviders(array $providers): array
{
$providerConfigs = [];
// 循环所有服务提供者 // [!code focus:11]
foreach ($providers as $provider) {
// 如果是字符串且存在该类且该类存在__invoke方法
if (is_string($provider) && class_exists($provider) && method_exists($provider, '__invoke')) {
// 当通过方法调用类时会触发__invoke魔术方法
// 所以(new $provider())()相当于执行服务提供者的__invoke方法
$providerConfigs[] = (new $provider())();
}
}
return static::merge(...$providerConfigs);
}
```
我们以`hyperf/config`包为例,打开目录下`ConfigProvider`类,打开`__invoke`方法,会发现,这里返回的是一个配置数组,代码如下,
```php
class ConfigProvider
{
public function __invoke(): array
{
return [
'dependencies' => [
ConfigInterface::class => ConfigFactory::class,
],
'aspects' => [
ValueAspect::class,
],
'listeners' => [
RegisterPropertyHandlerListener::class,
],
];
}
}
```
`$providerConfigs`数组格式如下,每个服务提供者都会生成一个数组。
```php
array(3) {
["dependencies"]=>
array(1) {
["Hyperf\Contract\ConfigInterface"]=>
string(27) "Hyperf\Config\ConfigFactory"
}
["aspects"]=>
array(1) {
[0]=>
string(36) "Hyperf\Config\Annotation\ValueAspect"
}
["listeners"]=>
array(1) {
[0]=>
string(54) "Hyperf\Config\Listener\RegisterPropertyHandlerListener"
}
}
```
最后通过`static::merge`方法,将所有服务提供者的配置数组进行合并,最终返回一个数组。
```php
protected static function merge(...$arrays): array
{
if (empty($arrays)) {
return [];
}
// 递归地合并一个或多个数组,
// 如果输入的数组中有相同的字符串键名,则这些值会被合并到一个数组中去
$result = array_merge_recursive(...$arrays);
// 如果存在dependencies key则对依赖进行合并
if (isset($result['dependencies'])) {
$result['dependencies'] = [];
foreach ($arrays as $item) {
foreach ($item['dependencies'] ?? [] as $key => $value) {
$depend = $result['dependencies'][$key] ?? null;
if (! $depend instanceof PriorityDefinition) {
$result['dependencies'][$key] = $value;
continue;
}
if ($value instanceof PriorityDefinition) {
$depend->merge($value);
}
}
}
}
// 返回合并后的配置数组
return $result;
}
```
以上,`ProviderConfig::load()`方法走完。
回到`initConfigByFile`方法,往下看,将`config`目录下配置与上面获取的配置进行合并,存在重复则配置文件优先
```php
private static function initConfigByFile(string $configDir): array
{
$config = [];
$configFromProviders = [];
$cacheable = false;
if (class_exists(ProviderConfig::class)) {
$configFromProviders = ProviderConfig::load();
}
// 加载 autoload 目录下的 dependencies.php 文件,并合并依赖配置
$serverDependencies = $configFromProviders['dependencies'] ?? []; // [!code focus:25]
if (file_exists($configDir . '/autoload/dependencies.php')) {
$definitions = include $configDir . '/autoload/dependencies.php';
$serverDependencies = array_replace($serverDependencies, $definitions ?? []);
}
// 将 Provider 配置中的 annotations 值分配给 config
$config = static::allocateConfigValue($configFromProviders['annotations'] ?? [], $config);
// 加载 autoload 目录下的 annotations.php 文件,并合并配置
if (file_exists($configDir . '/autoload/annotations.php')) {
$annotations = include $configDir . '/autoload/annotations.php';
$config = static::allocateConfigValue($annotations, $config);
}
// 加载 config 目录下的 config.php 文件,合并
if (file_exists($configDir . '/config.php')) {
$configContent = include $configDir . '/config.php';
$appEnv = $configContent['app_env'] ?? 'dev';
$cacheable = value($configContent['scan_cacheable'] ?? $appEnv === 'prod');
if (isset($configContent['annotations'])) {
$config = static::allocateConfigValue($configContent['annotations'], $config);
}
}
return [$config, $serverDependencies, $cacheable];
}
```
- `$config` 包含合并后的注解配置数组
- `serverDependencies` 包含合并后的依赖配置数组
- `cacheable` 布尔值,表示扫描结果是否可缓存(由`config`文件中的`scan_cacheable`参数决定)
接下来返回自身实例
```php
return self::$instance = new self(
$cacheable,
$configDir,
$config['paths'] ?? [],
$serverDependencies ?? [],
$config['ignore_annotations'] ?? [],
$config['global_imports'] ?? [],
$config['collectors'] ?? [],
$config['class_map'] ?? []
);
```
现在,`config`文件配置已经得到,
```php
$config = ScanConfig::instance($configDir);
```
### 扫描并更新 `composerLoader` 的类映射表
```php
public static function init(?string $proxyFileDirPath = null, ?string $configDir = null, ?ScanHandlerInterface $handler = null): void
{
if (! $proxyFileDirPath) {
// This dir is the default proxy file dir path of Hyperf
$proxyFileDirPath = BASE_PATH . '/runtime/container/proxy/';
}
if (! $configDir) {
// This dir is the default proxy file dir path of Hyperf
$configDir = BASE_PATH . '/config/';
}
if (! $handler) {
$handler = new PcntlScanHandler();
}
$composerLoader = Composer::getLoader();
if (file_exists(BASE_PATH . '/.env')) {
static::loadDotenv();
}
$config = ScanConfig::instance($configDir); // [!code focus:11]
// 将提供的类映射添加到 Composer 的自动加载器中
$composerLoader->addClassMap($config->getClassMap());
// 实例化Scanner扫描器
$scanner = new Scanner($config, $handler);
$composerLoader->addClassMap(
$scanner->scan($composerLoader->getClassMap(), $proxyFileDirPath)
);
// Initialize Lazy Loader. This will prepend LazyLoader to the top of autoload queue.
LazyLoader::bootstrap($configDir);
}
```
由上面配置文件可知,默认情况下,`class_map`为空。

View File

@ -1,8 +1,4 @@
--- ---
layout: wiki
wiki: hyperf #项目名
date: 2024-03-26 16:56
categories: [Hyperf]
title: $application实例化 title: $application实例化
--- ---
@ -16,7 +12,7 @@ $application->run();
### $application是什么 ### $application是什么
从之前的分析可知,`$container`是容器类,从容器中解析出`ApplicationInterface`实例。 从之前的分析可知,`$container`是容器类,从容器中解析出`ApplicationInterface`实例。
{%image https://oss.xiaokeaii.top/2024/applicationInterface.png %} ![](https://oss.xiaokeaii.top/2024/applicationInterface.png)
该接口对应的是`Hyperf\Framework\ApplicationFactory`类。分析一下取出源码, 该接口对应的是`Hyperf\Framework\ApplicationFactory`类。分析一下取出源码,
```php ```php
public function get($id) public function get($id)
@ -82,13 +78,13 @@ protected function normalizeDefinition(string $identifier, $definition): ?Defini
} }
``` ```
查看`ApplicationFactory`类,存在`__invoke`方法,返回的是一个`FactoryDefinition`对象。 查看`ApplicationFactory`类,存在`__invoke`方法,返回的是一个`FactoryDefinition`对象。
{%image https://oss.xiaokeaii.top/2024/applicationFactory.png%} ![](image https://oss.xiaokeaii.top/2024/applicationFactory.png)
```php ```php
$definition = $this->getDefinition($name); $definition = $this->getDefinition($name);
``` ```
所以这行返回的是一个`FactoryDefinition`对象。打印结果如下, 所以这行返回的是一个`FactoryDefinition`对象。打印结果如下,
{%image https://oss.xiaokeaii.top/2024/工厂定义.png %} ![](image https://oss.xiaokeaii.top/2024/工厂定义.png )
#### resolveDefinition方法 #### resolveDefinition方法
继续分析,查看`resolveDefinition`方法, 继续分析,查看`resolveDefinition`方法,
@ -258,7 +254,7 @@ public function add(Command $command)
} }
``` ```
打印配置的命令数组,打开第一个`/vendor/hyperf/server/src/Command/StartServer.php`命令类, 打印配置的命令数组,打开第一个`/vendor/hyperf/server/src/Command/StartServer.php`命令类,
{%image https://oss.xiaokeaii.top/2024/start.png%} ![](image https://oss.xiaokeaii.top/2024/start.png)
> 我们可以发现,在父类的构造方法中,会将`name`赋值给该命令类的成员变量,之后通过`getName`方法获取命令的名称 > 我们可以发现,在父类的构造方法中,会将`name`赋值给该命令类的成员变量,之后通过`getName`方法获取命令的名称
```php ```php
public function __construct(private ContainerInterface $container) public function __construct(private ContainerInterface $container)
@ -920,7 +916,7 @@ protected function registerSwooleEvents(SwoolePort|SwooleServer $server, array $
} }
``` ```
这是所有的回调事件,如下图 这是所有的回调事件,如下图
{%image https://oss.xiaokeaii.top/2024/event.png %} ![](image https://oss.xiaokeaii.top/2024/event.png )
查看以下代码,这里进行配置和初始化中间件相关的内容 查看以下代码,这里进行配置和初始化中间件相关的内容
```php ```php

View File

@ -1,8 +1,4 @@
--- ---
layout: wiki
wiki: hyperf #项目名
date: 2024-03-28 16:56
categories: [Hyperf]
title: 响应 title: 响应
--- ---
@ -108,7 +104,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
当匹配到路由时,执行`handleFound`方法。 当匹配到路由时,执行`handleFound`方法。
`dispatched`对象结构如下, `dispatched`对象结构如下,
{%image https://oss.xiaokeaii.top/2024/dispatched.png %} ![]( https://oss.xiaokeaii.top/2024/dispatched.png)
```php ```php
protected function handleFound(Dispatched $dispatched, ServerRequestInterface $request): mixed protected function handleFound(Dispatched $dispatched, ServerRequestInterface $request): mixed
{ {
@ -206,7 +202,7 @@ protected function transferToResponse($response, ServerRequestInterface $request
``` ```
1. 如果是字符串,则转换为文本类型的响应对象,并设置相应的头部信息和响应体 1. 如果是字符串,则转换为文本类型的响应对象,并设置相应的头部信息和响应体
2. 如果是`ResponseInterface`的实现类,则返回`ResponsePlusProxy`对象 {%u (?)%} 2. 如果是`ResponseInterface`的实现类,则返回`ResponsePlusProxy`对象
3. 如果是数组或者`Arrayable`的实现类,则转换为`JSON`类型的响应对象,并设置相应的头部信息和响应体 3. 如果是数组或者`Arrayable`的实现类,则转换为`JSON`类型的响应对象,并设置相应的头部信息和响应体
4. 如果是实现了`Jsonable`的接口,则转换为`JSON`类型的响应对象,并设置相应的头部信息和响应体 4. 如果是实现了`Jsonable`的接口,则转换为`JSON`类型的响应对象,并设置相应的头部信息和响应体
5. 如果响应已经设置了`content-type`头部信息,则将其转换为对应的响应对象 5. 如果响应已经设置了`content-type`头部信息,则将其转换为对应的响应对象

View File

@ -1,13 +1,9 @@
--- ---
layout: wiki
wiki: hyperf #项目名
date: 2024-03-22 16:56
categories: [Hyperf]
title: 请求 title: 请求
--- ---
由前一节可知,事件的注册在`Hyerf\Server\Server`类的`registerSwooleEvents`方法中完成。 由前一节可知,事件的注册在`Hyerf\Server\Server`类的`registerSwooleEvents`方法中完成。
{%image https://oss.xiaokeaii.top/2024/event.png %} ![](https://oss.xiaokeaii.top/2024/event.png)
注册的回调事件的执行顺序如下: 注册的回调事件的执行顺序如下:
| 所属类 | 事件 | 描述 | 触发时机 | 触发顺序 | | 所属类 | 事件 | 描述 | 触发时机 | 触发顺序 |
| --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- |
@ -21,7 +17,7 @@ title: 请求
> 要理解上面事件,请参考`Swoole`的运行流程。 > 要理解上面事件,请参考`Swoole`的运行流程。
### Swoole流程 ### Swoole流程
首先了解`Swoole`的运行流程,(图源网络) 首先了解`Swoole`的运行流程,(图源网络)
{%image https://oss.xiaokeaii.top/2024/swoole流程.jpg%} ![](https://oss.xiaokeaii.top/2024/swoole流程.jpg)
具体内容参考[swoole官网](https://wiki.swoole.com/) 具体内容参考[swoole官网](https://wiki.swoole.com/)
当请求过来时,会触发`onRequest`事件,然后执行`onRequest`回调函数。 当请求过来时,会触发`onRequest`事件,然后执行`onRequest`回调函数。

View File

@ -1,8 +1,4 @@
--- ---
layout: wiki
wiki: hyperf #项目名
date: 2024-03-28 16:56
categories: [Hyperf]
title: 路由寻址 title: 路由寻址
--- ---