まず、PHP内の標準エラーメソッドを見てくださったことを称賛したいと思います。残念ながら、ご存知のとおりerror_log
、いくつかの制限があります。
これは長い答えです。以下について調べるために読んでください。
- エラー
- エラーを直接ログに記録するvs
trigger_error
およびset_error_handler
- 良いエラーが悪くなるところ-致命的なエラー。
- 例外
- コード
TL;DRtrigger_error
エラーを発生させset_error_handler
てログに記録するために使用します。
1.エラー
プログラムで期待どおりに動作しない場合は、エラーを発生させて、誰かまたは何かに通知する必要があります。エラーは、プログラムが続行される可能性があるが、注目に値する、おそらく有害または誤った何かが発生した状況に対するものです。この時点で、多くの人は、選択したログパッケージを使用してエラーをすぐにログに記録したいと考えています。これはまったく間違ったことだと思います。trigger_error
で設定されたコールバックで処理できるように、を使用してエラーを発生させることをお勧めしますset_error_handler
。これらのオプションを比較してみましょう。
エラーを直接ログに記録する
したがって、ロギングパッケージを選択しました。これで、コードでエラーが発生した場合はいつでも、呼び出しをロガーに拡散する準備が整いました。あなたが行うかもしれない単一の呼び出しを見てみましょう(私はジャックの答えのものと同様のロガーを使用します):
Logger::getLogger('standard')->error('Ouch, this hurts');
このコードを実行するには、何が必要ですか?
クラス:ロガー
メソッド:getLogger
戻り値:メソッド「エラー」を持つオブジェクト
これらは、このコードを使用するために必要な依存関係です。このコードを再利用したい人は誰でも、これらの依存関係を提供する必要があります。これは、コードを再利用するには、標準のPHP構成ではもはや十分ではないことを意味します。最良の場合、依存性注入を使用すると、エラーを発行する可能性のあるすべてのコードにロガーオブジェクトを渡す必要があります。
また、コードが責任を負うものに加えて、エラーをログに記録する責任もあります。これは、単一責任の原則に反します。
エラーを直接ログに記録するのは悪いことがわかります。
救助へのtrigger_error
PHPにはtrigger_error
、標準関数と同じようにエラーを発生させるために使用できるという関数があります。使用するエラーレベルは、エラーレベル定数で定義されます。ユーザーとして、ユーザーエラーの1つE_USER_ERROR
、E_USER_WARNING
またはデフォルト値を使用する必要がありますE_USER_NOTICE
(他のエラーレベルは標準機能などのために予約されています)。標準のPHP関数を使用してエラーを発生させると、標準のPHPインストールでコードを再利用できます。私たちのコードは、エラーをログに記録する責任を負いません(エラーが発生したことを確認するだけです)。
これを使用trigger_error
すると、エラーロギングプロセスの半分(エラーの発生)のみを実行し、次に説明するエラーハンドラーのエラーに対応する責任を回避できます。
エラーハンドラ
関数を使用してカスタムエラーハンドラーを設定しset_error_handler
ます(コードのセットアップを参照)。このカスタムエラーハンドラーは、PHPの構成設定に応じて、通常はWebサーバーのエラーログにメッセージを記録する標準のPHPエラーハンドラーに代わるものです。false
カスタムエラーハンドラ内に戻ることで、この標準エラーハンドラを引き続き使用できます。
カスタムエラーハンドラには、エラーに応答するという単一の責任があります(実行するログを含む)。カスタムエラーハンドラー内では、システムへのフルアクセスが可能であり、必要なあらゆる種類のログを実行できます。事実上、オブザーバーデザインパターンを使用するすべてのロガーは問題ありません(二次的に重要であると信じているので、これについては説明しません)。これにより、新しいログオブザーバーをフックして、出力を必要な場所に送信できるようになります。
あなたはあなたのコードの単一の保守可能な部分のエラーであなたが好きなことをするための完全なコントロールを持っています。エラーログは、プロジェクト間で、または単一のプロジェクト内でページ間ですばやく簡単に変更できるようになりました。興味深いことに、抑制されたエラーでさえ、0@
のカスタムエラーハンドラーに到達します。これは、マスクが尊重されている場合は報告されるべきではありません。errno
error_reporting
良いエラーが悪くなるとき-致命的なエラー
特定のエラーから続行することはできません。次のエラーレベルはE_ERROR
、カスタムエラーハンドラからは処理できません:、、、、、、。これらの種類のエラーが標準の関数呼び出しによってトリガーされると、カスタムエラーハンドラーはスキップされ、システムがシャットダウンします。これは、次の方法で生成できます。E_PARSE
E_CORE_ERROR
E_CORE_WARNING
E_COMPILE_ERROR
E_COMPILE_WARNING
call_this_function_that_obviously_does_not_exist_or_was_misspelt();
これは重大な間違いです!から回復することは不可能であり、システムはシャットダウンしようとしています。私たちの唯一の選択はregister_shutdown_function
、シャットダウンに対処することです。ただし、この関数は、スクリプトが完了するたびに実行されます(成功した場合と失敗した場合)。これを使用するとerror_get_last
、最後のエラーが致命的なエラーであったときに、いくつかの基本情報をログに記録できます(この時点でシステムはほぼシャットダウンされます)。正しいステータスコードを送信し、選択した内部サーバーエラータイプのページを表示することも役立ちます。
2.例外
例外は、基本的なエラーと非常によく似た方法で処理できます。例外の代わりにtrigger_error
、コードによってスローされます(手動で、throw new Exception
または標準の関数呼び出しから)。で例外を処理するために使用するコールバックを定義するために使用set_exception_handler
します。
SPL
標準PHPライブラリ(SPL)は例外を提供します。trigger_error
これらは、コードに余分な依存関係を導入しないPHPの標準部分であるため、例外を発生させるための私の好ましい方法です。
それらをどうするか?
例外がスローされた場合、次の3つの選択肢があります。
- それをキャッチして修正します(その後、コードは何も悪いことが起こらなかったかのように続行します)。
- それをキャッチし、有用な情報を追加して、それを再スローします。
- それをより高いレベルに泡立たせましょう。
スタックの各レベルで、これらの選択が行われます。最終的に、バブルが最高レベルに達すると、設定したコールバックset_exception_handler
が実行されます。catch
これは、コード内のステートメント全体に広がるのではなく、(エラー処理と同じ理由で)ロギングコードが属する場所です。
3.コード
設定
エラーハンドラ
function errorHandler($errno , $errstr, $errfile, $errline, $errcontext)
{
// Perform your error handling here, respecting error_reporting() and
// $errno. This is where you can log the errors. The choice of logger
// that you use is based on your preference. So long as it implements
// the observer pattern you will be able to easily add logging for any
// type of output you desire.
}
$previousErrorHandler = set_error_handler('errorHandler');
例外ハンドラ
function exceptionHandler($e)
{
// Perform your exception handling here.
}
$previousExceptionHandler = set_exception_handler('exceptionHandler');
シャットダウン機能
function shutdownFunction()
{
$err = error_get_last();
if (!isset($err))
{
return;
}
$handledErrorTypes = array(
E_USER_ERROR => 'USER ERROR',
E_ERROR => 'ERROR',
E_PARSE => 'PARSE',
E_CORE_ERROR => 'CORE_ERROR',
E_CORE_WARNING => 'CORE_WARNING',
E_COMPILE_ERROR => 'COMPILE_ERROR',
E_COMPILE_WARNING => 'COMPILE_WARNING');
// If our last error wasn't fatal then this must be a normal shutdown.
if (!isset($handledErrorTypes[$err['type']]))
{
return;
}
if (!headers_sent())
{
header('HTTP/1.1 500 Internal Server Error');
}
// Perform simple logging here.
}
register_shutdown_function('shutdownFunction');
使用法
エラー
// Notices.
trigger_error('Disk space is below 20%.', E_USER_NOTICE);
trigger_error('Disk space is below 20%.'); // Defaults to E_USER_NOTICE
// Warnings.
fopen('BAD_ARGS'); // E_WARNING fopen() expects at least 2 parameters, 1 given
trigger_error('Warning, this mode could be dangerous', E_USER_WARNING);
// Fatal Errors.
// This function has not been defined and so a fatal error is generated that
// does not reach the custom error handler.
this_function_has_not_been_defined();
// Execution does not reach this point.
// The following will be received by the custom error handler but is fatal.
trigger_error('Error in the code, cannot continue.', E_USER_ERROR);
// Execution does not reach this point.
例外
以前の3つの選択肢のそれぞれが、一般的な方法でここにリストされ、修正し、追加して、バブルさせます。
1修正可能:
try
{
$value = code_that_can_generate_exception();
}
catch (Exception $e)
{
// We decide to emit a notice here (a warning could also be used).
trigger_error('We had to use the default value instead of ' .
'code_that_can_generate_exception\'s', E_USER_NOTICE);
// Fix the exception.
$value = DEFAULT_VALUE;
}
// Code continues executing happily here.
2追加:
code_that_can_generate_exception()
がについて知らない方法を以下で確認して$context
ください。このレベルのcatchブロックには、再スローすることで役立つ場合に例外に追加できる詳細情報があります。
try
{
$context = 'foo';
$value = code_that_can_generate_exception();
}
catch (Exception $e)
{
// Raise another exception, with extra information and the existing
// exception set as the previous exception.
throw new Exception('Context: ' . $context, 0, $e);
}
3それを泡立たせます:
// Don't catch it.