一,Exception和Error的不同点:
1,产生方式不同
- Exception是异常,只能由用户手动throw new Exception才能产生一个异常;
- Error是错误,是程序报错所产生,也可以由用户调用 trigger_error()产生;
错误主要包含以下几种(按严重级别排序): Parse error > Fatal Error > Waning > Notice > Deprecated
2,捕获方式不同
- Exception要通过 catch(Exception $e) 和 set_exception_handler 捕获;
- Error要通过 catch(Error $e) 和 set_error_handler捕获,其中catch只能捕获Fatal
Error,set_error_handler只能捕获Waning、Notice 和 Deprecated类型的错误。
在PHP7之后,set_exception_handler 不仅能捕获 Exception,也能捕获 Fatal Error类型(但是不能捕获notice和warning)。
因为PHP7之后,set_exception_handler的回调函数可以传入一个Throwable类的实例,而不仅限于Exception类的实例。
二,Exception和Error的相同点:
1,这两个类都继承自Throwable类,用 catch(Throwable $e) 可以同时捕获 Exception 和 Fatal Error类型的错误或异常。
2,Exception和Error具有相同的方法可以获得错误或异常信息:
getMessage();
getFile();
getLine();
三,异常捕获
- catch(Exception $e) 只能捕获Exception,无法捕获Error;
- catch(Error $e)只能捕获Fatal Error错误,不能捕获Warning、Notice、Deprecated错误,也不能捕获Exception;
- catch(Throwable $e)可以捕获Fatal Error错误 和 Exception异常。
- set_error_handler() 可以捕获Warning、Notice、Deprecated,不能捕获 Fatal Error和Parse error。
- set_exception_handler()可以捕获Exception和Error。
被 set_error_handler()的自定义错误处理函数捕获到错误后,主逻辑仍可继续执行(因为Warning和Notice错误不足以终止程序)。
被 set_exception_handler() 的自定义错误处理函数捕获到错误或异常后,主逻辑不会继续往下执行。
如何让一个try catch 能够捕获所有的 Exception、Fatal Error、Warning、Notice、Parse错误?
思路:在 set_error_handler的回调函数中,将捕获到的Warning和Notice作为Exception抛出。再用 set_exception_handler 捕获异常。这么一来所有错误都能被当做异常捕获。
/**
* 捕获Warning、Notice、Deprecated 错误
*/
function myErrorHandler($errno, $errStr, $errFile, $errLine)
{
if (error_reporting() & $errno == 0) {
return false;
}
$msg = "\"{$errStr}\" occured in File \"{$errFile}\" at Line {$errLine}\n";
throw new Exception($msg, $errno);
}
/**
* 捕获Exception和Error
*/
function myExceptionHandler(Throwable $e)
{
if ($e instanceof Exception) {
echo $e->getMessage();
return;
}
echo "\"" . $e->getMessage() . "\" occured in File \"" . $e->getFile() . "\" at Line " . $e->getLine() . "\n";
}
/**
* 错误兜底,无论程序是否报错,都会在结束时执行 myShutdownFunc()
* 如果有错误未被myExceptionHandler或者myErrorHandler或者catch捕获,myShutdownFunc用来做错误兜底
*/
function myShutdownFunc()
{
// 如果某个错误已经被 catch 或者 error_handler 或者 exception_handler 捕获,就不会再被 error_get_last()记录
$err = error_get_last();
if ($err && $err['type']) {
myErrorHandler($err["type"], $err["message"], $err['file'], $err['line']);
}
}
set_error_handler("myErrorHandler", E_ALL);
set_exception_handler("myExceptionHandler");
register_shutdown_function("myShutdownFunc"); // 注册一个 callback ,它会在脚本执行完成或者 exit() 后被调用。
try {
// 1/0; // Warning 先被 myErrorHandler 捕获,再被下方catch捕获,打印123
// a; // Notice 先被 myErrorHandler 捕获,再被下方catch捕获,打印123
// a(); // Fatal Error 被 myExceptionHandler 捕获,不打印123
} catch (Exception $e) {
var_dump($e);
}
echo 123;
exit;
最后还有一个问题没解决:如何捕获Parse Error语法错误?
实际上,php无法捕获语法错误,因为一个文件如果有语法错误,根本不会执行这个脚本里的任何代码就直接报错。
但可以通过 include/require 引入业务逻辑的php文件,如果 include(“test.php”) 的 test.php 有语法错误,则 include就会返回一个Fatal Error错误,Fatal Error错误就会被 myExceptionHandler() 捕获。
// ... 注册错误和异常的handler
try{
include("test.php"); // test.php有语法错误,include会引发致命错误,此时会被 myExceptionHandler() 捕获,不打印123。
}catch(Exception $e){
var_dump($e);
}
echo 123;