第1回 例外処理の実装を把握する

亀本 大地
アシアル株式会社

2008/5/7

新しい例外処理

 非常に広く知られているところであるが、PHP5になって、オブジェクト指向プログラミングを行うための機能が大幅に強化された。

 アクセス修飾子(public、protected、private)や__constructと__destruct、オブジェクトのCopy on Write実装など、変更点を挙げればきりがないが、その中でも重要な実装にもかかわらずノウハウの浸透が進んでいないものの1つに、例外処理の実装がある。

 PHP4までは例外処理の実装が存在しなかったため、例外処理には独自のエラーフラグやPEAR_Errorなどを使用した実装が行われてきた。しかしこの方法は、ずっと以前からいわれているように、特殊な値のチェックなどへの対応が必要になったり、予想外の動作が起きた場合の対処に弱いなど、多くの煩雑な問題を抱えている。

 PHP5からは、「try〜catch」ブロック「Exception」オブジェクトを利用した例外処理がネイティブで実装され、上述のような旧来の例外処理が抱える問題を解決できるようになった。

 この例外処理は非常に有用な仕組みなのだが、これまではPHP5を使っている開発現場でもなかなか浸透しづらい状態にあった。例外処理はライブラリを扱う際に特に有効になってくるのだが、多くのライブラリはPHP4とPHP5の両方で動作するように作成されていたため、PHP4では動作しないExceptionを組み込んでいないことが多かったからだ。

 しかし、2007年末でPHP4のサポートが終了したことを受けて、PEARを筆頭とする多くの著名なライブラリの多くがPHP5対応を進めていく方針を表明している。今後はtry〜catchブロックを使った例外処理に対応したライブラリが一般化してくることだろう。

 ここでは、このPHP5から導入された例外処理について、簡単にその挙動を見ていこう。

基本的な使い方と継承によるcatch句の用法

 PHPのExceptionクラスは、基本的に関数やメソッドの中で問題発生時にthrowして利用する。例外処理を含む関数/メソッドをtryブロック内で実行することで、例外が発生したときにcatchブロックに捕捉される。

<?php
function foo() {
        throw new Exception("例外発生!");
}

try {
        foo();
} catch (Exception $e) {
        $e->getMessage();
        exit;
}

 例外クラスとしては、Exceptionクラスを継承することで任意のクラスを定義することができ、これをcatch句のtype hintingで指定することで、例外の種類によって処理を分けることができる。

<?php
class TestException extends Exception
{
}

function foo() {
        throw new TestException("Hoge例外発生!");
}

try {
        foo();
} catch (TestException $e) {
        echo $e->getMessage();
        // TestExceptionが発生した場合は、メッセージだけ出力して処理を中断しない

} catch(Exception $e) {
        echo $e->getMessage();
        exit;
        // それ以外のExceptionだった場合は、メッセージ出力後に処理を中断する
        // (この例では発生しない)
}

 catch句は複数設定可能で、スクリプトの上から順に判定されていく。また、ある例外クラスのサブクラスは、継承関係にある親クラスが指定されたcatch句にも捕捉される。

 つまり上の例でいえば、発生した例外がTestExceptionであっても、Exceptionクラスを指定したcatch句にも捕捉されるということだ。そのため、catch句の順番をTestExceptionクラスとExceptionクラスとで逆にしてしまうと、例外TestExceptionは先に出てきたExceptionクラスが指定されたcatchブロックに捕捉されてしまい、TestExceptionを指定した2番目のcatchブロックは処理されなくなってしまう。

try {
        foo();
        // 発生しているのはTestException
} catch (Exception $e) {
        echo $e->getMessage();
        exit;
        // TestExceptionはExceptionクラスを継承しているので、先に出てきたExceptionクラスのcatchに引っかかる
} catch(TestException $e) {
        echo $e->getMessage();
        // こちら側は処理されない
}

 すべての例外クラスはExceptionクラスを継承しているので、catch句にExceptionクラスを指定すれば、発生してきた例外はすべて捕捉される。そのため、Exceptionクラスを指定したcatch句は、一番最後のブロックでなくてはならない。また同時に、よほど厳密にサブクラスのハンドリングを行わない限り、Exceptionクラスだけを抽出して補足することは困難であるともいえる。

 実用性を考えた場合には、基底クラスのExceptionクラスを直接利用することはせずに、これを継承した任意の例外クラスを定義して、それをベースとして利用する方がよいだろう。

