当前位置:首页 > 技术 > 正文内容

PHP 预定义异常及使用场景

代码老兵 技术 17


PHP 里所有可 throw 的对象都属于 Throwable。官方预定义分成两大类:

Throwable
├── Exception   // 业务、逻辑、可预期异常(用户代码逻辑中应该捕获并处理的异常)
└── Error       // PHP 运行时错误(如语法错误、类型错误)


Error 主线

这是 PHP 7+ 最重要的预定义异常体

Error
├── ArithmeticError
│   └── DivisionByZeroError
├── AssertionError
├── CompileError
│   └── ParseError
├── TypeError
│   └── ArgumentCountError
├── ValueError
├── UnhandledMatchError
├── FiberError
└── RequestParseBodyException

1)TypeError

表示:类型不符合声明

参数类型错误

function add(int $a, int $b) {}

add("1", []);

返回值类型错误

function getName(): string
{
   return 123;
}

属性类型错误

class User {
   public int $age;
}

$user->age = "18";

本质: 值存在,但类型错了

2)ArgumentCountError

它是 TypeError 子类。

表示:参数数量不对

strlen();

3)ValueError

PHP 8 很重要的新成员。

表示:类型正确,但值非法

示例

str_repeat("a", -1);

类型没错:

-1 // int

但值无效。

和 TypeError 的本质区别

TypeError

strlen([]);

类型错。

ValueError

str_repeat("a", -1);

类型对,值错。

4)ArithmeticError

表示:算术计算错误父类

它最常见子类是:

DivisionByZeroError

5)DivisionByZeroError

intdiv(10, 0);

注意

普通 / 有时只是 warning:

10 / 0

而:

intdiv()

一定抛异常。

6)AssertionError

来自:

assert()
assert($age > 0);

失败后抛它。

用途

  • 调试

  • 开发阶段契约验证

  • 测试辅助

7)CompileError

表示:编译阶段错误

子类:

ParseError

8)ParseError

语法解析失败。

典型场景

eval('$a = ;');

适用范围

  • eval

  • 动态模板

  • DSL

  • 动态生成 PHP 代码

9)UnhandledMatchError

PHP 8 match 专属。


$status = match($code) {
   200 => 'ok',
};

传:

404

就会抛。因为 match 是:严格穷举表达式, 不像 switch 。

10)FiberError

PHP Fiber(协程)相关异常。

触发场景

非法 Fiber 状态切换:

$fiber->resume();
$fiber->resume(); // 非法

主要用于

  • 协程框架

  • 异步 IO

  • Swoole / ReactPHP 风格系统

11)RequestParseBodyException

较新的请求体解析异常。

通常出现在:

  • multipart/form-data

  • request body parse

  • 上传解析

属于底层 HTTP 请求解析异常。


Exception 主线

Exception
├── ErrorException
├── ClosedGeneratorException
├── LogicException
│   ├── BadFunctionCallException
│   ├── BadMethodCallException
│   ├── DomainException
│   ├── InvalidArgumentException
│   ├── LengthException
│   └── OutOfRangeException
├── RuntimeException
│   ├── OutOfBoundsException
│   ├── OverflowException
│   ├── RangeException
│   ├── UnderflowException
│   └── UnexpectedValueException
├── DOMException
├── PharException
├── ReflectionException
├── JsonException
├── Random\RandomException
└── 扩展模块自己的异常

SPL 标准库子类

这是日常开发最常用的一棵树。

Exception
├── LogicException
│   ├── BadFunctionCallException
│   │   └── BadMethodCallException
│   ├── DomainException
│   ├── InvalidArgumentException
│   ├── LengthException
│   └── OutOfRangeException

└── RuntimeException
   ├── OutOfBoundsException
   ├── OverflowException
   ├── RangeException
   ├── UnderflowException
   └── UnexpectedValueException


逐个解释这些子类

1)ErrorException

class ErrorException extends Exception

用途:把 warning / notice 转成异常

set_error_handler(function ($severity, $message) {
   throw new ErrorException($message);
});

2)ClosedGeneratorException

生成器关闭后继续访问时抛出。

$gen = (function () {
   yield 1;
})();

$gen->next();
$gen->current(); // 可能触发相关问题

3) LogicException 系:代码调用方式有问题

