php bin/hyperf.php start 发生了啥

用了这么久的hyperf框架,还没细细研究过源码,先看看start,入个门

先安装一个脚手架吧

先直接运行一下,看看有啥变化

1
$ php bin/hyperf.php

可以看到整个项目有一些些变化,在runtime下面生成了几个文件

1
2
3
4
5
6
7
8
9
runtime
├── container
│   ├── aspects.cache
│   ├── classes.cache
│   ├── proxy
│   │   ├── App_Controller_AbstractController.proxy.php
│   │   └── App_Controller_IndexController.proxy.php
│   └── scan.cache
└── hyperf.pid

得到几个疑问与猜想:

  1. aspects 切面?收集了项目内的切面信息?
  2. classes 类?收集了类的什么信息?什么类?
  3. scan 扫描?扫描了啥?结果又是啥?
  4. proxy 代理?难道说有文件被代理了?
  5. hyperf.pid pid?进程id吧?

入口文件

bin/hyperf.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env php
<?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);

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();
})();

说明

  • error_reporting(E_ALL) 报告所有PHP错误
  • BASE_PATH 太重要了,大部分文件扫描都用到它
  • SWOOLE_HOOK_FLAGS 一键协程化的常量配置,后面start用到 官方说明
  • composer 的自动加载

朴实无华的4步走

  1. di加载器初始化
  2. 加载容器container
  3. get app
  4. app run

这里使用了(function() {})();的写法,还没看懂为啥,上面说的是 创建自己的作用域并保持全局命名空间干净

Hyperf\Di\ClassLoader::init() di加载器初始化

说明

  1. 定义代理类目录 $proxyFileDirPath
  2. 定义配置目录 $configDir
  3. 定义扫描器 $handler
  4. 代理composer的加载器 \Hyperf\Di\ClassLoader
  5. 初始化懒加载 \Hyperf\Di\LazyLoader\LazyLoader

不看细节,先看结果

万物皆可 var_dump($loaders);

以下内容做了美化与省略,只保留了一些关键点

composer被代理前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Array
(
[0] => Array
(
[0] => Composer\Autoload\ClassLoader Object
(
[vendorDir:Composer\Autoload\ClassLoader:private] => /Users/lihq1403/workspace/self/study-hyperf/vendor
[prefixLengthsPsr4:Composer\Autoload\ClassLoader:private] => Array
[prefixDirsPsr4:Composer\Autoload\ClassLoader:private] => Array
[fallbackDirsPsr4:Composer\Autoload\ClassLoader:private] => Array
[prefixesPsr0:Composer\Autoload\ClassLoader:private] => Array
[fallbackDirsPsr0:Composer\Autoload\ClassLoader:private] => Array
[useIncludePath:Composer\Autoload\ClassLoader:private] => Array
[classMap:Composer\Autoload\ClassLoader:private] => Array
(
[App\Controller\AbstractController] => /Users/lihq1403/workspace/self/study-hyperf/vendor/composer/../../app/Controller/AbstractController.php
[App\Controller\IndexController] => /Users/lihq1403/workspace/self/study-hyperf/vendor/composer/../../app/Controller/IndexController.php
。。。 略
)
[classMapAuthoritative:Composer\Autoload\ClassLoader:private] =>
[missingClasses:Composer\Autoload\ClassLoader:private] => Array
[apcuPrefix:Composer\Autoload\ClassLoader:private] =>
)
[1] => loadClass
)
[1] => Array
(
[0] => PHPStan\PharAutoloader
[1] => loadClass
)
)

当前被spl_autoload_register

  • Composer\Autoload\ClassLoader@loadClass()
  • PHPStan\PharAutoloader@loadClass()

