PHP - OOP - 8. 例外処理(try - catch文)

引き続き例外について学習していきましょう。ここではスローされた例外( Exception インスタンス)をキャッチ(捕捉)して処理を継続する方法(try - catch文)を学習します。

例外処理(try - catch文)の記述

これまでに学習してきたとおり、プログラムにおける例外を意味する Exception インスタンスは throw キーワードによってスローできます。以前に作成した MyClass クラスの myMethod では、引数 $x が空文字の場合に Excetpion インスタンスをスローしています。

<?php
class MyClass
{
    public $myProperty;

    public function __construct($value)
    {
        $this->myProperty = $value;
    }

    public function myMethod($x)
    {
        if ($x == "") {
            $e = new Exception("Invalid argument.");
            throw $e;
        }
        echo $this->myProperty . " " . $x;
    }
}

この myMethod からスローされた Exception インスタンスはメソッドの呼び出し元( my_class_runner.php )において try - catch 文を実装することでキャッチできます。

<?php
require_once("MyClass.php");

try {
    $myClass = new MyClass("Hello");
    $myClass->myMethod(""); #=> throw Exception
} catch (Exception $e) {
    echo "Catch exception." . PHP_EOL; # => Catch exception.
    echo $e->getMessage() . PHP_EOL; #=> Invalid argument.
}

try - catch 文はスローされた例外をキャッチするために使用します。try - catch 文の構文について整理しておきましょう。

try {
    // 例外の発生する可能性のある処理
} catch (キャッチ対象の例外クラス名 変数名) {
    // 例外発生時の処理
}

try の処理ブロック {} の中で例外がスローされると後の catch の処理ブロック {} に処理が移ります。このとき catch ブロックの引数 (Exception $e) にはスローされた Exception インスタンスが代入されます。また catch ブロックの中で以下のように実装しています。

    echo "Catch exception." . PHP_EOL; # => Catch exception.
    echo $e->getMessage() . PHP_EOL; #=> Invalid argument.

先頭の echo 命令で Catch exception. と改行コード付きで出力しています。それから $e->getMessage() の部分ではキャッチした Exception インスタンスに対して getMessage というメソッドを呼び出しています。Exception クラスはコンスタント引数で設定したメッセージを getMessage メソッドで取得できるようになっています。そのため画面には Invalid argument. と出力されます。

Exception クラスはPHPの中であらかじめ用意されているクラスです。PHPのマニュアルページを見るとコンストラクタの定義やメソッドの定義を確認できます。PHPマニュアル - Exception

try - catch 文の挙動について

try - catch 文の動作イメージを使うために次のプログラム( my_class_runner.php )の実行結果も考えてみましょう。

<?php
require_once("MyClass.php");

echo "1";
try {
    $myClass = new MyClass("Hello");
    $myClass->myMethod(""); #=> throw Exception
    echo "2";
} catch (Exception $e) {
    echo "3";
}
echo "4";

このプログラムでは $myClass->myMethod(""); の呼び出しで Exception インスタンスがスローされます。そのため処理は catch ブロックに移るため、 echo "2"; は実行されません。この場合の実行結果は 134 となります。

また $myClass->myMethod("Andy"); と変更した場合はどうでしょうか。

<?php
require_once("MyClass.php");

echo "1";
try {
    $myClass = new MyClass("Hello");
    $myClass->myMethod("Andy"); #=> Hello Andy
    echo "2";
} catch (Exception $e) {
    echo "3";
}
echo "4";

この場合は Exception インスタンスはスローされないため実行結果は 1Hello Andy24 となります。

PHPプログラムの開発(try - catch文)

それでは計算機プログラムの呼び出し時に例外処理( try - catch 文)を実装してみましょう。まずは前節までの計算機クラス( SimpleCalc クラス)を確認しておきましょう。

<?php
class SimpleCalc
{
    // ...省略

    public function divide($x)
    {
        if ($x == 0) {
            $e = new Exception("Divide by 0.");
            throw $e;
        }
        $this->number = $this->number / $x;
    }

    // ...省略
}

上記のように divide メソッドで 0 による除算の発生時に Exception インスタンスをスローしています。計算機クラス( SimpleCalc クラス)に修正はありません。

次に実行プログラム( calc_runner.php )を修正します。

<?php
require_once("SimpleCalc.php");

try {
    $calc = new SimpleCalc();

    $calc->add(10);
    $calc->subtract(5);
    $calc->multiply(10);
    $calc->divide(0);

    $calc->show();
} catch (Exception $e) {
    echo "Exception: " . $e->getMessage() . PHP_EOL;
}

try ブロックの中でスローされた Exception インスタンスは catch ブロックの引数( $e )に代入されます。変数 $eException クラスのインスタンスであるため、 Exception クラスに定義されているメソッド( getMessage メソッドなど)を呼び出すことができます。

Exception クラスの getMessage メソッドは戻り値にコンストラクタで指定されたメッセージを返します。

それではターミナルからプログラムを実行してみましょう。

$ php calc_runner.php
Exception: Divide by 0.

実行結果からスローされた Exception インスタンスを catch ブロックで処理できているのがわかります。

参考:finallyブロックの使用

PHP5.5以降は try - catch 文に finally ブロックを追加することもできます。

<?php
require_once("SimpleCalc.php");

try {
    $calc = new SimpleCalc();

    $calc->add(10);
    $calc->subtract(5);
    $calc->multiply(10);
    $calc->divide(0);

    $calc->show();
} catch (Exception $e) {
    echo "Exception: " . $e->getMessage() . PHP_EOL;
} finally {
    echo "finally" . PHP_EOL;
}

finally ブロックは例外発生の有無に関わらず動作します。

まとめ

  • スローされた例外( Exception クラスのインスタンス)は try - catch 文で処理できる
  • try ブロックの中で例外が発生すると catch ブロックに処理が移る
  • catch ブロックの定義にはキャッチ対象の例外クラス名と、発生した例外インスタンスを格納する変数を定義する