# [Bug] 协程模式下 `require_once` 加载 controller 存在 race,并发触发 `Cannot redeclare class` 致 worker 崩溃
- Dominant language
- PHP
- Stars
- 2.7k
- Forks
- 251
- PR merge metrics
- No merged PRs in 30d
Description
## 环境
- webman-framework: **v2.2.2**
- workerman: 5.x(含 `workerman/coroutine` 1.1.5)
- PHP: 8.4.4 (NTS),Linux
## 问题描述
安装 `workerman/coroutine` 后,webman 默认以 PHP Fiber 协程处理请求:
```php
// vendor/workerman/workerman/src/Worker.php:2655-2660
match (Worker::$eventLoopClass) {
Swoole::class, Swow::class, Fiber::class => Coroutine::create($callback),
default => (new \Fiber($callback))->start(),
};
```
但框架内部加载 controller 仍用传统 `require_once`:
- `vendor/workerman/webman-framework/src/Route.php:680-681`
- `vendor/workerman/webman-framework/src/App.php:1223-1224`
`require_once` 的"已加载去重"在 Fiber 协程并发下**不原子**:多个协程同时首次解析到同一 controller,各自穿过 `require_once` 守卫、重复 include 同一文件 → `Cannot redeclare class` 编译错误 → worker 崩溃 → 上游网关 502。
## 复现
1. `composer require workerman/coroutine`
2. 准备一个 controller `app/controller/FooController.php`,路由 `/foo`
3. 高并发首次加载:
```bash
seq 1 100 | xargs -I{} -P 50 curl http://127.0.0.1:8787/foo
```
4. `runtime/logs/workerman.log` 间歇出现:
```
Worker[...] process terminated with ERROR: E_COMPILE_ERROR
"Cannot redeclare class app\controller\FooController
(previously declared in .../FooController.php:N)
in .../FooController.php on line N"
```
**关键特征:同一文件、同一行**被声明两次。低并发不易触发,生产真实流量下偶发 502。
## 根因
- 协程默认开启且无法配置关闭(`default` 分支硬编码 Fiber);
- `require_once` / autoloader 在 PHP 协程下非原子,是语言层限制;但框架作为协程的启用方,应在加载层做协程安全处理。
## 期望
框架在协程模式下对 controller / 类文件加载做协程安全处理,建议任一:
1. `onWorkerStart` 启动期全量预加载所有 controller;
2. 用 `workerman/coroutine` 的 `Locker` 协程锁包裹 `require_once`;
3. 至少在文档明确提示协程模式下的类加载 race 及规避方式。
## 临时绕过(项目侧)
给类文件加文件级 guard,`namespace` 声明后紧跟:
```php
namespace app\controller\admin;
if (class_exists('app\controller\admin\ConfigController', false)) { return; }
```
文件被二次 include 时首句即 `return`,不执行到 `class` 声明,redeclare 在源头阻断。本项目对 29 个 controller 加 guard 后,100 并发压测零 crash。
## 证据
```
2026-07-22 02:32:13 pid:33055 Worker[33055] process terminated with ERROR:
E_COMPILE_ERROR "Cannot redeclare class app\controller\admin\ConfigController
(previously declared in /.../ConfigController.php:15)
in /.../ConfigController.php on line 15"
```
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the Fiber dispatch in vendor/workerman/workerman/src/Worker.php:2655-2660, then inspect the require_once calls in vendor/workerman/webman-framework/src/Route.php:680-681 and App.php:1223-1224. Reproduce the issue with workerman/coroutine, the /foo controller, and the provided concurrent curl command. Done means the first-load race no longer causes Cannot redeclare class or worker crashes under concurrent requests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100