php函数默认不抛异常,绝大多数内置函数出错时返回false或null;仅少数新特性(如json_decode加json_throw_on_error)或严格模式下(如datetime构造)原生抛异常。

PHP 函数默认不抛异常,得手动 throw
PHP 的绝大多数内置函数(比如 file_get_contents、json_decode、mysqli_query)在出错时**返回 false 或 null**,而不是抛出异常。这是历史原因决定的——PHP 5.0 才引入异常机制,而大量核心函数沿用了“返回错误值 + 设置错误码/消息”的老路。
所以,不能指望调用 strpos 时它自己 throw InvalidArgumentException,哪怕你传了个非字符串进去(它只会静默返回 false)。
- 想让函数抛异常,必须自己封装:检查返回值,手动
throw new Exception或更具体的异常类 - 部分扩展支持“异常模式”,比如
PDO可通过PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION开启;mysqli则需显式调用mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) - PHP 8+ 的某些函数开始原生支持异常(如
json_decode在无效 JSON 时可设flags参数触发JsonException),但仍是例外,不是通则
哪些 PHP 函数能直接 throw 异常?
真正原生抛异常的函数非常少,集中在几个新特性或严格模式下:
-
json_decode:PHP 7.3+ 支持JSON_THROW_ON_ERROR标志,传入后解析失败直接抛JsonException -
DateTime::__construct和date_create:无效日期字符串会抛Exception(PHP 5.5+ 默认行为,早期版本需显式启用) - 所有
throw语句、new实例化失败(如内存不足)、类型声明不匹配(PHP 7+ 启用严格模式后)都会触发异常 - 自定义函数里写
throw当然可以,但这不算“PHP 函数”,而是你的代码逻辑
别被文档里“throws”字样误导——很多函数文档只是标注“可能抛出异常”,实际指“在开启某选项后才抛”,比如 SimpleXML 加载失败是否抛异常,取决于 libxml_use_internal_errors 和后续处理。
立即学习“PHP免费学习笔记(深入)”;
为什么不能全局让所有函数都 throw 异常?
因为 PHP 没有这个机制,也没法安全地加。强行改写所有函数行为会破坏兼容性,甚至引发不可预知的崩溃:
- 大量现有代码依赖
=== false判断失败,如果底层突然抛异常,try/catch没包住就直接中断脚本 - 函数返回值语义混乱:比如
array_key_exists返回布尔值是设计意图,改成抛异常就失去存在意义 - 性能损耗:每次调用都做异常路径检查,对高频函数(如
isset、strlen)完全不可接受 - 错误类型难统一:文件不存在、权限不足、网络超时……这些该抛
RuntimeException还是LogicException?PHP 不可能替你决策
实际项目中怎么安全地用异常?
推荐分层处理,别幻想“一劳永逸”:
- 数据库操作:用
PDO并开启PDO::ERRMODE_EXCEPTION;mysqli调一次mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)(注意只对后续调用生效) - JSON 处理:始终传
JSON_THROW_ON_ERROR,避免手动检查json_last_error() - 文件操作:自己封装
safe_file_get_contents,检查file_exists和is_readable,再调用原生函数,失败就throw new RuntimeException - 不要 catch
Exception大包抄,优先捕获具体子类(如PDOException、JsonException),否则掩盖了真正该暴露的问题
最常被忽略的一点:异常不是银弹。有些场景用返回值更清晰,比如一个函数要返回 null 表示“未找到”,抛异常反而让调用方不得不写 try/catch 去处理正常业务分支——这违背了异常的设计初衷。











