web-dev-qa-db-ja.com

カスタム例外メッセージ:ベストプラクティス

例外メッセージを作成するときに有用なデバッグ情報を強制するためにどれだけの労力を費やすべきか疑問に思いますか、それともユーザーが正しい情報を提供するか、情報収集を例外ハンドラーに延期するかを信頼する必要がありますか?

私は多くの人々が彼らの例外をしているのを見ます:

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

または、デフォルトの例外を、それほど多くは行わないが例外の名前を変更するカスタム例外で拡張します。

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

しかし、これは多くのデバッグ情報を提供しません...そして、エラーメッセージでどんな種類のフォーマットも強制しません。したがって、まったく同じエラーで2つの異なるエラーメッセージが生成される可能性があります。たとえば、「データベース接続に失敗しました」と「dbに接続できませんでした」

確かに、それが上部にバブルすると、スタックトレースが出力されますが、これは便利ですが、常に知る必要があるすべてを教えてくれるわけではなく、通常、発見するためにvar_dump()ステートメントを撃ち始めなければなりません何が間違っていてどこで...これはまともな例外ハンドラで多少相殺される可能性があります。

私は次のコードのようなものについて考え始めています。ここで、Irequire正しいエラーメッセージを生成するために必要な引数を提供するための例外のスローアーです。私はこれがそれに行く方法かもしれないと思っています:

  • 最低限の有用な情報を提供する必要があります
  • ある程度一貫したエラーメッセージを生成する
  • 例外メッセージのテンプレートはすべて1つの場所(例外クラス)にあるため、メッセージを簡単に更新できます...

しかし、私はそれらを使用するのが難しく(例外定義を調べる必要がある)、したがって他のプログラマが提供された例外を使用するのを妨げる可能性があるという欠点があると思います...

このアイデアと、一貫した柔軟な例外メッセージフレームワークのベストプラクティスについてコメントをお願いします。

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

そして、このようなことについて何も言及されていないので、おそらく悪い考えです...

67
timoxley

Krzysztofのブログ に関するアドバイスを強くお勧めします。あなたの場合、彼が使用エラーと呼んでいるものに対処しようとしているように見えることに注意してください。

この場合、必要なのはそれを示すための新しい型ではなく、それが原因となったものに関するより良いエラーメッセージです。そのようなヘルパー関数として:

  1. 例外に配置するテキスト文字列を生成します
  2. 全体の例外とメッセージを生成する

必要なものです。

アプローチ1はより明確ですが、もう少し冗長な使用につながる可能性があります。2は反対であり、明確性を低くするために簡潔な構文を使用します。

関数は非常に安全である必要があり(それら自体は無関係の例外を決して発生させてはなりません)、特定の合理的な使用においてオプションであるデータの提供を強制しないことに注意してください。

これらのアプローチのいずれかを使用することにより、必要に応じて後でエラーメッセージを国際化しやすくなります。

最小限のスタックトレースは、関数と、場合によっては行番号を提供します。したがって、そこから簡単に解決できない情報を提供することに集中する必要があります。

34
ShuggyCoUk

Krzysztofのブログに関するアドバイスを損なうことはありませんが、カスタム例外を作成する非常に簡単な方法を次に示します。

例:

<?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()}";
    }
}
20
Orwellophile

"Framework Design Guidelines"の共著者であるKrzysztof Cwalinaのブログの How to Design Exception Hierarchies を参照してください。

11
John Saunders

ユーザーが「正しいことをする」ことを決して信用せず、デバッグのための情報を含めてください。情報が必要な場合は、自分で情報を収集し、アクセス可能な場所に保存する必要があります。

また、述べたように、何かをするのが難しい場合、ユーザーはそれを避けますので、繰り返しになりますが、送信する必要があるものについての善意と知識に依存しないでください。

この考え方は、情報を収集して記録する方法を意味します。つまり、どこかでvar_dump()を使用することを意味します。

また、Mark Harrisonが言ったように、エラーメッセージをどこかに簡単に送信できるボタンは、あなたにとってもユーザーにとっても素晴らしいものです。エラーを簡単に報告できます。 (受信者として)あなたは多くの重複を取得しますが、重複情報は情報がないよりも優れています。

3
Matthew Farwell

どんな詳細を追加しても、必ず

  • 全体を簡単にカットアンドペーストする、または
  • それらのエラーを報告するボタンがあります
0
Mark Harrison