catch语句中函数的错误处理

时间:2012-04-18 12:34:02

标签: php oop zend-framework error-handling dry

这是一段代码,我想知道是否应该重构它以使其更符合清洁代码实践。

这是一个负责退还客户订单的课程。

class RefundServiceInvoker {

    private $_orders;

    public function refundOrder() {   
        $this->getOrdersFromDB(); //This function gets all orders from DB and sets $_orders
        foreach ($this->_orders as $order) { 
            try {
                $order->refund(); //Some lines may throw an exception when refunded due to some business logic (ex. the order was already shipped)
                $this->updateOrderStatus('refunded')            
            } catch (Exception $e) {                   
                $this->logError($e);
                $this->sendMailToAdmin();
            }
        }
    }
}

当然这段代码比原始代码高度简化。

我的主要问题是,如果$order->refund();抛出异常,它将被捕获并记录到数据库,然后发送邮件。但是,如果$this->logError($e);本身抛出异常怎么办?或者如果邮件服务器关闭并抛出异常怎么办?

如果数据库失效并且$this->getOrdersFromDB();抛出异常会更好吗?

我的第一个解决方案是将所有内容包装在一个大的try{}catch{}

public function refundOrder() {   
            try {              
            $this->getOrdersFromDB(); //This function gets all orders from DB and sets $_orders
            foreach ($this->_orders as $order) { 

                    $order->refund(); //Some lines may throw an exception when refunded due to some business logic (ex. the order was already shipped)
                    $this->updateOrderStatus('refunded')            
                } catch (Exception $e) {                   
                    $this->logError($e);
                    $this->sendMailToAdmin();
                }
            }
        }

但这意味着如果一个订单失败那么全部都会失败!我应该为整个功能设置2 try{}catch{},为每个订单设置另一个吗?但在这种情况下,catch中的函数也可能抛出一个不会被捕获的异常。

注意:

应用程序是使用Zend framework 1.11.11构建的。

提前致谢。

3 个答案:

答案 0 :(得分:3)

解决此类问题没有灵丹妙药。如果一个函数可以抛出而你关心它,你必须在它周围包裹try / catch - 就这么简单。

进入细节:在没有关于应用程序架构的更多信息的情况下,不可能评估这种或那种方法的优点,但这里有一些一般性的建议:

  • 执行检查先决条件,然后再致电refundOrder。确保订单已成功加载;确保您要操作的订单全部可退款(尝试退还因业务逻辑而无法退款的订单的目的是什么?不应通知用户/运营商在尝试退款之前?)。
  • 执行使用多个try / catch级别,但可能并非全部位于refundOrder内。外部块用于捕获真正意外的任何错误,那么仅在refundOrder内捕获它们有什么意义呢?你也不想在app的其他部分那样做吗?最里面的try / catch是必要的,这样一个不可退款的订单就不会杀死所有流程。

答案 1 :(得分:1)

如果您需要记录并邮寄每个例外,那么您需要这样的事情:

class RefundServiceInvoker {

   private $_orders;

   public function refundOrder() {
      try {
         $this->getOrdersFromDB();
         foreach ($this->_orders as $order) {
            try {
               $order->refund();
            } catch (MyBusinessException $e) {
               # deals with the problematic $order without stopping the loop
               $this->logError($e);
               $this->sendMailToAdmin();
            }
            $this->updateOrderStatus('refunded');
         }
      } catch(Exception $e) {
         # deals with unexpected bugs
         $this->logError($e);
         $this->sendMailToAdmin();
      }
   }
}

但是你必须在日志和邮件方法中放置一个try / catch,以防止它们在服务器脱机时抛出任何异常。如果不这样做,则在日志/邮件失败时将停止$ orders循环。

如果您需要在refund()方法中仅记录/邮寄您的业务例外,那么您需要这样的事情:

class RefundServiceInvoker {

   private $_orders;

   public function refundOrder() {
      $this->getOrdersFromDB();
      foreach ($this->_orders as $order) {
         try {
            $order->refund();
         } catch (MyBusinessException $e) {
            # deals with the problematic $order without stopping the loop
            $this->logError($e);
            $this->sendMailToAdmin();
         }
         $this->updateOrderStatus('refunded');
      }
   }
}

任何其他异常都会导致http 500 - 内部服务器错误页面,这通常是您想要的,因为它是一个意外的错误。

还有其他方法可以解决这个问题,但正如您所见,这取决于您的需求。

答案 2 :(得分:0)

由于logError($e)sendMailToAdmin()可能是最后使用的函数,通常在try / catch块中,我会确保它们永远不会抛出异常。

function logError($e)
{
    try
    {
        //your logic that may throw an Exception here
    }
    catch {}
}

function sendMailToAdmin($e)
{
    try
    {
        //your logic that may throw an Exception here
    }
    catch {}
}