在 PHP 编程中,回调函数与异常处理是提升代码灵活性与健壮性的关键技术。回调函数能极大增强代码的复用性与扩展性;而异常处理则帮助开发者捕获并妥善处理运行时错误,避免程序意外崩溃。本篇文章将记录过回调函数与异常处理的学习过程。
一、回调函数
回调函数(通常简称为“回调”)是一个作为参数传递给另一个函数的函数。函数是以 string 形式传递的。可以使用任何内置或用户自定义函数(从 PHP 7 版本开始,回调参数也接受匿名函数),但除了语言结构,例如:array(),echo,empty(),eval(),exit(),isset(),list(),print 或 unset()。
示例 将回调函数传递给 PHP 的 array_map() 函数,将数组中每个字符串转换为大写
// array_map:为数组的每个元素应用回调函数
// 回调函数,将数组中每个字符串转换为大写
function my_callback($item) {
return strtoupper($item);
}
$strings = ["apple", "orange", "banana"];
$upper_strings = array_map('my_callback', $strings);
print_r($upper_strings);
示例 从用户定义的函数中运行回调函数
// 回调函数
function my_callback() {
echo 'Hello World!';
}
// 自定义函数,接受回调函数
function custom($callback) {
$callback();
}
// 调用自定义函数 custom(),将回调函数 my_callback() 作为参数传入
custom("my_callback");
二、异常处理
PHP 有一个和其他语言相似的异常模型。在 PHP 里可以 throw 并捕获(catch)异常。为了捕获潜在的异常,代码会包含在 try 块里。每个 try 都必须至少有一个相应的 catch 或 finally 块。
如果抛出异常的函数作用域内没有 catch 块,异常会沿调用栈"向上冒泡",直到找到匹配的 catch 块。沿途会执行所有遇到的 finally 块。在没有设置全局异常处理程序时,如果调用栈向上都没有遇到匹配的 catch,程序会抛出 fatal 错误并终止。
示例 抛出一个异常
function divide($dividend, $divisor) {
if($divisor == 0) {
// throw 语句允许用户定义的函数或方法抛出异常
throw new Exception("除数不能为0");
}
return $dividend / $divisor;
}
echo divide(1, 0);
上述示例输出:
异常对象(Exception)包含有关函数遇到的错误或意外行为的信息。
语法
new Exception(message, code, previous)
参数
message |
可选。描述为何抛出异常的字符串。 |
code |
可选。整数,可用于轻松区分同一类型的不同异常。 |
previous |
可选。如果此异常是在另一个异常的 catch 块中抛出的,建议将此异常传递给此参数。 |
当捕获异常时,下表显示了一些可用于获取有关异常信息的方法:
方法 |
描述 |
getMessage() |
返回描述为何抛出异常的字符串。 |
getPrevious() |
如果此异常是由另一个异常触发的,此方法返回前一个异常。如果不是,则返回 null。 |
getCode() |
返回异常代码。 |
getFile() |
返回抛出异常的文件的完整路径。 |
getLine() |
返回抛出异常的代码行的行号。 |
1、try...catch 语句
catch 定义了处理抛出异常的方式。 catch 块定义了它能处理的异常/错误的类型,并可以选择将异常赋值到变量中。如果遇到抛出对象的类型匹配了首个 catch 块的异常或错误,将会处理该对象。
可用多个 catch 捕获不同的异常类。正常情况下(try 代码块里没有抛出异常)会在最后一个定义的 catch 后面继续执行。 catch 代码块里也可以 throw 或者重新抛出异常。不抛出的话,会在触发的 catch 后面继续执行。
当 PHP 抛出一个异常时,将不会执行后续的代码语句,并会尝试查找首个匹配的 catch 代码块。如果没有用 set_exception_handler() 设置异常处理函数, PHP 会在异常未被捕获时产生 Fatal 级错误,提示 "Uncaught Exception ..." 消息。
从 PHP 7.1.0 起 catch 可以用竖线符(|) 指定多个异常。如果在不同的类层次结构中,不同的异常需要用同样的方式处理,就特别适用这种方式。
语法
try {
可能抛出异常的代码
} catch(Exception $e) {
当捕获到异常时运行的代码
}
示例 捕获并处理异常
function divide($dividend, $divisor) {
if($divisor == 0) {
// throw 语句允许用户定义的函数或方法抛出异常
throw new Exception("除数不能为0");
}
return $dividend / $divisor;
}
try {
echo divide(1, 0);
} catch (Exception $e) {
echo "出现错误,无法执行";
}
// 程序执行,输出 "出现错误,无法执行"
catch 块指示应该捕获哪种类型的异常以及用于访问异常的变量名称。在上面的示例中,异常的类型是 Exception,变量名称是 $e。
2、try...catch...finally 语句
finally 代码块可以放在 catch 之后,或者直接代替它。无论是否抛出了异常,放在 finally 里的代码总是会执行。
值得注意的是 finally 和 return 语句之间存在相互影响。如果在 try 或 catch 里遇到 return,仍然会执行 finally 里的代码。而且,遇到 return 语句时,会先执行 finally 再返回结果。此外,如果 finally 里也包含了 return 语句,将返回 finally 里的值。
示例 添加 finally 代码块
function divide($dividend, $divisor) {
if($divisor == 0) {
// throw 语句允许用户定义的函数或方法抛出异常
throw new Exception("除数不能为0");
}
return $dividend / $divisor;
}
try {
echo divide(1, 0);
} catch (Exception $e) {
echo "出现错误,无法执行<br>";
} finally {
echo "总会执行";
}
// 程序执行,输出:
/*
出现错误,无法执行
总会执行
*/
3、全局异常处理程序
当允许异常冒泡到全局作用域时,它可以被全局异常处理器捕获到。 set_exception_handler() 可以设置一个函数,在没有调用其它块时代替 catch。在本质上,实现的效果等同于整个程序被 try-catch 包裹起来。
set_exception_handler(?callable $callback): ?callable
设置默认的异常处理程序,用于没有用 try/catch 块来捕获的异常。在 callback 调用后异常会中止。
参数
callback:当一个未捕获的异常发生时所调用的函数。该处理函数需要接受一个参数,该参数是抛出的 Throwable 对象。也可以传递 null 值用于重置异常处理函数为默认值。
返回值
返回之前定义的异常处理程序,或者在错误时返回 null。如果之前没有定义错误处理程序,也会返回 null。
示例
function exception_handler(Throwable $e) {
$code = $e->getCode();
$message = $e->getMessage();
$file = $e->getFile();
$line = $e->getLine();
echo "在文件 $file 的第 $line 行抛出了异常:[代码 $code] <br>$message";
}
set_exception_handler('exception_handler');
throw new Exception('异常');
echo "未执行";