更新hyperf
This commit is contained in:
parent
a5ae1550d5
commit
b053c3481a
@ -6,7 +6,7 @@ export default defineConfig({
|
||||
title: "源码阅读",
|
||||
description: "记录源码阅读记录",
|
||||
srcDir: 'src',
|
||||
lastUpdated: true,
|
||||
// lastUpdated: true,
|
||||
outDir: '/usr/src/app/dist',
|
||||
markdown: {
|
||||
container: {
|
||||
@ -36,7 +36,7 @@ export default defineConfig({
|
||||
search: {
|
||||
provider: 'local'
|
||||
},
|
||||
lastUpdatedText: "最近更新时间",
|
||||
// lastUpdatedText: "最近更新时间",
|
||||
docFooter: { prev: '上一篇', next: '下一篇' },
|
||||
footer: {
|
||||
message: 'Released under the MIT License.',
|
||||
|
@ -9,13 +9,13 @@ export default [
|
||||
items: [
|
||||
{ text: '阅读版本', link: hyperf + '阅读版本' },
|
||||
{ text: '入口文件', link: hyperf + '入口文件' },
|
||||
{ text: 'init', link: hyperf + 'init' },
|
||||
{ text: '初始化依赖注入 (DI) 容器', link: hyperf + '初始化依赖注入 (DI) 容器' },
|
||||
{ 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 + '响应' },
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -23,20 +23,25 @@ export default [
|
||||
text: 'Laravel',
|
||||
collapsed: true,
|
||||
items: [
|
||||
{ text: 'index入口文件', link: laravel + 'index入口文件' },
|
||||
{ text: 'laravel配置', link: laravel + 'laravel配置' },
|
||||
{ text: '处理请求', link: 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: '解析HTTP内核', link: laravel + '解析HTTP内核' },
|
||||
{ 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: 'facades', link: laravel + 'facades' },
|
||||
{ text: '用户认证', link: laravel + 'auth' },
|
||||
{ text: 'Eloquent查询构造器_1', link: laravel + 'Eloquent查询构造器_1' },
|
||||
{ text: 'Eloquent查询构造器_2', link: laravel + 'Eloquent查询构造器_2' },
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -1,8 +1,4 @@
|
||||
---
|
||||
layout: wiki
|
||||
wiki: hyperf #项目名
|
||||
date: 2024-03-22 16:56
|
||||
categories: [Hyperf]
|
||||
title: 初始化容器类
|
||||
---
|
||||
|
||||
@ -182,7 +178,7 @@ public function __construct(protected Definition\DefinitionSourceInterface $defi
|
||||
2. 创建`ResolverDispatcher`解析器,调度不同类型的解析器来处理容器中的定义,它会根据定义的类型(如类、工厂函数等)来选择适当的解析器进行解析
|
||||
3. 注册容器本身的实例
|
||||
|
||||
{%quot el:h3 总结%}
|
||||
### 总结
|
||||
1. 首先创建一个`DefinitionSourceFactory`工厂实例,并且触发该工厂实例的`__invoke`方法
|
||||
2. `__invoke`方法中,首先加载配置文件包括类映射和依赖配置
|
||||
3. 跟`config`目录下`dependencies.php`文件中的依赖配置进行合并
|
||||
|
@ -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`为空。
|
@ -1,8 +1,4 @@
|
||||
---
|
||||
layout: wiki
|
||||
wiki: hyperf #项目名
|
||||
date: 2024-03-28 16:56
|
||||
categories: [Hyperf]
|
||||
title: 中间件
|
||||
---
|
||||
|
||||
|
@ -19,10 +19,10 @@ require BASE_PATH . '/vendor/autoload.php';
|
||||
// 同上,如果未定义,则定义该常量SWOOLE_HOOK_FLAGS
|
||||
! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', Hyperf\Engine\DefaultOption::hookFlags());
|
||||
|
||||
// 自调用的匿名函数,它创建了自己的作用域并保持全局命名空间的清洁。
|
||||
(function () {
|
||||
// 自调用的匿名函数,它创建了自己的作用域并保持全局命名空间的清洁。 // [!code focus:20]
|
||||
(function () {
|
||||
|
||||
Hyperf\Di\ClassLoader::init();
|
||||
Hyperf\Di\ClassLoader::init();
|
||||
|
||||
/** @var Psr\Container\ContainerInterface $container */
|
||||
$container = require BASE_PATH . '/config/container.php';
|
||||
|
412
src/hyperf/初始化依赖注入 (DI) 容器.md
Normal file
412
src/hyperf/初始化依赖注入 (DI) 容器.md
Normal 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`为空。
|
@ -1,8 +1,4 @@
|
||||
---
|
||||
layout: wiki
|
||||
wiki: hyperf #项目名
|
||||
date: 2024-03-26 16:56
|
||||
categories: [Hyperf]
|
||||
title: $application实例化
|
||||
---
|
||||
|
||||
@ -16,7 +12,7 @@ $application->run();
|
||||
### $application是什么
|
||||
|
||||
从之前的分析可知,`$container`是容器类,从容器中解析出`ApplicationInterface`实例。
|
||||
{%image https://oss.xiaokeaii.top/2024/applicationInterface.png %}
|
||||
![](https://oss.xiaokeaii.top/2024/applicationInterface.png)
|
||||
该接口对应的是`Hyperf\Framework\ApplicationFactory`类。分析一下取出源码,
|
||||
```php
|
||||
public function get($id)
|
||||
@ -82,13 +78,13 @@ protected function normalizeDefinition(string $identifier, $definition): ?Defini
|
||||
}
|
||||
```
|
||||
查看`ApplicationFactory`类,存在`__invoke`方法,返回的是一个`FactoryDefinition`对象。
|
||||
{%image https://oss.xiaokeaii.top/2024/applicationFactory.png%}
|
||||
![](image https://oss.xiaokeaii.top/2024/applicationFactory.png)
|
||||
|
||||
```php
|
||||
$definition = $this->getDefinition($name);
|
||||
```
|
||||
所以这行返回的是一个`FactoryDefinition`对象。打印结果如下,
|
||||
{%image https://oss.xiaokeaii.top/2024/工厂定义.png %}
|
||||
![](image https://oss.xiaokeaii.top/2024/工厂定义.png )
|
||||
|
||||
#### resolveDefinition方法
|
||||
继续分析,查看`resolveDefinition`方法,
|
||||
@ -258,7 +254,7 @@ public function add(Command $command)
|
||||
}
|
||||
```
|
||||
打印配置的命令数组,打开第一个`/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`方法获取命令的名称
|
||||
```php
|
||||
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
|
||||
|
@ -1,8 +1,4 @@
|
||||
---
|
||||
layout: wiki
|
||||
wiki: hyperf #项目名
|
||||
date: 2024-03-28 16:56
|
||||
categories: [Hyperf]
|
||||
title: 响应
|
||||
---
|
||||
|
||||
@ -108,7 +104,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
|
||||
|
||||
当匹配到路由时,执行`handleFound`方法。
|
||||
`dispatched`对象结构如下,
|
||||
{%image https://oss.xiaokeaii.top/2024/dispatched.png %}
|
||||
![]( https://oss.xiaokeaii.top/2024/dispatched.png)
|
||||
```php
|
||||
protected function handleFound(Dispatched $dispatched, ServerRequestInterface $request): mixed
|
||||
{
|
||||
@ -206,7 +202,7 @@ protected function transferToResponse($response, ServerRequestInterface $request
|
||||
```
|
||||
|
||||
1. 如果是字符串,则转换为文本类型的响应对象,并设置相应的头部信息和响应体
|
||||
2. 如果是`ResponseInterface`的实现类,则返回`ResponsePlusProxy`对象 {%u (?)%}
|
||||
2. 如果是`ResponseInterface`的实现类,则返回`ResponsePlusProxy`对象
|
||||
3. 如果是数组或者`Arrayable`的实现类,则转换为`JSON`类型的响应对象,并设置相应的头部信息和响应体
|
||||
4. 如果是实现了`Jsonable`的接口,则转换为`JSON`类型的响应对象,并设置相应的头部信息和响应体
|
||||
5. 如果响应已经设置了`content-type`头部信息,则将其转换为对应的响应对象
|
||||
|
@ -1,13 +1,9 @@
|
||||
---
|
||||
layout: wiki
|
||||
wiki: hyperf #项目名
|
||||
date: 2024-03-22 16:56
|
||||
categories: [Hyperf]
|
||||
title: 请求
|
||||
---
|
||||
|
||||
由前一节可知,事件的注册在`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`的运行流程,(图源网络)
|
||||
{%image https://oss.xiaokeaii.top/2024/swoole流程.jpg%}
|
||||
![](https://oss.xiaokeaii.top/2024/swoole流程.jpg)
|
||||
具体内容参考[swoole官网](https://wiki.swoole.com/)
|
||||
|
||||
当请求过来时,会触发`onRequest`事件,然后执行`onRequest`回调函数。
|
||||
|
@ -1,8 +1,4 @@
|
||||
---
|
||||
layout: wiki
|
||||
wiki: hyperf #项目名
|
||||
date: 2024-03-28 16:56
|
||||
categories: [Hyperf]
|
||||
title: 路由寻址
|
||||
---
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user