php的错误异常机制
当我们没有自定义错误异常处理函数,php会按照默认的方式处理和显示错误异常。
自定义默认的错误异常处理函数
//注册捕获错误的处理函数
set_error_handler([__CLASS__, 'appError']);
//注册捕获错误的处理函数
set_exception_handler([__CLASS__, 'appException']);
//程序结束后执行的函数,可以获取到进程最后一个错误,方便上面两种方式没有触发的情况下处理错误
register_shutdown_function([__CLASS__, 'appShutdown']);
不同的php版本对错误和异常的捕获有不同,php7以上版本,几乎所有的错误和异常都可以被set_exception_handler注册的函数捕获。
错误异常处理函数
public static function appException($e)
{
if (!$e instanceof \Exception) {
$e = new ThrowableError($e);
}
//把错误信息写入到log文件
self::getExceptionHandler()->report($e);
if (PHP_SAPI == 'cli') {
self::getExceptionHandler()->renderForConsole(new ConsoleOutput, $e);
} else {
//把错误信息写入到tmpl模板,发送给客户端
self::getExceptionHandler()->render($e)->send();
}
}
一定要接受一个参数$e,这是一个异常对象。它保存了错误的种类,行号,等等信息。我们处理错误异常就是根据这个对象来处理的。而且我们可以判断$e是属于哪个异常类,从而进行不同的处理。
tp5.1使用think\exception\handle类来处理异常的显示,比如report,render等等处理,我们就可以修改这个handle类的方法,来自定义显示错误。
php内置异常类的了解
实际上php内置了很多异常类, Error 类 和 Exception 类 都继承自 Throwable 接口,而这连个类下面又有很多子类,不同的php版本子类可能不一样,比如下面
Error
ParseError
TypeError
ArgumentCountError
Exception
RuntimeException
PDOException
php7以上ParseError继承Exception,所有说大部分异常都继承Exception
分析一下Exception类
Exception {
/* 属性 */
protected string $message ;
protected int $code ;
protected string $file ;
protected int $line ;
/* 方法 */
这个类的介绍可以查看php的手册,已经内置了很多方法。php为什么会内置这么多异常呢?主要是因为不同的错误和异常,php都会产生对应的异常类抛出。所以我们的异常处理函数要保证所有的异常都能处理。
自定义异常类,手动抛出异常类
有时候我们需要根据自己的需求,可以创建自定义异常类,只要继承php内置的异常类即可。比如tp5.1框架的HttpException异常
class HttpException extends \RuntimeException
{
private $statusCode;
private $headers;
public function __construct($statusCode, $message = null, \Exception $previous = null, array $headers = [], $code = 0)
{
$this->statusCode = $statusCode;
$this->headers = $headers;
parent::__construct($message, $code, $previous);
}
}
只是多加了两个属性$statuCode,$header。我们手动抛出这个异常的时候,就要多传这些数据进去,然后手动抛出
throw new HttpException(404, 'controller not exists:'); //手动抛出异常
有了这两个信息,我们再处理异常的时候就可以先判断获取的异常是属于哪个异常类,然后再根据不同的异常显示不同的信息。
php异常处理原理
在一个进程中,异常的抛出可以是php发现错误和异常自动抛出对应的异常对象,也可以我们手动抛出异常实例。抛出后代码将不再往下执行,而是执行异常处理函数,执行完捕获异常的函数,进程就会终止。
异常抛出后,最会执行最先捕获异常的那个函数。比如代码中写了try..catch{},如果在catch已经捕获了异常,就不会让set_exception_handler注册的函数捕获了。
register_shutdown_function注册的函数在进程结束的时候都会执行,关于这个函数可以查找php手册,我们只是恰好可以在里面处理一些非常特殊的问题(有些问题php可能不会抛出异常)。在这个函数中可以通过error_get_last()得到进程失败的问题。
public static function appShutdown()
{
if (!is_null($error = error_get_last()) && self::isFatal($error['type'])) {
// 将错误信息托管至think\ErrorException
$exception = new ErrorException($error['type'], $error['message'], $error['file'], $error['line']);
self::appException($exception);
}
// 写入日志
Container::get('log')->save();
}