PHPで例外処理をする方法について書いています。
PHPの例外処理について、解説やサンプルコードを載せています。
載せているサンプルコードは、PHPのバージョン8.1.12を使って、動作を検証しました。
公式ドキュメントではこちらに例外について記載があります。
PHPの例外処理とは?
PHPの例外処理は、プログラム実行時にエラーが発生したときに、エラーを捕捉して何か処理を行いたい場合に使います。
try
からcatch
までに書いた処理で、エラーが発生したときに、catch
でエラーを捕捉して処理をします。
例えば、下記のように使います。
<?php
try {
$result = 10 / 0;
} catch (Throwable $e) {
echo "エラーが発生しました\n";
echo $e->getMessage();
}
try
を書いて、波括弧({}
)を書いています。
この波括弧の中の処理で、エラーが起こった場合に、catch
で捕捉されて配下のエラー処理を行います。
エラーが起こらなかった場合には、catch
配下の処理はしません。
今回は1行だけエラーが発生する処理を書きました。
0
除算しているため、DivisionByZeroError
というエラーが発生し、catch
の配下に処理が移行します。
Throwable
は全てのエラーを捕捉することができるインターフェースです。
これをcatch
に書くと、try
からcatch
で発生した全てのエラーが捕捉できます。
$e
は発生したエラーのオブジェクトを受けることができます。
エラーのオブジェクトを参照することで、エラー内容やエラーコードを取得することが可能です。
今回のコードでは、「$e->getMessage()
」でエラーメッセージを取得しています。
結果、今回のコードではエラーが発生して、下記の2行が出力されて処理が終わります。
エラーが発生しました
Division by zero
Throwableについての公式ドキュメントの情報はこちらです。
例外クラスについて
catch
に指定することができる例外クラスについてです。
Throwable
インターフェースを指定すると、全ての例外を受けることができます。
Throwable
インターフェースを実装したクラスとして、Error
クラスとException
クラスがあります。
Error
クラスが全ての内部エラーの基底クラス、Exception
が全てのユーザー例外の基底クラスになります。
Qiitaのこちらの記事に、例外クラスが図になったものが書いてあります。参考にしてみてください。
PHP 7.0.0α2 の例外の例外の継承関係を可視化してみた
関連:Errorクラスの公式ドキュメント
関連:Exceptionクラスの公式ドキュメント
throwキーワードについて
throw
を使うことで、ソースコードの任意の箇所で例外を投げることができます。
throw
を書いた後に、任意の例外オブジェクトをnew
して作ることで、例外を発生させることができます。
例えば、下記のように使うことができます。
<?php
try {
$fruits = ['apple', 'grape', 'banana', 'lemon'];
foreach ($fruits as $fruit) {
if ($fruit === 'lemon') {
throw new \Exception("レモンはすっぱい!");
}
echo $fruit."\n";
}
} catch (Exception $e) {
echo $e->getMessage()."\n";
}
例外を捕捉する対象になるtry
の配下に処理を書いています。
$fruits変数に4つの果物名で配列を作りました。
次の処理で、foreach文でループして、中身の文字列を出力しています。
if分を使って、値がlemon
だった場合にthrow
キーワードを使っています。
Exception
オブジェクトをnew
で作成して、例外を発生させます。
結果、$fruit変数がlemon
だったときに、catch配下に処理が移ります。
そして、出力内容は下記のようになります。
apple
grape
banana
レモンはすっぱい!
このようにthrow
キーワードを使うことで、処理の途中でも例外を発生させることができます。
そして、例外が発生したあとはcatch
配下のブロックを処理します。
finallyキーワードについて
finally
はtry
を書いたときに、書くことができるブロックになります。
try
ブロックやcatch
ブロックの処理が終わって、通常のコードの実行に戻る前に、必ず実行されるコードを書いておくことができます。
下記のように使うことができます。
<?
try {
echo "hello!!";
} catch(Throwable $e) {
echo "error!!";
} finally {
echo "world!!";
}
エラーが発生しないコードですが、catchブロックも用意しました。
最初に「hello!!」を出力して、tryブロックの処理が終わります。
その次にfinally
ブロックの処理が行われて、「world!!」が出力されます。
結果、「hello!!world!!」が出力されます。
このように、try
またはcatch
ブロックの処理が終わった後に処理されます。
またfinally
ブロックに書いておくと、try
やcatch
にreturn
文が書いている場合でも、必ず処理を実行してくれます。
先ほどのプログラムの処理を少し変えました。
<?
try {
echo "hello!!";
return;
} finally {
echo "world!!";
}
このように、return
文があった場合でもfinally
の処理は実行されます。
結果は同じように「hello!!world!!」が出力されます。
try/catch/throw/finallyのまとめ
try/catch/throw/finallyについてまとめると、下記のようになります。
項目 | 内容 |
---|---|
try | 例外処理の対象にしたい処理ブロックの先頭に書く。例外が発生しない場合には、tryを通って後続処理へ。 例外が発生した場合にはcatchブロックに処理が移る。 |
catch | tryブロックの中に書いた処理で、例外が発生した場合にcatchブロック配下の処理が行われる。 |
throw | throwキーワードを使うと、tryブロックの処理の任意の箇所で、例外を発生させることができる。 |
finally | finallyキーワードを使うと、tryブロックまたはcatchブロックの処理の後で、必ず実行される処理を書くことができる。 |
例外処理で複数のcatchを用意する
実行したときに発生する例外に合わせて、複数の例外クラスをcatch
で設定しておくこともできます。
例えば、下記のように書くことが可能です。
$divideByZero = true;
try {
if ($divideByZero) {
100 / 0;
} else {
test();
}
echo "処理終了";
} catch(DivisionByZeroError $e) {
echo "0で除算を実行しました。\n";
echo $e->getMessage();
} catch(Error $e) {
echo "その他のエラー\n";
echo $e->getMessage();
}
最初に$divideByZero変数にtrueを代入しています。
次に例外処理対象になるtry
ブロックです。
処理の中で、if文で分岐していますが、どちらの処理もエラーになるようにしました。
今回は$divideByZero変数の値がtrueなので、「100 / 0
」が実行されてエラーになります。
0除算のエラーなので、DivisionByZeroErrorクラスでエラーオブジェクトを受けているcatch
ブロックの処理に移行します。
結果、下記が出力されて処理が終了します。
0で除算を実行しました。
Division by zero
$divideByZero変数がfalseの場合は、「test()
」が実行されて、未定義の関数呼び出しエラーになります。
この場合は、Errorクラスの方のcatch
ブロックに処理が移行します。
結果、下記が出力されて処理が終了します。
その他のエラー
Call to undefined function test()
このように発生した例外に合わせて、catch
ブロックを分けて書くこともできます。
例外クラスを自作する
例外クラスを継承して、自作したクラスを使うこともできます。
自作したクラスを使うことで、ソースコードの任意の箇所で、作った例外を投げるようにすることが可能です。
こうすることで、どの機能で発生したエラーなのか分かりやすくなります。
また、例外に何らかの処理を書いておくこともできます。
class TestException extends Exception {
public function __construct()
{
parent::__construct('テスト例外発生!!', 999);
}
public function execute()
{
echo "何かの処理...\n";
}
}
try {
echo "例外クラスを自作してテスト...\n";
throw new TestException();
} catch (TestException $e) {
echo $e->getMessage()."\n";
echo $e->getCode()."\n";
$e->execute();
}
最初にTestExceptionクラスをExceptionクラスを継承して作りました。
こうすることで、新しい例外として使えるようになります。
コンストラクタ(__construct
)で、親のコンストラクタに対して、メッセージとエラーコードを渡しています。
第1引数がgetMessage
メソッドで取り出せるメッセージです。
第2引数がgetCode
メソッドで取り出せるエラーコードになります。
executeメソッドでは、文字列を出力するだけの処理を書きました。
このように何らかのメソッドを作って、処理させることも可能です。
try
ブロックの中で、まず文字列をechoで出力させた後に、TestExceptionクラスの例外を投げています。
catch
ブロックで例外を受け取って処理します。
エラーメッセージとエラーコードを出力して、最後にTestExceptionクラスのexecuteメソッドを実行するようにしてみました。
実行すると、下記のように出力されます。
例外クラスを自作してテスト...
テスト例外発生!!
999
何かの処理...
自作のTestExceptionクラスに設定したメッセージとエラーコードが取り出せています。
また、TestExceptionクラスに実装したexecuteメソッドも動きました。
このように例外クラスを自作して、作成しておくこともできます。
終わりに
今回はPHPの例外処理について確認していきました。
Webシステム開発をするときには、基本的に処理するソースコードを例外処理で囲んでおいて、ログ出力されるようにしておきます。
作ったときに想定していないエラーが発生することもあるためです。
ログを確認することで、どういうエラーが起きたか確認でき、その後の対処ができます。
ユーザーが使うようなシステムの場合には、基本的には例外処理を書いていた方が良いでしょう。
コメント