必须用 Hyperf\Utils\Coroutine::inCoroutine() 判断是否在协程环境,因为 Coroutine::id() 或 Co::pid() 在非协程环境会触发无法捕获的致命错误,而 inCoroutine() 内部安全探测、返回 bool 且绝不会 fatal。

必须用 Hyperf\Utils\Coroutine::inCoroutine() 判断,其他方式不可靠。 直接调用 Coroutine::id() 或 Co::pid() 在非协程环境会触发致命错误,无法被 try/catch 捕获,服务直接崩溃。
为什么不能用 Coroutine::id() 或 Co::pid()
这两个函数在非协程上下文(如命令行脚本、单元测试、php bin/hyperf.php gen:command 创建的同步命令)中调用,会立即抛出:Fatal error: Uncaught Swoole\Error: Must be called in the coroutine environment。这不是异常,是 PHP 致命错误,连自定义错误处理器都收不到。
常见踩坑场景包括:
- 在
Command类的handle()方法里直接写Coroutine::id() - 在
tests/下的 PHPUnit 测试中调用协程 API 前没做环境判断 - 把协程安全的工具类(如封装了
Channel的队列)误用于php -r临时调试脚本
Hyperf\Utils\Coroutine::inCoroutine() 是唯一安全入口
这个方法内部做了防御性封装:先检查 Swoole 扩展是否存在,再尝试轻量级探测,全程不触发任何协程 API 调用。返回 bool,且绝不会 fatal。
典型用法:
use Hyperf\Utils\Coroutine;
if (Coroutine::inCoroutine()) {
// 安全使用 co::sleep(), Channel, Redis::pool() 等
$channel = new Channel(1);
$channel->push('data');
} else {
// 降级为同步逻辑,比如用 array_push / file_get_contents
$data = ['data'];
}
注意:Coroutine::inCoroutine() 返回 true 仅表示“当前线程已进入协程调度器”,不代表你一定在「用户创建的协程」里——Swoole Worker 进程启动后主协程也满足该条件。
哪些地方必须加判断
以下位置不加判断就调协程 API,90% 会炸:
- 所有
Command类的handle()方法(Hyperf 默认以同步模式运行命令) - 单元测试中的
setUp()或测试方法体(PHPUnit 不启动 Swoole) - 自定义的
Process类中,若未显式配置'enable_coroutine' => true - HTTP 中间件或控制器里调用外部 SDK(该 SDK 内部硬编码了
co::sleep())
尤其注意:Hyperf 的 parallel() 和 wait() 函数内部已自动处理协程环境,但它们的回调函数执行时仍需自行判断——因为回调可能被调度到任意协程,包括刚 fork 出来的子协程。
别依赖 php_sapi_name() 或进程名判断
php_sapi_name() === 'cli' 不能说明是否在协程中——php bin/hyperf.php start 也是 CLI,但它跑在 Swoole 协程里;反过来,php -S 启动的内置服务器是 CLI,但完全无协程支持。
同理,检查进程是否存在 swoole 字符串(如 ps aux | grep swoole)只是运维手段,无法在代码中做运行时决策。
真正需要的是「代码执行路径上的确定性判断」,只有 Hyperf\Utils\Coroutine::inCoroutine() 提供这个能力。漏掉它,协程代码就等于裸奔。


















