在这里插入图片描述

今天将正式踏入 Hyperf 的世界。Hyperf 是一个基于 Swoole 的、高性能的协程框架,它将之前几天我们手写的各种机制(HTTP 服务、协程、依赖注入等)进行了企业级的封装和抽象。理解它的骨架和生命周期,是后续微服务开发的前提。


今日目标

  1. 使用 Composer 创建 Hyperf 项目,并成功启动服务。
  2. 彻底理解 Hyperf 的骨架目录结构,知道每块代码应该放在哪里。
  3. 搞懂 Hyperf 一次请求的完整生命周期:Request → 路由 → 中间件 → 控制器 → Response。
  4. 能够编写一个自定义路由和控制器,并访问成功。
  5. 观察常驻内存下,修改代码后需要手动重启的现象,加深对 Swoole 特性的理解。

一、环境准备与 Hyperf 安装 (约 1 小时)

我们继续使用之前的 swoole-course 目录和 Docker 容器,但今天的内容放在一个全新的 Hyperf 项目中。

1. 进入容器并检查环境
cd swoole-course
docker-compose exec swoole bash

确认 Swoole 扩展和 PHP 版本:

php -v           # 应为 8.2.x
php -m | grep swoole   # 必须有 swoole

Hyperf 需要一些基础扩展,我们的 phpswoole/swoole:5.1-php8.2 镜像已经全部包含,无需额外安装。

2. 使用 Composer 创建 Hyperf 项目

我们将项目创建在容器内的 /var/www/hyperf-app 目录,由于目录已挂载,宿主机也能直接编辑代码。

cd /var/www
composer create-project hyperf/hyperf-skeleton hyperf-app

如果网络较慢,可先配置 Composer 中国镜像:

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

安装过程中会提示一些选项,对于第一次体验,我们可以全部按回车使用默认值(比如数据库选择 hyperf/database 等,后续会用到)。如果不想交互,可以使用 --no-dev 选项,但建议保留开发依赖。等待安装完成,看到 success 即可。

3. 检查项目结构

进入项目目录,看看生成了什么:

cd hyperf-app
ls -la

你会看到如下主要目录和文件:

app/            # 应用核心代码
bin/            # 启动脚本
config/         # 所有配置文件
runtime/        # 运行时日志、缓存
vendor/         # Composer 依赖
.env            # 环境变量
composer.json
4. 配置环境变量

复制默认环境文件:

cp .env.example .env

打开 .env,确认 APP_ENV=devDB_ 相关配置(今天不用数据库,可以不管)。关键点:Hyperf 默认监听 0.0.0.0:9501,与之前我们手工创建的 Swoole HTTP 服务器端口一致。


二、知识核心:Hyperf 骨架与生命周期 (约 1.5 小时)

1. Hyperf 与 Swoole 的关系

Hyperf 是完全建立在 Swoole 协程之上的。它不替代 Swoole,而是提供了:

  • 依赖注入容器(PSR-11)
  • AOP 面向切面编程
  • 注解机制
  • 丰富的协程客户端(MySQL、Redis、RPC 等)

你可以把 Hyperf 看作一个装备精良的“太空舱”,而我们周一至周三写的代码好比是“手工打造的火箭零件”。今天我们要钻进去看看内部构造。

2. 骨架目录深度剖析
目录/文件作用你将放置的内容
app/业务核心,包含控制器、模型、中间件、自定义注解等控制器 Controller、模型 Model、中间件 Middleware
app/Controller/默认控制器目录所有 HTTP 控制器,每个方法对应一个路由
config/所有配置文件,覆盖框架默认值autoload/ 下的文件会被自动加载,如数据库、Redis、路由配置
config/routes.php路由配置文件定义 URL 到控制器的映射
config/autoload/dependencies.php依赖注入的绑定关系接口到实现的映射
bin/hyperf.php应用启动入口一般不需要修改
runtime/日志、缓存、编译后的代理类等所有运行时产生的文件,可随时删除
vendor/Composer 依赖绝不要手动修改
.env环境变量数据库密码、应用密钥等

