error 和 exception 均实现 throwable 接口但属并列分支,catch(exception $e) 无法捕获 typeerror 等 error 子类,必须显式用 catch(error $e) 或统一用 catch(throwable $e) 区分处理。

Error 和 Exception 都实现 Throwable 接口,但不能混用 catch 块捕获——直接写 catch (Error $e) 是合法的,而试图用 catch (Exception $e) 捕获 TypeError 会失败。
catch Error 必须显式声明类型
PHP 8.3 中,Error 子类(如 TypeError、ParseError、ArgumentCountError)不再被 catch (Exception $e) 隐式覆盖。它们是独立于 Exception 的分支。
- 错误写法:
catch (Exception $e)—— 漏掉所有Error类型,比如TypeError仍会冒泡为致命错误 - 正确写法:分别捕获,或统一用
catch (Throwable $e) - 若只想处理
Error(如记录崩溃但不干预业务异常),必须明确写catch (Error $e) -
catch (Throwable $e)是最宽泛的兜底,但注意它也会捕获Exception,需在内部用get_class($e)或$e instanceof区分
TypeError 不再静默转换,必须提前防御
PHP 8.3 对类型声明更严格,函数参数/返回值不匹配时直接抛 TypeError,而不是尝试强制转换。这会让很多旧逻辑突然中断。
- 例如:
function foo(int $x): string { return (string)$x; },调用foo("123")将抛TypeError,不是返回"123" - 不能靠
set_error_handler()拦截TypeError——它是Error,不是传统E_WARNING - 真正可行的容错方式只有两种:
try/catch (TypeError $e),或在调用前用is_int()、filter_var(..., FILTER_VALIDATE_INT)等做预检 - 注意:预检成本略高,而
catch有性能开销;高频路径建议预检,低频或不可控输入(如 API 请求体)建议catch
display_errors=0 不等于错误消失,log_errors 才决定是否落盘
线上环境设了 ini_set('display_errors', '0'),不代表错误被忽略——若 log_errors 关闭,错误就真的丢了。
- 必须配对使用:
ini_set('display_errors', '0'); ini_set('log_errors', '1'); -
error_log路径要可写,且注意 FPM 下该设置只对当前请求生效;若用 opcache,修改后需清缓存或重启 - CLI 模式下
display_errors默认为'0',但log_errors也默认关,容易误以为没报错 - 调试时可临时加一行:
error_log("DEBUG: " . print_r($_POST, true), 3, "/tmp/php-debug.log");,绕过配置限制
#[AllowDynamicProperties] 不是错误处理,而是避免弃用警告
PHP 8.3 对动态属性触发的是 E_DEPRECATED,不是 Error 或 Exception,所以不会进 catch 块,也不会中断执行,但会写进日志并影响性能。
- 它不是运行时错误,因此
try/catch完全无效 - 若你看到 “Creation of dynamic property XXX is deprecated”,说明代码在向未声明属性赋值,应改用数组、
stdClass或显式声明 -
#[AllowDynamicProperties]只是压制警告,不解决设计问题;仅用于遗留 ORM 或配置类等确实需要动态结构的场景 - 静态分析工具(如 PHPStan)会把动态属性标为 error,加注解只是让工具“闭嘴”,不是让代码更健壮
最易被忽略的一点:PHP 8.3 的 TypeError 和 ParseError 在 finally 块中仍可能被抛出——也就是说,finally 不是绝对安全区,里面调用的函数也可能触发新 Error,覆盖原异常。真要保底,得在 finally 内部再套一层 try/catch。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











