PHP - OOP - 10. Exceptionクラスの継承

引き続き継承について学習していきましょう。ここでは Exception クラスを継承して例外クラスの作成に取り組みます。

Exceptionクラスの継承

これまでに学習してきたようにPHPには例外を表す Exception クラスが用意されています。この Exception クラスを継承して独自の例外クラスを定義できます。

class MyException extends Exception
{

}

この場合 MyException という名前で例外クラスを定義しています。 MyException クラスも Exception 型のインスタンスと言えるので throw キーワードでスロー可能です。

require_once("MyClass.php");
require_once("MyException.php");

class MySubClass extends MyClass
{
    public function myMethod($x)
    {
        if ($x == "") {
            $e = new MyException();
            throw $e;        
        }
        echo "Override!" . PHP_EOL;
    }

    public function myMethod2()
    {
        echo "Hello World!" . PHP_EOL;
    }
}

また独自に作成した例外クラス( Exception クラスのサブクラス)は catch ブロックに指定できます。

require_once("MySubClass.php");

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

catch (MyException $e) と実装することで MyException インスタンスだけをキャッチするようになります。

以前のような catch (Exception $e) という実装はあまり好ましいものではありません。この場合 Exception クラスおよび、そのサブクラスのインスタンスもすべてキャッチしてしまうからです。開発の現場では独自の例外クラスを作成するなどして、キャッチ対象の例外クラスを具体的に指定することが一般的です。

PHPプログラムの開発(計算機例外クラス)

ここでは SimpleCalc クラスの divide メソッドにおいて、 0 による除算が発生した際に、独自の例外クラスである計算機例外( CalcException )をスローするように変更します。

まずは計算機例外クラス( CalcException.php )を作成します。

<?php
class CalcException extends Exception
{
    const DIVIDE_BY_ZERO = "Divide by 0.";
}

CalcException クラスでは extends キーワードを指定してスーパークラスに Exception クラスを指定しています。また class 定義時に const キーワードによってクラス定数を定義できます。クラス定数は通常のプロパティのように値を変更することができません。変更のないデータを定義する際に利用します。またクラス定数はアクセス権が public となります。

次に SimpleCalc クラスの divide メソッドを修正します。

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

class SimpleCalc
{
    // ...省略

    public function divide($x)
    {
        if ($x == 0) {
            $e = new CalcException(CalcException::DIVIDE_BY_ZERO);
            throw $e;
        }
        $this->number = $this->number / $x;
    }

    // ...省略
}

ここでは CalcException クラスのコンストラクタ引数に CalcException::DIVIDE_BY_ZERO を指定しています。このようにクラス定数にアクセスするには :: スコープ定義演算子(ダブルコロン演算子)を指定します。

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

<?php
require_once("GreatCalc.php");
require_once("CalcException.php");

try {
    $calc = new GreatCalc(10);

    $calc->divide(0);

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

ここでは catch ブロックでキャッチ対象の例外クラスに CalcException を指定している点を確認しておきましょう。作成した例外クラスはこのように catch ブロックに指定可能です。

それではコマンドラインからプログラムを実行してみましょう。

$ php calc_runner.php
CalcException: Divide by 0.

実行結果から divide メソッドによってスローされた CalcExceptioncatch ブロックで適切に処理できているのがわかります。

まとめ

  • PHPの定義済みクラスを継承して新たなクラスを作成できる
  • ユーザー例外 Exception クラスを継承して独自の例外クラスを定義できる
  • 例外処理( try - catch 文)の catch ブロックの引数に定義した例外クラスを指定できる