重点config/ 目录下的配置分为 autoload/ 和其他。autoload/ 下的配置文件不需要显式引入,框架会自动扫描合并。这是 Hyperf 的约定。

3. 一次请求的生命周期(核心!)

理解这个流程,后续开发才能游刃有余。我们以一个访问 /index/index 的请求为例:

  1. Swoole HTTP 服务器接收到请求,触发 onRequest 回调。
  2. Hyperf 框架核心接管,从容器中解析 Hyperf\HttpServer\Server
  3. 创建协程上下文:为该请求创建全新的协程和专属的 Context。
  4. 路由匹配:根据 config/routes.php 找到对应的控制器和方法。
  5. 全局中间件执行CoreMiddleware):处理请求和响应,如解析 JSON 体、设置协程上下文等。
  6. 路由级中间件:按顺序执行,可以进行权限校验、日志记录等。
  7. 控制器方法调用:依赖注入容器会自动解析控制器构造函数和方法的参数,然后执行。
  8. 返回响应:控制器返回值或 Response 对象交给 Swoole 的 $response->end()
  9. 清理协程上下文:请求结束,协程销毁。

图解

Request → [路由匹配] → [全局中间件] → [路由中间件1] → [路由中间件2] → [控制器] → Response

中间件可以在控制器之前或之后执行(通过 $handler->handle($request) 分割)。这个机制我们明天会亲手实践。


三、实战:启动项目并编写第一个接口 (约 2 小时)

1. 启动 Hyperf 服务

在项目根目录下执行:

php bin/hyperf.php start

你会看到熟悉的 Swoole 启动信息,显示 Worker 进程数、监听端口等。

[INFO] Server listening on 0.0.0.0:9501

注意:如果之前的手写 Swoole 服务器还在运行,先 Ctrl+C 关闭,端口才不会冲突。

现在打开浏览器访问 http://localhost:9501,会看到 Hyperf 的欢迎页面,显示 “Hello Hyperf.”。这说明框架已成功运行。

2. 分析默认路由与控制器

打开 config/routes.php

<?php
use Hyperf\HttpServer\Router\Router;

Router::get('/favicon.ico', function () {
    return '';
});
Router::get('/', function () {
    return 'Hello Hyperf.';
});

可见,默认是闭包路由。我们再看看控制器路由的样子(被注释掉的):

Router::get('/hello-hyperf', [App\Controller\IndexController::class, 'index']);

取消这行注释(如果存在),然后打开 app/Controller/IndexController.php

<?php
declare(strict_types=1);
namespace App\Controller;

use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\RequestMapping;

class IndexController extends AbstractController
{
    public function index()
    {
        $user = $this->request->input('user', 'Hyperf');
        $method = $this->request->getMethod();
        return [
            'method' => $method,
            'message' => "Hello {$user}.",
        ];
    }
}

注意:Hyperf 控制器继承了 AbstractController,可以直接使用 $this->request$this->response

3. 动手:添加一个新路由和控制器

我们现在自己创建一个接口。

第一步:创建控制器 app/Controller/UserController.php

<?php
declare(strict_types=1);
namespace App\Controller;

class UserController extends AbstractController
{
    public function info(int $id)
    {
        return [
            'code' => 200,
            'data' => [
                'id' => $id,
                'name' => 'Swoole',
                'email' => 'swoole@hyperf.io',
            ]
        ];
    }
}

第二步:在 config/routes.php 中添加路由(在文件最底部):

// 添加一个 GET 路由,带参数
Router::get('/user/{id:\d+}', [App\Controller\UserController::class, 'info']);
4. 测试新接口

保存文件后,你会发现并没有自动生效。这是因为 Swoole 是常驻内存的,代码只在启动时加载。必须重启服务才能看到新代码。

回到终端,Ctrl+C 停止服务,然后重新执行 php bin/hyperf.php start。或者使用 Hyperf 的 watch 热重启(后续会学)。

现在访问:

curl http://localhost:9501/user/123

输出:

{"code":200,"data":{"id":123,"name":"Swoole","email":"swoole@hyperf.io"}}

