前書き
主にエラー処理の時によく使われるのが「例外処理」になります。
処理が飛ぶため、あまり乱用するとコードを追いかけにくくなる側面もありますが、基本的には「普通に使われる」傾向にあると思います。
一方で「以外と使われていない(知られていない)、けど便利な機能」もあるので、しっかりと学んでいきましょう。
例外
https://www.php.net/manual/ja/language.exceptions.php
try ブロックで囲まれたPHP コード内で例外が スロー(throw)されると、それが 捕捉(catch)されます。
また、try ブロックには finally ブロックが存在する事もあります。
try ブロックには、必ずそれに対応する catch ブロックあるいは finally ブロックが存在する必要があります。
例外(throw)は、PHPの内部関数(クラス)等が throw する事もありますし、自分で throw する事もできます。
throw されるオブジェクトは、 Throwable のインスタンスでなければなりません。
PHP 8.0.0 以降では、throw キーワードは式として扱えるようになり、 様々なコンテクストで使えるようになりました。
catch ブロックは、 throw された例外にどのように反応するかを定義します。 catch ブロックは、捕捉する例外の型を1つ以上指定します。複数の型を指定する場合は、パイプ文字 (|) を使って指定する事ができます。
また、さまざまな型の例外を捕捉するために 複数の catch ブロックを使用することができます。解決順番に注意しましょう。
PHP 8.0.0 以降では、キャッチされた例外に対応する変数はオプションになりました。そのため「投げられた例外インスタンスを取得しない catch ブロック」を書く事ができます。
例外が捕捉されない場合で、かつset_exception_handler() でハンドラが 定義されていない場合、PHP は “Uncaught 投げられた例外クラス名 …” というメッセージとともに 致命的なエラー(fatal error)を発生させます。
catch ブロックの後、または catch ブロックの代わりに、 finally ブロックも指定できます。 finally ブロックは、tryの中の処理が終わった後、または例外がthrowされた後に実行されます。
return文があるときはその挙動に注意が必要です。
tryやcatchの中にreturn文がある時にも、finally ブロックは実行されます。その時returnは「finally ブロックが実行された後」に動きます。
また、finally ブロックにも return 文が存在した場合は、 finally ブロックから値が返されます。
例外クラスの拡張
https://www.php.net/manual/ja/language.exceptions.extending.php
組み込みの Exception クラスを拡張することで、例外クラスをユーザーが 定義することが可能です。 ただし、Throwable インターフェイスを直接実装することはできません。
例外を複製することはできません。Exception を clone しようとすると 致命的な E_ERROR エラーが発生します。
定義済みの例外
http://php.net/manual/ja/reserved.exceptions.phphttps://www.php.net/manual/ja/spl.exceptions.php
SPLも含めて、いくつもの定義済みの例外があります。
- Throwable
- Error
- ArithmeticError
- DivisionByZeroError
- AssertionError
- CompileError
- ParseError
- TypeError
- ArgumentCountError
- ValueError
- UnhandledMatchError
- FiberError
- ArithmeticError
- Exception
- ErrorException
- LogicException
- BadFunctionCallException
- BadMethodCallException
- DomainException
- InvalidArgumentException
- LengthException
- OutOfRangeException
- BadFunctionCallException
- RuntimeException
- OutOfBoundsException
- OverflowException
- RangeException
- UnderflowException
- UnexpectedValueException
- Error
例外のいくつかを解説していきます。
https://www.php.net/manual/ja/class.argumentcounterror.php
関数あるいはメソッドに渡された引数が少なすぎる場合や、可変長引数でない組み込み関数に、多過ぎる引数を渡した場合などに throw されます。
https://www.php.net/manual/ja/class.divisionbyzeroerror.php
数値をゼロで割ろうとした場合に throw されます。
https://www.php.net/manual/ja/class.unhandledmatcherror.php
match 式がどの分岐でも処理できなかったことを検知した場合に throw されます。
https://www.php.net/manual/ja/class.logicexception.php
「プログラム側のバグに起因する」と思われるエラーを検知した場合に throw されます。
例外クラスの拡張をする時は、この例外を基底にする事も多いでしょう。
https://www.php.net/manual/ja/class.runtimeexception.php
実行時にだけ発生するようなエラーの時に throw されます。
この例外は、普段実装するコードのレベルではcatchされない事があります(もう少しコードの上位でcatchして一意にエラー処理をする)。
このコラムに関連するコードはこちらになります
https://github.com/php-engineer-examination/php8_column_expert/blob/main/src/036_1.php
https://github.com/php-engineer-examination/php8_column_expert/blob/main/src/036_2.php
https://github.com/php-engineer-examination/php8_column_expert/blob/main/src/036_3.php
https://github.com/php-engineer-examination/php8_column_expert/blob/main/src/036_4.php
https://github.com/php-engineer-examination/php8_column_expert/blob/main/src/036_5.php