web-dev-qa-db-ja.com

PHP MVC使用時のエラー処理

私は最近Codeigniterを頻繁に使用していますが、気になるのは、エラーを処理してユーザーに表示することです。面倒になることなく、エラー処理に長けたことがありません。私の主な懸念は、ユーザーにエラーを返すときです。

関数から0または1を返し、if/elseを使用してエラーを処理するのではなく、例外を使用して例外をスロー/キャッチすることは良い習慣ですか?したがって、問題についてユーザーに簡単に通知できます。

私は例外から離れる傾向があります。私のJava数年前の大学の家庭教師は、「例外はプロダクションコードでは使用すべきではなく、デバッグのために使用するべきだ」と言っていました。彼が嘘をついているように感じます。

しかし、例として、ユーザーをデータベースに追加するコードがあります。プロセス中に、データベースの問題、重複したエントリ、サーバーの問題など、複数の問題が発生する可能性があります。登録中に問題が発生した場合、ユーザーはそれについて知る必要があります。

MVCフレームワークを使用していることを念頭に置いて、PHPでエラーを処理するための最良の方法は何ですか。

12
James Jeffery

関数から0または1を返し、if/elseを使用してエラーを処理するのではなく、例外を使用して例外をスロー/キャッチすることは良い習慣ですか?したがって、問題についてユーザーに簡単に通知できます。

ダメダメダメ!

例外とエラーを混在させないでください。例外はまあ、例外的です。エラーはありません。ユーザーに製品の数量の入力を要求し、ユーザーが「hello」と入力した場合、それはエラーです。これは例外ではありません。ユーザーからの無効な入力を見ても例外はありません。入力を検証するときなど、例外的でない場合に例外を使用できないのはなぜですか? 他の人々 はすでにそれを説明し、入力検証の有効な代替案を示しました。

これは、ユーザーがあなたの例外を気にしないこと、および例外を表示することは不親切で危険なこと。たとえば、SQLクエリの実行中に例外が発生すると、クエリ自体が明らかになることがよくあります。このようなメッセージをみんなに見せるためにリスクを取ってよろしいですか?

データベースの問題、重複するエントリ、サーバーの問題など、複数の問題が発生する可能性があります。登録中に問題が発生した場合、ユーザーはそれについて知る必要があります。

違う。 ユーザーとして、私はあなたのデータベースの問題、重複したエントリなどを知る必要はありません。私は本当にあなたの問題を気にしません。私がdoに知っておくべきことは、すでに存在するユーザー名を入力したことです。すでに述べたように、私からの間違った入力は例外ではなくエラーを引き起こさなければなりません。

これらのエラーを出力する方法は? 状況によって異なります。すでに使用されているユーザー名の場合、フォームを送信する前に、ユーザー名の近くに小さな赤い旗が表示されるようにしたいのですが、ユーザー名はすでに使用されていると言っています。 JavaScriptがない場合、送信後に同じフラグが表示される必要があります。

An example of an AJAX-enabled error

その他のエラーの場合は、エラーのあるページ全体を表示するか、何か問題が発生したことをユーザーに通知する別の方法を選択します(たとえば、メッセージが表示され、ページの上部で消える)。質問はプログラミングよりも ユーザーエクスペリエンス に関連しています。

プログラマーの観点から見ると、エラーのタイプに応じて、さまざまな方法でエラーを伝搬します。たとえば、すでに使用されているユーザー名の場合、AJAXへのリクエストhttp://example.com/?ajax=1&user-exists=Johnは、以下を示すJSONオブジェクトを返します。

  • ユーザーが既に存在していること
  • ユーザーに表示するエラーメッセージ。

2番目のポイントは重要です。JavaScriptを無効にしてフォームを送信するときと、JavaScriptを有効にして重複するユーザー名を入力するときの両方で、同じメッセージが表示されるようにする必要があります。サーバー側のソースコードとJavaScriptでエラーメッセージのテキストを複製する必要はありません。

これは実際にはStack ExhangeのWebサイトで使用されている手法です。たとえば、自分の回答に賛成票を入れようとすると、AJAX応答に表示するエラーが含まれます。

{"Success":false,"Warning":false,"NewScore":0,"Message":"You can't vote for your own post.",
"Refresh":false}

別の方法を選択して、フォームに入力する前にHTMLページでエラーを事前設定することもできます。長所:AJAX応答でエラーメッセージを送信する必要はありません。短所:アクセシビリティについてはどうですか?CSSなしでページを参照すると、発生する可能性のあるすべてのエラーが表示されます。

14

関数から0または1を返し、if/elseを使用してエラーを処理するのではなく、例外を使用して例外をスロー/キャッチすることは良い習慣ですか?したがって、問題についてユーザーに簡単に通知できます。

はいはいはい!

