迁移Laravel至Hyperf3.1需分五步:一验环境(Swoole协程启用、PHP≥8.1、shortname关闭);二替同步I/O为协程调用并改造第三方包;三重构生命周期(依赖绑定改配置、中间件重路径注册、路由转注解);四适配数据库(手动建库、迁移文件规范、禁用DB::table);五切换调试方式(禁用dd、启用协程日志、注意Xdebug限制)。

从Laravel项目迁移到Hyperf3.1,不是简单替换框架入口,而是要重审I/O模型、生命周期管理与协程安全边界。你必须确认Swoole 5.1+已启用协程,否则迁移后服务启动即失败。
第一步:验证运行环境是否满足Hyperf3.1硬性要求
执行 php --ri swoole,检查输出中 coroutine => enabled 且 version ≥ 5.1.0;若显示 disabled 或版本低于5.1,Hyperf3.1进程将直接退出,不报详细错误。
确认 php.ini 中已设置 swoole.use_shortname=0,否则注解扫描会因函数名冲突而静默失效。
检查 PHP 版本是否 ≥ 8.1 —— Hyperf3.1 不再支持 PHP 8.0 及以下版本,composer install 时会跳过关键组件而不提示。
第二步:剥离 Laravel 的同步依赖链
方法一:识别并替换阻塞式 I/O 调用
把所有 file_get_contents()、curl_exec()、sleep() 替换为 Hyperf 提供的协程版:Co::readFile()、HttpClient->get()、Co::sleep()。原生函数在协程中会阻塞整个 worker 进程。
方法二:改造第三方包调用路径
若使用 spatie/laravel-permission,不能直接 require,需改用 hyperf/permission 并重写 GateServiceProvider 注册逻辑;未适配协程的包会在并发请求下出现权限缓存错乱或数据库连接泄漏。
【关键前提】 所有自定义 Artisan 命令必须重写为 Hyperf Command 类,继承 Hyperf\Command\Command,原 Laravel Illuminate\Console\Command 在常驻内存下会因静态属性残留导致命令间数据污染。
第三步:重构服务生命周期与容器绑定
① 删除 app/Providers/AppServiceProvider.php 中所有 $this->app->singleton() 调用。
Hyperf3.1 容器默认启用单例自动解析,手动绑定需通过 config/autoload/dependencies.php 声明,否则服务实例在协程间共享状态引发竞态。
② 将中间件类从 app/Http/Middleware 移至 app/Middleware,命名空间改为 App\Middleware,并在 config/autoload/middlewares.php 中显式注册——Laravel 自动发现机制在 Hyperf 中完全失效。
③ 把 Route::get() 等闭包路由全部转为注解路由:
在控制器方法上添加 @GetMapping(path="/users"),同时确保 config/autoload/annotations.php 已启用扫描路径 App\Controller。
这一步操作起来很简单,直接把文件拖进去就行,但漏掉任何一条路径配置,注解就不会被加载,接口返回 404 且无日志提示。
第四步:数据库与迁移适配
运行 php bin/hyperf.php migrate 前,先手动创建目标数据库(Hyperf 不提供 migrate:install),否则命令卡死在连接阶段。
迁移文件必须放在 app/Migrations/ 下,命名格式严格为 YYYY_MM_DD_HHMMSS_create_users_table.php,Laravel 的 2023_01_01_000000_create_users_table.php 会被忽略。
迁移类需继承 Hyperf\Database\Migrations\Migration,且 up() 方法内所有 DB 操作必须使用 $this->schema 实例,不能调用 DB::table() —— 后者在协程中会复用连接池句柄导致事务混乱。
第五步:调试与日志行为切换
关闭所有 var_dump() 和 dd() 调用:它们会中断协程调度,使后续请求挂起;改用 Log::info() 或 Hyperf\Logger\LoggerFactory::get()->info()。
启用协程上下文追踪:在 config/autoload/processes.php 中添加 'logger' => ['class' => \Hyperf\Logger\Process\LoggerProcess::class],否则 go(function () { Log::info('test'); }); 的日志可能丢失。
Xdebug 断点在协程中仅对主协程生效,子协程断点会被跳过;调试高并发场景必须配合 --pretend 参数查看 SQL 执行计划,而非依赖单步执行。


















