PHP 치명적인 오류를 어떻게 잡을 수 있습니까?
PHPPHP 치명적인 오류를 어떻게 잡을 수 있습니까?
set_error_handler ()를 사용하여 대부분의 PHP 오류를 catch 할 수 있지만 존재하지 않는 함수를 호출하는 등의 치명적인 (E_ERROR) 오류에는 작동하지 않습니다. 이러한 오류를 잡을 수있는 또 다른 방법이 있습니까?
모든 오류에 대해 mail ()을 호출하고 PHP 5.2.3을 실행하려고합니다.
해결법
-
==============================
1.
register_shutdown_function을 사용하여 치명적인 오류를 기록하십시오. PHP 5.2 이상이 필요합니다 :
register_shutdown_function( "fatal_handler" ); function fatal_handler() { $errfile = "unknown file"; $errstr = "shutdown"; $errno = E_CORE_ERROR; $errline = 0; $error = error_get_last(); if( $error !== NULL) { $errno = $error["type"]; $errfile = $error["file"]; $errline = $error["line"]; $errstr = $error["message"]; error_mail(format_error( $errno, $errstr, $errfile, $errline)); } }
error_mail 및 format_error 함수를 정의해야합니다. 예 :
function format_error( $errno, $errstr, $errfile, $errline ) { $trace = print_r( debug_backtrace( false ), true ); $content = " <table> <thead><th>Item</th><th>Description</th></thead> <tbody> <tr> <th>Error</th> <td><pre>$errstr</pre></td> </tr> <tr> <th>Errno</th> <td><pre>$errno</pre></td> </tr> <tr> <th>File</th> <td>$errfile</td> </tr> <tr> <th>Line</th> <td>$errline</td> </tr> <tr> <th>Trace</th> <td><pre>$trace</pre></td> </tr> </tbody> </table>"; return $content; }
Swift Mailer를 사용하여 error_mail 기능을 작성하십시오.
참조 :
-
==============================
2.
방금이 솔루션을 제안했습니다 (PHP 5.2.0 이상).
function shutDownFunction() { $error = error_get_last(); // fatal error, E_ERROR === 1 if ($error['type'] === E_ERROR) { //do your stuff } } register_shutdown_function('shutDownFunction');
http://www.php.net/manual/en/errorfunc.constants.php에 정의 된 다양한 오류 유형
-
==============================
3.
PHP는 치명적인 오류를 포착하고 복구하기위한 일반적인 방법을 제공하지 않습니다. 이는 치명적인 오류가 발생한 후에 처리가 일반적으로 복구되지 않아야하기 때문입니다. 출력 버퍼와 일치하는 문자열 (PHP.net에서 설명한 기술에 대한 원래 게시물에서 제안한 것처럼)은 분명히 좋지 않습니다. 그것은 단순히 신뢰할 수 없습니다.
오류 핸들러 메서드 내에서 mail () 함수를 호출하면 문제가 될 수도 있습니다. 많은 오류가 발생하면 메일 서버에 작업이로드되고 불쾌한받은 편지함으로 자신을 찾을 수 있습니다. 이를 피하려면 cron을 실행하여 오류 로그를 주기적으로 스캔하고 그에 따라 알림을 보내도록하십시오. 또한 Nagios와 같은 시스템 모니터링 소프트웨어를 살펴볼 수도 있습니다.
종료 기능을 등록하는 방법에 대해 이야기하려면 다음을 수행하십시오.
종료 기능을 등록 할 수 있다는 것은 사실이며, 이는 좋은 대답입니다.
여기에서 중요한 것은 치명적인 오류를 복구하려고 시도해서는 안되며, 특히 출력 버퍼에 대해 정규 표현식을 사용하는 것이 아닙니다. 나는 받아 들여진 대답에 응답했는데, 그 대답은 이후 변경되거나 제거 된 php.net에 대한 제안과 관련이있다.
그 제안은 예외 처리 중에 출력 버퍼에 대한 정규 표현식을 사용하는 것이 었습니다. 치명적인 오류 (예상되는 구성 오류 텍스트와 일치하는 것으로 감지 됨)의 경우 복구 또는 계속 처리를 수행하십시오. 그것은 권장 사례가 아니기 때문에 (나는 그것이 원래의 제안을 찾을 수없는 이유라고 생각한다. 나는 그것을 간과하고있다.
PHP (5.1 버전)의 최신 버전이 출력 버퍼링 콜백이 부기되기 전에 더 일찍 종료 함수를 호출하는 것처럼 보일 수도 있습니다. 버전 5 및 이전 버전에서는 그 순서가 역순이었습니다 (출력 버퍼링 콜백에 shutdown 기능이 뒤 따랐습니다). 또한 5.0.5 (질문자 버전 5.2.3보다 훨씬 빠름)부터 등록 된 시스템 종료 함수가 호출되기 전에 객체가 잘 언로드되므로 인 메모리 객체를 사용하여 수행 할 수 없습니다 무엇보다도.
따라서 셧다운 기능을 등록하는 것은 괜찮지 만, 셧다운 기능에 의해 수행되어야하는 일종의 종류는 아마도 소수의 완만 한 셧다운 절차로 제한 될 것입니다.
여기서 핵심은이 질문에 걸려 넘어지고 원래 받아 들여진 대답에 대한 조언을 보는 사람을위한 지혜의 단어입니다. 출력 버퍼를 정규 표현하지 마십시오.
-
==============================
4.
그럼 치명적인 오류를 잡는 것이 가능해 보입니다 :)
ob_start('fatal_error_handler'); function fatal_error_handler($buffer){ $error=error_get_last(); if($error['type'] == 1){ // type, message, file, line $newBuffer='<html><header><title>Fatal Error </title></header> <style> .error_content{ background: ghostwhite; vertical-align: middle; margin:0 auto; padding:10px; width:50%; } .error_content label{color: red;font-family: Georgia;font-size: 16pt;font-style: italic;} .error_content ul li{ background: none repeat scroll 0 0 FloralWhite; border: 1px solid AliceBlue; display: block; font-family: monospace; padding: 2%; text-align: left; } </style> <body style="text-align: center;"> <div class="error_content"> <label >Fatal Error </label> <ul> <li><b>Line</b> '.$error['line'].'</li> <li><b>Message</b> '.$error['message'].'</li> <li><b>File</b> '.$error['file'].'</li> </ul> <a href="javascript:history.back()"> Back </a> </div> </body></html>'; return $newBuffer; } return $buffer; }
-
==============================
5.
PHP (거의 모든 것)에서 모든 오류 유형을 잡을 수있는 방법을 개발했습니다! 나는 E_CORE_ERROR에 대해 확신하지 못한다. (나는 그 에러에 대해서만 작동하지 않을 것이라고 생각한다)! 하지만 다른 치명적인 오류 (E_ERROR, E_PARSE, E_COMPILE ...)는 하나의 오류 처리기 기능 만 사용하여 올바르게 작동합니다! 내 해결책이 간다.
메인 파일 (index.php)에 다음 코드를 입력하십시오 :
<?php define('E_FATAL', E_ERROR | E_USER_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_RECOVERABLE_ERROR); define('ENV', 'dev'); //Custom error handling vars define('DISPLAY_ERRORS', TRUE); define('ERROR_REPORTING', E_ALL | E_STRICT); define('LOG_ERRORS', TRUE); register_shutdown_function('shut'); set_error_handler('handler'); //Function to catch no user error handler function errors... function shut(){ $error = error_get_last(); if($error && ($error['type'] & E_FATAL)){ handler($error['type'], $error['message'], $error['file'], $error['line']); } } function handler( $errno, $errstr, $errfile, $errline ) { switch ($errno){ case E_ERROR: // 1 // $typestr = 'E_ERROR'; break; case E_WARNING: // 2 // $typestr = 'E_WARNING'; break; case E_PARSE: // 4 // $typestr = 'E_PARSE'; break; case E_NOTICE: // 8 // $typestr = 'E_NOTICE'; break; case E_CORE_ERROR: // 16 // $typestr = 'E_CORE_ERROR'; break; case E_CORE_WARNING: // 32 // $typestr = 'E_CORE_WARNING'; break; case E_COMPILE_ERROR: // 64 // $typestr = 'E_COMPILE_ERROR'; break; case E_CORE_WARNING: // 128 // $typestr = 'E_COMPILE_WARNING'; break; case E_USER_ERROR: // 256 // $typestr = 'E_USER_ERROR'; break; case E_USER_WARNING: // 512 // $typestr = 'E_USER_WARNING'; break; case E_USER_NOTICE: // 1024 // $typestr = 'E_USER_NOTICE'; break; case E_STRICT: // 2048 // $typestr = 'E_STRICT'; break; case E_RECOVERABLE_ERROR: // 4096 // $typestr = 'E_RECOVERABLE_ERROR'; break; case E_DEPRECATED: // 8192 // $typestr = 'E_DEPRECATED'; break; case E_USER_DEPRECATED: // 16384 // $typestr = 'E_USER_DEPRECATED'; break; } $message = '<b>'.$typestr.': </b>'.$errstr.' in <b>'.$errfile.'</b> on line <b>'.$errline.'</b><br/>'; if(($errno & E_FATAL) && ENV === 'production'){ header('Location: 500.html'); header('Status: 500 Internal Server Error'); } if(!($errno & ERROR_REPORTING)) return; if(DISPLAY_ERRORS) printf('%s', $message); //Logging error on php file error log... if(LOG_ERRORS) error_log(strip_tags($message), 0); } ob_start(); @include 'content.php'; ob_end_flush(); ?>
나는 이것이 많은 사람들을 돕기를 바랍니다! 나는이 솔루션을 너무 오랜 시간 동안 찾고 있었고 찾지 못했습니다! 그럼 나는 하나를 개발했다!
-
==============================
6.
치명적인 오류는 catch / handle 할 수 없지만 기록 /보고 할 수는 있습니다. 빠른 디버깅을 위해이 간단한 코드에 대한 하나의 대답을 수정했습니다.
function __fatalHandler() { $error = error_get_last(); //check if it's a core/fatal error, otherwise it's a normal shutdown if ($error !== NULL && in_array($error['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING,E_RECOVERABLE_ERROR))) { echo "<pre>fatal error:\n"; print_r($error); echo "</pre>"; die; } } register_shutdown_function('__fatalHandler');
-
==============================
7.
다음과 같이 등록 된 시스템 종료 기능에 예외를 던질 수 없습니다.
<?php function shutdown() { if (($error = error_get_last())) { ob_clean(); throw new Exception("fatal error"); } } try { $x = null; $x->method() } catch(Exception $e) { # this won't work } ?>
그러나 요청을 캡처하여 다른 페이지로 리디렉션 할 수 있습니다.
<?php function shutdown() { if (($error = error_get_last())) { ob_clean(); # raport the event, send email etc. header("Location: http://localhost/error-capture"); # from /error-capture, you can use another redirect, to e.g. home page } } register_shutdown_function('shutdown'); $x = null; $x->method() ?>
-
==============================
8.
PHP> = 5.1.0을 사용하는 경우 ErrorException 클래스를 사용하면 다음과 같이 할 수 있습니다.
<?php //define an error handler function exception_error_handler($errno, $errstr, $errfile, $errline ) { throw new ErrorException($errstr, $errno, 0, $errfile, $errline); } //set ur error handle set_error_handler("exception_error_handler"); /* Trigger exception */ try { //try to do something like finding the end of the internet } catch(ErrorException $e) { //anything you want to do with $e } ?>
-
==============================
9.
/** * ErrorHandler that can be used to catch internal PHP errors * and convert to an ErrorException instance. */ abstract class ErrorHandler { /** * Active stack * * @var array */ protected static $stack = array(); /** * Check if this error handler is active * * @return bool */ public static function started() { return (bool) static::getNestedLevel(); } /** * Get the current nested level * * @return int */ public static function getNestedLevel() { return count(static::$stack); } /** * Starting the error handler * * @param int $errorLevel */ public static function start($errorLevel = \E_WARNING) { if (!static::$stack) { set_error_handler(array(get_called_class(), 'addError'), $errorLevel); } static::$stack[] = null; } /** * Stopping the error handler * * @param bool $throw Throw the ErrorException if any * @return null|ErrorException * @throws ErrorException If an error has been catched and $throw is true */ public static function stop($throw = false) { $errorException = null; if (static::$stack) { $errorException = array_pop(static::$stack); if (!static::$stack) { restore_error_handler(); } if ($errorException && $throw) { throw $errorException; } } return $errorException; } /** * Stop all active handler * * @return void */ public static function clean() { if (static::$stack) { restore_error_handler(); } static::$stack = array(); } /** * Add an error to the stack * * @param int $errno * @param string $errstr * @param string $errfile * @param int $errline * @return void */ public static function addError($errno, $errstr = '', $errfile = '', $errline = 0) { $stack = & static::$stack[count(static::$stack) - 1]; $stack = new ErrorException($errstr, 0, $errno, $errfile, $errline, $stack); } }
이 클래스를 사용하면 필요할 때마다 특정 ErrorHandler를 시작할 수 있습니다. 그리고 핸들러를 멈출 수 있습니다.
예 :이 클래스 사용 이렇게 :
ErrorHandler::start(E_WARNING); $return = call_function_raises_E_WARNING(); if ($innerException = ErrorHandler::stop()) { throw new Exception('Special Exception Text', 0, $innerException); } // or ErrorHandler::stop(true); // directly throws an Exception;
전체 클래스 코드 링크 : https://github.com/zendframework/zf2/blob/master/library/Zend/Stdlib/ErrorHandler.php
아마 더 나은 해결책은 Monolog에서 나온 것입니다. 전체 클래스 코드 링크 : https://github.com/Seldaek/monolog/blob/master/src/Monolog/ErrorHandler.php
register_shutdown_function 함수를 사용하여 FATAL_ERRORS를 처리 할 수도 있습니다. 이 클래스에 따르면 FATAL_ERROR는 다음 배열 중 하나입니다 (E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR).
class ErrorHandler { // [...] public function registerExceptionHandler($level = null, $callPrevious = true) { $prev = set_exception_handler(array($this, 'handleException')); $this->uncaughtExceptionLevel = $level; if ($callPrevious && $prev) { $this->previousExceptionHandler = $prev; } } public function registerErrorHandler(array $levelMap = array(), $callPrevious = true, $errorTypes = -1) { $prev = set_error_handler(array($this, 'handleError'), $errorTypes); $this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap); if ($callPrevious) { $this->previousErrorHandler = $prev ?: true; } } public function registerFatalHandler($level = null, $reservedMemorySize = 20) { register_shutdown_function(array($this, 'handleFatalError')); $this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize); $this->fatalLevel = $level; } // [...] }
-
==============================
10.
대신 프로덕션에서 치명적 오류를 처리하여 정적 스타일의 503 Service Unavailable HTML 출력을 표시해야합니다. 이것은 분명히 "치명적 오류를 잡는"합리적인 접근법입니다. 이것이 내가 한 일이다.
나는 "E_ERROR, E_USER_ERROR 등등"에 내 "503 서비스를 사용할 수 없음"HTML 페이지를 표시 할 함수 "error_handler"를 처리하는 사용자 정의 오류가 있습니다. 이것은 이제 치명적인 오류를 포착하는 종료 기능에서 호출됩니다.
function fatal_error_handler() { if (@is_array($e = @error_get_last())) { $code = isset($e['type']) ? $e['type'] : 0; $msg = isset($e['message']) ? $e['message'] : ''; $file = isset($e['file']) ? $e['file'] : ''; $line = isset($e['line']) ? $e['line'] : ''; if ($code>0) error_handler($code,$msg,$file,$line); } } set_error_handler("error_handler"); register_shutdown_function('fatal_error_handler');
내 사용자 지정 error_handler 함수에서 오류가 E_ERROR 또는 E_USER_ERROR이면 등등. 또한 @ob_end_clean () 호출합니다. 버퍼를 비우고 PHP의 "치명적인 오류"메시지를 제거합니다.
error_handler 스크립트가 오류를 발생시키지 않기 때문에 엄격한 isset () 검사와 @ silencing 함수를주의 깊게 읽으십시오.
치명적인 오류를 잡는 것은 keparo에 동의하면서 "치명적인 오류"의 목적을 무효화하므로 더 이상 처리 할 수 없습니다. 메일 서버 나받은 편지함을 확실히 백업하므로이 종료 프로세스에서 mail () 함수를 실행하지 마십시오. 이러한 오류를 파일에 기록하고 cron을 예약하여 이러한 error.log 파일을 찾아 관리자에게 우편으로 보냅니다.
-
==============================
11.
PHP는 catchable 치명적인 오류가 있습니다. 이들은 E_RECOVERABLE_ERROR로 정의됩니다. PHP 매뉴얼은 E_RECOVERABLE_ERROR를 다음과 같이 설명합니다 :
set_error_handler ()를 사용하고 E_RECOVERABLE_ERROR를 검사하여 이러한 "치명적인"오류를 "catch"할 수 있습니다. 이 오류가 잡히면 Exception을 throw하는 것이 유용하다는 것을 알게되면 try / catch를 사용할 수 있습니다.
이 질문과 대답은 유용한 예를 제공합니다 : PHP 타입 힌팅에서 "catchable fatal error"를 어떻게 잡을 수 있습니까?
그러나 E_ERROR 오류는 엔진을 불안정한 상태로 처리 할 수는 있지만 복구 할 수는 없습니다.
-
==============================
12.
현재의 error_handler 메소드를 얻는 멋진 트릭 =)
<?php register_shutdown_function('__fatalHandler'); function __fatalHandler() { $error = error_get_last(); //check if it's a core/fatal error, otherwise it's a normal shutdown if($error !== NULL && $error['type'] === E_ERROR) { //Bit hackish, but the set_exception_handler will return the old handler function fakeHandler() { } $handler = set_exception_handler('fakeHandler'); restore_exception_handler(); if($handler !== null) { call_user_func($handler, new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line'])); } exit; } } ?>
또한 나는 당신이 전화하는 경우에주의하는 wan't
<?php ini_set('display_errors', false); ?>
PHP는 오류 표시를 중지합니다. 그렇지 않으면 오류 텍스트가 오류 핸들러보다 먼저 클라이언트로 전송됩니다.
-
==============================
13.
여기에 대부분의 대답은 불필요하게 장황하기 때문에, 여기에 내가 뽑은 답이 가장 좋지 않은 버전이있다.
function errorHandler($errno, $errstr, $errfile = '', $errline = 0, $errcontext = array()) { //Do stuff: mail, log, etc } function fatalHandler() { $error = error_get_last(); if($error) errorHandler($error["type"], $error["message"], $error["file"], $error["line"]); } set_error_handler("errorHandler") register_shutdown_function("fatalHandler");
-
==============================
14.
그렇지 않아. 치명적인 오류는 치명적인 오류라고합니다. 당신은 그들로부터 복구 할 수 없습니다.
-
==============================
15.
치명적인 오류라도 잡아야하는 특정 상황이 있습니다 (정상적으로 종료되기 전에 정리를해야 할 수도 있습니다.). 전자 메일을 통해 치명적인 오류를 얻을 수 있도록 codeigniter 응용 프로그램에 pre_system 후크를 구현했습니다. 이로 인해보고되지 않은 버그를 발견하는 데 도움이되었습니다 (또는 이미 수정 된 것으로보고 됨). Sendemail은 이미 알려진 오류가 발생하여 여러 번 스팸하지 않도록 오류가 이미보고되었는지 확인합니다.
class PHPFatalError { public function setHandler() { register_shutdown_function('handleShutdown'); } } function handleShutdown() { if (($error = error_get_last())) { ob_start(); echo "<pre>"; var_dump($error); echo "</pre>"; $message = ob_get_clean(); sendEmail($message); ob_start(); echo '{"status":"error","message":"Internal application error!"}'; ob_flush(); exit(); } }
-
==============================
16.
치명적인 오류나 회복 가능한 치명적인 오류는 PHP 7 이상의 버전에서 Error 인스턴스를 던졌습니다. 다른 예외와 마찬가지로 try / catch 블록을 사용하여 Error 객체를 포착 할 수 있습니다.
예:
<?php $variable = 'not an object'; try { $variable->method(); // Throws an Error object in PHP 7 or higger. } catch (Error $e) { // Handle error echo $e->getMessage(); // Call to a member function method() on string }
https://3v4l.org/67vbk
또는 Throwable 인터페이스를 사용하여 모든 예외를 catch 할 수 있습니다.
예:
<?php try { undefinedFunctionCall(); } catch (Throwable $e) { // Handle error echo $e->getMessage(); // Call to undefined function undefinedFunctionCall() }
https://3v4l.org/Br0MG
자세한 정보 : http://php.net/manual/tr/language.errors.php7.php
-
==============================
17.
치명적인 오류를 일으킬 수있는 "샌드 박스"코드를 만들 수 있도록이 함수를 개발했습니다. closure register_shutdown_function에서 던져진 예외는 pre-fatal error 호출 스택에서 방출되지 않기 때문에,이 함수 다음에 끝내야 만 일관된 사용 방법을 제공 할 수 있습니다.
function superTryCatchFinallyAndExit( Closure $try, Closure $catch = NULL, Closure $finally ) { $finished = FALSE; register_shutdown_function( function() use ( &$finished, $catch, $finally ) { if( ! $finished ) { $finished = TRUE; print "EXPLODE!".PHP_EOL; if( $catch ) { superTryCatchFinallyAndExit( function() use ( $catch ) { $catch( new Exception( "Fatal Error!!!" ) ); }, NULL, $finally ); } else { $finally(); } } } ); try { $try(); } catch( Exception $e ) { if( $catch ) { try { $catch( $e ); } catch( Exception $e ) {} } } $finished = TRUE; $finally(); exit(); }
-
==============================
18.
나는 PHP에서 모든 오류를 잡기위한 완벽한 솔루션으로 Wiki 스타일의 Q & A를 썼다. 여기서 볼 수 / 수집 / 도난 당하거나 비평받을 수 있습니다.
이 솔루션에는 PHP가 생성 할 수있는 모든 오류를 래핑하는 5 가지 방법이 포함되어 있으며, 결국 오류를 'ErrorHandler'유형의 객체로 전달합니다.
일부 사람들은 그걸 사용 해줄 수 있기를 바랍니다. 비록 당신이 그것을 철저히 훔치지 않는다 할지라도, 나는 해결책이 모든면에서 PHP에서 오류를 다루는 방법의 좋은 예라고 확신합니다.
@Lucas Batistussi는 독창성에 대한 포인트를 얻었습니다. 제 솔루션을 공유하고 비슷한 점을 찍을 수 있다고 생각했습니다.
from https://stackoverflow.com/questions/277224/how-do-i-catch-a-php-fatal-error by cc-by-sa and MIT lisence
'PHP' 카테고리의 다른 글
엄격한 표준 : 변수 만 참조로 전달해야합니다. (0) | 2018.09.02 |
---|---|
자바 스크립트에서 PHP 함수 호출하기 (0) | 2018.09.02 |
문자열에 특정 단어가 포함되어 있는지 어떻게 확인합니까? (0) | 2018.09.02 |
정의되지 않은 메소드 호출 mysqli_stmt :: get_result (0) | 2018.09.02 |
mysql_fetch_array () / mysql_fetch_assoc () / mysql_fetch_row () / mysql_num_rows 등 ... 매개 변수 1이 리소스가 될 것으로 예상 함 (0) | 2018.09.02 |