2006/10/20

set_error_handlerでExceptionに渡す。

phpproを読んでいたら、こんな記事を発見。【Alex@Net】try/catch構文を用いてPHPコアで注意や警告を扱う方法

なるほどー。set_error_handlerでcallbackを指定して、そこでExceptionを投げればいいのね。頭いいな。つーか、これ普通でした?と思っていろいろやってみる。

って、phpのマニュアルに書いてあるじゃん。しかし、グローバルな関数は作りたくないので、
PHP::set_error_handler
を見ながら、Exceptionクラスにcallback関数を書いてみる。しかし、気づいたのだが、error_handlerをセットするのはいいのだが、元にどうやって戻すの?そのクラスを出たら、ここで セットしたerror_handlerは使ってもらいたくないのだ。まぁ、そのソフトウェア全体のエラーハンドリングとして一つのものとして扱いたければいいのかもしれんけど、ここでは、モジュールとして使う方には影響を与えたくないのよ。。

そのためか、restore_error_handler関数なるものを発見。これで元に戻せばいいのね。
で、さらに、悩む。つーか、コンストラクタで、set_error_handlerして、デストラクタでrestore_error_handlerを使うのはいいのだけど、デストラクタが呼ばれる前に呼び出し元がエラーを吐いたら、すでにセットしてあるerror_handlerに投げちゃうじゃん。。

というわけで、PEARのパッケージをrestore_error_handlerで、grepしてみる。そうするとSOAP_ServerとPHPUnit2_Framework_TestResultなんかが引っかかった。あと、Piece_Unitiy_Plugin_Renderer_Smartyも引っかかった。Piece Frameworkは、使ってみようと思ってインストールをしたけど、なかなか試せてないなぁ。。

で、見てみると、メソッド内でset_error_handlerして、そしてごにょごにょした後にrestore_error_handlerしているようだ。なるほどね。メソッドの一部だけ、error_handlerを独自のエラー関数などに付けているのね。

というわけで、Services_YouTubeに付け加えて0.2.0をリリース。前から懸念していたsimplexml_load_stringのE_WarningをこれでExceptionを投げることができるかな。。

と思っていて、簡単なスクリプトを書いていたのだが、本当にいいのかな。Exceptionを投げてしまったら、restore_error_handlerできないじゃん。。。

というわけでそれもcatchしてrestore_error_handlerしないといけないの?なんか嫌だなー。
さっきリリースした0.2.0はそれ対応してないよ。。いきなり、0.2.1か?まぁ、ちょっと今日は他の仕事をして、その問題が出るかどうかを見てから考えてみよう。

つーわけで、下のソースは、さらにcatchしてrestore_error_handlerしているものね。なんかもっとスマートにできないものか。。

<?php require_once 'PEAR/Exception.php';

class Ganchiku_Exception extends PEAR_Exception
{
    public static function 
errorHandlerCallback($code$string$file$line$context) {
        
$e = new self($string$code);
        
$e->line $line;
        
$e->file $file;
        throw 
$e;
    }
}

class Ganchiku_Test
{
    private 
$name;
    public function 
__construct($name null)
    {
        
$this->name $name;
    }

    public function getName()
    {
        return 
$this->name;
    }
    public function 
setName($name)
    {
        if (!
is_string($name)) {
            throw new 
Ganchiku_Exception('name has to be string'E_ALL);
        }
        
$this->name $name;
    }
    public function 
error()
    {
        
set_error_handler(array('Ganchiku_Exception''errorHandlerCallback'), E_ALL);
        try {
            
trigger_error('Error');
            
restore_error_handler();
        } catch (
Ganchiku_Exception $e) {
            
restore_error_handler();
            throw 
$e;
        }
    }
}

// main
try {
    
$g = new Ganchiku_Test;
    
// throw Exception
    
$g->setName(array('Shin''Ohno'));
} catch (
Ganchiku_Exception $e) {
    echo 
$e;
}

try {
    $g = new Ganchiku_Test;
    
// error_handler throws Exception
    
$g->error();
} catch (
Ganchiku_Exception $e) {
    echo 
$e;
}
try {
    
// default error handling
    
trigger_error('Not in Ganchku_Test'E_ALL);
} catch (
GanchikU_Exception $e) {
    echo 
$e;
}

?>

Leave a comment

Bloglines feedburner