適切な処理が意味すること:
- 既存のコードに挿入できる汎用ソリューション。
- 簡単に拡張可能なソリューション。
- PHPにはすでに3つの「エラーメカニズム」があります。エラー自体、例外、およびアサーションです。 3つのメカニズムを1つに減らす-例外。 このテーマに関する以前の記事のコメントでは、例外は悪いおよび/または複雑なエラー処理方法であるという意見が表明されました。 私はそうは思わず、コメントでこれについて議論する準備ができています。
- オプションのロギング。
- さまざまな出力形式とデバッグ/プロダクションモードをサポートする一般的な例外ハンドラー。
- デバッグモードでは、トレースが表示されます。 トレースの要件:コンパクト、クリア、および可能であればIDEでファイルを開くためのリンク。
ワンストップソリューション
これを行うために、exceptionHandlerClassクラスを作成します。 exceptionHandlerClassは、設定と静的メソッド(エラー、例外、およびアサーションハンドラー)を格納します。 setupHandlersメソッドとrestoreHandlersメソッドも必要です。 最初の方法は、エラートラップを構成します。 エラーおよびアサーションハンドラーはErrorExceptionをスローします。 例外ハンドラーは未処理の例外を処理し、設定に応じて適切な応答を出力します。 restoreHandlersはすべてのハンドラーを元の状態に戻します-これは、既存のエラー処理メカニズムを備えたコードにクラスを埋め込むときに役立ちます。 接続は次のようになります。
- require 'exceptionHandler / exceptionHandlerClass.php' ;
- exceptionHandlerClass :: setupHandlers ( ) ;
デバッグモードを有効にする(デフォルトではオフ):
- exceptionHandlerClass :: $ debug = true ;
出力形式
例で説明する方が簡単です。Webページにトレースを出力するには、preタグでラップしてhtmlspecialchars()を使用します。一方、コンソールに出力するときに同じトレースを読むのは不便です。 エラーをSapServerの応答として出力する必要がある場合は、適切に形成されたXMLドキュメント(SoapFault)である必要があります。 スクリプトが画像などのバイナリデータを表示する場合、WildFireを介してエラーを表示する方が便利です。 これらのすべての状況で、異なる出力形式を適用するだけです。
さまざまな形式に対して、さまざまなクラスを作成します。 まずは、exceptionHandlerOutputWeb(Web用)とexceptionHandlerOutputCli(コマンドライン用)の2つの出力形式を実装します。 また、ファクトリクラス(exceptionHandlerOutputFactory)も必要です。このクラスでは、ロジックがカプセル化され、どの出力形式をいつ使用するかが決まります。
- パブリック 関数 getExceptionHandlerOutput ( ) {
- if ( php_sapi_name ( ) == 'cli' ) {
- 新しい exceptionHandlerOutputCli ( )を 返し ます。
- }
- 新しい exceptionHandlerOutputWeb ( )を 返し ます。
- }
setupHandlersを呼び出すとき、exceptionHandlerOutput *またはexceptionHandlerOutputFactory *クラスのインスタンスを渡すことにより、出力形式を設定できます。
- exceptionHandlerClass :: setupHandlers ( 新しい exceptionHandlerOutputAjax ( ) ) ;
このアーキテクチャのおかげで、フォーマットを簡単に拡張できます。 新しい形式を作成するには、抽象クラスexceptionHandlerOutputから継承し、1つのメソッド(出力)を実装するクラスを作成するだけで十分です。
- class exceptionHandlerOutputAjax は exceptionHandlerOutput {
- public function output ( $ exception 、 $ debug ) {
- header ( 'HTTP / 1.0 500 Internal Server Error' 、 true 、 500 ) ;
- header ( 'Status:500 Internal Server Error' 、 true 、 500 ) ;
- $応答 = 配列 (
- 'エラー' => true 、
- 'メッセージ' => '' 、
- ) ;
- if ( $ debug ) {
- $ response [ 'message' ] = $ exception- > getMessage ( ) ;
- } else {
- $ response [ 'message' ] = self :: $ productionMessage ;
- }
- exit ( json_encode ( $ response ) ) ;
- }
- }
出力形式を自動的に選択するより複雑なロジックが必要な場合は、exceptionHandlerOutputFactoryを継承するクラスを作成し、getExceptionHandlerOutputメソッドを実装する必要があります。
- class exceptionHandlerOutputAjaxFactory は exceptionHandlerOutputDefaultFactoryを拡張します{
- パブリック 関数 getExceptionHandlerOutput ( ) {
- if ( self :: detect ( ) ) {
- 新しい exceptionHandlerOutputAjax ( )を 返し ます。
- }
- 親:: getExceptionHandlerOutput ( ) ;
- }
- public static function detect ( ) {
- return ( ! empty ( $ _SERVER [ 'HTTP_X_REQUESTED_WITH' ] )
- && strtolower ( $ _SERVER [ 'HTTP_X_REQUESTED_WITH' ] ) == 'xmlhttprequest' ) ;
- }
- }
- exceptionHandlerClass :: setupHandlers ( 新しい exceptionHandlerOutputAjaxFactory ( ) ) ;
ロギング
上で述べたように、ログは必要に応じて有効にできます。 これを行うために、exceptionHandlerメソッドでexceptionLogメソッドが作成されました
- public static function exceptionLog ( $ exception 、 $ logPriority = null ) {
- if ( ! is_null ( self :: $ exceptionHandlerLog ) ) {
- self :: $ exceptionHandlerLog- > log ( $ exception 、 $ logPriority ) ;
- }
- }
ロギングを有効にする必要がある場合は、次を実行します。
- exceptionHandlerClass :: $ exceptionHandlerLog = new exceptionHandlerSimpleLog ( ) ;
ロギングクラスは、抽象exceptionHandlerLogを継承し、logメソッドを実装する必要があります
- class exceptionHandlerSimpleLog は exceptionHandlerLogを拡張します{
- public function log ( $ exception 、 $ logType ) {
- switch ( $ logType ) {
- ケース self :: uncaughtException :
- error_log ( $ exception- > getMessage ( ) ) ;
- 休憩 ;
- }
- }
- }
logTypeは、exceptionHandlerLogで宣言された定数の1つです。
- const uncaughtException = 0 ; //未処理の例外
- const caughtException = 1 ; //エラーハンドラの外部でロギングメソッドを呼び出します
- const ignoreError = 2 ; // screamオプションが無効な場合、@でマスクされたエラーがログに記録されます
- const lowPriorityError = 3 ; //例外にならないエラー
- const assertion = 4 ; //アサーション
logTypeと例外を使用して、開発者はどの例外とどのようにログを記録するかを自分で決定できます。 たとえば、uncaughtExceptionをメールで送信したり、重大度がE_ERRORのignoreErrorをファイルに記録したりできます。
トレース
トレースの出力では、例外のタイプ、メッセージ、および実際のトレース自体を確認します。 各呼び出しのトレースでは、どの関数が呼び出されたか、「短い」パラメーターのリスト、ファイルと呼び出しが発生した行を表示する必要があります。 例として「短い」パラメーターが何であるかを説明します:関数が1000文字の文字列で呼び出された場合、トレースにこの文字列が存在しても問題の解決には役立ちません。読み取りトレースが複雑になるだけです。同じことが大きなネストを持つ配列に適用されます。 画面へのトレース出力は、単にどこを見るべきかを伝えるだけです。 何が起こっているのかを正確に把握するには、xdebugまたはプリミティブvar_dump()およびdie()を使用して、必要に応じてデバッグする必要があります。
トレースの例:
[ErrorException]:E_WARNING-mysql_connect(): 'localhost'上のMySQLサーバーに接続できません(10061)
#0:mysql_connect()
D:\ projects1 \ d \ index.php:19
#1:testClass :: test1( "長い文字列... eeeeeery長い文字列"(56))
D:\ projects1 \ d \ index.php:22
#2:testClass-> test2(testClass()、-∞、i:iTest、c:testClass、fa:testClass :: test2)
D:\ projects1 \ d \ index.php:27
#3:testAll(r:ストリーム、fs:testClass :: test1)
D:\ projects1 \ d \ index.php:30
凡例
- r:-リソース
- fs:-関数(呼び出し可能な文字列)
- fa:-関数(呼び出し可能な配列)
- i:-インターフェース(文字列)
- c:-クラス(文字列)
- ∞/ INF-無限大
- testClass()-タイプtestClassのオブジェクト
- ""(n)-文字列、長さは括弧で示されます、...-文字列の一部がカットされる場所
- array(n)-配列、長さは括弧で示されています
そして、最も便利な...トレースから直接IDEでファイルを開くためのリンク。
IDEのリンクをクリックすると、対応する行の対応するファイルが開きます。
コンソールモード(NetBeansコンソール)の場合:
- exceptionHandlerOutputCli :: setFileLinkFormat ( ':in%f on line%l' ) ;
Webモード(TextMate)の場合:
- exceptionHandlerOutputWeb :: setFileLinkFormat ( 'txmt:// open /?file://%f&line =%l' ) ;
NetbBeans(または別のIDE)に実装できます。 このために必要なこと:プロトコルを登録します。 このプロトコルのハンドラーを作成します(最も単純なのはbatファイルです)。 ハンドラーで、コマンドラインを使用して、対応するファイルと行でNetBeansを呼び出します。 しかし、これは次の記事のトピックです。
コードは2日で作成されたため、小さな欠陥が発生する可能性があります。
ダウンロード (リポジトリに置く時間がありませんでした)。
UPD: PHPブログに移植
UPD2: PHPで例外を処理するトピックの継続