关于配置文件先解释下命令行的实现。
bin/hyperf.php先加载config/container.php,通过容器获取$application,调用$application->run()。
#/config/container.php
$container = new Container((new DefinitionSourceFactory(true))());
if (! $container instanceof \Psr\Container\ContainerInterface) {
throw new RuntimeException('The dependency injection container is invalid.');
}
return ApplicationContext::setContainer($container);
#/bin/hyperf.php
require BASE_PATH . '/vendor/autoload.php';
// Self-called anonymous function that creates its own scope and keep the global namespace clean.
(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();
})();
#Hyperf\Di\Definition\DefinitionSourceFactory
public function __invoke()
{
$configDir = $this->baseUri . '/config';
$configFromProviders = [];
if (class_exists(ProviderConfig::class)) {
$configFromProviders = ProviderConfig::load();
}
$serverDependencies = $configFromProviders['dependencies'] ?? [];
if (file_exists($configDir . '/autoload/dependencies.php')) {
$definitions = include $configDir . '/autoload/dependencies.php';
$serverDependencies = array_replace($serverDependencies, $definitions ?? []);
}
return new DefinitionSource($serverDependencies);
}
container.php初始化容器,$resolvedEntries中设置默认对应内容。其get方法可获取对应实现类或设置对应类。
#Hyperf\Di\Container
public function __construct(Definition\DefinitionSourceInterface $definitionSource)
{
$this->definitionSource = $definitionSource;
$this->definitionResolver = new ResolverDispatcher($this);
// Auto-register the container.
$this->resolvedEntries = [
self::class => $this,
PsrContainerInterface::class => $this,
HyperfContainerInterface::class => $this,
];
}
public function get($name)
{
// If the entry is already resolved we return it
if (isset($this->resolvedEntries[$name]) || array_key_exists($name, $this->resolvedEntries)) {
return $this->resolvedEntries[$name];
}
return $this->resolvedEntries[$name] = $this->make($name);
}
每个模块的配置中设置有依赖、配置、注释等。其配置的位置在每个模块对应的composer.json。
# Hyperf\Framework\ConfigProvider
class ConfigProvider
{
public function __invoke(): array
{
return [
'dependencies' => [
ApplicationInterface::class => ApplicationFactory::class,
StdoutLoggerInterface::class => StdoutLogger::class,
],
'annotations' => [
'scan' => [
'paths' => [
__DIR__,
],
],
],
];
}
}
#Hyperf\ExceptionHandler\ConfigProvider
class ConfigProvider
{
public function __invoke(): array
{
return [
'dependencies' => [
FormatterInterface::class => DefaultFormatter::class,
],
'listeners' => [
ExceptionHandlerListener::class,
],
'annotations' => [
'scan' => [
'paths' => [
__DIR__,
],
],
],
];
}
}
#Hyperf\Config
class ConfigProvider
{
public function __invoke(): array
{
return [
'dependencies' => [
ConfigInterface::class => ConfigFactory::class,
],
'aspects' => [
ValueAspect::class,
],
'listeners' => [
RegisterPropertyHandlerListener::class,
],
'annotations' => [
'scan' => [
'paths' => [
__DIR__,
],
],
],
];
}
}
#vendor/hyperf/framework/composer.json
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
},
"hyperf": {
"config": "Hyperf\\Framework\\ConfigProvider"
}
}
#vendor/hyperf/exception-handler/composer.json
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
},
"hyperf": {
"config": "Hyperf\\ExceptionHandler\\ConfigProvider"
}
}
#vendor/hyperf/config/composer.json
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
},
"hyperf": {
"config": "Hyperf\\Config\\ConfigProvider"
}
}
根据hyper.php调用,配置文件加载后应该有Hyperf\Contract\ApplicationInterface::class对应的实现类或者通过make函数处理获取对应的实现类。
初始化$container = new Container((new DefinitionSourceFactory(true))())将 DefinitionSourceFactory类作为方法使用,调用__invoke()。
DefinitionSourceFactory::__invoke()通过ProviderConfig::load()加载/composer.lock。composer.lock将上述各个模块的composer.json合并到一个文件,作为json文件解析。
#Hyperf\Config\ProviderConfig
public static function load(): array
{
if (! static::$providerConfigs) {
$providers = Composer::getMergedExtra('hyperf')['config'] ?? [];
static::$providerConfigs = static::loadProviders($providers);
}
return static::$providerConfigs;
}
所以/vendor/autoload.php运行之后,各个模块入口的接口类对应的实体类都能通过容器获取到。
所有配置文件在Hyperf\Config\ConfigFactory中加载入项目,该类在其他模块中用于获取配置。
#Hyperf\Config\ConfigFactory
public function __invoke(ContainerInterface $container)
{
$configPath = BASE_PATH . '/config/';
$config = $this->readConfig($configPath . 'config.php');
$autoloadConfig = $this->readPaths([BASE_PATH . '/config/autoload']);
$merged = array_merge_recursive(ProviderConfig::load(), $config, ...$autoloadConfig);
return new Config($merged);
}
以下为配置文件中hyperf.config的加载流程。在Hyperf\Devtool\VendorPublishCommand::execute()中设置,最终在Symfony\Component\Console\Application::run()中使用。
# Hyperf\Devtool\VendorPublishCommand
protected function execute(InputInterface $input, OutputInterface $output)
{
……
$provider = Arr::get($extra, 'hyperf.config');
$config = (new $provider())();
$publish = Arr::get($config, 'publish');
……
}
#Symfony\Component\Console\Command\Command
public function run(InputInterface $input, OutputInterface $output)
{
……
if ($this->code) {
$statusCode = ($this->code)($input, $output);
} else {
$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)));
}
}
……
}
#Symfony\Component\Console\Application
protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output)
{
……
if (null === $this->dispatcher) {
return $command->run($input, $output);
}
……
}
public function doRun(InputInterface $input, OutputInterface $output)
{
……
$this->runningCommand = $command;
$exitCode = $this->doRunCommand($command, $input, $output);
$this->runningCommand = null;
……
}
public function run(InputInterface $input = null, OutputInterface $output = null)
{
……
try {
$exitCode = $this->doRun($input, $output);
} catch (\Exception $e) {
……
}
……
}
所以接下来的问题是config模块的加载时间和我好奇的linster和exception配置文件的使用。
不过留在以后再研究,还是先把整个框架学完。