成功!你已经完成了第一个 Hyperf 接口。

5. 使用注解路由(可选,提前感受)

Hyperf 还支持注解方式定义路由,这样不需要修改 routes.php。将 UserController 改成:

<?php
declare(strict_types=1);
namespace App\Controller;

use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\RequestMapping;

#[Controller(prefix: '/user')]
class UserController extends AbstractController
{
    #[RequestMapping(path: 'info/{id:\d+}', methods: 'get')]
    public function info(int $id)
    {
        // ...
    }
}

然后删除 routes.php 中对应路由,重启服务,一样有效。注解是 Hyperf 强大的特性之一,我们将在第二周深入学习。


四、成果测试与生命周期观察 (约 1 小时)

1. 观察常驻内存与变量共享

我们做一个小实验来理解常驻内存的影响。

修改 UserController,在类中增加一个静态属性:

class UserController extends AbstractController
{
    private static int $count = 0;
    
    public function info(int $id)
    {
        self::$count++;
        return [
            'count' => self::$count,
            'data' => ['id' => $id]
        ];
    }
}

重启服务,用 curl 连续访问 http://localhost:9501/user/1,你会看到 count 依次递增:1, 2, 3…

{"count":1,"data":{"id":1}}
{"count":2,"data":{"id":1}}

这说明静态属性在多个请求之间是共享的!这就是常驻内存的危险与优势:你可以用来做计数器,但也要小心全局状态污染。在生产中,应该使用协程安全的工具(如 ContextRedis)来存储请求间数据,而不是静态变量。

2. 生命周期验证:追踪中间件和控制器执行

Hyperf 提供了非常方便的日志通道,我们可以在控制器和未来的中间件中打印日志,观察执行顺序。但今天我们可以简单地:
app/Controller/IndexController.phpindex 方法中,添加 var_dump('Controller executed');,然后重启服务,访问,终端会直接输出。这在实际开发中非常有用。

3. 测试清单
检验项方法通过标准
Hyperf 成功启动php bin/hyperf.php start无报错,显示监听 9501
欢迎页浏览器访问 http://localhost:9501显示 “Hello Hyperf.”
自定义路由访问curl http://localhost:9501/user/5返回 JSON 包含用户信息
静态变量递增现象连续 curl 同一接口count 字段每次递增
重启后生效修改控制器返回值,重启后 curl返回修改后的内容
热重启缺失修改代码不重启,curl返回结果不变,证明常驻内存
4. 进阶思考与自测
  • 问题:为什么 Hyperf 启动后,修改代码必须重启?Swoole 的 reload 机制是怎样的?你会看到 bin/hyperf.php start 启动的是 Master 和 Manager 进程,Worker 进程负责处理请求。修改文件后可以发送 SIGUSR1 信号给 Manager 实现热重启。Hyperf 提供了 php bin/hyperf.php server:watch 命令支持文件监听(需要额外安装 hyperf/watcher 组件,我们之后会用到)。
  • 任务:尝试在 config/autoload/ 中新建一个 custom.php,返回一个数组配置,并在控制器中通过 config('custom.key') 读取(使用 Hyperf\Utils\ApplicationContext 或注入 ConfigInterface)。

五、今日作业与学习产出

  1. 提交代码:将整个 hyperf-app 项目提交到 Git 仓库(注意 .env 不要提交敏感信息,可提交 .env.example)。
  2. 绘制生命周期图:使用流程图工具或手绘,画出从 HTTP 请求进入到响应用户的完整过程,标注核心中间件、路由、控制器位置。
  3. 学习笔记:记录 Hyperf 骨架目录的作用,以及常驻内存带来的优缺点。
  4. 挑战任务:实现一个 GET /time 接口,返回当前服务器时间,但要求时间格式通过配置文件 config/autoload/custom.php 中的 time_format 项控制。考验你对配置读取的掌握。

经过今天的学习,你已经能熟练搭建 Hyperf 工程,理解其运行机制,为明天深入依赖注入、注解、AOP 等核心特性打下了坚实基础。

更多推荐