例外の再throwとset_exception_handler

 catch句で捕捉されなかったり、そもそもtryブロック外で実行された場合に発生した例外は、透過的に上位階層へ再throwされる。また、catchブロック内での明示的なthrowも可能なので、例外の処理を上位階層に渡してしまい、そちらで一括処理することが可能になる。

<?php

function foo() {
        throw new Exception("例外発生!");
}

function bar() {
        // foo()の中で例外が発生しているが、try〜catchがないので上位層へ透過される
        foo();
}

try {
        bar();
} catch (Exception $e) {
        echo $e->getMessage();
}

 上述のように、例外処理を上位階層に渡してしまう場合、最上位階層での例外処理を忘れずに実装しておかなくてはならない。処理の記述漏れなどで例外が最上位層でもキャッチされなかった場合には、Fetal Errorが発生し、処理が中断されてしまう。

 そのような場合に備え、どこにも捕捉されなかった例外をハンドリングして処理する方法として、set_exception_handlerという関数が用意されている。この関数にcallback関数を設定しておくことで、めったに発生しない例外のキャッチ漏れなどに対処することができる。

<?php
class TestException extends Exception
{
}

function foo() {
        throw new Exception("例外発生!\n");
}

function handler($exception) {
        echo $exception->getMessage();
}

// 例外ハンドラを設定
set_exception_handler("handler");

try {
        foo();
} catch (TestException $e) {
        $e->getMessage();
}

 これによって、少なくともキャッチ漏れによるエラー終了は防ぐことができるが、この例外ハンドラによって例外がハンドリングされた場合でも、callback関数が実行し終えた段階で処理が中断してしまう。そのため、この関数はあくまでエラー終了という事態を防ぐための気休めと考えておいた方がいい。実用的には、callback関数の中で例外処理の漏れを報告するなどして、修正につなぐための情報を取得するようにしておくとよいだろう。

 今回はPHP5の例外処理の使用法に関して簡単に説明した。次回は、PHP5から実装されたSimpleXMLやPDOなどのモジュールについて解説していく予定である。

2/2
 

Index
例外処理の実装を把握する
  Page1
PHP5を利用したこれからの開発に備える
その前に……あなたはPHP5へ移行すべきか否か?
Page2
新しい例外処理
 基本的な使い方と継承によるcatch句の用法
 例外の再throwとset_exception_handler

PHP5で広がる! 開発環境

 PHP関連記事
例外処理の実装を把握する
PHP5で広がる! 開発環境(1)
 PHP4のサポートが終了し、いよいよPHP5への移行を視野に入れる時期が来た。PHP5の機能を生かした開発のポイントを紹介
クライアントPCに言語環境を入れる理由
Mac OS X+PHPでオールインワン環境(準備編)
 Webアプリ開発者に人気のMac OS X。効率的な開発のために複数バージョンのPHPを実行する環境を構築してみよう
PHPに押し寄せるリスクと国際化の波
PHPカンファレンス2008レポート(前編)
 PHP4のサポートが完全に終了する。多くの新機能が投入されるPHP5.3へ移行か、国際化対応で開発が遅れるPHP6を待つか
PHPによる大規模商用サービスの裏側
PHPカンファレンス2008レポート(中編)
 企業のWebアプリケーション開発現場で利用されるPHP。開発現場の裏側にはさまざまなドラマが隠されている
PHPユーザーは本当にほかの言語を知らないのか?
PHPカンファレンス2008レポート(後編)
 PHPは本当にダメな言語なのだろうか。Perl、Ruby、Python、Java、JavaScriptの使い手が白熱した議論を行った
  Coding Edgeフォーラムフィード  2.01.00.91


Coding Edge フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

>

Coding Edge 記事ランキング

本日 月間