PHPの例外を深掘ってみる
PHPにおける例外処理の10のベストプラクティス
- フロー制御に例外を使用しない
- 例外的な状況のみを報告するために例外を使う
- 可能な限りエラーの原因に近い例外をcatchして処理する
- 一般的な例外クラスをcatchしないようにする
- メソッドが実装されていない場合に例外をスローする
- 例外ごとに常に意味のあるメッセージを提供する
- 例外を飲み込まない
- すべての例外をログに記録する
- 例外クラスにコンテキスト情報を提供する
- カスタム例外クラスを作成する
背景
PHPerKaigi2022のt_wadaさんの発表がYotubeでアーカイブとしてあったので見ていた時、ふと例外の継承構造について触れたスライドがありちゃんと調べようと思ったことがきっかけ。
参考ページ
※Throwable
インターフェースが導入されたPHP 7以降の話
// 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のインスタンス
- チェック例外
- 非チェック例外
- 例外安全
- ポケモン問題
PHPの例外には主に4つの種類がある。
以下のような継承構造の中で
- Throwable
- Error
- Exception
-
RuntimeException
- OutOfBoundsException
- OverflowException
- RangeException
- UnderflowException
- UnexpectedValueException
-
LogicException
- BadFunctionCallException
- BadMethodCallException
- DomainException
- InvalidArgumentException
- LengthException
- OutOfRangeException
- BadFunctionCallException
- CustomException
-
RuntimeException
Error
この例外(もしくはこのクラスを継承した例外)は捕捉(catch)すべきでない。
PHPの言語の仕様上基本的な間違いが存在することを知らせるためのもの。迂闊な例外処理で紛れないようになっている(※Throwable
やError
を捕捉すべきでない)。
ポケモンキャッチ
で、こういう「全ての例外を捕まえてやる!」的な例外処理方法のことを "Pokemon Exception Handling(ポケモン例外処理)" って呼ぶんですね。知らなかった・・・
catch(\Exception $e){
// 何かしらの代替処理
}
↑上記のように広い範囲の例外をごそっと捕捉してしまうことは発生した例外特有の意図を汲み取りにくくなるためアンチパターンとして考えておく。
RuntimeException
この例外(もしくはこのクラスを継承した例外)は捕捉しても、しなくてもいい。が、しない方が良い。
理由は、この例外が発生するのはシステムの実行時であり、メモリ不足やアクセス過多によるバッファ枯渇などインフラレベルでの異常終了であるため、捕捉したところで代替処理できないようなケースが多い。また、この例外が発生するであろう箇所をすべて予測し、例外処理を書くのは現実的ではないからである。
LogicException
この例外(もしくはこのクラスを継承した例外)は捕捉すべきでない。
理由は、公式ドキュメントに書いてあるように、ソースコードのロジックが間違っていることを教えるための例外であるため、これを修正しないとソフトウェアの品質を担保できないということになるから。
プログラムのロジック内でのエラーを表す例外です。 この類の例外が出た場合は、自分が書いたコードを修正すべきです。
CustomException
Exceptionクラスを継承したRuntimeException、LogicException以外のExceptionはエラーハンドリングを行う必要がある(この例外を投げる場合、エラーハンドリングされることを期待する)。PHPはコンパイル時にエラーが起きないので、捕捉されるべき例外をPHPDocに記載しておく。
API仕様として例外発生時の大体処理が定められている場合、捕捉必須のCustomExceptionを投げる。第二のreturn。