浅谈 PHP 中异常类的使用

为什么要使用异常类

  1. 优化代码的可读性

    在没有异常的时候我们在编写函数时往往会出现以下情况:

    
    function doSomething(){
    if(something error){
       return false;
    }
    
    ...
    do something
    }
    //调用
    $result = doSomething();
    
    if(!$result){
     echo "something goes error";
    }

    如果使用异常则可以这么写:

    function doSomething(){
        if(something error){
             throw new SomethingException('something error');
        }
        ...
        do something
    }
    //调用
    try{
       doSomething();
    }catch(SomethingException $e){
       echo "something goes error";
    }

    无论时调用层面还是函数本身层面,抛出异常的可读性比返回 false 的可读性要更高,因为我们能够一眼就看出来哪些代码是异常时运行的,哪些时正常时运行的。

  2. 更加符合语义化

    异常类往往都有自己的名字,在函数调用层面,即使不进入函数内部也能够通过异常名来判断调用函数会伴有哪些异常,如果函数没有异常,返回的是 falsetrue ,这会让函数的调用者非常头痛,因为你永远不知道这个函数调用什么时候是异常的,只能通过返回值判断,一个函数如果有多种异常情况,通过返回值判断就会在调用层面生成多个 if,如果使用了异常类,调用层能够轻松的分别 catch 不同的异常来进行处理。

    // 调用方无法从外部得知时哪里出了问题,因为只返回了 false
    function checkoutOrder($orderNumber){
      $orderModel = OrderModel::query()->where('order_number',$orderNumber)->first();
      if(!$orderModel){
         return false;
      }
      $payResut = PayService::pay($orderModel);
      if(!$payResut){
          return false;
      }
    }

    为每种程序异常命名:

    function checkoutOrder($orderNumber){
      $orderModel = OrderModel::query()->where('order_number',$orderNumber)->first();
      if(!$orderModel){
         throw new OrderNotFoundException($orderNumber);
      }
      $payResut = PayService::pay($orderModel);
      if(!$payResut){
          throw new PaymentException(); // 这个异常应该上面的 PayService 中抛出,为了更清晰就写在这
      }
    }
    
    // 调用
    try{
        checkoutOrder('ORDER00001');
    }catch(OrderNotFoundException $e){
        return response('订单不存在:'.$e->getMessage(),404);
    }catch(PaymentException $e){
        return response('支付失败:'.$e->getMessage(),500);
    }

    在 Laravel 中使用异常类

    • Laravel 异常处理流程:

      浅谈 PHP 中异常类的使用

    注:如果在控制器中 catch 最底层的 \Exception ,异常就不会走到 Handler 里面,未被 catch 掉的 \Exception 都会记录在 storage/logs/laravel.log 中,所以在控制器中 catch 异常要考虑清楚,否则可能在日志文件中查询不到错误原因。

    • 使用 Handler 做项目错误告警

      app/Exceptions/Handler.php 中根据异常名、紧急程度调用第三方通知工具(钉钉、邮件等)通知项目错误。

      public function report(Exception $exception){
          if ($this->shouldntReport($exception)) {
              return;
          }
          // 如果异常类中存在 report 方法,就使用自身的
          if (method_exists($exception, 'report')) {
              return $exception->report();
          }
      
          $msg = "系统异常:" . $exception->getMessage();
          $msg .= "\n文件:" . $exception->getFile();
          $msg .= "\n行号:" . $exception->getLine();
          $msg .= "\n参数:" . json_encode(['form_params' => request()->all()]);
      
          DingService::sendWarning($msg);
          parent::report($exception);
      }
    • render 方法根据异常名返回不同的客户端响应:

    public function render($request, Exception $exception)
    {
        if ($exception instanceof OrderException) {
            return $this->handleOrderException($exception, $request);
        }
        if ($exception instanceof PaymentException) {
            return $this->handlePaymentException($exception, $request);
        }
        return parent::render($request, $exception);
    }

    更多详细说明可查看 Laravel--错误处理

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 4

这样写 感觉一个代码写来会有很多异常文件需要新建 而且调用者 还需要看你方法使用了哪些异常类 然后在try catch

3年前 评论

@steven4 IDE会有提醒的,按 ALT + ENTER 可快速 try catch

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!