简介
php程序在运行过程中,会在出现错误的时候抛出异常。异常可能来自php内部,也可以由用户自行抛出。为此,php提供了Throwable接口,这个接口就是所有可抛出异常的公共接口。与很多编程语言类似,php也是通过throw抛出异常。能够通过throw进行抛出的对象都必须实现一个php的预定义接口——Throwable,此接口提供了以下方法:
abstract public string getMessage ( void )
- 获取消息
abstract public int getCode ( void )
- 获取出错代码
abstract public string getFile ( void )
- 获取抛出此异常的的文件名
abstract public int getLine ( void )
- 异常对象在哪一行被实例化
abstract public array getTrace ( void )
- 获取堆栈追踪
abstract public string getTraceAsString ( void )
- 获取堆栈追踪(字符串)
abstract public Throwable getPrevious ( void )
- 返回之前的Throwable
abstract public string __toString ( void )
- 异常对象的字符串表示
注意,用户代码中不应该直接使用此接口。php提供了Exception类,可作为用户异常的基类。而对于内部错误,php更是提供了大量的预定义类,包括:
Throwable - 接口
Error - 内部错误的基类
ArithmeticError - 数学运算错误
DivisionByZeroError - 除0错误
AssertionError - 执行 assert()时断言失败
ParseError - 当解析PHP代码时出现错误时,会抛出ParseError,比如调用eval()时。
TypeError - 参数类型错误,包括函数参数类型错误、函数返回值类型错误
ArgumentCountError - 实参数量少于形参数量
注意,Exception和Error是属于同一级别,只不过是提供给用户使用的。Error类和Excepton类都使用如下构造函数:
__construct ([ string $message = "" [, int $code = 0 [, Throwable $previous = NULL ]]] )
配置(php.ini文件)
在php.ini配置文件中,有几个选项可以配置php的错误处理方式:
error_reporting
设置错误报告的级别
display_errors
该选项设置是否将错误信息作为输出的一部分显示到屏幕,或者对用户隐藏而不显示。
display_startup_errors
即使 display_errors 设置为开启, PHP 启动过程中的错误信息也不会被显示。
log_errors
设置是否将脚本运行的错误信息记录到服务器错误日志或者error_log之中。
log_errors_max_len
设置 log_errors 的最大字节数
ignore_repeated_errors
不记录重复的信息
ignore_repeated_source
忽略重复消息时,也忽略消息的来源。
report_memleaks
如果这个参数设置为Off,则内存泄露信息不会显示 (在 stdout 或者日志中)。
track_errors
如果开启,最后的一个错误将永远存在于变量 $php_errormsg 中。
html_errors
在错误信息中关闭HTML标签。
xmlrpc_errors
关闭正常的错误报告,并将错误的格式设置为XML-RPC错误信息的格式。
xmlrpc_error_number
用作 XML-RPC faultCode 元素的值。
docref_root
新的错误信息格式包含了对应的参考页面,该页面对错误进行具体描述,或者描述了导致该错误发生的函数。
error_prepend_string
错误信息之前输出的内容。
error_append_string
错误信息之后输出的内容。
error_log
设置脚本错误将被记录到的文件。
配置(执行函数)
除了可以在php.ini配置错误处理方式以外,程序也可以通过一些函数来进行配置。
error_reporting()
int error_reporting ([ int $level ] )
设置应该报告何种 PHP 错误
error_reporting() 函数能够在运行时设置 error_reporting 指令。 PHP 有诸多错误级别,使用该函数可以设置在脚本运行时的级别。 如果没有设置可选参数 level, error_reporting() 仅会返回当前的错误报告级别。
异常处理(捕获)
捕获异常的基本语法结构为:
try { // 可能抛出异常的业务代码 }
catch (Exception $e) { // 处理捕获到的异常 }
catch (Exception $e) { // 处理捕获到的异常 }
catch (Exception $e) { // 处理捕获到的异常 }
...
finally { // 总是会执行的代码 }
注意,catch块和finally块至少必须存在一个,而且抛出的对象必须是Exception类(或子类)的一个实例。也就是说,Error类型的错误并不能直接通过try来捕获。
可以使用多个catch块来捕捉不同类别的异常。 也可以在catch块中抛出(或重新抛出)异常。如果异常未能在catch捕获,还可以通过set_exception_handler()注册异常处理函数进行处理
无论是否抛出异常,finally块中的代码将始终在try和catch块之后执行。
finally块并不是用于“捕获所有异常”的,没有被catch捕获的异常就会是未捕获的异常。
try {
throw new Exception("exception");
}
finally {
echo "finally\n";
}
// 未处理异常~
对于未处理的异常,也可以由上层try进行捕获。
try {
try {
throw new Exception("Oh no~");
}
finally {
echo "Zzz~\n";
}
}
catch (Exception $e) {
echo "catch: ".$e->getMessage()."\n";
}
finally {
echo "finally\n";
}
/*
Zzz~
catch: Oh no~
finally
*/
catch中也可以抛出异常
try {
try {
throw new Exception("Oh no~");
}
catch(Exception $e) {
echo "catch 01: " . $e->getMessage() . "\n";
throw $e;
}
}
catch (Exception $e) {
echo "catch 02: ".$e->getMessage()."\n";
}
/*
catch 01: Oh no~
catch 02: Oh no~
*/
如果catch和finally都抛出异常,则catch中的异常会被嵌套
try {
try {
throw new Exception("Oh no~");
}
catch(Exception $e) {
echo "catch 01: " . $e->getMessage() . "\n";
throw $e;
}
finally {
throw new Exception("Oh finally~");
}
}
catch (Exception $e) {
echo "catch 02: ".$e->getMessage()."\n";
}
/*
catch 01: Oh no~
catch 02: Oh finally~
*/
<?php
function getException(Throwable $ex)
{
echo $ex->getMessage()."\n";
echo $ex->getCode()."\n";
echo $ex->getFile()."\n";
echo $ex->getLine()."\n";
echo $ex->getTraceAsString()."\n";
var_dump($ex->getPrevious());
var_dump($ex->getTrace());
}
set_exception_handler("getException");
function f1() {
try {
throw new Exception("hello exception 0");
}
finally {
throw new Exception("hello exception 1");
}
}
function f2() {
try {
f1();
}
finally {
throw new Exception("hello exception 2");
}
}
function f3() {
f2();
}
f3();
/*
hello exception 2 // $ex->getMessage()
0 // $ex->getCode()
F:\server\www\other.php // $ex->getFile()
29 // $ex->getLine()
#0 F:\server\www\other.php(33): f2() // $ex->getTraceAsString()
#1 F:\server\www\other.php(36): f3()
#2 {main}
object(Exception)#2 (7) { // $ex->getPrevious()
["message":protected]=>
string(17) "hello exception 1"
["string":"Exception":private]=>
string(0) ""
["code":protected]=>
int(0)
["file":protected]=>
string(23) "F:\server\www\other.php"
["line":protected]=>
int(21)
["trace":"Exception":private]=>
array(3) {
[0]=>
array(4) {
["file"]=>
string(23) "F:\server\www\other.php"
["line"]=>
int(26)
["function"]=>
string(2) "f1"
["args"]=>
array(0) {
}
}
[1]=>
array(4) {
["file"]=>
string(23) "F:\server\www\other.php"
["line"]=>
int(33)
["function"]=>
string(2) "f2"
["args"]=>
array(0) {
}
}
[2]=>
array(4) {
["file"]=>
string(23) "F:\server\www\other.php"
["line"]=>
int(36)
["function"]=>
string(2) "f3"
["args"]=>
array(0) {
}
}
}
["previous":"Exception":private]=>
object(Exception)#1 (7) {
["message":protected]=>
string(17) "hello exception 0"
["string":"Exception":private]=>
string(0) ""
["code":protected]=>
int(0)
["file":protected]=>
string(23) "F:\server\www\other.php"
["line":protected]=>
int(18)
["trace":"Exception":private]=>
array(3) {
[0]=>
array(4) {
["file"]=>
string(23) "F:\server\www\other.php"
["line"]=>
int(26)
["function"]=>
string(2) "f1"
["args"]=>
array(0) {
}
}
[1]=>
array(4) {
["file"]=>
string(23) "F:\server\www\other.php"
["line"]=>
int(33)
["function"]=>
string(2) "f2"
["args"]=>
array(0) {
}
}
[2]=>
array(4) {
["file"]=>
string(23) "F:\server\www\other.php"
["line"]=>
int(36)
["function"]=>
string(2) "f3"
["args"]=>
array(0) {
}
}
}
["previous":"Exception":private]=>
NULL
}
}
array(2) { // $ex->getTrace()
[0]=>
array(4) {
["file"]=>
string(23) "F:\server\www\other.php"
["line"]=>
int(33)
["function"]=>
string(2) "f2"
["args"]=>
array(0) {
}
}
[1]=>
array(4) {
["file"]=>
string(23) "F:\server\www\other.php"
["line"]=>
int(36)
["function"]=>
string(2) "f3"
["args"]=>
array(0) {
}
}
}
*/
#0 F:\server\www\other.php(33): f2() // $ex->getTraceAsString()
#1 F:\server\www\other.php(36): f3()
#2 {main}
这段信息表示异常从f2()中抛出,f2()被f3()调用,f3()被main调用
array(2) { // $ex->getTrace()
[0]=>
array(4) {
["file"]=>
string(23) "F:\server\www\other.php"
["line"]=>
int(33)
["function"]=>
string(2) "f2"
["args"]=>
array(0) {
}
}
[1]=>
array(4) {
["file"]=>
string(23) "F:\server\www\other.php"
["line"]=>
int(36)
["function"]=>
string(2) "f3"
["args"]=>
array(0) {
}
}
}
这段信息记录函数调用栈的详细信息
下面是一个相对清晰一些的打印方式:
<?php
function getException(Throwable $ex)
{
do{
echo $ex->getMessage()."\n";
echo $ex->getFile()." : ".$ex->getLine()."\n";
$arr = $ex->getTrace();
$count = count($arr);
for($i=0; $i<$count; $i++) {
echo $arr[$i]["function"]."()";
if($i+1 < $count)
echo " -> ";
}
echo "\n|--------------------\n";
}
while($ex = $ex->getPrevious());
}
set_exception_handler("getException");
function f1() {
try {
throw new Exception("hello exception 0");
}
finally {
throw new Exception("hello exception 1");
}
}
function f2() {
try {
f1();
}
finally {
throw new Exception("hello exception 2");
}
}
function f3() {
f2();
}
f3();
/*
hello exception 2
F:\server\www\other.php : 36
f2() -> f3()
|--------------------
hello exception 1
F:\server\www\other.php : 28
f1() -> f2() -> f3()
|--------------------
hello exception 0
F:\server\www\other.php : 25
f1() -> f2() -> f3()
|--------------------
*/
异常处理(回调函数)
set_error_handler()
mixed set_error_handler ( callable $error_handler [, int $error_types = E_ALL | E_STRICT ] )设置用户自定义的错误处理函数
$error_handler
用户自定义错误处理,可以传入 NULL 重置处理程序到默认状态。 除了可以传入函数名,还可以传入引用对象和对象方法名的数组。具体的函数或方法应按如下格式定义:
bool handler ( int $errno , string $errstr [, string $errfile [, int $errline ]] )
errno,包含了错误的级别,
errstr,包含了错误的信息,
errfile, 包含了发生错误的文件名,
errline, 包含了错误发生的行号,
如果函数返回 FALSE,标准错误处理处理程序将会继续调用。
$error_types
本参数里指定的错误类型都会绕过 PHP 标准错误处理程序, 除非回调函数返回了 FALSE。 error_reporting() 设置将不会起到作用而你的错误处理函数继续会被调用 —— 不过你仍然可以获取 error_reporting 的当前值,并做适当处理。 需要特别注意的是带 @ error-control operator 前缀的语句发生错误时,这个值会是 0。
此参数用于屏蔽 error_handler 的触发。 如果没有该掩码, 无论 error_reporting 是如何设置的, error_handler 都会在每个错误发生时被调用。
注意,以下级别的错误不能由用户定义的函数来处理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,和在 调用 set_error_handler() 函数所在文件中产生的大多数 E_STRICT。
对于本函数(set_error_handler)的返回值,如果之前有定义过错误处理程序,则返回该程序名称的 string;如果是内置的错误处理程序,则返回 NULL。 如果你指定了一个无效的回调函数,同样会返回 NULL。 如果之前的错误处理程序是一个类的方法,此函数会返回一个带类和方法名的索引数组(indexed array)。
通过set_error_handler()可以将运行错误转换为异常,只需要自定义一个错误处理函数,该函数抛出一个异常即可。php已经预定义了一个专门用于此场合的异常类,即ErrorException,该类继承自Exception。该类的构造函数为:
public __construct ([ string $message = "" [, int $code = 0 [, int $severity = E_ERROR [, string $filename = __FILE__ [, int $lineno = __LINE__ [, Exception $previous = NULL ]]]]]] )
function getError($errno, $errstr, $errfile, $errline)
{
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler ("getError");
try{
$a = 3 / 0;
}
catch(ErrorException $e)
{
echo "error";
}
set_exception_handler()
callable set_exception_handler ( callable $exception_handler )设置默认的异常处理程序,用于没有用 try/catch 块来捕获的异常。 在 exception_handler 调用后异常会中止。
$exception_handler
用户自定义的异常处理函数,也可以传递 NULL 值用于重置异常处理函数为默认值。 函数以以下方式定义:
void handler ( Throwable $ex )
大多数错误抛出 Error 异常,也能被此函数捕获。 Error 和 Exception 都实现了 Throwable 接口。
set_exception_handler()函数的返回值为之前定义的异常处理程序的名称,或者在错误时返回 NULL。 如果之前没有定义错误处理程序,也会返回 NULL。
function getException(Throwable $ex) {
echo "my Error: ".$ex->getMessage();
}
set_exception_handler("getException");
throw new Exception("a err");
/*
my Error: a err
*/
bool restore_exception_handler ( void )
在使用 set_exception_handler() 改变异常处理函数之后,此函数可以用于还原之前的异常处理程序(可以是内置的或者也可以是用户所定义的函数)。
bool restore_error_handler ( void )
在使用 set_error_handler() 改变错误处理函数之后,此函数可以用于还原之前的错误处理程序(可以是内置的或者也可以是用户所定义的函数)。
扩展Exception异常类
可以通过派生Exception类来实现一些自定义的功能,为此,需要了解Exception类中成员的可访问性:
class Exception
{
protected $message = 'Unknown exception'; // 异常信息
private $string; // __toString cache
protected $code = 0; // 用户自定义异常代码
protected $file; // 发生异常的文件名
protected $line; // 发生异常的代码行号
private $trace; // backtrace
private $previous; // 当异常嵌套时,保存之前的异常
public function __construct($message = null, $code = 0, Exception $previous = null);
final private function __clone(); // 异常对象不能克隆
final public function getMessage(); // 返回异常信息
final public function getCode(); // 返回异常代码
final public function getFile(); // 返回发生异常的文件名
final public function getLine(); // 返回发生异常的代码行号
final public function getTrace(); // backtrace() 数组
final public function getPrevious(); // 之前的 exception
final public function getTraceAsString(); // 已格成化成字符串的 getTrace() 信息
// 可覆盖的方法
public function __toString(); // 输出字符串
}
class MyException extends Exception
{
// 添加一个简短消息
private $msg;
// 重定义构造器使 message 变为必须被指定的属性
public function __construct($msg, $message, $code = 0, Exception $previous = null)
{
// 自定义的代码
$this->msg = $msg;
// 确保所有变量都被正确赋值
parent::__construct($message, $code, $previous);
}
// 自定义字符串输出的样式
public function __toString()
{
return __CLASS__ . ": [{$this->code}]: {$this->msg}\n";
}
}
try {
throw new MyException("出错了", "我是故意抛出异常的");
}
catch(MyException $e) {
echo $e;
echo $e->getMessage();
}
/*
MyException: [0]: 出错了
我是故意抛出异常的
*/