入口文件 bin/hyperf.php
設(shè)置錯(cuò)誤信息提示,內(nèi)存使用限制和常量
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);
調(diào)用 composer 自動(dòng)加載
require BASE_PATH . '/vendor/autoload.php';
初始化所有的容器對(duì)象;
// vender/Hyperf/Di/ClassLoader.php
Hyperf\Di\ClassLoader::init()
1. 設(shè)定代理類所在位置
if (! $proxyFileDirPath) {
// This dir is the default proxy file dir path of Hyperf
$proxyFileDirPath = BASE_PATH . '/runtime/container/proxy/';
}
2. 設(shè)定配置目錄
if (! $configDir) {
// This dir is the default proxy file dir path of Hyperf
$configDir = BASE_PATH . '/config/';
}
3. 設(shè)置掃描類
if (!$handler) {
$handler = new PcntlScanHandler();
}
4. 注冊(cè)自動(dòng)加載
獲取
composer原來注冊(cè)的所有自動(dòng)加載函數(shù)spl_autoload_functions(),使用到注解的自動(dòng)加載回調(diào)函數(shù)中$composerClassLoader->findFile($class),并替換 composer 中 ClassLoader類的原有注冊(cè)函數(shù),改為使用自身類的loadClass方法,取消原來所有的注冊(cè),并在重寫后重新注冊(cè)
$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 類,執(zhí)行構(gòu)造方法
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 自動(dòng)加載中添加額外的 classmap
- 獲取各個(gè)配置項(xiàng)
// ScanConfig.php
public static function instance(string $configDir): self
{
if (self::$instance) {
return self::$instance;
}
$configDir = rtrim($configDir, '/');
[$config, $serverDependencies, $cacheable] = static::initConfigByFile($configDir); // 獲取配置參數(shù)
return self::$instance = new self(
$cacheable,
$configDir,
$config['paths'] ?? [],
$serverDependencies ?? [],
$config['ignore_annotations'] ?? [],
$config['global_imports'] ?? [],
$config['collectors'] ?? [],
$config['class_map'] ?? []
);
}
initConfigByFile方法內(nèi)部,會(huì)讀取composer.lock文件,獲取所有packages和packages-dev的extra->hyperf->config字段,根據(jù)類名實(shí)例化各個(gè)ConfigProvider,執(zhí)行__invoke方法,對(duì)執(zhí)行結(jié)果的返回?cái)?shù)組按照類別進(jìn)行合并獲取所有配置
- extra 格式
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
},
"hyperf": {
"config": "Hyperf\\Di\\ConfigProvider"
}
}
- $configFromProviders 合并后數(shù)據(jù)格式
// 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"
]
]
提取配置項(xiàng)中的
dependencies數(shù)據(jù),然后讀取$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 ?? []);
}
提取配置項(xiàng)中的
annotations數(shù)據(jù),合并數(shù)據(jù),然后讀取$configDir . '/autoload/annotations.php',再次合并數(shù)據(jù),生成$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);
- 執(zhí)行邏輯
$scanned = $this->handler->scan(); // 進(jìn)行fork,子進(jìn)程是 false,父進(jìn)程是true,子進(jìn)程邏輯處理完之后再執(zhí)行父進(jìn)程邏輯
if ($scanned->isScanned()) {
return $this->deserializeCachedScanData($collectors);
}
// 下面的是子進(jìn)程邏輯,之后的 exit 會(huì)觸發(fā) pcntl_wait($status),執(zhí)行上面的父進(jìn)程邏輯;
...
exit;
-
pnctlfork 進(jìn)程
public function scan(): Scanned
{
$pid = pcntl_fork();
// 父進(jìn)程和子進(jìn)程都會(huì)執(zhí)行下面代碼
if ($pid == -1) {
throw new Exception('The process fork failed');
}
if ($pid) {
// 父進(jìn)程執(zhí)行邏輯
pcntl_wait($status); // 掛起等待子進(jìn)程完成后,才繼續(xù)執(zhí)行
return new Scanned(true);
}
// 子進(jìn)程執(zhí)行邏輯
return new Scanned(false);
}
5. 初始化懶加載配置
根據(jù)AST生成代理文件,講這些代理類的自動(dòng)加載以 prepend 參數(shù)調(diào)整到隊(duì)列頭
LazyLoader::bootstrap($configDir);
獲取容器,獲取 application 對(duì)象,啟動(dòng)程序
$container = require BASE_PATH . '/config/container.php';
/**
* @var \Symfony\Component\Console\Application
*/
$application = $container->get(Hyperf\Contract\ApplicationInterface::class);
$application->run();
container 的獲取對(duì)象方式
container的get方法用于獲取對(duì)象,根據(jù)dependencies的classmap尋找對(duì)應(yīng)的類,如果已經(jīng)實(shí)例化直接返回,沒有時(shí)調(diào)用make方法進(jìn)行實(shí)例化,緩存后返回。
- 實(shí)例化方法
// 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); // 實(shí)例化類
}
...
}
// 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 對(duì)象
默認(rèn)為
\Symfony\Component\Console\Application對(duì)象, 獲取所有的命令添加事件監(jiān)聽
// 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 環(huán)境
$this->checkEnvironment($output);
- 獲取
server對(duì)象,從配置中獲取所有server config,初始化SwooleServer,如果有多個(gè)server時(shí),添加額外的監(jiān)聽
// 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));
}
}
}
- 最后啟動(dòng) Server
// vendor\hyperf\server\src\Server.php
public function start()
{
$this->server->start();
}
默認(rèn) http server 回調(diào) 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 初始化時(shí),會(huì)進(jìn)行 Hyperf\HttpServer\Server 的實(shí)例化和初始化
// 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); // 獲取實(shí)例
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);
}
}
- 創(chuàng)建核心路由器和路由配置
// vendor\hyperf\http-server\src\Server.php
public function initCoreMiddleware(string $serverName): void
{
...
$this->coreMiddleware = $this->createCoreMiddleware();
$this->routerDispatcher = $this->createDispatcher($serverName);
...
}
- 核心路由器中負(fù)責(zé)分發(fā)路由
// 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;
}