composer被代理后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Array
(
[0] => Array
(
[0] => Hyperf\Di\LazyLoader\LazyLoader Object
(
[registered:protected] => 1
[config:protected] => Array
(
)

)

[1] => load
)
[1] => Array
(
[0] => Hyperf\Di\ClassLoader Object
(
[composerClassLoader:protected] => Composer\Autoload\ClassLoader Object
(
[vendorDir:Composer\Autoload\ClassLoader:private] => /Users/lihq1403/workspace/self/study-hyperf/vendor
[prefixLengthsPsr4:Composer\Autoload\ClassLoader:private] => Array
[prefixDirsPsr4:Composer\Autoload\ClassLoader:private] => Array
[fallbackDirsPsr4:Composer\Autoload\ClassLoader:private] => Array
[prefixesPsr0:Composer\Autoload\ClassLoader:private] => Array
[fallbackDirsPsr0:Composer\Autoload\ClassLoader:private] => Array
[useIncludePath:Composer\Autoload\ClassLoader:private] =>
[classMap:Composer\Autoload\ClassLoader:private] => Array
(
[App\Controller\AbstractController] => /Users/lihq1403/workspace/self/study-hyperf/vendor/composer/../../app/Controller/AbstractController.php
[App\Controller\IndexController] => /Users/lihq1403/workspace/self/study-hyperf/vendor/composer/../../app/Controller/IndexController.php
。。。 略
)
[classMapAuthoritative:Composer\Autoload\ClassLoader:private] =>
[missingClasses:Composer\Autoload\ClassLoader:private] => Array
[apcuPrefix:Composer\Autoload\ClassLoader:private] =>
)

[proxies:protected] => Array
(
[App\Controller\AbstractController] => /Users/lihq1403/workspace/self/study-hyperf/runtime/container/proxy/App_Controller_AbstractController.proxy.php
[App\Controller\IndexController] => /Users/lihq1403/workspace/self/study-hyperf/runtime/container/proxy/App_Controller_IndexController.proxy.php
)
)
[1] => loadClass
)
[2] => Array
(
[0] => PHPStan\PharAutoloader
[1] => loadClass
)
)

当前被spl_autoload_register

  • Hyperf\Di\LazyLoader\LazyLoader@load()
  • Hyperf\Di\ClassLoader@loadClass()
  • PHPStan\PharAutoloader@loadClass()

可以明显的看到,spl_autoload_register注册的东西被替换了,多了一个proxies的classmap,这不就是被代理的那两个类,看来以后加载这俩的时候,都去到了代理类

先看Hyperf\Di\ClassLoader@loadClass()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public function loadClass(string $class): void
{
$path = $this->locateFile($class);

if ($path) {
include $path;
}
}

protected function locateFile(string $className): ?string
{
if (isset($this->proxies[$className]) && file_exists($this->proxies[$className])) {
$file = $this->proxies[$className];
} else {
$file = $this->getComposerClassLoader()->findFile($className);
}

return is_string($file) ? $file : null;
}
  • 果然,在进行自动加载的时候,先判断了是否在proxies里面。如果不在,走composer的老路。如果存在,则加载代理类

深入Proxy the composer class loader

1
2
3
4
5
6
7
8
9
10
11
12
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);
}

这里只对ComposerClassLoader做了处理,AnnotationRegistry::registerLoader是为了doctrine/annotations注解扫描做准备的

在hyperf3的版本,弃用了phpdoc的注解模式,所以AnnotationRegistry::registerLoader也就没有了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}

1. 设置composer的老路

  • 这里除了保留composer原有的loader,还初始化了\Hyperf\Utils\Composer,后续再看

2. 加载 .env

  • 也就是说,env函数,要在这一行之后才会生效,env的加载,使用了 vlucas/phpdotenv
  • 如果我们的项目需要在不同的时候,读取不同的env,看了这里,就简单了,比如,BASE_PATH . ‘/.env’ 可以不存在,然后在启动文件自己一个Dotenv加载就行

3. 获取配置,每个包的ConfigProviderconfig目录下 合并成一个Hyperf\Di\Annotation\ScanConfig

