自定义异常消息:最佳实践

tim*_*ley 67 php exception

想知道在创建异常消息时我应该花多少精力来强制使用有用的调试信息,或者我应该只是信任用户提供正确的信息,或者将信息收集推迟到异常处理程序?

我看到很多人都在做他们的例外,例如:

throw new RuntimeException('MyObject is not an array')
Run Code Online (Sandbox Code Playgroud)

或者使用自定义异常扩展默认异常但不会做太多但只更改异常名称:

throw new WrongTypeException('MyObject is not an array')
Run Code Online (Sandbox Code Playgroud)

但这并没有提供太多的调试信息......并且不会使用错误消息强制执行任何类型的格式化.所以你可能会得到完全相同的错误产生两个不同的错误消息...例如"数据库连接失败"vs"无法连接到数据库"

当然,如果它冒泡到顶部,它将打印堆栈跟踪,这是有用的,但它并不总是告诉我我需要知道的一切,通常我最终不得不开始拍摄var_dump()语句来发现出了什么问题,哪里......虽然这可能会被一个体面的异常处理程序所抵消.

我开始考虑类似下面的代码,我需要异常的thrower来提供必要的args来产生正确的错误消息.我想这可能就是这样的方式:

  • 必须提供最低有用信息
  • 产生一些一致的错误消息
  • 异常消息的模板都在一个位置(异常类),因此更容易更新消息...

但我发现缺点是它们更难使用(需要你查找异常定义),因此可能会阻止其他程序员使用提供的异常......

我想对这个想法和一致,灵活的异常消息框架的最佳实践发表评论.

/**
* @package MyExceptions
* MyWrongTypeException occurs when an object or 
* datastructure is of the incorrect datatype.
* Program defensively!
* @param $objectName string name of object, eg "\$myObject"
* @param $object object object of the wrong type
* @param $expect string expected type of object eg 'integer'
* @param $message any additional human readable info.
* @param $code error code.
* @return Informative exception error message.
* @author secoif
*/
class MyWrongTypeException extends RuntimeException {
    public function __construct($objectName, $object, $expected, $message = '', $code = 0) {
        $receivedType = gettype($object) 
        $message = "Wrong Type: $objectName. Expected $expected, received $receivedType";
        debug_dump($message, $object);
        return parent::__construct($message, $code);
    }
}
Run Code Online (Sandbox Code Playgroud)

....

/**
 * If we are in debug mode, append the var_dump of $object to $message
 */
function debug_dump(&$message, &$object) {
     if (App::get_mode() == 'debug') {
         ob_start();
         var_dump($object);
         $message = $message . "Debug Info: " . ob_get_clean();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后使用像:

// Hypothetical, supposed to return an array of user objects
$users = get_users(); // but instead returns the string 'bad'
// Ideally the $users model object would provide a validate() but for the sake
// of the example
if (is_array($users)) {
  throw new MyWrongTypeException('$users', $users, 'array')
  // returns 
  //"Wrong Type: $users. Expected array, received string
}
Run Code Online (Sandbox Code Playgroud)

我们可能会在自定义异常处理程序中执行类似nl2br的操作,以使html输出更好.

正在阅读:http: //msdn.microsoft.com/en-us/library/cc511859.aspx#

并没有提到这样的事情,所以也许这是一个坏主意......

Shu*_*oUk 34

我强烈推荐Krzysztof博客上的建议,并注意到在你的情况下,你似乎试图处理他所谓的用法错误.

在这种情况下,所需要的不是指示它的新类型,而是关于导致它的更好的错误消息.作为这样的辅助函数:

  1. 生成要放入异常的文本字符串
  2. 生成整个异常和消息

是什么需要.

方法1更清晰,但可能导致更冗长的使用,2则相反,交易更简洁的语法.

请注意,这些函数必须非常安全(它们本身永远不会导致不相关的异常),并且不强制提供在某些合理用途中可选的数据.

通过使用这些方法中的任何一种,您可以在以后根据需要更轻松地将错误消息国际化.

堆栈跟踪至少为您提供了功能,可能还有行号,因此您应该专注于提供不易于解决的信息.


Orw*_*ile 20

我不会减损有关Krzysztof博客的建议,但这是一种创建自定义异常的简单方法.

例:

<?php
   require_once "CustomException.php";
   class SqlProxyException extends CustomException {}

   throw new SqlProxyException($errorMsg, mysql_errno());     
?>
Run Code Online (Sandbox Code Playgroud)

那背后的代码(我借用某处,向任何人道歉)

<?php

interface IException
{
    /* Protected methods inherited from Exception class */
    public function getMessage();                 // Exception message
    public function getCode();                    // User-defined Exception code
    public function getFile();                    // Source filename
    public function getLine();                    // Source line
    public function getTrace();                   // An array of the backtrace()
    public function getTraceAsString();           // Formated string of trace

    /* Overrideable methods inherited from Exception class */
    public function __toString();                 // formated string for display
    public function __construct($message = null, $code = 0);
}

abstract class CustomException extends Exception implements IException
{
    protected $message = 'Unknown exception';     // Exception message
    private   $string;                            // Unknown
    protected $code    = 0;                       // User-defined exception code
    protected $file;                              // Source filename of exception
    protected $line;                              // Source line of exception
    private   $trace;                             // Unknown

    public function __construct($message = null, $code = 0)
    {
        if (!$message) {
            throw new $this('Unknown '. get_class($this));
        }
        parent::__construct($message, $code);
    }

    public function __toString()
    {
        return get_class($this) . " '{$this->message}' in {$this->file}({$this->line})\n"
                                . "{$this->getTraceAsString()}";
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 对于任何对此答案进行投票的人,请不要这样解释.我刚刚对PHP 7.0.6和PHP 5.4,43进行了检查,没有问题. (3认同)
  • +1,因为它是我发现的第一个真正告诉我如何定义 PHP 异常以及如何抛出它的地方 (2认同)

Joh*_*ers 11

请参阅"框架设计指南"的合着者Krzysztof Cwalina的博客,了解如何设计异常层次结构.