0x00 简单应用

当执行部分可能抛出异常的代码时,catch 可以捕捉到 try 代码段内抛出的异常(前提是 catch 的参数类型和抛出的异常类型相符),类型不符合的异常会继续向上层抛出。比如以下代码:

直接捕获:

1
2
3
4
5
6
7
8
9
10
11
function foo()
{
throw new Exception('hello world' . PHP_EOL);
}

try {
foo();
} catch (Exception $e) {
echo $e->getMessage();
}
// 输出 hello world

抛出至上层捕获:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function foo()
{
throw new RuntimeException();
}

try {
try {
foo();
} catch (OutOfRangeException $e) {
echo 'catch OutOfRangeException';
}
} catch (RuntimeException $e) {
echo 'catch RuntimeException';
}
// 输出 catch RuntimeException

可以使用多个连续 catch 捕获不同类型的异常,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function foo()
{
if (random_int(0, 1)) {
throw new RuntimeException();
} else {
throw new OutOfRangeException();
}
}

try {
foo();
} catch (OutOfRangeException $e) {
echo 'catch OutOfRangeException';
} catch (RuntimeException $e) {
echo 'catch RuntimeException';
}
// 根据随机抛出的异常分别输出
// catch OutOfRangeException
// catch RuntimeException

PHP 7.1 新特性:同一个 catch 可以通过 | 分割异常类型,实现捕获多种异常。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function foo()
{
if (random_int(0, 1)) {
throw new RuntimeException('I am RuntimeException');
} else {
throw new OutOfRangeException('I am OutOfRangeException');
}
}

try {
foo();
} catch (OutOfRangeException|RuntimeException $e) {
echo $e->getMessage();
}
// 根据随机抛出的异常分别输出
// I am RuntimeException
// I am OutOfRangeException

0x01 返回值的注意事项

在没有引入 finally 关键字时,在 try...catch 的中返回值是符合直觉的如下例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function foo()
{
try {
throw new Exception();
return 1;
} catch (Exception $e) {
return 2;
}
}

echo foo();
// 输出 2
// 若只删除抛出异常的代码则输出 1
// 若只删除 catch 中的 return 则无输出

但引入 finally 关键字的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function foo()
{
try {
throw new Exception();
return 1;
} catch (Exception $e) {
return 2;
} finally {
return 3;
}
}

echo foo();
// 输出 3

finally 内的代码是无论是否捕获到异常都会执行的代码,并且当 finally 中存在 return 返回值时,整个try..catch...finally段的返回值只会是 finally 的返回值。

猜测下例代码输出

1
2
3
4
5
6
7
8
9
10
11
12
$a = 1;
function foo(&$a)
{
try {
$a = 2;
return $a;
} finally {
$a = 3;
}
}
echo foo($a);
echo $a;

输出结果为 23 这说明了finally 即使在 trycatch 中执行了 return 也同样会执行。

0x02 Throwable 接口

Throwable 是 PHP 7 新出的一个预定义接口,ErrorException 类都实现了这个接口,也就是说我们现在有能力通过 catch 捕获到 Error。示例如下:

1
2
3
4
5
6
7
8
9
10
11
function foo(int $a): int
{
return $a;
}

try {
foo('string');
} catch (Throwable $e) {
echo get_class($e);
}
// 输出 Throwable

例子表明了参数类型不符所抛出的 TypeError 错误可以被 catch 捕获。

0x03 全局异常处理函数

官方文档 set_exception_handler 设置一个全局的异常处理函数可以捕获所有没有被 catch 到的异常。使用示例:

1
2
3
4
5
6
7
8
9
10
11
set_exception_handler(function (Exception $e) {
echo $e->getMessage();
});

function foo()
{
throw new Exception('nobody catch me');
}

foo();
// 输出 nobody catch me

类似的对于错误处理有 set_error_handler 函数。