Пользовательские сообщения об исключениях: лучшие практики - PullRequest
66 голосов
/ 10 марта 2009

Хотите знать, сколько усилий я должен приложить для принудительного использования полезной информации отладки при создании сообщений об исключениях, или я должен просто доверить пользователю предоставление правильной информации или отложить сбор информации до обработчика исключений?

Я вижу, как много людей делают исключения:

throw new RuntimeException('MyObject is not an array')

или расширение исключений по умолчанию с помощью пользовательских исключений, которые мало что делают, но меняют имя исключения:

throw new WrongTypeException('MyObject is not an array')

Но это не дает много отладочной информации ... и не требует какого-либо форматирования с сообщением об ошибке. Таким образом, вы можете получить точно такую ​​же ошибку, которая выдаст два разных сообщения об ошибке ... например, "Ошибка подключения к базе данных" против "Не удалось подключиться к базе данных"

Конечно, если он всплывает наверх, он напечатает трассировку стека, что полезно, но не всегда говорит мне все, что мне нужно знать, и обычно мне приходится начинать снимать с var_dump () операторы, чтобы узнать, что пошло не так и где ... хотя это может быть несколько компенсировано приличным обработчиком исключений.

Я начинаю думать о чем-то вроде приведенного ниже кода, где мне требуется метатель исключения для предоставления необходимых аргументов для получения правильного сообщения об ошибке. Я думаю, что это может быть путь к этому:

  • Минимальный уровень полезной информации должен быть предоставлен
  • Создает несколько непротиворечивые сообщения об ошибках
  • Шаблоны для сообщений об исключениях все в одном месте (классы исключений), так что проще обновлять сообщения ...

Но я вижу недостаток в том, что их сложнее использовать (требует, чтобы вы посмотрели определение исключений), и, таким образом, они могли бы отговорить других программистов использовать предоставленные исключения ...

Я хотел бы прокомментировать эту идею и лучшие практики для согласованной, гибкой структуры сообщений об исключениях.

/**
* @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
}

и мы могли бы сделать что-то вроде nl2br в пользовательском обработчике исключений, чтобы все было удобно для вывода html.

Читал: http://msdn.microsoft.com/en-us/library/cc511859.aspx#

И ничего подобного не упоминается, так что, может быть, это плохая идея ...

Ответы [ 5 ]

34 голосов
/ 10 марта 2009

Я настоятельно рекомендую совет по блогу Кшиштофа и хотел бы отметить, что в вашем случае вы, похоже, пытаетесь разобраться с тем, что он называет «Ошибки использования».

В этом случае требуется указать не новый тип, а лучшее сообщение об ошибке, вызвавшее его. В качестве такой вспомогательной функции можно либо:

  1. создать текстовую строку для помещения в исключение
  2. сгенерировать все исключение и сообщение

Это то, что требуется.

Подход 1 более понятен, но может привести к более подробному использованию, 2 - наоборот, торгуя более коротким синтаксисом для меньшей ясности.

Обратите внимание, что функции должны быть чрезвычайно безопасными (они никогда не должны сами вызывать несвязанное исключение) и не принуждать к предоставлению данных, которые являются необязательными в определенных разумных целях.

Используя любой из этих подходов, вы упростите интернационализацию сообщения об ошибке позже, если потребуется.

Как минимум, трассировка стека дает вам функцию и, возможно, номер строки, поэтому вам следует сосредоточиться на предоставлении информации, из которой нелегко разобраться.

19 голосов
/ 23 июля 2013

Я не буду отвлекать внимание от совета относительно блога Кшиштофа, но вот простой способ создания пользовательских исключений.

Пример:

<?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()}";
    }
}
11 голосов
/ 10 марта 2009

См. Как проектировать иерархии исключений в блоге Кшиштофа Квалины, соавтора "Руководства по разработке структуры".

3 голосов
/ 10 марта 2009

Никогда, никогда не доверяйте пользователю «делать правильные вещи» и включайте информацию для отладки. Если вам нужна информация, вам нужно собрать ее самостоятельно и сохранить где-нибудь, где она будет доступна.

Также, как уже говорилось, если что-то трудно (э) сделать, пользователи будут избегать этого, поэтому, опять же, не зависят от своей доброй воли и своего знания того, что им нужно отправить.

Это мышление подразумевает метод, с помощью которого вы собираете информацию и регистрируете ее, что подразумевает где-то использование var_dump ().

Кроме того, как сказал Марк Харрисон, кнопка, позволяющая легко отправлять сообщения об ошибках куда-то, является фантастической для вас и пользователей. Это позволяет им легко сообщить об ошибке. Вы (как получатель) получаете много дубликатов, но дублирующая информация лучше, чем отсутствие.

0 голосов
/ 10 марта 2009

Сколько бы деталей вы ни добавили, убедитесь, что либо

  • позволяет легко вырезать и вставлять все, или
  • есть кнопка, которая сообщит об ошибке для них
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...