每一个ConfigProvider都需要有__invoke才能生效,也就是说,这里可以去执行一些东东
  • \Hyperf\Di\ConfigProvider 果然di包,就写了一些特殊执行,但我觉得这
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // Register AST visitors to the collector.
    if (! AstVisitorRegistry::exists(PropertyHandlerVisitor::class)) {
    AstVisitorRegistry::insert(PropertyHandlerVisitor::class, PHP_INT_MAX / 2);
    }

    if (! AstVisitorRegistry::exists(ProxyCallVisitor::class)) {
    AstVisitorRegistry::insert(ProxyCallVisitor::class, PHP_INT_MAX / 2);
    }

    // Register Property Handler.
    RegisterInjectPropertyHandler::register();
  • AstVisitorRegistry优先队列里面丢了两个代理类处理器
  • PropertyHandlerVisitor 在__construct里处理属性的注解 use \Hyperf\Di\Aop\PropertyHandlerTrait;
  • ProxyCallVisitor 每个方法添加特殊的__proxyCalluse \Hyperf\Di\Aop\ProxyTrait;
  • PropertyHandlerManager注册了一个Inject的回调函数,后面有大用。简单的说,就是把当前注解里面映射的类,从容器里面取出来
合并ConfigProvider和config的配置
  • 获取所有的annotations配置,即paths 需要扫描的目录ignore_annotations 忽略的注解collectors 收集器class_map 替换加载的类global_imports
  • 获取所有定义的dependencies DI的依赖关系和类对应关系
  • 获取scan_cacheable,扫描时是否使用缓存

这里内置的collectors有

  • Hyperf\Cache\CacheListenerCollector
  • Hyperf\Di\Annotation\AnnotationCollector 注解收集
  • Hyperf\Di\Annotation\AspectCollector 切面收集
  • Hyperf\ModelListener\Collector\ListenerCollector
将上面获取到的数据,初始化\Hyperf\Di\Annotation\ScanConfig

4. 添加composer的ClassMap

4. 初始化注解扫描器\Hyperf\Di\Annotation\Scanner

  • 添加忽略注解与全局导入

5. 扫扫扫

使用缓存
  • 文件存在,并且缓存配置是开启的,那么直接读取scan.cache
  • 给每个collector添加扫描结果
fork子进程进行扫描

通过子进程来完成缓存文件的生成,巧妙的避开了spl_autoload_register下会污染当前父进程的autoload的弊端。通过子进程完成扫描动作,传递结果文件给父进程,父进程直接进行自动加载即可。

1
$scanned = $this->handler->scan();

当不传入handler时,使用默认的\Hyperf\Di\ScanHandler\PcntlScanHandler,先进行 pcntl_fork之后父进程一直等待pcntl_wait子进程结束。也就是说,这个地方的$scanned,其实会返回两次,一次在父进程里(true),一次在子进程里(false)

$scanned === false 子进程
再次运行$this->deserializeCachedScanData($collectors);
  • 没有scan.cache时,不做处理
  • scan.cache时,$collectors存入上次扫描的结果
初始化 \Hyperf\Di\Annotation\AnnotationReader
  • todo
获取所有的反射类
1
$classes = ReflectionManager::getAllClasses($paths);
  • 扫描目录下的所有php文件
  • 解析文件内容,如果是class,就反射
  • 形成一个所有反射类的数组映射
清理$collectors被移除的class
1
$this->clearRemovedClasses($collectors, $classes);
  • 这个地方总是生成最新反射类扫描结果 classes.cache
  • 没有classes.cache时,array_diff不会有结果,也就不做处理
  • classes.cache时,比较上次与现在的区别,$collectors清理被移除的类
清理修改过的类文件
  • 循环所有的反射类 $classes
  • 如果文件修改时间大于scan.cache文件修改时间,即上次扫描时间,那么就清理$collectors中的当前类,重新收集该类的信息
收集注解的信息
1
$this->collect($annotationReader, $reflectionClass);

普通注解\Hyperf\Di\Annotation\AbstractAnnotation

  • 只是保存注解到AnnotationCollector
  • AnnotationCollector::collectClass($className, static::class, $this);
  • AnnotationCollector::collectProperty($className, $target, static::class, $this);
  • AnnotationCollector::collectMethod($className, $target, static::class, $this);

1. 解析class的注解

public function collectClass(string $className): void;

\Hyperf\Di\Annotation\Aspect作为特殊例子

  • 先保存注解到AnnotationCollector 注解收集器 [_c]
  • AspectLoader::load($className); 用于获取注解里面的配置信息
  • 通过代码可以发现,类的配置优先于注解的配置
  • 保存注解的配置到AspectCollector 切面收集器

2. 解析property的注解

