日々の開発において、ほとんどの人は、開発環境ではデバッグ モードをオンにし、運用環境ではデバッグ モードをオフにするというアプローチをとります。開発中にさまざまなエラーや例外を確認できますが、オンラインではエラー表示をオフにします。
上記の状況は非常に安全であると説明する人もいますが、重要な情報の漏洩を避けるために他の人はエラーを見ることができません...
しかし、オフラインでは問題なく動作するのですが、このような状況に遭遇したことがありますか? 、しかし、オンラインにするとすべてがうまくいきません。実行できない理由がわかりません...
スクリプトは長い間問題なく実行されていましたが、ある日突然停止しました。記録も理由もありませんでした...
Line 最後の支払いについては、他の人は明らかに支払いましたが、私たちはそれを記録しませんでした。自分たちで実験して良かったです...
上記はすべて、みんなが締め切ったからです。エラー メッセージが表示され、エラーが報告されなかった場合、例外がログに記録されるため、ランダムに発生するエラーの追跡が困難になります。これにより、エラーが表示されないだけでなく、エラーが追跡されるという矛盾が生じます。これはどのように実現されるのでしょうか。
上記の問題は、PHP のエラーおよび例外メカニズムとその組み込み関数 'set_Exception_handler'、'set_error_handler'、'register_shutdown_function' によって実現できます
'set_Exception_handler' 関数は、さまざまなキャッチされなかった例外をインターセプトするために使用され、その後ハンドルされますこれらはユーザー定義の方法で実行できます。
「set_error_handler」関数はさまざまなエラーをインターセプトし、ユーザー定義の方法に引き渡すことができます。「register_shutdown_function」関数は、PHP スクリプトの最後に呼び出される関数です。「error_get_last」を実行できます。最後の致命的なエラーを取得する
このアイデアは、一般に、エラー、例外、および致命的なエラーをインターセプトし、それらを処理のためにカスタム メソッドに引き渡すことです。そうである場合、記録されたデータベースまたは。ファイル システムを監視し、スクリプトを使用してこれらのログを継続的にスキャンし、重大なエラーが見つかった場合は、すぐに電子メールまたはテキスト メッセージを送信して警告します
まず、エラーと例外をインターセプトするために使用されるエラー インターセプト クラスを定義します。 、独自に定義した処理メソッドを使用します。このクラスは「errorHandler.class.php」という名前のファイルに配置され、コードは次のとおりです
/** * 文件名称:baseErrorHandler.class.php * 摘 要:错误拦截器父类 */require 'errorHandlerException.class.php';//异常类class errorHandler{ public $argvs = array(); public $memoryReserveSize = 262144;//备用内存大小 private $_memoryReserve;//备用内存 /** * 方 法:注册自定义错误、异常拦截器 * 参 数:void * 返 回:void */ public function register() { ini_set('display_errors', 0); set_exception_handler(array($this, 'handleException'));//截获未捕获的异常 set_error_handler(array($this, 'handleError'));//截获各种错误 此处切不可掉换位置 //留下备用内存 供后面拦截致命错误使用 $this->memoryReserveSize > 0 && $this->_memoryReserve = str_repeat('x', $this->memoryReserveSize); register_shutdown_function(array($this, 'handleFatalError'));//截获致命性错误 } /** * 方 法:取消自定义错误、异常拦截器 * 参 数:void * 返 回:void */ public function unregister() { restore_error_handler(); restore_exception_handler(); } /** * 方 法:处理截获的未捕获的异常 * 参 数:Exception $exception * 返 回:void */ public function handleException($exception) { $this->unregister(); try { $this->logException($exception); exit(1); } catch(Exception $e) { exit(1); } } /** * 方 法:处理截获的错误 * 参 数:int $code 错误代码 * 参 数:string $message 错误信息 * 参 数:string $file 错误文件 * 参 数:int $line 错误的行数 * 返 回:boolean */ public function handleError($code, $message, $file, $line) { //该处思想是将错误变成异常抛出 统一交给异常处理函数进行处理 if((error_reporting() & $code) && !in_array($code, array(E_NOTICE, E_WARNING, E_USER_NOTICE, E_USER_WARNING, E_DEPRECATED))) {//此处只记录严重的错误 对于各种WARNING NOTICE不作处理 $exception = new errorHandlerException($message, $code, $code, $file, $line); $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); array_shift($trace);//trace的第一个元素为当前对象 移除 foreach($trace as $frame) { if($frame['function'] == '__toString') {//如果错误出现在 __toString 方法中 不抛出任何异常 $this->handleException($exception); exit(1); } } throw $exception; } return false; } /** * 方 法:截获致命性错误 * 参 数:void * 返 回:void */ public function handleFatalError() { unset($this->_memoryReserve);//释放内存供下面处理程序使用 $error = error_get_last();//最后一条错误信息 if(errorHandlerException::isFatalError($error)) {//如果是致命错误进行处理 $exception = new errorHandlerException($error['message'], $error['type'], $error['type'], $error['file'], $error['line']); $this->logException($exception); exit(1); } } /** * 方 法:获取服务器IP * 参 数:void * 返 回:string */ final public function getServerIp() { $serverIp = ''; if(isset($_SERVER['SERVER_ADDR'])) { $serverIp = $_SERVER['SERVER_ADDR']; } elseif(isset($_SERVER['LOCAL_ADDR'])) { $serverIp = $_SERVER['LOCAL_ADDR']; } elseif(isset($_SERVER['HOSTNAME'])) { $serverIp = gethostbyname($_SERVER['HOSTNAME']); } else { $serverIp = getenv('SERVER_ADDR'); } return $serverIp; } /** * 方 法:获取当前URI信息 * 参 数:void * 返 回:string $url */ public function getCurrentUri() { $uri = ''; if($_SERVER ["REMOTE_ADDR"]) {//浏览器浏览模式 $uri = 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']; } else {//命令行模式 $params = $this->argvs; $uri = $params[0]; array_shift($params); for($i = 0, $len = count($params); $i < $len; $i++) { $uri .= ' ' . $params[$i]; } } return $uri; } /** * 方 法:记录异常信息 * 参 数:errorHandlerException $e 错误异常 * 返 回:boolean 是否保存成功 */ final public function logException($e) { $error = array( 'add_time' => time(), 'title' => errorHandlerException::getName($e->getCode()),//这里获取用户友好型名称 'message' => array(), 'server_ip' => $this->getServerIp(), 'code' => errorHandlerException::getLocalCode($e->getCode()),//这里为各种错误定义一个编号以便查找 'file' => $e->getFile(), 'line' => $e->getLine(), 'url' => $this->getCurrentUri(), ); do { //$e->getFile() . ':' . $e->getLine() . ' ' . $e->getMessage() . '(' . $e->getCode() . ')' $message = (string)$e; $error['message'][] = $message; } while($e = $e->getPrevious()); $error['message'] = implode("\r\n", $error['message']); $this->logError($error); } /** * 方 法:记录异常信息 * 参 数:array $error = array( * 'time' => int, * 'title' => 'string', * 'message' => 'string', * 'code' => int, * 'server_ip' => 'string' * 'file' => 'string', * 'line' => int, * 'url' => 'string', * ); * 返 回:boolean 是否保存成功 */ public function logError($error) { /*这里去实现如何将错误信息记录到日志*/ }}
上記のコードには、「errorHandlerException」クラスがあります。ファイルに配置 「errorHandlerException.class.php」では、このクラスは、エラーが発生したファイル、行番号、エラーコード、エラーメッセージおよびその他の情報を記録するために、エラーを例外に変換するために使用されます。そのメソッド「isFatalError」は、エラーが致命的エラーかどうかを識別するために使用されます。ここでは、管理の便宜のためにエラーに番号を付け、名前を付けます。このクラスのコードは次のとおりです
/** * 文件名称:errorHandlerException.class.php * 摘 要:自定义错误异常类 该类继承至PHP内置的错误异常类 */class errorHandlerException extends ErrorException{ public static $localCode = array( E_COMPILE_ERROR => 4001, E_COMPILE_WARNING => 4002, E_CORE_ERROR => 4003, E_CORE_WARNING => 4004, E_DEPRECATED => 4005, E_ERROR => 4006, E_NOTICE => 4007, E_PARSE => 4008, E_RECOVERABLE_ERROR => 4009, E_STRICT => 4010, E_USER_DEPRECATED => 4011, E_USER_ERROR => 4012, E_USER_NOTICE => 4013, E_USER_WARNING => 4014, E_WARNING => 4015, 4016 => 4016, ); public static $localName = array( E_COMPILE_ERROR => 'PHP Compile Error', E_COMPILE_WARNING => 'PHP Compile Warning', E_CORE_ERROR => 'PHP Core Error', E_CORE_WARNING => 'PHP Core Warning', E_DEPRECATED => 'PHP Deprecated Warning', E_ERROR => 'PHP Fatal Error', E_NOTICE => 'PHP Notice', E_PARSE => 'PHP Parse Error', E_RECOVERABLE_ERROR => 'PHP Recoverable Error', E_STRICT => 'PHP Strict Warning', E_USER_DEPRECATED => 'PHP User Deprecated Warning', E_USER_ERROR => 'PHP User Error', E_USER_NOTICE => 'PHP User Notice', E_USER_WARNING => 'PHP User Warning', E_WARNING => 'PHP Warning', 4016 => 'Customer`s Error', ); /** * 方 法:构造函数 * 摘 要:相关知识请查看 http://php.net/manual/en/errorexception.construct.php * * 参 数:string $message 异常信息(可选) * int $code 异常代码(可选) * int $severity * string $filename 异常文件(可选) * int $line 异常的行数(可选) * Exception $previous 上一个异常(可选) * * 返 回:void */ public function __construct($message = '', $code = 0, $severity = 1, $filename = __FILE__, $line = __LINE__, Exception $previous = null) { parent::__construct($message, $code, $severity, $filename, $line, $previous); } /** * 方 法:是否是致命性错误 * 参 数:array $error * 返 回:boolean */ public static function isFatalError($error) { $fatalErrors = array( E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING ); return isset($error['type']) && in_array($error['type'], $fatalErrors); } /** * 方 法:根据原始的错误代码得到本地的错误代码 * 参 数:int $code * 返 回:int $localCode */ public static function getLocalCode($code) { return isset(self::$localCode[$code]) ? self::$localCode[$code] : self::$localCode[4016]; } /** * 方 法:根据原始的错误代码获取用户友好型名称 * 参 数:int * 返 回:string $name */ public static function getName($code) { return isset(self::$localName[$code]) ? self::$localName[$code] : self::$localName[4016]; }
エラーインターセプトクラスでは、ユーザーはエラーを記録するための独自のメソッド ('logException') を定義する必要があります。ここで、一部のエラーは一定期間にわたって発生し続ける可能性があることに注意してください。指定された時間内にエラーが記録されたかどうかを記録するために、エラー コード、ファイル、行番号、およびエラーの詳細を使用して MD5 値を生成できます。 (1 時間)、再度記録する必要はありません
次に、上記のクラスをインスタンス化し、さまざまなエラーと例外をキャプチャするファイルを定義します。ファイルの名前は「registerErrorHandler.php」で、内容は次のとおりです。
/** 使用方法介绍:* 在入口处引入该文件即可,然后可以在该文件中定义调试模式常量'DEBUG_ERROR'** <?php* * require 'registerErrorHandler.php';* * ?>*//*** 调试错误模式:* 0 => 非调试模式,不显示异常、错误信息但记录异常、错误信息* 1 => 调试模式,显示异常、错误信息但不记录异常、错误信息*/define('DEBUG_ERROR', 0);require 'errorHandler.class.php';class registerErrorHandler{ /** * 方 法:注册异常、错误拦截 * 参 数:void * 返 回:void */ public static function register() { global $argv; if(DEBUG_ERROR) {//如果开启调试模式 ini_set('display_errors', 1); return; } //如果不开启调试模式 ini_set('error_reporting', -1); ini_set('display_errors', 0); $handler = new errorHandler(); $handler->argvs = $argv;//此处主要兼容命令行模式下获取参数 $handler->register(); } }registerErrorHandler::register();
残りは、このファイルをエントリファイルにインポートし、デバッグモードを定義し、エラーを記録する独自の方法を実装する必要があります
いくつかのエラーが発生していることに注意してください登録してスクリプトを中断させる前のエラーは記録できません。現時点では、'registerErrorHandler::register()' が実行前に中断されているためです
また、関数 'set_error_handler' は次のタイプのエラーをキャプチャできません: E_ERROR、E_PARSE 、E_CORE_ERROR、E_CORE_WARNING、E_COMPILE_ERROR、E_COMPILE_WARNING は公式ドキュメントに記載されていますが、これらのエラーが渡されない場合は、オンラインで公開することはできないため、ここでは問題ありません。
上記のコードは厳密にテストされており、オンライン環境に適用されています。必要に応じて変更を加えることができます。