クリーンなコードが必要な場合は、例外のみを使用し、エラーコードを使用しないでください。エラーコードは意味がありません。ほとんどの場合、多くの情報を明らかにしないいくつかの数値定数に関連付けられています。コードが読みにくくなる可能性があり、エラーと一緒にデータを伝播することが難しくなります。

ただし、例外はクラスであり、任意の情報を含めることができます。そのため、ユーザーは数値フィールドに「abc」などの誤った入力をしました。エラーコードを使用すると、大量のバブリングを行わずにこの情報をエラーのハンドラーに伝達することはできません。例外が無料で提供するもの。また、例外を使用すると、エレガントに失敗する方法を維持しながら、関数やメソッドに意味のある戻り値を設定できます。さらに良いことに、例外は処理したい場所に直接伝達されます!意味のあるデータを含むエラーコードを1つまたは2つの上のレイヤーのハンドラーに伝播するために必要なスパゲッティコードの量を想像してください。

また、例外は、エラーコードよりも意味論的に表現されます。エラーコードはスパゲッティコードにつながり、例外処理はクリーンなコードにつながります。

さらに、ステータスコードの確認を忘れがちです。 Javaのような言語では、例外(たとえば、C#が見逃すもの)を処理する必要があります)。

MVCフレームワークを使用していることを念頭に置いて、PHPでエラーを処理するための最良の方法は何ですか。

例外を使用して、コントローラーでそれらを処理します。

13
Falcon

この便利な小さなクラスを考えてみましょう:

_class FunkyFile {               

    private $path;
    private $contents = null;

    public function __construct($path) { 
        $this->setPath($path); 
    }

    private function setPath($path) {
        if( !is_file($path) || !is_readable($path) ) 
            throw new \InvalidArgumentException("Hm, that's not a valid file!");

        $this->path = realpath($path);
        return $this; 
    }

    public function getContents() {
        if( is_null($this->contents) ) {
            $this->contents = @file_get_contents( $this->path );
            if($this->contents === false) 
                throw new \Exception("Hm, I can't read the file, for some reason!");                                 
        }

        return $this->contents;            
    }

}
_

これは例外の完全な使用法です。 _FunkyFile's_の観点から見ると、パスが無効であるか_file_get_contents_が失敗した場合に、状況を改善するためにできることはまったくありません。本当に例外的な状況;)

しかし、コードのどこかで間違ったファイルパスに遭遇したことをユーザーが知る価値があるでしょうか。例えば:

_class Welcome extends Controller {

    public function index() {

        /**
         * Ah, let's show user this file she asked for
         */                 
        try {
            $file = new File("HelloWorld.txt");
            $contents = $file->getContents();   
            echo $contents;
        } catch(\Exception $e) {
            log($e->getMessage());

            echo "Sorry, I'm having a bad day!"; 
        }                           
    }        
}
_

あなたに悪い日を過ごしていると伝える以外に、あなたの選択肢は:

  1. フォールバック

    情報を取得する別の方法はありますか?上記の簡単な例では、そうは思われませんが、マスター/スレーブデータベーススキーマを検討してください。マスターが応答しなかった可能性がありますが、スレーブがまだ存在している可能性があります(またはその逆)。

  2. それはユーザーの責任ですか?

    ユーザーは誤った入力を送信しましたか?さて、それについて彼女に教えてください。エラーメッセージを鳴らすか、ニースになってエラーメッセージにフォームを添付すると、正しいパスを入力できます。

  3. それはあなたのせいですか?

    そして、つまり、ユーザーではないではないため、間違ったファイルパスを入力したことから、サーバーで問題が発生することまで、すべてを意味します。厳密に言えば、 503 HTTPエラー の時間です。サービスも利用できません。 CIにはshow_404()関数があり、show_503()を簡単に構築できます。

アドバイスの言葉、あなたは不正な例外を考慮に入れるべきです。 CodeIgniterは厄介なコードであり、いつ例外がポップアップするかはわかりません。同様に、独自の例外を忘れる可能性があり、最も安全なオプションは、すべての例外ハンドラーを実装することです。 PHPあなたは set_exception_handler でそれを行うことができます:

_function FunkyExceptionHandler($exception) {
    if(ENVIRONMENT == "production") {
        log($e->getMessage());
        show_503();
    } else {
        echo "Uncaught exception: " , $exception->getMessage(), "\n";
    }   
}

set_exception_handler("FunkyExceptionHandler");
_

set_error_handler を使用して、不正なエラーを処理することもできます。例外の場合と同じハンドラーを作成するか、すべてのエラーをErrorExceptionに変換して、例外ハンドラーで処理することができます。

_function FunkyErrorHandler($errno, $errstr, $errfile, $errline) {
    // will be caught by FunkyExceptionHandler if not handled
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}

set_error_handler("FunkyErrorHandler");
_
6
yannis