只要修代码,问题就消失, 不是环境问题,也不是网络问题,而是:

  • 调用者传错参数

  • 方法调用顺序不对

  • 值超定义域

  • 程序进入“不该到达”的状态

1. InvalidArgumentException

参数不合法, 参数不符合预期类型/约束。

最常见场景:

function setAge(int $age): void
{
   if ($age < 0) {
       throw new InvalidArgumentException('Age cannot be negative');
   }
}

函数入口参数检查失败,例如:

  • 年龄不能小于 0

  • URL 不能为空

  • 用户 ID 必须大于 0

  • 页码不能是负数


2. DomainException

值不在允许集合里, 值不符合已定义的数据域。

和参数合法不同,它强调:值超出了业务定义域

$status = 'archived';

if (!in_array($status, ['draft', 'published'])) {
   throw new DomainException('Invalid status');
}

典型场景

  • 枚举值非法

  • 状态机状态非法

  • 不支持的类型

  • switch/match 不允许的分支

3. LengthException

长度违规

if (strlen($username) > 20) {
   throw new LengthException('Username too long');
}

典型场景

  • 用户名长度限制

  • token 长度固定

  • 数组元素数量超限制

  • 上传文件名长度限制

4. OutOfRangeException

逻辑范围错误(偏“业务规则”)

$page = 0;

if ($page < 1) {
   throw new OutOfRangeException('Page starts from 1');
}

典型场景

  • 分页页码从 1 开始

  • 月份必须 1~12

  • 星级评分只能 1~5

  • 游戏等级不能超过 max level

5. BadFunctionCallException

函数调用顺序错误

if (!$this->initialized) {
   throw new BadFunctionCallException('Call init() first');
}

场景理解

调用顺序有前置条件:

connect()
query()
close()

如果没 connect()query()

6. BadMethodCallException

和上面一样,只是强调:对象方法调用错误

$user->save();

但对象未初始化。

4) RuntimeException 系:运行过程出问题

代码没问题,但运行时失败

1. RuntimeException

场景:最通用运行失败

if (!file_exists($file)) {
   throw new RuntimeException('File missing');
}

最典型场景

  • 文件不存在

  • 网络超时

  • Redis 连不上

  • API 请求失败

  • 权限不足

  • 配置文件缺失

2. UnexpectedValueException

结果和预期不一致, 返回值不在预期集合中。

$data = json_decode($json, true);

if (!isset($data['id'])) {
   throw new UnexpectedValueException('Missing id');
}

典型场景

  • API 返回字段缺失

  • 配置值不是预期格式

  • 第三方返回状态异常

  • 解析结果结构不对

3. OutOfBoundsException

访问不存在的 key/index

$list = ['a', 'b'];

if (!isset($list[10])) {
   throw new OutOfBoundsException('Index not found');
}

常见场景

  • 数组 key 不存在

  • 集合类 offset 不存在

  • DTO 字段缺失

  • Map 查不到 key

4. OverflowException

容器满了
if ($queue->count() >= $max) {
   throw new OverflowException('Queue full');
}
常见场景
  • 固定长度队列

  • 缓冲区写满

  • 线程池满

  • 连接池满

5. UnderflowException

容器空了

if ($stack->isEmpty()) {
   throw new UnderflowException('Stack empty');
}

常见场景

  • 空栈 pop

  • 空队列 dequeue

  • 缓冲区读空

  • 连接池无空闲资源

6. RangeException

运行时数值超范围

OutOfRangeException 很像,但偏运行结果。

if ($temperature > $maxSensorValue) {
   throw new RangeException('Sensor overflow');
}

场景区别

OutOfRangeException: 输入参数超业务范围

setMonth(13)

RangeException: 程序运行结果超允许范围

$calculated = 999999999999;

5)JsonException

JSON 解析失败。

json_decode($json, true, 512, JSON_THROW_ON_ERROR);

失败就抛:

JsonException

6)PDOException

数据库异常(继承 RuntimeException)。

$pdo->query("BAD SQL");

7)ReflectionException

反射失败。

new ReflectionClass('NotExist');

8)DOMException

DOM/XML 操作失败。

$dom->appendChild($wrongNode);

9)PharException

phar 包操作失败。

标签: PHPException

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。