【PHP入門】例外処理をする方法について!(try/catch)

PHP

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キーワードについて

finallytryを書いたときに、書くことができるブロックになります。
tryブロックやcatchブロックの処理が終わって、通常のコードの実行に戻る前に、必ず実行されるコードを書いておくことができます。

下記のように使うことができます。

<?
try {
    echo "hello!!";
} catch(Throwable $e) {
    echo "error!!";
} finally {
    echo "world!!";
}

エラーが発生しないコードですが、catchブロックも用意しました。

最初に「hello!!」を出力して、tryブロックの処理が終わります。
その次にfinallyブロックの処理が行われて、「world!!」が出力されます。

結果、「hello!!world!!」が出力されます。
このように、tryまたはcatchブロックの処理が終わった後に処理されます。

またfinallyブロックに書いておくと、trycatchreturn文が書いている場合でも、必ず処理を実行してくれます。
先ほどのプログラムの処理を少し変えました。

<?
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システム開発をするときには、基本的に処理するソースコードを例外処理で囲んでおいて、ログ出力されるようにしておきます。
作ったときに想定していないエラーが発生することもあるためです。

ログを確認することで、どういうエラーが起きたか確認でき、その後の対処ができます。
ユーザーが使うようなシステムの場合には、基本的には例外処理を書いていた方が良いでしょう。

コメント

タイトルとURLをコピーしました