Php的错误和异常及laravel的错误处理机制
php 的错误和异常
错误(error)指的是因为语法错误或者环境配置问题产生的报错,错误有16个错误等级, 有的错误等级会使脚本中断运行。 而异常(exception)指的是程序因为业务逻辑问题产生的意外情况。
在 php5 的时代, 错误不能被 try … catch … 捕获, 只能通过 error_reporting() 设置是否显示错误信息, 而遇到 E_ERROR
设置 error_reporting 也不管用, 因为 E_ERROR
报错脚本会直接终止运行。 异常是可以被捕获的, 不过同样比较悲催的是: php 解释器不会自动抛出异常。 它不能分辨你到底是业务逻辑问题还是语法问题, 几乎所有的业务逻辑问题最后都变成了两种情况: 一种是跑的通的bug(不容易察觉), 另一种是跑不通的错误(不能被捕获)。 除非你预先发现了你的业务逻辑问题, 然后手动抛出(throw), try…catch… 才能捕获到你抛出的异常。听起来好像多余, 因为如果事先预料到了问题, 直接改代码避免出问题不就好了? 但这并不是多此一举, 手动抛出异常然后捕获, 你就可以把你的异常处理代码和正常业务逻辑抽离出来, 在 try … catch 里面写正常代码, catch 里面写异常处理代码。
在 php7 事情有了转机, php7 中大多数错误可以作为 Error 异常(ErrorException)抛出了。 然后在 php8 时代所有的 Error 都能被捕获了。(不希望显示错误信息还是要设置 error_reporting 和 set_error_handler)
在 php8 时代所有的 Error 都能被捕获了
这句话是我说的,不是官网说的。具体情况还是自己测试一下比较好。
下面是 php 一些预置的错误和异常:
- Throwable
- Error
- ArithmeticError
- DivisionByZeroError
- AssertionError
- ParseError
- TypeError
- ArgumentCountError
- CompileError
- ValueError
- UnhandledMatchError
- FiberError
- ArithmeticError
- Exception
- ClosedGeneratorException
- DOMException
- ErrorException
- IntlException
- LogicException
- BadFunctionCallException
- BadMethodCallException
- DomainException
- InvalidArgumentException
- LengthException
- OutOfRangeException
- BadFunctionCallException
- PharException
- ReflectionException
- RuntimeException
- OutOfBoundsException
- OverflowException
- PDOException
- RangeException
- UnderflowException
- UnexpectedValueException
- SodiumException
- Error
可以通过下面的测试检查某个错误是否能被捕获。
<?php
set_error_handler("final_error_handle", E_ALL);
error_reporting(-1);
function final_error_handle($level,$message,$file,$line,$context){
echo $level."\r\n";
}
function error_handle_custom($error){
echo "是".get_class($error)."错误\r\n";
}
function exception_handle($exception){
echo "是".get_class($exception)."异常\r\n";
}
function test($test, $testName){
try{
echo $testName.":\r\n";
$test();
}catch(Error $error){
error_handle_custom($error);
echo "错误已经处理了\r\n";
}catch(Exception $exception){
exception_handle($exception);
echo "异常已经处理了\r\n";
}
echo "继续检查\r\n\r\n";
}
test(function(){
$a = 5 / 0;
}, '除数为0');
// 除数为0:
// 捕获到了错误
// 是DivisionByZeroError错误
// 继续检查
test(function(){
function a($a, $b){
return $a+$b;
}
a(1);
}, '传递的参数太少');
// 传递的参数太少:
// 捕获到了错误
// 是ArgumentCountError错误
// 继续检查
test(function(){
function b($a, $b){
return $a+$b;
}
var_dump(b(1, 2, 3));
}, '传递的参数太多');
// 传递的参数太多:
// int(3)
// 继续检查
test(function(){
function c(array $str){
var_dump($str);
}
c(1);
}, '传入参数类型与参数类型申明不一致');
// 传入参数类型与参数类型申明不一致:
// 捕获到了错误
// 是TypeError错误
// 继续检查
test(function(){
echo $what;
}, '调用未定义的变量');
// 调用未定义的变量:
// 捕获到了错误
// 是TypeError错误
// 继续检查
test(function(){
u();
}, '调用未定义的函数');
// 调用未定义的函数:
// 捕获到了错误
// 是Error错误
// 继续检查
test(function(){
$arr = json_decode('啊这这这');
var_dump($arr);
}, 'json_decode 一个普通字符串');
// json_decode一个字符串:
// NULL
// 继续检查
test(function(){
throw new Exception('发生了一个错误');
}, '手动抛出异常');
// 手动抛出异常:
// 捕获到了异常
// 是Exception异常
// 继续检查
test(function(){
file_get_contents('https://what.com');
}, '网络请求超时');
// 网络请求超时:
// 捕获到了错误
// 是TypeError错误
// 继续检查
Laravel 的错误处理机制
先看下 laravel 的 ExceptionHandle 是在哪里加载的。
Illuminate\Foundation\Http\Kernel 的 框架会载入 $bootstrappers
中的引导项。 第一项是 Event 第二项是 Config 第三个就是 Exception。
Illuminate\Foundation\Bootstrap\HandleExceptions 的 bootstrap 设置了 error_reporting
set_error_handler
set_exception_handler
和 register_shutdown_function
。我们知道设置了这几项之之后,所有的错误都可以被捕获并且不显示错误信息了。
首先看 handleError
, 这里如果是 deprecation
(弃用)错误的话,会直接处理, 如果不是的话会抛出 ErrorException
然后看 handleException
, handleException
会 getExceptionHandler
然后执行 ExcptionHandler 的 report
和 render
方法。
getExceptionHandler
到的就是 App\Exceptions\Handler::class
然后其他的知识直接看官方文档就好 renderable
和 render
直接给用户返回 response
, report
是给程序员报告错误信息。
补充一下, App\Exceptions\Handler
继承了 Illuminate\Foundation\Exceptions\Handler
, 然后里面有一些 Laravel 预置的异常。
protected $internalDontReport = [
AuthenticationException::class,
AuthorizationException::class,
BackedEnumCaseNotFoundException::class,
HttpException::class,
HttpResponseException::class,
ModelNotFoundException::class,
MultipleRecordsFoundException::class,
RecordsNotFoundException::class,
SuspiciousOperationException::class,
TokenMismatchException::class,
ValidationException::class,
];
本作品采用《CC 协议》,转载必须注明作者和本文链接