入口文件 bin/hyperf.php
设置错误信息提示,内存使用限制和常量
ini_set('display_errors', 'on');
ini_set('display_startup_errors', 'on');
ini_set('memory_limit', '1G');
error_reporting(E_ALL);
! defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1));
! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', SWOOLE_HOOK_ALL);
调用 composer 自动加载
require BASE_PATH . '/vendor/autoload.php';
初始化所有的容器对象;
// vender/Hyperf/Di/ClassLoader.php
Hyperf\Di\ClassLoader::init()
1. 设定代理类所在位置
if (! $proxyFileDirPath) {
// This dir is the default proxy file dir path of Hyperf
$proxyFileDirPath = BASE_PATH . '/runtime/container/proxy/';
}
2. 设定配置目录
if (! $configDir) {
// This dir is the default proxy file dir path of Hyperf
$configDir = BASE_PATH . '/config/';
}
3. 设置扫描类
if (!$handler) {
$handler = new PcntlScanHandler();
}
4. 注册自动加载
获取
composer
原来注册的所有自动加载函数spl_autoload_functions()
,使用到注解的自动加载回调函数中$composerClassLoader->findFile($class)
,并替换 composer 中 ClassLoader类的原有注册函数,改为使用自身类的loadClass
方法,取消原来所有的注册,并在重写后重新注册
$loaders = spl_autoload_functions();
// Proxy the composer class loader
foreach ($loaders as &$loader) {
$unregisterLoader = $loader;
if (is_array($loader) && $loader[0] instanceof ComposerClassLoader) {
/** @var ComposerClassLoader $composerClassLoader */
$composerClassLoader = $loader[0];
AnnotationRegistry::registerLoader(function ($class) use ($composerClassLoader) {
return (bool) $composerClassLoader->findFile($class);
});
$loader[0] = new static($composerClassLoader, $proxyFileDirPath, $configDir, $handler);
}
spl_autoload_unregister($unregisterLoader);
}
unset($loader);
// Re-register the loaders
foreach ($loaders as $loader) {
spl_autoload_register($loader);
}
4.1 替换原有 composer 中 ClassLoader 类,执行构造方法
public function __construct(ComposerClassLoader $classLoader, string $proxyFileDir, string $configDir, ScanHandlerInterface $handler)
{
$this->setComposerClassLoader($classLoader);
if (file_exists(BASE_PATH . '/.env')) {
$this->loadDotenv();
}
// Scan by ScanConfig to generate the reflection class map
$config = ScanConfig::instance($configDir);
$classLoader->addClassMap($config->getClassMap());
$scanner = new Scanner($this, $config, $handler);
$this->proxies = $scanner->scan($this->getComposerClassLoader()->getClassMap(), $proxyFileDir);
}
4.1.1 加载 .env 配置
if (file_exists(BASE_PATH . '/.env')) {
$this->loadDotenv();
}
4.1.2 初始化扫描配置
// ClassLoader.php
$config = ScanConfig::instance($configDir); /** $configDir = BASE_PATH . '/config/'; */
$classLoader->addClassMap($config->getClassMap()); // 在 composer 自动加载中添加额外的 classmap
- 获取各个配置项
// ScanConfig.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
方法内部,会读取composer.lock
文件,获取所有packages
和packages-dev
的extra->hyperf->config
字段,根据类名实例化各个ConfigProvider
,执行__invoke
方法,对执行结果的返回数组按照类别进行合并获取所有配置
- extra 格式
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
},
"hyperf": {
"config": "Hyperf\\Di\\ConfigProvider"
}
}
- $configFromProviders 合并后数据格式
// ScanConfig.php
if (class_exists(ProviderConfig::class)) {
$configFromProviders = ProviderConfig::load();
}
[
"dependencies" => [
"Psr\SimpleCache\CacheInterface" => "Hyperf\Cache\Cache",
],
"listeners" => [
"Hyperf\Cache\Listener\DeleteListener",
],
"annotations" => [
"scan" => [
"paths" => [
"/mnt/d/work/docker/hyperf-skeleton/vendor/hyperf/cache/src",
]
],
"collectors" => [
"Hyperf\Cache\CacheListenerCollector",
]
],
"ignore_annotations" => [
"mixin"
]
]
提取配置项中的
dependencies
数据,然后读取$configDir . '/autoload/dependencies.php'
来替换原有的依赖, 最终生成$serverDependencies
// ScanConfig.php
$serverDependencies = $configFromProviders['dependencies'] ?? [];
if (file_exists($configDir . '/autoload/dependencies.php')) {
$definitions = include $configDir . '/autoload/dependencies.php';
$serverDependencies = array_replace($serverDependencies, $definitions ?? []);
}
提取配置项中的
annotations
数据,合并数据,然后读取$configDir . '/autoload/annotations.php'
,再次合并数据,生成$config
$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);
}
读取
$configDir . '/config.php'
,合并annotations
到$config
,并获取$cacheable
// 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);
}
}
4.1.3 获取注解扫描仪,开始扫描
$scanner = new Scanner($this, $config, $handler);
$this->proxies = $scanner->scan($this->getComposerClassLoader()->getClassMap(), $proxyFileDir);
- 执行逻辑
$scanned = $this->handler->scan(); // 进行fork,子进程是 false,父进程是true,子进程逻辑处理完之后再执行父进程逻辑
if ($scanned->isScanned()) {
return $this->deserializeCachedScanData($collectors);
}
// 下面的是子进程逻辑,之后的 exit 会触发 pcntl_wait($status),执行上面的父进程逻辑;
...
exit;
-
pnctl
fork 进程
public function scan(): Scanned
{
$pid = pcntl_fork();
// 父进程和子进程都会执行下面代码
if ($pid == -1) {
throw new Exception('The process fork failed');
}
if ($pid) {
// 父进程执行逻辑
pcntl_wait($status); // 挂起等待子进程完成后,才继续执行
return new Scanned(true);
}
// 子进程执行逻辑
return new Scanned(false);
}
5. 初始化懒加载配置
根据AST生成代理文件,讲这些代理类的自动加载以 prepend 参数调整到队列头
LazyLoader::bootstrap($configDir);
获取容器,获取 application
对象,启动程序
$container = require BASE_PATH . '/config/container.php';
/**
* @var \Symfony\Component\Console\Application
*/
$application = $container->get(Hyperf\Contract\ApplicationInterface::class);
$application->run();
container 的获取对象方式
container
的get
方法用于获取对象,根据dependencies
的classmap
寻找对应的类,如果已经实例化直接返回,没有时调用make
方法进行实例化,缓存后返回。
- 实例化方法
// vendor\hyperf\di\src\Resolver\ObjectResolver.php
private function createInstance(ObjectDefinition $definition, array $parameters)
{
...
try {
$className = $definition->getClassName();
$classReflection = ReflectionManager::reflectClass($className); // 获取类的反射
$constructorInjection = $definition->getConstructorInjection();
$args = $this->parameterResolver->resolveParameters($constructorInjection, $classReflection->getConstructor(), $parameters);
$object = new $className(...$args); // 实例化类
}
...
}
// vendor\hyperf\di\src\ReflectionManager.php
public static function reflectClass(string $className): ReflectionClass
{
if (!isset(static::$container['class'][$className])) {
if (!class_exists($className) && !interface_exists($className) && !trait_exists($className)) {
throw new InvalidArgumentException("Class {$className} not exist");
}
static::$container['class'][$className] = new ReflectionClass($className);
}
return static::$container['class'][$className];
}
- 装载扫描配置文件后获取到的所有依赖配置
dependencies
到容器中
// config\container.php
$container = new Container((new DefinitionSourceFactory(true))());
application 对象
默认为
\Symfony\Component\Console\Application
对象, 获取所有的命令添加事件监听
// vendor\hyperf\framework\src\ApplicationFactory.php
$config = $container->get(ConfigInterface::class);
$commands = $config->get('commands', []); // 从所有组件的 `ConfigProvider.php` 中获取
// Append commands that defined by annotation.
$annotationCommands = [];
if (class_exists(AnnotationCollector::class) && class_exists(Command::class)) {
$annotationCommands = AnnotationCollector::getClassesByAnnotation(Command::class);
$annotationCommands = array_keys($annotationCommands);
}
$commands = array_unique(array_merge($commands, $annotationCommands));
$application = new Application();
if (isset($eventDispatcher) && class_exists(SymfonyEventDispatcher::class)) {
$application->setDispatcher(new SymfonyEventDispatcher($eventDispatcher));
}
foreach ($commands as $command) {
$application->add($container->get($command));
}
return $application;
php bin/hyperf.php start 命令
命令来自于
vendor\hyperf\server\src\Command\StartServer.php
文件
- 检查 swoole 环境
$this->checkEnvironment($output);
- 获取
server
对象,从配置中获取所有server config
,初始化SwooleServer
,如果有多个server时,添加额外的监听
// vendor\hyperf\server\src\Server.php
protected function initServers(ServerConfig $config)
{
$servers = $this->sortServers($config->getServers());
foreach ($servers as $server) {
$name = $server->getName();
$type = $server->getType();
$host = $server->getHost();
$port = $server->getPort();
$sockType = $server->getSockType();
$callbacks = $server->getCallbacks();
if (! $this->server instanceof SwooleServer) {
$this->server = $this->makeServer($type, $host, $port, $config->getMode(), $sockType);
$callbacks = array_replace($this->defaultCallbacks(), $config->getCallbacks(), $callbacks);
$this->registerSwooleEvents($this->server, $callbacks, $name);
$this->server->set(array_replace($config->getSettings(), $server->getSettings()));
ServerManager::add($name, [$type, current($this->server->ports)]);
if (class_exists(BeforeMainServerStart::class)) {
// Trigger BeforeMainServerStart event, this event only trigger once before main server start.
$this->eventDispatcher->dispatch(new BeforeMainServerStart($this->server, $config->toArray()));
}
} else {
/** @var bool|\Swoole\Server\Port $slaveServer */
$slaveServer = $this->server->addlistener($host, $port, $sockType);
if (! $slaveServer) {
throw new \RuntimeException("Failed to listen server port [{$host}:{$port}]");
}
$server->getSettings() && $slaveServer->set(array_replace($config->getSettings(), $server->getSettings()));
$this->registerSwooleEvents($slaveServer, $callbacks, $name);
ServerManager::add($name, [$type, $slaveServer]);
}
// Trigger beforeStart event.
if (isset($callbacks[Event::ON_BEFORE_START])) {
[$class, $method] = $callbacks[Event::ON_BEFORE_START];
if ($this->container->has($class)) {
$this->container->get($class)->{$method}();
}
}
if (class_exists(BeforeServerStart::class)) {
// Trigger BeforeServerStart event.
$this->eventDispatcher->dispatch(new BeforeServerStart($name));
}
}
}
- 最后启动 Server
// vendor\hyperf\server\src\Server.php
public function start()
{
$this->server->start();
}
默认 http server 回调 Hyperf\HttpServer\Server
的 onRequest
'servers' => [
[
'name' => 'http',
'type' => Server::SERVER_HTTP,
'host' => '0.0.0.0',
'port' => 9501,
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [
Event::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'],
],
],
],
在 server 初始化时,会进行 Hyperf\HttpServer\Server
的实例化和初始化
// vendor\hyperf\server\src\Server.php
protected function registerSwooleEvents($server, array $events, string $serverName): void
{
foreach ($events as $event => $callback) {
...
if (is_array($callback)) {
...
$this->onRequestCallbacks[$className . $method] = $serverName;
$class = $this->container->get($className); // 获取实例
if (method_exists($class, 'setServerName')) {
// Override the server name.
$class->setServerName($serverName);
}
if ($class instanceof MiddlewareInitializerInterface) {
$class->initCoreMiddleware($serverName); // 初始化
}
$callback = [$class, $method];
}
$server->on($event, $callback);
}
}
- 创建核心路由器和路由配置
// vendor\hyperf\http-server\src\Server.php
public function initCoreMiddleware(string $serverName): void
{
...
$this->coreMiddleware = $this->createCoreMiddleware();
$this->routerDispatcher = $this->createDispatcher($serverName);
...
}
- 核心路由器中负责分发路由
// vendor\hyperf\http-server\src\CoreMiddleware.php
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$request = Context::set(ServerRequestInterface::class, $request);
/** @var Dispatched $dispatched */
$dispatched = $request->getAttribute(Dispatched::class);
if (! $dispatched instanceof Dispatched) {
throw new ServerException(sprintf('The dispatched object is not a %s object.', Dispatched::class));
}
$response = null;
switch ($dispatched->status) {
case Dispatcher::NOT_FOUND:
$response = $this->handleNotFound($request);
break;
case Dispatcher::METHOD_NOT_ALLOWED:
$response = $this->handleMethodNotAllowed($dispatched->params, $request);
break;
case Dispatcher::FOUND:
$response = $this->handleFound($dispatched, $request);
break;
}
if (! $response instanceof ResponseInterface) {
$response = $this->transferToResponse($response, $request);
}
return $response->withAddedHeader('Server', 'Hyperf');
}
- 路由的逻辑处理
// vendor\hyperf\http-server\src\CoreMiddleware.php
protected function handleFound(Dispatched $dispatched, ServerRequestInterface $request)
{
if ($dispatched->handler->callback instanceof Closure) {
$parameters = $this->parseClosureParameters($dispatched->handler->callback, $dispatched->params);
$response = call($dispatched->handler->callback, $parameters);
} else {
[$controller, $action] = $this->prepareHandler($dispatched->handler->callback);
$controllerInstance = $this->container->get($controller);
if (! method_exists($controllerInstance, $action)) {
// Route found, but the handler does not exist.
throw new ServerErrorHttpException('Method of class does not exist.');
}
$parameters = $this->parseMethodParameters($controller, $action, $dispatched->params);
$response = $controllerInstance->{$action}(...$parameters);
}
return $response;
}