エラー|エラー処理
PEAR は、強力なエラー処理メカニズムを提供します。この記事では、このシステムを活用する方法を説明します。
多くのプログラムはすでに PEAR パッケージを使用しています。 PHP プログラマの多くは、多かれ少なかれ PEAR でのエラー処理に精通しています。ただし、このメカニズムは PEAR パッケージに限定されません。誰でもクラスやプログラムでこれらのメソッドを使用できます。
この記事は 2 つの部分に分かれています。最初にエラー処理用のクラスの関数を見ていき、次に PEAR エラー処理メカニズムに基づいてエラーを処理する方法を見ていきます。
私たちのサンプルクラスは cvs2db と呼ばれ、CSV ファイルからデータベーステーブルにデータを挿入します。データは手書きである可能性があるため、挿入前にデータを検証する必要があります。郵便番号を実装します。関数 import() は、読み取り、チェック、挿入の作業を完了し、破損したレコードの数を返します。戻り値が 0 より大きい場合は、exportUnvalid() を使用して、問題のあるレコードセットを新しい CSV ファイルに書き込むことができます。一般的な使用法は次のとおりです:
$cd = new csv2db();
$dsn = 'mysql://root@localhost/csv2db';
if( 0 < $cd->import ( "./dat.csv", $dsn, 'address')) {
$cd->exportUnvalid("./dat2.csv");
}
?>
考えられるエラーは次のとおりです:
インポートする CSV ファイルが存在しません。
データベースへの接続に失敗しました。
レコードセットが破損しているため、CSV エクスポート ファイルを作成できません。
エラー メッセージを提供する古典的なソリューションでは、次のようなコードを作成します:
$cd = new csv2db();
$dsn = 'mysql://root@localhost/csv2db';
$ result = $cd->import("./dat.csv", $dsn, 'address')
switch($result) {
case FILE_NOT_OPENED:
...
break;
case DATABASE_ERROR:
.. .
break;
default:
if(0 < $result) {
$cd->exportUnvalid("./dat2.csv");
} else {
echo 'すべてok!'
}
}
?>
これは、短いスクリプトでは受け入れられ、一般的な方法ですが、エラー処理が常に懸念される大規模なプログラムではそうではありません。従来の可能性では、クラス作成者が最終決定を下す必要があります。ほとんどの場合、決定は長期的な使用や再利用可能なコードではなく、その時点でのクラスの呼び出しに基づいて行われます。柔軟なエラー処理メカニズムは再利用可能なコードの重要な部分であり、PEAR Error API はそのような十分にテストされたメカニズムの 1 つです。
ユーザーから見たクラス
これら 2 つの関数に加えて、このクラスは、一連のエラー処理関数と、ローカライズされたエラー情報の特別な機能を備えた DB2CVS_Error と呼ばれる独自のエラー オブジェクトを提供します。
ここで、エラーが発生したときにクラスの動作を制御する方法を説明します。
ローカルおよびグローバル エラー処理
エラー処理は setErrorHandling() で管理します。この関数は 2 つの引数を受け取ります: 1 つ目はエラー モード、2 つ目 (オプション) の引数はエラー モード固有のオプションです。たとえば、setErrorHandling(PEAR_ERROR_PRINT, 'このエラーは %s で発生しました') および setErrorHandling(PEAR_ERROR_TRIGGER, E_USER_WARNING) です。
この関数の呼び出しメソッドは、一般的な動作において最も重要です: static または Entity。 cvs2db クラスでは、これらの呼び出しはすべて同じ構造を持ち、クラスのエラー モードを設定します。
// インスタンスごと
$cd = new csv2db(); >setErrorHandling(PEAR_ERROR_DIE):
// static
CVS2DB::setErrorHandling(PEAR_ERROR_DIE);
PEAR::setErrorHandling(PEAR_ERROR_DIE);
2 つの結果が同じである場合、違いは何ですか?エンティティ呼び出しはそのクラスに対してのみ設定されますが、静的呼び出しは PEAR_Error を使用するすべてのクラス、またはそのクラスから派生したすべてのクラスに対して機能します。これは最初の静的コマンド CVS2DB::setErrorHandling(PEAR_ERROR_DIE) にも影響しますが、影響を受けるのは cvs2db クラスのみであるように見えます。
概要: コマンドをエンティティ関数として使用すると、このエンティティのみ (ローカル) にエラー モードを設定し、静的関数として呼び出すと、スクリプト全体のエラー モード (グローバル) が設定されます。
setErrorHandling() と raiseError()
どちらの関数も静的に呼び出すことも、エンティティの関数として呼び出すこともできます。組み合わせが互いにどのような影響を与えるかを覚えておくことが重要です。
基本的に: setErrorHandling() の静的呼び出しは、raiseError() の静的呼び出しにのみ影響します。エンティティ関数としての setErrorHandling() は、静的関数呼び出しとしての raiseError() にのみ影響します。 csv2db クラスでは、$this->raiseError(...) を使用するため、 csv2db::setErrorHandling() を使用してエラー モードを設定することは不可能です。 Wen Tian はこの問題を解決するためのちょっとしたトリックを持っています - raiseError() を書き換えます:
function raiseError(..., $mode=null, $options=null,...) {
if($mode==null && $ this ->_default_error_mode!=null) {
$mode = $this->_default_error_mode;
$options = $this->_default_error_options;
}
return PEAR::raiseError(...,$mode, $options 、 ...);
}
このようにして、エンティティ呼び出しを static にマップします。エラー モードで raiseError() を呼び出すと、このモードがこれらの設定 (この場合はグローバル設定) をオーバーライドします。
クラスによってエラーがどのようにスローされるかに注意する必要があります。注意しないと、予期しない副作用が発生する可能性があります。
エラー パターン
PEAR を使用したエラー処理には、エラー パターンを理解することが重要です。 PEAR エラー処理では、ユーザーが何をすべきかを決定できます。 注: 以下のユーザーという用語は、スクリプトの結果や Web ページを表示するユーザーではなく、実際に PEAR_Error プログラムを使用する開発者を指します。考えられるエラーパターンを詳しく紹介します。
PEAR_ERROR_DIE - このモードをオンにすると、プログラムが終了し、エラー メッセージが出力されます。オプションで、メッセージの生成に使用できる printf() スタイルの文字列を定義できます。文字列の最初の '%s' が、エラー オブジェクトに格納されているエラー メッセージと置き換えられます。
PEAR_ERROR_PRINT - PEAR_ERROR_DIE に使用されるのと同じオプションの文字列を含むエラー メッセージを印刷するだけです。
PEAR_ERROR_RETURN - エラーが発生したときの一般的な動作。クラスを使用して isError() 関数または PEAR::isError() を提供し、エラーをチェックできます。
$db->setErrorhandling(PEAR_ERROR_RETURN)
if(!csv2db::isError(0 < $d = $cd->import("./dat.csv", $dsn, 'address'))) {
if(!csv2db::isError($cd->exportUnvalid("./dat2.csv")) {
} else {
// エラーを処理します
}
} else {
// エラーを処理します
}
PEAR_ERROR_TRIGGER - この関数は、PHP ランタイム エラーと同じように動作します: E_USER_NOTICE、E_USER_WARNING、または E_USER_ERROR のいずれかのエラーが発生することを定義する必要があります。エラー メッセージでは、エラーが発生する行 (yy の xxx) を参照しています。 PEAR.php 内の、trigger_error が呼び出される行 - エラーが直接発生する行ではありません
PEAR_ERROR_CALLBACK - これは、エラーが 1 か所でのみ処理される場所であり、コードは最善のものを心配する必要はありません。エラーを処理するには、関数またはクラス関数が必要です。リスト 2 に示すようなスクリプトを作成すると、クラス関連のエラー オブジェクトの利点がわかります。 CSV ベースのエラーの場合は CSV2DB_Error 、データベース アクセスに関連するエラーの場合は DB_Error オブジェクトです。
$cd = new csv2db();
$cd->setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError');
$dsn = 'mysql://root@localhost/csv2db';
if( 0 < $d = $cd->import("./dat.csv", $dsn, 'address')) {
$ cd-> ;exportUnvalid("./dat2.csv");
}
function handleError($error) {
if(DB::isError($error) {
// データベースエラーを処理します
}
if(csv2db ::isError ($error) {
switch($error->getCode()) {
case FILE_NOT_OPENED :
...
break;
case CORRUPTED_RECORD :
...
break;
}
}
}
個人エラー処理
考えられるエラーは 2 つあります。無視できるエラー (レコードの破損) と、プログラムを実行できなくなるエラー (ファイルが見つからない、またはデータベースを開けない) です。シェル スクリプトでクラスを使用する場合、スクリプトをタイプ 2 エラーで終了させることができます。
当然、$cd->setErrorHandling(PEAR_ERROR_DIE) と書くこともできますが、レコード破損エラーが発生した場合に問題が発生する可能性があります。この場合、特定のエラーに対するエラー処理の可能性を無効にするか、置き換える必要があります。解決策は、expectError() です。この関数にエラー コードを渡すと、指定されたエラーのエラー モードがデフォルトのエラー モードとは別に PEAR_ERROR_RETURN に設定されます。
expectError() 関数は、渡されたエラー コードをスタックに保存し、popExpected() を使用して最後に渡されたエラー コードを削除します。 PHP 4.3 以降では、delExpect(); を使用することもできます。この関数は、指定されたエラー コードの一致をスタックから削除するため、位置を気にする必要はありません。
実際の使用では、次のようになります:
$cd->setErrorHandling(PEAR_ERROR_DIE);
...
$cd->gt;expectError(CORRUPTED_RECORD);
$cd->import(...) ;
$cd->popExpect();
pushErrorHandling() と PopErrorHandling() は似ており、一時的にエラー処理を制御できます。例:exportUnvalid() のファイルを開けず、エラーを無視したい場合:
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
$cd->exportUnvalid("./dat2.csv");
PEAR ::popErrorHandling();
メソッドの呼び出しの違いに注意してください! ExpectError()/popExpect() はエンティティ関数として呼び出す必要があります。pushErrorHandling と PopErrorHandling は静的に呼び出すことができます。エンティティ関数として使用した場合、そのエンティティにのみ影響します。
ユーザーには多くの可能性がありますが、これはプログラマーが多くの仕事をしなければならないことを意味しますか?はい。 false を返す以上のことを行う必要があるためです。いいえ。PEAR Error API が多くの作業を行ってくれるからです。
エラー処理に関するいくつかの考え
優れたプログラマとして、クラスのユーザーに対してエラーの正確な原因を隠すべきではありません。これにより、単純な return false の使用が防止されます。また、PHP によって自動的に 0 に型キャストされる可能性があることにも注意してください。これは、import() 関数にとって、すべてのレコードが正しく挿入されたことを意味します。スクリプトを終了するだけですか?これは、単純な PHP シェル スクリプトでは許容できるかもしれませんが、Web アプリケーションでは不適切な選択です。さらに、レコード破損の場合、エラーは無視できます。何がtrigger_error()にならないのでしょうか?これは可能なオプションですが、2 つの欠点があります。クラスの動作は php.ini 設定に依存し、この動作はクラスでは珍しいものです。追加の関数を使用してエラー状態を検出できる場合があります。すべてのクラスが非標準の関数名を提供しているとしても、これには依然として問題があり、メーリング リストやニュースグループで見られるように、クラスのユーザーはそのような関数呼び出しを忘れているようです。何をするか? PEAR エラー処理 API をユーザーが決定できるようにします。 PEAR エラー システムは広く知られており、多くのクラスがすでに PEAR クラスを使用しているため、とにかく PEAR エラー処理メカニズムを使用する必要があります。なぜそれをベースにして構築しないのでしょうか。これにより、前述の問題が回避され、ユーザーに多くの可能性が与えられます。リスト 1 を見てください。csv2db クラスとそのエラー オブジェクトの実装が示されています。少し怖いかもしれませんが、ソース コードを 1 行ずつ見ていきます。
リスト 1
require_once 'PEAR.php';
require_once 'DB.php';
define("FILE_NOT_OPENED", 10);
define("CORRUPTED_RECORD", 20);
class csv2db extends PEAR{
var $records=array();
var $unvalid=array();
function csv2db() {
$this->PEAR("CSV2DB_Error");
}
function import( $file, $dsn, $table) {
$this->PEAR("CSV2DB_Error");
if($fp=@fopen($file, 'r')) {
while($data=fgetcsv($) fp, 1024,';')) {
$this->records[]=$data;
}
fclose($fp);
} else {
return $this->raiseError(null, FILE_NOT_OPENED);
}
$unvalidCount=0;
$storeMode = $GLOBALS['_PEAR_default_error_mode'];
$storeOpts = $GLOBALS['_PEAR_default_error_options'];
$GLOBALS['_PEAR_default_error_mode'] = $this->_default_エラーモード;
$GLOBALS['_PEAR_default_error_options'] = $this->_default_error_options;
$db = DB::connect($dsn);
$GLOBALS['_PEAR_default_error_mode']= $storeMode;
$GLOBALS['_PEAR_default_error_options' ] = $storeOpts;
if(!DB::isError($db)) {
$db->setErrorHandling($this->_default_error_mode,
$this->_default_error_options);
$qp = $db ->prepare("INSERT INTO $table VALUES (?, ?, ?, ?)");
foreach( $this->records as $record) {
if(preg_match('/d{5}/') ,$record[2])) {
$db->execute($qp, $record);
} else {
$unvalidCount++;
$this->unvalid[]=$record;
$this-> ;raiseError(破損したレコード、CORRUPTED_RECORD);
}
}
$db->disconnect();
} else {
return $db;
}
return $unvalidCount;
}
function exportUnvalid($file) {
if($fp=@fopen($file, "w")) {
foreach($this->$data として無効) {
fwrite($fp, implode(';', $data)."n ", 1024);
}
fclose($fp);
} else {
return $this->raiseError(null,FILE_NOT_OPENED);
}
}
function isError($data) {
return (bool) (is_object($data) &&
(get_class($data) == 'CSV2DB_Error' ||
is_subclass_of($data, 'CSV2DB_Error')));
}
}
class CSV2DB_Error extends PEAR_Error {
var $msgs = array(
FILE_NOT_OPENED => ( 'de' =>"fehlerhafter Datensatz",
'en' => "破損したレコード")
);
function CSV2DB_Error($message=null, $code = null, $mode = null,
$level = null, $debuginfo = null) {
$this->PEAR_Error(null, $code, $mode, $level, $debuginfo);
}
function getMessage($lang = "ja ") {
return $ this->msgs[$this->code][$lang];
}
}
?>
独自のエラー オブジェクト
独自のエラー クラスを持つことは常に良いことですが、このような小さなクラスには余分なオーバーヘッドが多すぎるかもしれませんが、このクラスは単なる例であり、エラー オブジェクトを使用せずに実装するには多くのコードが必要となる機能から大きな恩恵を受けます。利点は次のとおりです。まず、エラーがクラスに直接割り当てられ、ローカライズが容易になります。
実装をシンプルに保つために、クラスは PEAR_Error を継承する必要があります。そうしないと、PEAR::isError() が正しく動作しません。
実装にはコンストラクターが含まれており、パラメーターを変更せずに PEAR_Error コンストラクターに渡します。
getMessage() 関数をオーバーライドすることが、ローカライズされたエラー メッセージを提供する鍵となります。エラー コードはクラス変数として定義され、言語の動的割り当てに依存します。これは、メイン クラスのソース コード全体にメッセージを分散させるのではなく、メッセージを 1 か所に集めるのにも役立ちます。
PEAR エラー処理の実装
記事の最初の部分で、クラスが多数の関数を提供していることがわかりましたが、直接実装されるのはそのうちの 4 つだけです。すべての関連関数のエラー処理は、PEAR 基本クラスによって提供されます。これらすべてのエラー処理機能を活用するには、cvs2db クラスを PEAR 基本クラスから継承させる必要があります。つまり、 class csv2db extends PEAR です。
前のエラー オブジェクトの段落では、isError() の説明から始めました。このメソッドをオーバーライドする必要はありませんが、オーバーライドするとエラー クラスを直接検査できるようになり、エラー追跡がより正確になり、数ミリ秒を節約できる可能性があります。
クラスのコンストラクターは、間違ったクラス名をパラメーターとして使用して親クラスのコンストラクターを呼び出しているだけです。この呼び出しによりエラー オブジェクトが登録され、エラーがトリガーされるたびにエラー クラスが使用されるようになります。
raiseError
import() と exportUnvalid() の関数本体での raiseError() の使用は注目に値します。これは、エラーを作成するための重要な関数です。PEAR は、この目的のために、raiseError() と throwError() という 2 つの関数を提供します。後者は PHP 4.3 以降に存在し、raiseError() の簡略化されたバリアントであり、その動作は同一であり、それらのパラメータについては「raiseError と throwError」の項で説明します。
raiseError と throwError
プロトタイプ:
&raiseError( $message, $code, $mode, $options, $userinfo, $errorclass, $skipmessage)
&throwError( $message, $code, $userinfo)
パラメータ説明
$message (文字列) エラーメッセージ
$code (int) エラー番号
$mode (定数) エラーモード
$options (混合) エラーモード固有のパラメータ
$userinfo (混合) 追加データ (デバッグ情報など) )
$errorclass (文字列) クラス名
オプションで、既存のエラー オブジェクトを次の関数に渡すことができます:
&raiseError($error_object)
&throwError($error_object)
ソース コードから開始する場合 パラメータの比較これら 2 つの関数のリストを見ると、クラスがメッセージ パラメーターを設定していないことがわかります。エラー クラスの getMessage() 関数を使用してエラー メッセージを割り当てるため、これは必要ありません。さらに、エラー クラスを導入するために PEAR コンストラクターを呼び出す必要はありません。raiseError() の呼び出しでエラー クラスを指定できます。このオプションを覚えておいてください。たとえば、クラスが静的関数または複数のエラー オブジェクトを提供する場合、csv2db のようにそれらをクラスに対してグローバルに設定することはできません。
raiseError() と throwError() は、静的に呼び出すことも、setErrorHandling() のようなエンティティ関数として呼び出すこともできます。静的呼び出しを行うかどうかを適切に決定することは重要です。これは、ユーザーが setErrorHandling() を使用してクラスをどのように誤解するかに直接影響します。 setErrorHandling() と raiseError() に注目してください。これにより、あなたとユーザーの頭痛の種が軽減されます。
クラスのこの部分から、グローバルおよびローカルのエラー設定とトリガーの悪影響を確認できます。
$storeMode = $GLOBALS['_PEAR_default_error_mode'];
$storeOpts = $GLOBALS['_PEAR_default_error_options'];
$GLOBALS['_PEAR_default_error_mode'] = $this->_default_error_mode;
$GLOBALS['_PEAR_default_エラーオプション' ] = $this->_default_error_options;
$db = DB::connect($dsn);
$GLOBALS['_PEAR_default_error_mode'] = $storeMode;
$GLOBALS['_PEAR_default_error_options'] = $storeOpts;
First 、グローバル エラー モードが保存され、次にグローバル エラー モードがローカル エラー モードに設定され、最後の数行で元のエラー モードが復元されます。なぜ? Connect() は静的関数です。 PEAR::raiseError() を使用する必要があります。したがって、設定を保存して復元しないと、問題が発生します。リスト 3 を見てください。クラスが import() 関数でデータベースに接続できない場合はどうなりますか? raiseError() への静的呼び出しは、ローカルの $cd->setErrorHandling(...) ではなく、グローバル エラー モードの影響を受けるため、スクリプトは実行を終了します。実際、push と PopErrorHandling() はそのようなタスク用に設計されていますが、PHP の現在のバグにより、うまく機能しないようです。
$db オブジェクトにエラー モードの使用を強制する方がより快適な方法です。完全な PEAR Error API がサポートされており、コードを次のように記述できます: $db->setErrorHandling($this->_default_error_mode, $this-> ;_default_error_options)。どちらのエンティティ変数も PEAR_Error クラスによって提供されます。
その行 $this->raiseError(corrupted record, CORRUPTED_RECORD) は注目に値するように見えますが、リターンが欠落しているのは見た目には好ましくありません。その理由は、破損したレコードが見つかったときに関数の実行を中止したくないからです。これを、警告をトリガーすることに例えることができます。唯一の制限は、モード PEAR_ERROR_RETURN が機能しないことです。
リスト 3
...
PEAR::setErrorHandling(PEAR_ERROR_DIE)
$cd = new csv2db();
$cd->setErrorHandling(PEAR_ERROR_CALLBACK, 'handleError');
$dsn = 'mysql://root@localhost/csv2db';
if ( 0 < $d = $cd->import("./dat.csv", $dsn, 'アドレス')) {
$cd->exportUnvalid("./dat2.csv");
}
$db = DB::connect($dsn);
$db->query(...);
...
function handleError($error) {
if(DB::isError($error) ) {
// データベース エラーを処理します
}
if(csv2db::isError($error) {
switch($error->getCode()) {
case FILE_NOT_OPENED :
...
break;
case CORRUPTED_RECORD :
...
break;
}
}
}
?>
PEAR エラー処理と PHP 5
エラーを作成するために関数を使用しているため、PHP 5 では try/catch/throw については考慮していません。 PHP 5 では、関数はクラスに対して透過的に PEAR_Error() をスローできます。PHP5 で使用する場合は、エラー モード PEAR_ERROR_EXCEPTION を使用できます。 ;?php
$i = new csv2db();
$dsn = 'mysql://root@localhost/csv2db';
try {
if( 0 < $d = $i->import("./ dat.csv", $dsn, 'address')) {
$i->exportUnvalid("./dat2.csv");
}
}
catch CSV2DB_Error {
// エラーを取得します
}
?> ;
結論
トラブルシューティングとエラー処理のための強力なメカニズムを提供する PEAR エラー処理の概要を理解できたと思います。PEAR マニュアル [1] のコード セクションを参照してください。これらの機能によって得られるメリット。
Alexander Merz (php dot net の alexmerz) は、PEAR ハンドブックの編集者であり、フリーランスのクリエイター兼ライターとして働いています。