1.1 什么是错误文本将从PHP原生错误/异常 讲解到 Laravel 错误/异常,耐心看完,相信你会有所收获。
在 PHP 中,最常见错误的级别有
| 错误类型 | 解释 |
|---|---|
| Deprecated | 比如 API 过期 ,属于低级错误 |
| Notice | 变量未定义 |
| Warning | 结果不符合逻辑,比如函数里面 $num + 100,但 $num 传递进来的是 ‘ab’ |
| Fetal | 致命错误,直接终止运行后面的运行,比如调用不存在的函数 |
| Prase | 最高级别错误,以上的都是运行期间报错,这里直接在语法解析期间报错。 |
给上面对应的错误类型举几个例子
1.2 什么是异常另外,PHP 也提供了 set_error_handler ,此函数将会把所有把错误交给你接管,而 Laravel 正是利用了此函数进行重写,比如格式化输出、将所有错误以异常抛出去等等,具体怎么使用这里不在讲述,可参考文档。
PHP 是在 PHP5 后才引入异常的,每个语言对异常的理解各有不同,比如在 PHP 中 除 0 它是一个 warning 级别错误行为而不是异常,而在 JAVA 中它是属于异常行为。
$abc = 10 / 0; // 10 / 0 属于报错 warning 行为,并不属于异常。 echo $abc;所以很多时候异常都是由我们通过 if else 来手动控制抛出的,比如用户手机未输入
getMessage(); // 接受异常传递过来的消息,我们直接打印。 }注意:捕获异常需要用到 try / catch,如果直接在外部使用 throw new Exception,它将会是一个错误行为
throw new Exception('请输入手机号');其实异常更多的作用是用来进行一些补救措施,比如数据库事务的回滚,
举个简单例子:用户提交订单beginTransaction(); // 启动事务 try { DB::update('从数据库中扣掉商品库存'); // 发现用户未输入手机号,我们手动抛出异常 if (empty($_POST['phone'])) { throw new Exceptions('请输入手机'); } commit(); // 提交事务:由于上面抛出异常,这里不会触发。 } catch(Exception $e) { rollback(); // 回滚事务:这里捕获到了异常,所以对扣掉库存这一操作进行回滚。 }值得注意的是,在 PHP 7 以前,try/catch 是捕获不了错误的,比如调用不存在的函数
try { abc(); } catch(Exception $e) { echo '函数不存在' . $e->getMessage(); }后来为了让部分错误能够捕获到,在 PHP 7 中引入了 Throwable ,可以说是 Exception 的升级版,但它只能对部分错误进行捕获,像下面的调用未知函数就可以捕获到。
try { abc(); // 你也可以像 Exception 一样手动抛出 // throw new Throable("xxxx"); } catch (Throwable $e) { echo "函数不存在:" . $e->getMessage(); }而像下面的访问未定义变量,依然还是捕获不了,直接一个报错行为。
try { echo $abc; } catch (Throwable $e){ echo "变量不存在" . $e->getMessage(); }另外,PHP 也提供了 set_exception_handler ,次函数将所有异常都交给你接管, 显然 Laravel 也不会放过这个函数。
好了,接下来我们开始进入 Laravel
二、Laravel 中的错误与异常理解了 PHP 中的基本错误/异常后,现在我们来开始使用 Laravel 中的错误/异常。
2.1 错误与异常的默认处理本文假设你已创建 Laravel 项目。
我们首先抛出一个错误,看看在 Laravel 中会发生什么,假设我们有个 test 路由。
// route/web.php use IlluminateSupportFacadesRoute; Route::get('test', function(){ echo $abc; // 打印不存在的变量。 });接下来访问 http://localhost:8000/test ,输出如下:
可以看到,Laravel 展示的是一个美化过的错误页面。另外我们也知道原生 try/catch 是无法捕获到未知变量这种错误行为的,但 Laravel 为我们做到了
use IlluminateSupportFacadesRoute; Route::get('/test', function(){ try { echo $abc; } catch(Exception $e) { echo '变量不存在:'.$e->getMessage(); } });2.2 关于错误/异常发生后,Laravel 的日志记录问题
很明显 Laravel 利用 set_error_handler 对错误进行了重写并以异常的形式抛出,这点值得称赞。每次发生异常或者报错 Laravel 都会为我们写入日志文件里,下面是针对 try 内外的情况进行分析。
try 外
两者都被记录在 app/storage/logs 里面
echo $abc; throw new Exception("抛出异常");try 内
异常不会被记录,但可以手动调用 report() 进行记录,而错误会自动被记录。
try { throw new Exception("抛出异常"); // 不会被记录,只能在 catch 中手动记录。 // echo $abc; // 会自动被记录 } catch (Exception $e) { report(); // 手动记录 // .. do something }我们只需记得:使用 try/catch 后,权限交给用户,不使用则交给 Laravel
2.3 给 log 日志添加额外数据每次发生异常或错误时我们想要给日志增加额外数据方便后续排查,需要怎么做?很简单,只需在 app/Exceptions/Handler.php 里面新增 context 方法进行添加即可,比如
// app/Exceptions/Handler.php ============= public function context() { return array_merge(parent::context(), [ 'user' => 'Cookcyq', ]); }2.4 自定义错误/异常页面如上所知,默认情况下 Laravel 会自动返回一个美化过的页面,如果你不喜欢这种风格,Laravel 还提供了 $this->renderable 让你重写,我们来改造一下,依然是在 app/Exceptions/Handler.php 里面修改。
// app/Exceptions/Handler.php ================== public function register(){ // 重写 $this->renderable(function(Throwable $e) { return response([ 'msg' => $e->getMessage(), 'line' => $e->getLine(), ]); }); } // route/web.php ================== use IlluminateSupportFacadesRoute; Route::get('/test', function(){ echo $abc; // 或 throw new Exception("Something is wrong."); });现在我们来访问 http://localhost:8000/test
可以看到页面变成我们自定义的了,想要更多日志操作可参考官方文档:https://laravel.com/docs/9.x/errors
三、Laravel 自定义异常由于上面的 $this->renderable 是全局性的,每个异常/错误都会触发,有时我们并不像这样做,而是仅针对某个功能,其它则按照 Laravel 默认模板正常显示,比如我们有个注册,如果注册有问题,就抛出注册异常,正好 Laravel 也为我们提供了这种能力。
首先在 app/Exceptions/目录下新增 RegisterException.php 文件,代码如下:
'Cookcyq']; } public function render($request) { return response([ 'msg' => $this->getMessage() ]); } }route/web.php 代码如下
use IlluminateSupportFacadesRoute; use AppExceptionsRegisterException; Route::get('/test', function(){ throw new RegisterException("Please make sure your account or password are correct."); });效果
四、业务案例
在实际业务中,假如我们是 API 提供者,我们需要对传递进来的参数不正确、残缺等情况进行响应处理,这个时候我们就可以利用自定义 Exception。
还是老样子,首先在 app/Exceptions/ 新建 MyException.php 文件,代码如下
$this->getMessage(), 'status' => $this->code ]; // JSON_UNESCAPED_UNICODE 解决中文变成 unicode 问题。 return response()->json($data)->setEncodingOptions(JSON_UNESCAPED_UNICODE); } }定义 /login 路由,代码如下
use IlluminateHttpRequest; use IlluminateSupportFacadesRoute; use AppExceptionsMyException; Route::get('/login', function(Request $request){ if (empty($request['phone'])) { throw new MyException("请输入手机", -10000); } if (empty($request['password'])) { throw new MyException("请输入密码", -10000); } return response([ 'msg' => '登录成功', 'status' => 10000, ]); });此时我们访问:http://localhost:8000/login
访问 http://localhost:8000/login?phone=10086&password=secret
总结
怎么样,异常的处理是不是很简单?Laravel 利用了 set_error_handler / set_exception_handler 对错误/异常进行了重写,给开发者带来了许多便利之处。
希望本文能让你对 PHP 原生和 Laravel 中错误/异常的区别有一定的认识。



