PHP执行超时(max_execution_time)无法被Yii2 errorHandler捕获,因属致命错误、直接中断进程;数据库/cURL超时可捕获并触发errorAction,需显式try-catch处理。

Yii2 中超时错误(如 max_execution_time 超限、数据库查询超时、cURL 超时等)不会自动触发 errorHandler 的 errorAction,因为 PHP 的致命超时(Fatal Error)无法被常规异常机制捕获——它直接中断脚本,不抛出可 catch 的异常。所以“捕获后显示”这事,得先分清是哪类超时。
PHP 执行超时(max_execution_time)根本捕获不到
这是最常被误解的一点:当脚本运行超过 max_execution_time 限制,PHP 会直接发送 Fatal error: Maximum execution time of X seconds exceeded 并终止进程,yii\web\ErrorHandler 完全无从介入。
- 你无法在
try/catch里捕获它,也无法通过set_error_handler()或register_shutdown_function()可靠拦截并跳转到友好页面 -
YII_DEBUG=true下你会看到原始致命错误页;YII_DEBUG=false下通常返回空白或 500 状态码,但不会走site/error - 真正能做的只有预防:拆分耗时任务、用
set_time_limit(0)(谨慎!)、加进度检查点、或改用队列(如 Yii2-queue)异步执行
数据库/HTTP 客户端超时可捕获,但必须主动处理
这类超时属于可抛异常的范畴,比如 PDOException(MySQL 连接超时)、yii\db\Exception(Query timeout)、GuzzleHttp\Exception\ConnectException(cURL 超时),它们能被正常 catch 并交由 errorHandler 渲染。
- 确保你在配置中启用了
'errorAction' => 'site/error',且SiteController::actions()正确注册了'error' => ['class' => 'yii\web\ErrorAction'] - 在业务代码中显式 try-catch,例如:
try { $rows = Yii::$app->db->createCommand('SELECT SLEEP(10)')->queryAll(); } catch (\yii\db\Exception $e) { if (strpos($e->getMessage(), 'timeout') !== false) { Yii::$app->errorHandler->exception($e); } throw $e; } - 注意:不是所有超时都会被包装成
yii\db\Exception,原始PDOException需要你自己 catch 后重新 throw 或手动调用Yii::$app->errorHandler->exception()
自定义超时响应需绕过 errorHandler 直接控制输出
如果你需要对某类超时(如 API 接口)返回 JSON 错误而非 HTML 页面,就不能依赖 errorAction,而应在控制器动作中做判断和响应。
- 在
beforeAction()或具体 action 内部设超时标记,例如:set_time_limit(30),再配合microtime(true)计时 - 捕获到超时相关异常后,直接返回 JSON:
return $this->asJson([ 'code' => 504, 'message' => 'Request timeout', ]); - 避免在超时处理逻辑中调用任何可能再次阻塞的操作(如日志写入磁盘、DB 查询),否则可能二次超时
超时问题的棘手之处在于:它横跨 PHP 引擎层、扩展层(PDO/cURL)、框架层三层,没有统一入口。你看到的“没跳转到 error 页面”,大概率不是配置错了,而是压根没走到框架的异常处理链路里——这时候查 phpinfo() 里的 max_execution_time、数据库连接参数里的 connect_timeout 和 wait_timeout,比调 errorHandler 配置更管用。


















