Tag Archives: Exceptions

PHP5の13の組み込み例外

このエントリーをはてなブックマークに追加
はてなブックマーク - PHP5の13の組み込み例外
Share on Facebook

今さらですが、PHP5の組み込み例外についてメモしておきます。どこまで使い分けるべきかわかりませんが、Exceptionよりもう少し細かい分類がしたいときに使えば良いかと思います。

大きく分けるとExceptionの下にLogicException系RuntimeException系があります。継承関係は SPL-StandardPHPLibrary: Exception Class Reference がわかりやすいです。

13の例外

  1. BadFunctionCallException
    • 未定義の関数が呼ばれた、または引数が足りない
    • LogicExceptionを継承
  2. BadMethodCallException
    • 未定義の関数が呼ばれた、または引数が足りない
    • BadFunctionCallExceptionを継承
  3. DomainException
    • 定義済みのデータドメインと値が合わない
    • LogicExceptionを継承
  4. InvalidArgumentException
    • 引数が期待されている値と異なる
    • LogicExceptionを継承
  5. LengthException
    • 長さが不正
    • LogicExceptionを継承
  6. LogicException
    • 論理式が不正
    • Exceptionsを継承
  7. OutOfBoundsException
    • 値が正しいキーではない
    • RuntimeExceptionを継承
  8. OutOfRangeException
    • 値が範囲外
    • LogicExceptionを継承
  9. OverflowException
    • 新しい要素がコンテナ内に入らない
    • RuntimeExceptionを継承
  10. RangeException
    • 不正な範囲が与えられた
    • RuntimeExceptionを継承
  11. RuntimeException
    • ランタイム(プログラム実行時)のエラーに対する例外
    • Exceptionを継承
  12. UnderflowException
    • 空のコンテナから要素を削除することはできない
    • RuntimeExceptionを継承
  13. UnexpectedValueException
    • 予期しない値が渡された
    • RuntimeExceptionを継承

参考

コントローラを単純化するために例外を使う

このエントリーをはてなブックマークに追加
はてなブックマーク - コントローラを単純化するために例外を使う
Share on Facebook

2009年6月12日の記事でだいぶ古いのですが、CakePHPのコードを良くするTIPSがあったので訳してみました。

Simplifying Controller logic with Exceptions | Mark Story

要約すると、「モデル内で例外を吐くようにすると、エラーコードが読みやすくなり、コントローラのテストもしやすくなるよ」ということです。


増大するコードと格闘する日々・・・どうにかクリエイティブな方法で解決したい。そんな悩みを、モデルのメソッドから例外を投げる方法で解決しました。別にビックリするようなものではないです。ただfalseを返すより、ちょっと便利な点がいくつかあります。

第一に、if-elseを減らすことができます。第二に、エラーが起こる部分のソースにエラーメッセージを書くことができるので、何度も使うメソッドならエラーメッセージを重複させずに書くことができます。

例えば以下のメソッドは、リモートアドレスからリソースをダウンロードし、 ローカルファイルシステムとデータベースに記録するものです。(訳注: ダウンロードの時点で失敗すると例外を、保存に成功するとtrueを、保存に失敗するとfalseを返します。)

public function downloadResource($url, $userId, $type = 'image') {
    if (!isset($this->_fetchMimes[$type])) {
        throw new OutOfBoundsException(__('Invalid media type', true));
    }
    $this->_loadSocket($url);
    $resource = $this->Socket->get($url);
    if (!isset($this->Socket->response['header']['Content-Type'])) {
        throw new OutOfBoundsException(__('Submitted url has no Mime-Type', true));
    }
    $allowedContentTypes = $this->_fetchMimes[$type];
    if (!in_array($this->Socket->response['header']['Content-Type'], $allowedContentTypes)) {
        throw new OutOfBoundsException(__('Submitted url has an invalid Mime-Type', true));
    }
    $newFile = array(
        'File' => array(
            'file' => $this->_saveFetchedFile($resource, $url, $this->Socket->response['header']['Content-Type']),
            'user_id' => $userId,
            'title' => $url,
        )
    );
    $this->create($newMedia);
    if ($this->save()) {
        return true;
    }
    return false;
}

見てわかる通り、起きてはならないことが起きた時には OutOfBoundsExceptions を投げています。最近のバージョンのPHPに含まれているSPLライブラリ (訳注: 標準PHPライブラリ) には、便利なクラスが多数用意されています。もちろん自分で例外を作ることもできますが、たいていは組み込みの例外を使うだけで十分でしょう。

これを使えば、コントローラのメソッドをかなりスッキリさせることができます。何重ものifでチェックする必要が無くなります。また、シンプルでエラーの見通しも良いコードを書くことができます。

try {
    $this->File->downloadResource($this->data['File']['url'], $this->Auth->user('id'), 'image');
    //do some additional file handling and data processing.
 
    $this->Session->setFlash(__('File uploaded successfully', true));
} catch(OutOfBoundsException $e) {
    $this->Session->setFlash($e->getMessage());
}
$this->redirect(array('action' => 'index'));

このメソッドの例外発生をテストするのは簡単です。assertFalse を使うのではなく、pass()とfail()を使えばいいのです。

// (訳注) 不正なURLを送った時に例外が発生することをテストする
try {
    $this->File->downloadResource('http:/bogus.com/', 1, 'image');  // 間違ったURL
    $this->fail('No exception thrown with bogus arguments');
} catch (Exception $e) {
    $this->pass('Exception thrown');
}

例外を使うと便利になることは多いです。だからと言って、falseを返すタイプのメソッドすべてを例外を返すようにする必要はないでしょう。たとえばヘルパから例外を返すようにすると、要望を満たしてくれるよりも苦痛を感じることのほうが多くなるでしょう。

どんなツールもそうですが、正しく使えばメンテナンス性の良いコードを生み出すことができるのです。