例外を使う
via. http://d.hatena.ne.jp/uratch/20100303/1267587165、2010-03-18 - ます’s Diary - どうでもいい事100選
前のエントリにも続きますが、Javaっぽく書くのであれば、例外を使うのもそうかもしれません。
一番のメリットが、ロジックの中にCライク(?)な"戻り値が0だったら〜、そうじゃなかったら〜"みたいな処理を書かなくて済むのと、例外をcatchする箇所を絞って、エラー処理をまとめて書ける事じゃないかと思います。
たとえば、"DBに繋いでデータを取得するコード"を考えてみます。例外を使わない場合は次のようなコード。
<?php $conn = oci_connect("scott", "tiger", $db); if ($conn === false) { // エラー処理 } $stmt = oci_parse($conn, "..."); if ($stmt === false) { // エラー処理 } if (oci_execute($stmt, OCI_DEFAULT) === false) { // エラー処理 } while (oci_fetch($stmt)) { // データ処理 }
例外を使うとこんな感じ(ホントはoci関数は例外を投げません。イメージです)。
<?php try { $conn = oci_connect("scott", "tiger", $db); $stmt = oci_parse($conn, "..."); oci_execute($stmt, OCI_DEFAULT); while (oci_fetch($stmt)) { // データ処理 } } catch (Exception $e) { // エラー処理 }
実際のエラー処理は、上のコードのような箇所ではなく、それを呼び出した側のコード、あるいはフレームワークに近い"層"で書くことが多い感じです。逆に、ビジネスロジック実行・クエリ実行などの"具体的な処理"をするコードになればなるほど、『処理してエラーが発生したら、とりあえず例外を投げるだけ』にしてます。
catchするタイミングですが、その例外が『予期している』のか『予期していない』のかで、どの層でcatchするかが違ってきます。たとえば、エラー時に何らかの処理を行う予定がある(必須パラメータがが入力されていない場合に入力を促すメッセージを表示するとか)場合は、『予期している例外』を呼び出した側(ActionクラスとかService/Logicクラス)でcatchし、然るべき処理をします。
逆に、それ以外の例外は『予期していない例外』になります。DB接続に失敗した時に投げられる例外や、あるはずのデータが無くて処理に失敗した時に投げられる例外、とかです。こちらはActionなどでもcatchしないで、フレームワークで一括に処理(ゴメンナサイ画面とか"ただいま混み合ってます"画面を出すとか)させることが多いです。
Webアプリの場合、個人的には
- ProjectException extends Exception
- UnauthenticatedException extends ProjectException
- ApplicationException extends ProjectException
- InvalidRequestMethodException extends ApplicationException
- NotFoundException extends ApplicationException
- ApplicationError extends ProjectException
みたいな例外を作っておいて、UnauthenticatedExceptionをcatchした場合はログイン画面を出す(HTTP401を返すことも)、ApplicationExceptionの場合はエラーメッセージとHTTP403、ApplicationErrorの場合はゴメンナサイ画面を出しつつHTTP500を返す、といったことをしてます。SQLExceptionとか処理系固有の例外もApplicationErrorと同じ扱いにします。ちなみに、catchしたタイミングでtrace情報のロギングなどもしてます。
catchする側はちょっと面倒かもしれませんが、ロジック側を『なんかあったら例外投げとけ』という具合にシンプルに記述できるのは楽です:-)