public function collectProperty(string $className, ?string $target): void;

\Hyperf\Di\Annotation\Inject作为特殊例子

  • 获取注入的类,优先通过反射获取$reflectionProperty->getType()->getName(),再通过phpdoc去获取
  • 如果是lazy===true,那么在注入类前面加上'HyperfLazy\\'前缀,用于标记该类为懒加载
  • 保存注解到AnnotationCollector 注解收集器 [_p]

3. 解析method的注解

public function collectMethod(string $className, ?string $target): void;

\Hyperf\Cache\Annotation\Cacheable作为特殊例子

  • 如果有listener,则添加CacheListenerCollector
  • 保存注解到AnnotationCollector 注解收集器 [_m]
加载配置文件的切面
1
$this->loadAspects($lastCacheModified);
  • 先合并ConfigProvider、config.php、config/aspect.php的配置,得到最终的$aspects
  • getChangedAspects出现了aspects.cache,可以看出config/aspect.php配置的无法定义优先级
  • 获取移除和修改的切面
  • 接下来跟\Hyperf\Di\Annotation\Aspect注解的收集是一样的
生成代理类

1. 通过反射类初始化代理

1
$this->initProxiesByReflectionClassMap($this->classMap)

需要生成代理类的有两种文件

  • 文件是被切面的class,如
  • 文件内有被切面的annotations

遍历所有的classmap,符合上述条件的类,最后都放到$proxies中,示例:

1
2
3
4
5
6
7
8
9
10
11
12
array(2) {
["App\Controller\AbstractController"]=>
array(1) {
[0]=>
string(33) "Hyperf\Di\Annotation\InjectAspect"
}
["App\Controller\IndexController"]=>
array(1) {
[0]=>
string(33) "Hyperf\Di\Annotation\InjectAspect"
}
}

2. 生成代理文件

1
$this->generateProxyFiles()
生成扫描缓存 scan.cache
  • 生成扫描缓存文件,父进程要读这个文件
  • exit标志着该子进程结束
$scanned === true 父进程
  • 读取scan.cache
  • 给每个collector添加扫描结果

LazyLoader

  • 注册了一个自动加载,优先级置顶 \Hyperf\Di\LazyLoader\LazyLoader::load
  • 加载类时,如果配置文件存在或者是HyperfLazy开头的类,才会进行代理类的生成,而不是一开始就生成,大部分情况下,是请求过来的时候,这个类才会走上面的一些步骤去生成代理

总结

  • 代理composer加载器
  • 注解收集器
  • 切面收集器
  • 生成代理类。这一步决定着hyperf的注解与切面的实现
  • 懒加载优先

加载容器container

定义源工厂

1
(new DefinitionSourceFactory(true))()

先看结果

这里是把ConfigProvider和config/auto/dependencies.php合并起来,进行DI源的初始化

看下source,这里只截取了ApplicationInterfaceStdoutLoggerInterface

1
2
3
4
5
6
7
array(22) {
。。。略
["Hyperf\Contract\ApplicationInterface"]=>
string(35) "Hyperf\Framework\ApplicationFactory"
["Hyperf\Contract\StdoutLoggerInterface"]=>
string(36) "Hyperf\Framework\Logger\StdoutLogger"
}

