diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 4c11fc0..ce8f720 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -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.', diff --git a/.vitepress/sidebar.ts b/.vitepress/sidebar.ts index 3797db3..d0403a0 100644 --- a/.vitepress/sidebar.ts +++ b/.vitepress/sidebar.ts @@ -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' }, ] }, { diff --git a/src/hyperf/container.md b/src/hyperf/container.md index c468911..7291548 100644 --- a/src/hyperf/container.md +++ b/src/hyperf/container.md @@ -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`文件中的依赖配置进行合并 diff --git a/src/hyperf/init.md b/src/hyperf/init.md deleted file mode 100644 index bb1c1c6..0000000 --- a/src/hyperf/init.md +++ /dev/null @@ -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`为空。 \ No newline at end of file diff --git a/src/hyperf/中间件.md b/src/hyperf/中间件.md index 597e5fd..13f6761 100644 --- a/src/hyperf/中间件.md +++ b/src/hyperf/中间件.md @@ -1,8 +1,4 @@ --- -layout: wiki -wiki: hyperf #项目名 -date: 2024-03-28 16:56 -categories: [Hyperf] title: 中间件 --- diff --git a/src/hyperf/入口文件.md b/src/hyperf/入口文件.md index ed9f1e7..be58fb1 100644 --- a/src/hyperf/入口文件.md +++ b/src/hyperf/入口文件.md @@ -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'; diff --git a/src/hyperf/初始化依赖注入 (DI) 容器.md b/src/hyperf/初始化依赖注入 (DI) 容器.md new file mode 100644 index 0000000..afdbf77 --- /dev/null +++ b/src/hyperf/初始化依赖注入 (DI) 容器.md @@ -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`为空。 \ No newline at end of file diff --git a/src/hyperf/启动服务.md b/src/hyperf/启动服务.md index afb03fa..3e01fb3 100644 --- a/src/hyperf/启动服务.md +++ b/src/hyperf/启动服务.md @@ -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 diff --git a/src/hyperf/响应.md b/src/hyperf/响应.md index 515d65f..99031bc 100644 --- a/src/hyperf/响应.md +++ b/src/hyperf/响应.md @@ -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`头部信息,则将其转换为对应的响应对象 diff --git a/src/hyperf/请求.md b/src/hyperf/请求.md index 4797d57..cc96450 100644 --- a/src/hyperf/请求.md +++ b/src/hyperf/请求.md @@ -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`回调函数。 diff --git a/src/hyperf/路由寻址.md b/src/hyperf/路由寻址.md index 1518aa2..a85ac44 100644 --- a/src/hyperf/路由寻址.md +++ b/src/hyperf/路由寻址.md @@ -1,8 +1,4 @@ --- -layout: wiki -wiki: hyperf #项目名 -date: 2024-03-28 16:56 -categories: [Hyperf] title: 路由寻址 ---