zf1升级自定义错误处理需禁用errorhandler插件并改用response对象拦截异常:设noerrorhandler、throwexceptions(false)、returnresponse(true),dispatch后用isexception()判断,按异常类型分发错误页面,php 8.7需额外处理error类。

Zend Framework 老项目(尤其是 ZF1)升级自定义错误处理,核心不是“换新写法”,而是绕开已被弃用的 Zend_Controller_Plugin_ErrorHandler 插件,改用响应对象直接拦截异常。ZF1 后期版本(1.12+)已明确不鼓励插件式错误处理,因为其无法区分 404 和 500,且与 throwExceptions 冲突。
禁用 ErrorHandler 插件并启用 returnResponse
老项目默认启用 Zend_Controller_Plugin_ErrorHandler,它会提前捕获异常、重定向到错误控制器,导致你无法在 dispatch 后统一处理。必须显式关闭:
-
$front->setParam('noErrorHandler', true)—— 关键一步,否则后续逻辑无效 -
$front->throwExceptions(false)—— 防止未捕获异常直接崩掉脚本 -
$front->returnResponse(true)—— 让dispatch()返回Zend_Controller_Response_Http实例而非直接输出
这三行必须在 dispatch() 前调用,顺序无关紧要,但缺一不可。
在 dispatch 后检查 response 的 isException() 状态
不再依赖插件回调或 try/catch 包裹 dispatch,而是信任 response 对象的自我报告能力:
$response = $front->dispatch();
if ($response->isException()) {
$exceptions = $response->getException(); // 注意:返回数组,即使只有一个异常
$e = $exceptions[0];
handleException($e);
} else {
$response->sendHeaders();
$response->outputBody();
}
常见坑点:
-
$response->getException()总是返回array,哪怕只抛出一个异常,别直接当对象用 - 404 异常在 ZF1 中通常为
Zend_Controller_Dispatcher_Exception,$e->getCode()不可靠,应优先用get_class($e)判断 - 不要在
handleException()里再调用$response->sendHeaders(),此时 headers 很可能已发送
按异常类型分发到不同错误页面(非仅靠 HTTP 状态码)
ZF1 的 ErrorCode 属性不统一(路由失败、动作不存在、ACL 拒绝都可能返回 0),不能只 switch $e->getCode()。更稳妥的方式是类型匹配:
function handleException(Exception $e) {
if ($e instanceof Zend_Controller_Dispatcher_Exception) {
header('HTTP/1.1 404 Not Found');
include PROJECT_ROOT . '/error/404.html';
} elseif ($e instanceof Zend_Db_Adapter_Exception || $e instanceof Zend_Config_Exception) {
header('HTTP/1.1 500 Internal Server Error');
include PROJECT_ROOT . '/error/500.html';
} else {
header('HTTP/1.1 500 Internal Server Error');
error_log('[ZF1 ERROR] ' . get_class($e) . ': ' . $e->getMessage());
include PROJECT_ROOT . '/error/500.html';
}
exit;
}
注意:
- 务必用
header()显式设置状态码,ZF1 的renderExceptions(true)已被废弃且不兼容此模式 - 避免在生产环境显示
$e->getTraceAsString(),调试时可临时加日志,但不要输出到 HTML -
PROJECT_ROOT必须定义且路径正确,老项目常因相对路径错位导致 include 失败却静默忽略
PHP 8.7 兼容性需额外处理 E_WARNING 类错误
ZF1 原生不处理传统 PHP 错误(如 E_WARNING),但在 PHP 8.7 下,这些错误会被自动转为 Error 实例并继承 Throwable。若你在控制器中写了类似 file_get_contents('/nonexistent'),它现在会触发异常流程,但类型是 TypeError 或 ValueError,而非 ZF1 原有异常类。
解决方法是在 handleException() 开头加兜底判断:
if ($e instanceof Error && !($e instanceof Exception)) {
// PHP 8.7 新增的 Error 类型,统一归为 500
header('HTTP/1.1 500 Internal Server Error');
include PROJECT_ROOT . '/error/500.html';
exit;
}
这个分支必须放在最前面,否则会被后面的 instanceof Exception 分支漏掉——因为 Error 不是 Exception 的子类(尽管同属 Throwable)。
真正麻烦的不是代码改几行,而是老项目里散落各处的 try/catch 和 trigger_error() 调用。它们不会自动进入这套新流程,得逐个翻查控制器和模型里的错误分支,把裸 throw 改成抛出 ZF1 兼容的异常类,否则 404/500 分流会失效。











