dietcake advent calendar 2014 の18日目です。
dietcake に限った話ではありませんが、PHPはバージョン5から例外を使えるようになっています。
老害ネタですが、PHP4の時代はエラーハンドリングといえば PEAR::isError で、何か実行するたびに、
if (PEAR::isError()) と打ち込んで結果の判定を行っていました。
もちろん処理によっては、実行直後に成功可否の判定を行なう事も必要なのですが、最初から復旧不可能である事がわかっている
処理については、例外を投げてあげる事で、判定をまとめる事ができます。

devtool における例を見てみましょう。

app/controllers/code_controller.php の show() メソッドを見てもらうと、以下の様な実装になっています。

    public function show()
    {
        $user = $this->start();

        $path = Param::get('p');

        $code_packs = CodePack::getAll($user);
        $code_pack = CodePack::get($user, $path);
        $codes = $code_pack->getCodes();

        $this->set(get_defined_vars());
    }

$user を作り、外部パラメータ p を元にCodePack::get() からデータを取り出し view に渡しています。
このコントローラーには、先ほど説明した if (PEAR::isError()) や、try-catch は存在しません。
ここで、外部パラメータpに存在しない値を与えるとどうなるか見てみましょう。

CodePack::get() は、 app/models/code_pack.php に定義されています。

    public static function get(User $user, $path)
    {
        $db = DB::conn();

        $row = $db->row('SELECT * FROM code_pack WHERE path = ?', array($path));
        if (!$row) {
            throw new RecordNotFoundException();
        }

        if ($user->id == $row['user_id']) {
            $row['writable'] = true;
        }

        $row['user'] = $user;

        return new self($row);
    }

pが存在しない場合は、DBからレコードを取り出せないため、 RecordNotFoundException が発生します。
この例外は、code_controller.phpではスルーされ、その上位の app/app_controller.php が受け取り、汎用エラー画面に飛ばします。

    public function dispatchAction()
    {
        try{
            try {
                parent::dispatchAction();
            } catch(Exception $e) {
                $log = sprintf('Exception:(%s)%[email protected]%s %s', Session::getId(), $e->getFile(), $e->getLine(), $e->getMessage());
                $this->set('user', $this->start());
                error_log($log);
                throw $e;
            }
        } catch (PDOException $e) {
            $this->set('exception', $e);
            $this->render('error/database');
        } catch (RecordNotFoundException $e) {
            $this->render('error/not_found');
        } catch (PermissionDeniedException $e) {
            $this->render('error/permission');
        } catch (Exception $e) {
            $this->set('exception', $e);
            $this->render('error/unexpected');
        }
    }

この仕組みによって、コントローラー内でのエラー判定処理を減らし、全体の見通しをよくしています。

例外の仕組み自体はなかなか奥が深く、私自身もまだ完璧に使いこなせている感覚はありませんが、
1つのメソッドの中で、「 実行 -> 結果確認とエラー遷移 -> 実行 -> 結果確認とエラー遷移」 みたいになっている時は、
コードの内容に応じて例外を活用してうまく共通化できないか確認してみると良いですね。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

ねこ認証:9つのパネルの中からねこを3匹選んでください