例外処理とは、予期しない結果が発生した場合に強制的に別の処理をすることで、たとえばデータベースに接続できない、数値計算でゼロ除算した、ユーザーの入力値が不正の場合など、正常で無い事が起きた場合にそれを復旧する処理のことです。使用するメリットとしては、エラーの対処が簡単になります。

 なお、PHPで例外処理を行うにはPHP5以上を使用する必要があります。

 では、以下の割り算をする関数を例に説明します。

function div($val1, $val2) {  //割り算
  $ans = $val1 / $val2;
  return $ans;
}

 この関数に

$ans = div(10, 0);

と入力すると、ゼロ除算になりますが、PHPではWarningを出すのみで、そのまま処理が続行されてしまいます。このような場合に例外を使用して記述すると下記のようになります。

function div($val1, $val2) {  //割り算
  if ($val2 == 0){
    //例外発生
    throw new Exception("ゼロ除算");
  }
  $ans = $val1 / $val2;
  return $ans;
}

 $val2に0が入っている場合に、throwが実行され、例外クラス(Exception)を例外として投げます。

 この関数を使用する場合は、例外が発生する部分( $ans = div(10, 0) )を try{ }で囲みます。その後にcatch句を指定し、引数で例外を取ります。

try {
  $ans = div(10, 0);
} catch (Exception $e) {
  echo "エラー:" . $e->getMessage(); //メッセージ表示
  //ここでエラー回復処理
}
//次の処理

 catch句の中では例外の通知や回復処理などをしてから正常な場合の処理に戻ります。 また、catch句を指定しない場合はthrowが呼び出されると、基本的に処理は停止します。

 また、$eにはExceptionのインスタンスが入っており、値や関数を内部に持っているので、getMessage()でnew Exceptionをしたときにセットしておいた文字列を取り出すことが出来ます。

 さて、例外に付いて簡単に説明をしましたが、上記の例ではメリットをあまり感じないかとおもわれます。例外処理はもっと複雑な処理の時に効力を発揮します。

 例えば、データベースに接続、sqlを生成、クエリ発行、結果からデータを取得する処理の場合、発生する可能性のある例外はいくつも考えられますが、その中から

・データベースに接続失敗
・クエリ失敗(構文エラーなど)

の2つが発生するときの処理を考えてみましょう。

 それらの処理を関数にして、それぞれの関数で処理に失敗した場合は例外を投げるようにすれば、if文などを省くことができ、コードはシンプルになります。

try{
  //接続失敗したら例外を投げる
  db_connect();

  $sql = "select * from hoge";
  //クエリに失敗したら例外を投げる
  $res = query($sql);

} catch (Exception $e){
  echo "エラー発生:" . $e->getMessage();
  exit();
}

 ですが、この場合は発生したエラーをまとめて処理してしまうので、接続エラー、クエリエラーで別々の処理をすることは出来ません。

 例外の種類で分岐を行うには、

class db_connect_exception extends Exception{
  function __construct(){
    __parent("DB error");
  }
}

 Exceptionを継承して独自例外(db_connect_exception、query_exception)を作成し、catch句でそれぞれの例外を指定すれば、発生する例外によって処理の分岐ができます。

try{
  db_connect();

  $sql = "select * from hoge";
  $res = query($sql);

} catch (db_connect_exception $e){
  echo "DB接続エラー";
} catch (query_exception $e){
  echo "クエリエラー";
} catch (Exception $e){
  echo "その他のエラー";
}

 また、呼び出している関数内で発生した例外をcatchしない場合、例外はその上位の呼び出し元に投げられます。

function hoge1(){
  try{
    hoge2();
  } catch (Exception $e){
    echo "例外";
  }
}
function hoge2(){
  hoge3();
}
function hoge3(){
  throw new Exception();
}

 hoge3が投げた例外をhoge2がcatchしないままhoge1に投げてhoge1でcatchしています。

 これらを組み合わせれば、呼び出し元で例外処理を書くだけで済むので、関数内部はシンプルにすることができ、if文などを省いた見通しのよいコードになります。

 もし機会があれば使ってみてはいかがでしょうか。



(アシアル 門脇優児)


この記事は、アシアルが運営するPHP開発者のためのポータル&コミュニティサイト「PHPプロ!」で毎週配信しているPHP・TIPSメーリングリストを再録したものです。
同サイトでは、他にもPHP最新ニュースや、困ったときのQ&A掲示板、初心者向けのPHP講座など、PHP開発者をサポートする情報を掲載しています。