質問: プログラム全般的な書き方について質問させてください。 「ログインチェック」と「バリデーションチェック」のエラー処理を try-catchで処理する場合、どういう風に書くのが一般的なのでしょうか? 案1.try-catchをそれぞれのチェックに分ける。 ``` try{ /* ログインチェック */ throw new Exception('ログインエラー!'); } catch (Exception $e) { /* ログインチェックのエラー処理 */ } try{ /* バリデーションチェック */ throw new Exception('バリデーションエラー!'); } catch (Exception $e) { /* バリデーションチェックのエラー処理 */ } ``` 案2.Exceptionを拡張する。 ``` try{ /* ログインチェック */ throw new LoginException('ログインエラー!'); /* バリデーションチェック */ throw new ValidationException('バリデーションエラー!'); } catch (LoginException $e) { /* ログインチェックのエラー処理 */ } catch (ValidationException $e) { /* バリデーションチェックのエラー処理 */ } ``` ↓のメリットを考慮しても個人的には案2だと思っているのですが、 他にも書き方がある等教えて頂ければor意見を頂ければ幸いです。 案1. メリット:例外を拡張しなくても良い(楽に書ける)。 デメリット:可読性が悪い(見づらい)。 案2. メリット:可読性が良い。 デメリット:例外を拡張する必要がある。 拡張する数が増えた場合は収拾がつかなくなる可能性がある?
回答: ログイン状態で送信しないといけないフォームとかでしょうか? ログインしていないのなら先にログインしてもらう必要があるでしょうし、そうなるとフォームの処理は行わないわけですから、バリデーションをする甲斐がありません。なので案2でしょうか。 もしそのコードをコントローラに書くのであれば、案1と似たような形で、チェック後すぐにdieやreturnでその後の処理を中止することができるかもしれません。 ``` if (ログイン済みか() === false) { print "先にログインしてください"; return; } ``` 可読性に関して言えば、「エラー処理を一か所にまとめたい」と考えればひとつのtryにcatchを複数並べる方法に分がありますし、「チェック処理とエラー処理を近くに置きたい」と考えればtryを分けたり、すぐさまreturnやdieする方に分があるでしょう。 例外には以下のようなメリットがあります。 * 例外をthrowした時点で後続の処理を無視して脱出できる(return/breakのように) * 例外は自動的に外側のブロックへと伝播されるため、エラーコードをひたすらreturnするといった処理を書く必要がない * 例外は好きなところでtry-catchで捕まえられ、必要なら再度throwできる * 例外にはExceptionから派生した任意のクラスを使え、生成時のスタックトレースを持っているため、エラー情報の取り回しが楽 * finally節を使えば、正常時でも異常時でも実行される処理をかける(後始末の類) このような理由から、様々な関数を経由したのちに発生するエラーや、発生時の取り扱いが様々なもの、例えばライブラリ内部でよく使われます。また後始末の必要なファイル操作等でも使いますね。 しかし質問にあるような「エラー発生後すぐにエラー処理を行える」場合では大して伝播させる必要もないですよね。正常系と異常系の出力処理をまとめるという場合でも、ローカル変数でエラーを識別してもよいでしょう。となると前述のように、例外を使わずif+return等で事足りてしまうケースが多いと思います。 ``` // バリデーションでありそうなコード $errors = []; if(empty($_POST['inputA'])) $errors[] = "inputA is empty."; if(empty($_POST['inputB'])) $errors[] = "inputB is empty."; if(empty($_POST['inputC'])) $errors[] = "inputC is empty."; ... if($errors !== []) { foreach($errors as $error) { print "Error: $error\n"; } return; } ```