Open6

PHPの例外を深掘ってみる

ピン留めされたアイテム
awonosukeawonosuke

https://climbtheladder.com/10-php-exception-handling-best-practices/

PHPにおける例外処理の10のベストプラクティス

  1. フロー制御に例外を使用しない
  2. 例外的な状況のみを報告するために例外を使う
  3. 可能な限りエラーの原因に近い例外をcatchして処理する
  4. 一般的な例外クラスをcatchしないようにする
  5. メソッドが実装されていない場合に例外をスローする
  6. 例外ごとに常に意味のあるメッセージを提供する
  7. 例外を飲み込まない
  8. すべての例外をログに記録する
  9. 例外クラスにコンテキスト情報を提供する
  10. カスタム例外クラスを作成する
awonosukeawonosuke

Throwableインターフェースが導入されたPHP 7以降の話
https://www.php.net/manual/ja/class.throwable.php

// Throwableの中身
interface Throwable extends Stringable {
    /* メソッド */
    public getMessage(): string
    public getCode(): int
    public getFile(): string
    public getLine(): int
    public getTrace(): array
    public getTraceAsString(): string
    public getPrevious(): ?Throwable
    public __toString(): string

    /* 継承したメソッド */ <-これは8系から
    public Stringable::__toString(): string
}
  • エラーと例外
  • 例外の種類
    • Throwableのインスタンス
    • チェック例外
    • 非チェック例外
  • 例外安全
  • ポケモン問題
awonosukeawonosuke

https://qiita.com/tanakahisateru/items/e3e24f3825c4ba0c60e6

PHPの例外には主に4つの種類がある。
以下のような継承構造の中で

  • Throwable
    • Error
    • Exception
      • RuntimeException
        • OutOfBoundsException
        • OverflowException
        • RangeException
        • UnderflowException
        • UnexpectedValueException
      • LogicException
        • BadFunctionCallException
          • BadMethodCallException
        • DomainException
        • InvalidArgumentException
        • LengthException
        • OutOfRangeException
      • CustomException

https://www.php.net/manual/ja/spl.exceptions.php

Error

この例外(もしくはこのクラスを継承した例外)は捕捉(catch)すべきでない。
PHPの言語の仕様上基本的な間違いが存在することを知らせるためのもの。迂闊な例外処理で紛れないようになっている(※ThrowableErrorを捕捉すべきでない)。

ポケモンキャッチ

で、こういう「全ての例外を捕まえてやる!」的な例外処理方法のことを "Pokemon Exception Handling(ポケモン例外処理)" って呼ぶんですね。知らなかった・・・

catch(\Exception $e){
  // 何かしらの代替処理
}

↑上記のように広い範囲の例外をごそっと捕捉してしまうことは発生した例外特有の意図を汲み取りにくくなるためアンチパターンとして考えておく。

http://dotnsf.blog.jp/archives/1061565357.html
https://daisuki.nichiyoubi.land/entry/2020/11/28/180332

RuntimeException

この例外(もしくはこのクラスを継承した例外)は捕捉しても、しなくてもいい。が、しない方が良い。
理由は、この例外が発生するのはシステムの実行時であり、メモリ不足やアクセス過多によるバッファ枯渇などインフラレベルでの異常終了であるため、捕捉したところで代替処理できないようなケースが多い。また、この例外が発生するであろう箇所をすべて予測し、例外処理を書くのは現実的ではないからである。

LogicException

この例外(もしくはこのクラスを継承した例外)は捕捉すべきでない。
理由は、公式ドキュメントに書いてあるように、ソースコードのロジックが間違っていることを教えるための例外であるため、これを修正しないとソフトウェアの品質を担保できないということになるから。

プログラムのロジック内でのエラーを表す例外です。 この類の例外が出た場合は、自分が書いたコードを修正すべきです。

https://www.php.net/manual/ja/class.logicexception.php

CustomException

Exceptionクラスを継承したRuntimeException、LogicException以外のExceptionはエラーハンドリングを行う必要がある(この例外を投げる場合、エラーハンドリングされることを期待する)。PHPはコンパイル時にエラーが起きないので、捕捉されるべき例外をPHPDocに記載しておく。
API仕様として例外発生時の大体処理が定められている場合、捕捉必須のCustomExceptionを投げる。第二のreturn。