经过autowire之后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
array(22) {
。。。略
["Hyperf\Contract\ApplicationInterface"]=>
object(Hyperf\Di\Definition\FactoryDefinition)#73 (4) {
["name":"Hyperf\Di\Definition\FactoryDefinition":private]=>
string(36) "Hyperf\Contract\ApplicationInterface"
["factory":"Hyperf\Di\Definition\FactoryDefinition":private]=>
string(35) "Hyperf\Framework\ApplicationFactory"
["parameters":"Hyperf\Di\Definition\FactoryDefinition":private]=>
array(0) {
}
["needProxy":"Hyperf\Di\Definition\FactoryDefinition":private]=>
bool(false)
}
["Hyperf\Contract\StdoutLoggerInterface"]=>
object(Hyperf\Di\Definition\ObjectDefinition)#74 (5) {
["constructorInjection":protected]=>
object(Hyperf\Di\Definition\MethodInjection)#77 (2) {
["methodName":"Hyperf\Di\Definition\MethodInjection":private]=>
string(11) "__construct"
["parameters":"Hyperf\Di\Definition\MethodInjection":private]=>
array(1) {
[0]=>
object(Hyperf\Di\Definition\Reference)#81 (3) {
["name":"Hyperf\Di\Definition\Reference":private]=>
string(0) ""
["targetEntryName":"Hyperf\Di\Definition\Reference":private]=>
string(31) "Hyperf\Contract\ConfigInterface"
["needProxy":"Hyperf\Di\Definition\Reference":private]=>
bool(false)
}
}
}
["name":"Hyperf\Di\Definition\ObjectDefinition":private]=>
string(37) "Hyperf\Contract\StdoutLoggerInterface"
["className":"Hyperf\Di\Definition\ObjectDefinition":private]=>
string(36) "Hyperf\Framework\Logger\StdoutLogger"
["classExists":"Hyperf\Di\Definition\ObjectDefinition":private]=>
bool(true)
["instantiable":"Hyperf\Di\Definition\ObjectDefinition":private]=>
bool(true)
}
}

猜想:通过上面的结果可以看到,di做了一个映射,后续再get的时候,应该就是用里面的值

normalizeDefinition 规范化di

  • 类里面存在__invoke或者是匿名函数,那使用\Hyperf\Di\Definition\FactoryDefinition初始化
  • 其余都是\Hyperf\Di\Definition\ObjectDefinition
  • 类里面的__construct,会生成\Hyperf\Di\Definition\MethodInjection
  • __construct里面的参数,会生成Hyperf\Di\Definition\Reference

将规范化的di,注入到容器

  • definitionSource 上面的normalizeDefinition
  • definitionResolver 解析调度器,那几个Definition的调度器
  • resolvedEntries 已经解决的依赖

总结

  • 初始化di,定义好dependencies源
  • 利用初始化的di生成容器
  • 容器很强大,等会用ApplicationInterface来示例是如何从容器中get
  • 在这之后,就已经可以通过容器去get其他类了,di会帮你完成一切

get 获取application

这里将会展示di是如何运行的

get()

判断是否已经解决过依赖了,如果有,就直接返回

1
2
3
if (isset($this->resolvedEntries[$name]) || array_key_exists($name, $this->resolvedEntries)) {
return $this->resolvedEntries[$name];
}

这里的写法很有意思,isset和array_key_exists都有用上

make()

1. 先获取di源,主要是为了那些未加载到dependencies源的类

1
$definition = $this->getDefinition($name);

2. 依赖注入解决

1
$this->resolveDefinition($definition, $parameters);
  • 先是\Hyperf\Di\Resolver\ResolverDispatcher::resolve,这里了定义一个DepthGuard 递归深度
    是为了防止出现死循环,依赖注入的时候,有时候会写出循环依赖的问题,这里定义了最大深度为500,也就是说,我们项目里面如果有超过500层的di,也不会执行成功。哈哈哈哈,项目里面应该不会出现吧

  • 进行强大的di,细节就不展示了,反射拿到定义源,继续get的时候,又会回到ObjectDefinition,递归一直new下去,实例化所有的__construct参数,直到构造里面再也没有DefinitionInterface为止

  • 如果是一个FactoryDefinition形式的类,那么在解析的时候,是执行了factory的__invoke

  • 简单的说,di就是一个递归、反射、实例的过程,利用程序,实现构造自动注入

  • 在hyperf里面,aop和annotations,是利用代理类进行操作的,代理类里面的__construct也会定义好依赖的类,再利用di进行一个get

所以ApplicationInterface在get的时候,实际上是运行了\Hyperf\Framework\ApplicationFactory::__invoke()方法来生成一个Application

ApplicationFactory的创建

1. 触发\Hyperf\Framework\Event\BootApplication事件

搜索源码发现,有这几个内置的监听

  • \Hyperf\Config\Listener\RegisterPropertyHandlerListener 注册了一个Value注解的回调
  • \Hyperf\DbConnection\Listener\RegisterConnectionResolverListener 注册数据库连接解析器
  • \Hyperf\ExceptionHandler\Listener\ErrorExceptionHandler 设置用户自定义的错误处理函数,规定发生错误时运行的函数
  • \Hyperf\ExceptionHandler\Listener\ExceptionHandlerListener 利用优先队列注册了异常处理器

