自定义exception消息:最佳实践
想知道在创buildexception消息时,我应该多less努力来强制有用的debugging信息,还是应该只相信用户提供正确的信息,或者将信息收集推迟到exception处理程序?
我看到很多人在做例外,如:
throw new RuntimeException('MyObject is not an array')
或者用自定义的exception来扩展默认exception,这些exception不会做太多的事情,但可以改变exception的名称:
throw new WrongTypeException('MyObject is not an array')
但是这不提供太多的debugging信息…并且不强制任何forms的错误信息。 所以,你可能会得到完全相同的错误产生两个不同的错误信息…例如“数据库连接失败”vs“无法连接到数据库”
当然,如果它顶到顶部,它会打印堆栈跟踪,这是有用的,但它并不总是告诉我我需要知道的一切,通常我最终不得不开始拍摄var_dump()语句来发现什么地方出了问题,在哪里…虽然这可能有点抵消了一个像样的exception处理程序。
我开始考虑类似下面的代码,在那里我要求exception的抛出者提供必要的参数来产生正确的错误信息。 我想这可能是这样的:
- 必须提供最低级别的有用信息
- 产生一些一致的错误消息
- exception消息的模板都在一个位置(exception类),因此更容易更新消息…
但我发现它的缺点是它们很难使用(需要查找exception定义),因此可能会阻止其他程序员使用提供的exception。
我想就这个想法发表一些意见,并提供一个一致的,灵活的exception消息框架的最佳实践。
/** * @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); } }
….
/** * 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(); } }
然后用于:
// 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 }
我们可能会在自定义exception处理程序中执行类似于nl2br的操作,以使html输出变得更好。
一直在阅读: http : //msdn.microsoft.com/en-us/library/cc511859.aspx#
没有提到这样的事情,所以也许这是一个坏主意…
我强烈build议Krzysztof的博客上的build议,并会注意到在你的情况下,你似乎试图处理他所谓的使用错误。
在这种情况下,所需要的不是一种新的types来表示它,而是一个更好的错误信息,说明是什么造成的。 作为这样的辅助function,要么:
- 生成放入exception的文本string
- 生成整个exception和消息
是什么要求。
方法1更清晰,但可能会导致更详细的使用,2则相反,交易语言清晰度较差。
请注意,这些函数必须是非常安全的(它们决不应该永远导致一个无关的exception本身),并且不强制提供某些合理用途的可选数据。
通过使用这些方法中的任何一种,您可以稍后根据需要更轻松地将错误消息国际化。
一个堆栈跟踪至less给你的function,可能是行号,因此你应该把重点放在提供的信息是不容易解决的。
我不会对Krzysztof的博客提出build议,但是这里是创build自定义exception的简单方法。
例:
<?php require_once "CustomException.php"; class SqlProxyException extends CustomException {} throw new SqlProxyException($errorMsg, mysql_errno()); ?>
这个代码背后(我借来的地方,向任何人道歉)
<?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()}"; } }
请参阅“框架devise指南”的合着者Krzysztof Cwalina博客上的如何deviseexception层次结构 。
永远不要信任用户“做正确的事情”,并包含debugging信息。 如果您需要信息,您需要自己收集信息并将其存储在可访问的地方。
同样如上所述,如果做某事很难,用户将会避免这样做,所以不要依赖于他们的善意和他们需要发送的知识。
这个思想意味着你收集信息并logging它的方法,这意味着在某处使用var_dump()。
此外,正如马克·哈里森(Mark Harrison)所说的那样,一个让你轻松发送错误信息的button对你和用户来说都是非常棒的。 这让他们很容易报告错误。 你(作为收件人)得到很多重复,但重复的信息比没有信息好。
不pipe你添加多less细节,都可以
- 很容易剪切和粘贴整个东西,或者
- 有一个button,将报告他们的错误