PHP - OOP - 7. 例外(Exception) - 2/2

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

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

これまでに学習してきたとおり Exception インスタンスは throw キーワードによってスローできます。たとえば MyClass クラスの myMethod の中で引数 $x が空文字の場合に Excetpion インスタンスをスローするとしましょう。

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 インスタンスはメソッドの呼び出し元で try - catch 文を実装することでキャッチできます。

require_once("MyClass.php");

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

try - catch 文はスローされた例外をキャッチするために使用します。

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

try の処理ブロック {} の中で例外がスローされると後の catch の処理ブロック {} に処理が移ります。このとき catch 引数 (Exception $e) にはスローされた Exception インスタンスが代入されます。このプログラムを実行すると画面に Catch exception. と出力されます。

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

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"); と変更した場合はどうでしょうか。

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("GreatCalc.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 ブロックの定義にはキャッチ対象の例外クラス名と、発生した例外インスタンスを格納する変数を定義する