2. command命令

  • 注解和配置文件合并
  • 利用symfony/console组件,注册所有的command

这一步骤,已经结束了php bin/hyperf.php

总结

  • 从容器中获取Application
  • 注册命令行

Start

从command命令得知,start的文件为Hyperf\Server\Command\StartServer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
protected function execute(InputInterface $input, OutputInterface $output)
{
// 检查当前环境是否满足swoole运行
$this->checkEnvironment($output);

// 初始化 服务工厂
$serverFactory = $this->container->get(ServerFactory::class)
->setEventDispatcher($this->container->get(EventDispatcherInterface::class))
->setLogger($this->container->get(StdoutLoggerInterface::class));

// 加载 服务配置
$serverConfig = $this->container->get(ConfigInterface::class)->get('server', []);
if (! $serverConfig) {
throw new InvalidArgumentException('At least one server should be defined.');
}

// 通过配置初始化 服务
$serverFactory->configure($serverConfig);

// 一键化协程
Coroutine::set(['hook_flags' => swoole_hook_flags()]);

// 启动服务
$serverFactory->start();

return 0;
}

服务配置 \Hyperf\Server\ServerConfig

相当于把 config/autoload/server.php 转化为类 (实体?),方便获取与操作

具体要启动什么服务,都是靠着这个配置文件进行,默认的server启动方式是\Hyperf\Server\Server

初始化服务 \Hyperf\Server\Server::initServers

遍历服务配置,一个一个实例相应的类

1
2
3
4
5
6
7
8
switch ($type) {
case ServerInterface::SERVER_HTTP:
return new SwooleHttpServer($host, $port, $mode, $sockType);
case ServerInterface::SERVER_WEBSOCKET:
return new SwooleWebSocketServer($host, $port, $mode, $sockType);
case ServerInterface::SERVER_BASE:
return new SwooleServer($host, $port, $mode, $sockType);
}

注册一些swoole的事件

https://wiki.swoole.com/#/server/events

  • start事件 [Hyperf\Framework\Bootstrap\StartCallback, onStart]
  • managerStart事件 [Hyperf\Framework\Bootstrap\ManagerStartCallback, onManagerStart]
  • workerStart事件 [Hyperf\Framework\Bootstrap\WorkerStartCallback, onWorkerStart]
  • workerStop事件 [Hyperf\Framework\Bootstrap\WorkerStopCallback, onWorkerStop]
  • workerExit事件 [Hyperf\Framework\Bootstrap\WorkerExitCallback, onWorkerExit]
  • pipeMessage事件 [Hyperf\Framework\Bootstrap\PipeMessageCallback, onPipeMessage]
  • request事件 [Hyperf\HttpServer\Server, onRequest]

request事件 [Hyperf\HttpServer\Server, onRequest]

除了注册该事件,还会触发\Hyperf\HttpServer\Server::initCoreMiddleware

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 public function initCoreMiddleware(string $serverName): void
{
$this->serverName = $serverName;
// 创建内核中间件
$this->coreMiddleware = $this->createCoreMiddleware();
// 上面这个内核中间件已经初始化了一次,这一行其实没用
$this->routerDispatcher = $this->createDispatcher($serverName);

$config = $this->container->get(ConfigInterface::class);
// 初始化配置的中间件
$this->middlewares = $config->get('middlewares.' . $serverName, []);
// 初始化异常处理器
$this->exceptionHandlers = $config->get('exceptions.handler.' . $serverName, $this->getDefaultExceptionHandler());
}

createCoreMiddleware

关注点:\Hyperf\HttpServer\Router\DispatcherFactory::__construct

初始化路由的时候,也会顺便把中间件也一起定义了

1
2
3
4
// 初始化注解路由,即AutoController和Controller
$this->initAnnotationRoute(AnnotationCollector::list());
// 初始化配置路由
$this->initConfigRoute();

总结

  • 通过配置文件来定义启动什么服务
  • 初始化路由调度器、中间件,用于请求分发到各个controller