[PHP]例外(Exception) / 例外ハンドラ

自己問答

  • 例外クラスのプロパティとメソッドは何がある?→PHP: Exception - Manual
  • 例外ハンドラのセット、リストア
  • 例外ハンドラ関連の関数はどんな関数?引数は?返り値は?

Exceptionの中身とか

<?php
try
{
  throw new Exception;
}
catch(Exception $exception)
{
  echo $exception;
}

結果

exception 'Exception' in ***:4
Stack trace:
#0 {main}

なんじゃこれって感じだけどとりあえずやってみた。

<?php
var_dump(
  new Exception("message", 666)
)

結果

object(Exception)#1 (6) {
  ["message:protected"]=>
  string(7) "message"
  ["string:private"]=>
  string(0) ""
  ["code:protected"]=>
  int(666)
  ["file:protected"]=>
  string(15) "/***/public_html/hoge.php"
  ["line:protected"]=>
  int(3)
  ["trace:private"]=>
  array(0) {
  }
}

Exceptionのコンストラクタの引数は3つある。全て省略可能。

  • 最初の引数は protected $massage に格納される。
  • 次の引数は protected $code に格納される。
  • protected $file は 例外が投げられたファイルの絶対パス
  • protected $line は行ナンバー
  • private $string は気にしないでいいらしい

    <?php
    class Test
    {
    public function __construct(){
    throw new Exception;
    }
    }
    function trace_test(){
    return new Test;
    }
    
    try
    {
    trace_test();
    }
    catch(Exception $e)
    {
    var_dump($e->getTrace());
    echo $e->getTraceAsString();
    }
    

結果

array(2) {
  [0]=>
  array(6) {
    ["file"]=>
    string(41) "/***/public_html/***.php"
    ["line"]=>
    int(9)
    ["function"]=>
    string(11) "__construct"
    ["class"]=>
    string(4) "Test"
    ["type"]=>
    string(2) "->"
    ["args"]=>
    array(0) {
    }
  }
  [1]=>
  array(4) {
    ["file"]=>
    string(41) "/***/public_html/***.php"
    ["line"]=>
    int(14)
    ["function"]=>
    string(10) "trace_test"
    ["args"]=>
    array(0) {
    }
  }
}
#0 /***/public_html/***.php(9): Test->__construct()
#1 /***/public_html/***.php(14): trace_test()
#2 {main}

とまあ protected $trace プロパティはどのような経緯で例外が投げられたかを遡ってトレースした結果が配列で入っております。

<?php
function p($str, $pre = NULL ){
  echo empty($pre) ? "" : "{$pre} | ";
  echo $str, "\n";
}
function method_test(){
  throw new Exception("FOO", 96);
}
try
{
  method_test();
}
catch(Exception $e)
{
  // こっからExceptionオブジェクトの
  // メソッドを試す

  // protected properties のそれぞれの
  // 値を返すメソッドが定義されている

  p( $e->getMessage(), 'getMassage');
  p( $e->getCode(), 'getCode');
  p( $e->getLine(), 'getLine');
  p( $e->getFile(), 'getFile');
  p( $e->getTrace(), 'getTrace');
  p( $e->getTraceAsString(), 'getTraceAsString');

  // __toString()
  echo "\n", $e;
}

結果

getMassage | FOO
getCode | 96
getLine | 7
getFile | /***/public_html/***.php
getTrace | Array
getTraceAsString | #0 /***/public_html/***.php(11): method_test()
#1 {main}

exception 'Exception' with message 'FOO' in /***/public_html/***.php:7
Stack trace:
#0 /***/public_html/***.php(11): method_test()
#1 {main}

そのほかのメソッドは以下を参照

<?php
try
{
  throw new Exception("First.\n", 1, new Exception("TEST.\n"));
}
catch(Exception $e)
{
  echo $e->getMessage();
  echo $e->getPrevious()->getMessage();

  try
  {
    throw new Exception("\nSecond.\n", 1, $e);
  }
  catch(Exception $e)
  {
    echo $e->getMessage();
    echo $e->getPrevious()->getMessage();
  }
}

結果

First.
TEST.

Second.
First.

getPreviousメソッドはPHP5.3.0より使える。

マニュアルには前の例外を返すって書いてあるけど…前の例外ってなんだ? 前の例外というか投げられた例外の3つ目の引数(引数は例外に限る。初期値はNULL)ってことか…

例外ハンドラ

投げられた例外がキャッチされなかった場合には例外ハンドラが拾ってくれる

<?php
//例外ハンドラを設定
//コールバック関数を引数にいれる
set_exception_handler(
  function(Exception $e){
    echo $e->getMessage();
  }
);
throw new Exception("例外ハンドらテスト");
echo "例外ハンドラがコールされると
処理は終わるので、この文章は表示されない";

結果

例外ハンドらテスト
<?php
set_exception_handler(
  function(){
    echo "hoge";
  }
);
throw new Exception;

結果

hoge

try & catch と違って例外ハンドラにセットする関数はかなり自由。例外がキャッチされないと実行されるという点が特殊なだけっぽい。

<?php
function foo(FooException $e){
    echo $e->getMessage(), "\n";
}
function bar(BarException $e){
    echo $e->getMessage(), "\n";
}
class FooException extends Exception{}
class BarException extends Exception{}
var_dump( set_exception_handler('foo') );
var_dump( set_exception_handler('bar') );
throw new FooException();

結果

NULL
string(3) "foo"

Catchable fatal error: Argument 1 passed to bar()
 must be an instance of BarException,
 instance of FooException given in *** on line 5

set_exception_handler() には返り値がある。前に定義された例外ハンドラの名前、またはエラー発生時に NULL を返す。前にハンドラが定義されていない場合も NULL を返す。

複数の例外ハンドラをセットすると、最新のもの一つだけが適用される。

catch と違ってタイプヒンティングによりE_ERRORが出される。例外ハンドラの中身の関数自体は普通の関数で、catch が特殊なだけなんだね。

<?php
function foo(){
  echo "FOO";
}
function bar(){
  echo "BAR";
}
set_exception_handler('foo');
set_exception_handler('bar');
restore_exception_handler();
throw new Exception;

結果

FOO

restore_exception_handler() で一つ前の例外ハンドラに戻せる。だから function foo が走る。この関数は引数も返り値もない。

Share
関連記事