set_error_handler()
대부분의 PHP 오류를 잡는 데 사용할 수 있지만 E_ERROR
존재하지 않는 함수 호출과 같은 치명적인 ( ) 오류에는 작동하지 않습니다. 이러한 오류를 잡는 다른 방법이 있습니까?
나는 호출에 노력하고 mail()
모든 오류 및 PHP 5.2.3을 실행하고 있습니다.
set_error_handler()
대부분의 PHP 오류를 잡는 데 사용할 수 있지만 E_ERROR
존재하지 않는 함수 호출과 같은 치명적인 ( ) 오류에는 작동하지 않습니다. 이러한 오류를 잡는 다른 방법이 있습니까?
나는 호출에 노력하고 mail()
모든 오류 및 PHP 5.2.3을 실행하고 있습니다.
답변:
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
함수 를 작성하십시오 .
또한보십시오:
mail("myname@myemail.com", "My Site: FATAL ERROR", "Details: " . $errno . ' ' . $errstr . ' ' . $errfile . ' ' . $errline);
방금이 솔루션 (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');
미리 정의 된 상수에 다른 오류 유형이 정의되어 있습니다.
If an error handler (see set_error_handler ) successfully handles an error then that error will not be reported by this function.
"
register_shutdown_function()
가 치명적인 오류보다 빠르다 는 것이 분명 합니다. use_1T_memory(); /* memory exhausted error here! */ register_shutdown_function('shutDownFunction');
예상대로 작동하지 않습니다.
PHP는 치명적인 오류를 포착하고 복구하는 일반적인 방법을 제공하지 않습니다. 치명적인 오류 후에 처리를 복구하지 않아야하기 때문입니다. 출력 버퍼와 일치하는 문자열 (PHP.net에 기술 된 원본 게시물에서 제안한 바와 같이)은 잘못 권장됩니다. 단순히 신뢰할 수 없습니다.
에러 핸들러 메소드 내에서 mail () 함수를 호출하는 것도 문제가있는 것으로 판명되었습니다. 많은 오류가있는 경우 메일 서버에 작업이로드되고 불완전한받은 편지함을 찾을 수 있습니다. 이를 피하기 위해 cron을 실행하여 주기적으로 오류 로그를 스캔하고 그에 따라 알림을 보내는 것을 고려할 수 있습니다. Nagios 와 같은 시스템 모니터링 소프트웨어를 살펴볼 수도 있습니다 .
종료 기능을 등록하는 방법에 대해 설명하려면 :
셧다운 기능을 등록 할 수 있다는 것은 사실이며, 정답입니다.
여기서 중요한 점은 일반적으로 출력 버퍼에 정규식을 사용하여 치명적인 오류를 복구하려고 시도하지 않아야한다는 것입니다. 나는 php.net에 대한 제안과 연결되거나 변경 된 수락 된 답변 에 응답하고있었습니다 .
그 제안은 예외 처리 중에 출력 버퍼에 대해 정규 표현식을 사용하는 것이며 치명적인 오류 (예상치 않은 구성 오류 텍스트와 일치하는 것으로 감지)의 경우 일종의 복구 또는 지속적인 처리를 시도하십시오. 그것은 권장되는 관행이 아닐 것입니다 (저는 원래 제안도 찾을 수없는 이유라고 생각합니다. 간과하거나 PHP 커뮤니티가 제안하지 않았습니다).
출력 버퍼링 콜백이 호출되기 전에 최신 버전의 PHP (약 5.1)가 셧다운 기능을 더 일찍 호출하는 것 같습니다. 버전 5 이하에서는 그 순서가 역순이었습니다 (출력 버퍼링 콜백에 종료 기능이 뒤 따름). 또한 약 5.0.5 (질문의 버전 5.2.3보다 훨씬 빠름) 이후 등록 된 종료 기능이 호출되기 전에 객체가 언로드되므로 메모리 내 객체를 사용하여 수행 할 수 없습니다. 많은 것.
따라서 셧다운 기능을 등록하는 것은 좋지만 셧다운 기능으로 수행해야하는 작업 종류는 아마도 몇 가지 부드러운 셧다운 절차로 제한 될 수 있습니다.
여기서 중요한 것은이 질문을 우연히 발견하고 원래 받아 들여진 대답의 조언을 보는 사람에게는 지혜로운 말입니다. 출력 버퍼를 정규식으로 변환하지 마십시오.
글쎄, 다른 방법으로 치명적인 오류를 잡을 수있는 것 같습니다 :)
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;
}
치명적 오류 또는 복구 가능한 치명적 오류는 이제 PHP 7 이상의 버전Error
에서 인스턴스를 발생시킵니다 . 다른 예외와 마찬가지로 블록을 사용하여 객체를 잡을 수 있습니다 .Error
try/catch
예:
<?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
}
또는 Throwable
인터페이스를 사용 하여 모든 예외를 포착 할 수 있습니다 .
예:
<?php
try {
undefinedFunctionCall();
} catch (Throwable $e) {
// Handle error
echo $e->getMessage(); // Call to undefined function undefinedFunctionCall()
}
Fatal error: Trait 'FailedTrait' not found in
때 와 같은 오류를 잡기 위해 이것을 사용하는 방법에 대한 어떤 아이디어 ReflectionClass
?
include "filename.php"
대신 try
블록 으로 감싸고 블록을 Throwable
잡으십시오 ParseError
.
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();
?>
치명적인 오류는 포착 / 처리 할 수 없지만 기록 /보고 할 수는 있습니다. 빠른 디버깅을 위해이 간단한 코드에 대한 하나의 답변을 수정했습니다.
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');
다음과 같이 등록 된 종료 기능 내에서 예외를 throw 할 수 없습니다.
<?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();
# Report the event, send email, etc.
header("Location: http://localhost/error-capture");
# From /error-capture. You can use another
# redirect, to e.g. the home page
}
}
register_shutdown_function('shutdown');
$x = null;
$x->method()
?>
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 your error handler
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
}
?>
/**
* 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
register_shutdown_function
함수를 사용하여 FATAL_ERRORS를 처리 할 수도 있습니다 . 이 클래스에 따르면 FATAL_ERROR는 다음 중 하나입니다 array(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;
}
// [...]
}
정적 스타일의 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 () 확인 및 침묵 함수에 유의하십시오 .
여전히 keparo에 동의하면서 치명적 오류를 발견하면 "FATAL 오류"의 목적을 무효화하므로 추가 처리를 수행 할 의도는 없습니다. 메일 서버 또는받은 편지함을 확실히 백업하므로이 종료 프로세스에서 mail () 함수를 실행하지 마십시오. 대신 이러한 발생을 파일에 기록하고 cron 작업을 예약하여 이러한 error.log 파일 을 찾아서 관리자에게 우편으로 보내십시오.
PHP에는 치명적인 오류가 있습니다. 그것들은 E_RECOVERABLE_ERROR로 정의됩니다. PHP 매뉴얼은 E_RECOVERABLE_ERROR를 다음과 같이 설명합니다.
잡을 수있는 치명적인 오류. 위험한 오류가 발생했지만 엔진을 불안정한 상태로 두지 않았습니다. 사용자 정의 핸들에 의해 오류가 발견되지 않으면 ( set_error_handler () 참조 ) 응용 프로그램은 E_ERROR이므로 중단됩니다.
set_error_handler ()를 사용 하고 E_RECOVERABLE_ERROR를 확인 하여 이러한 "치명적인"오류를 "잡을"수 있습니다 . 이 오류가 발생했을 때 예외를 발생시키는 것이 유용하다는 것을 알았습니다. 그러면 try / catch를 사용할 수 있습니다.
이 질문과 답변은 유용한 예를 제공합니다 . PHP 유형 힌트에서 "catchable fatal error"를 어떻게 잡을 수 있습니까?
그러나 E_ERROR 오류는 처리 할 수 있지만 엔진이 불안정한 상태에서는 복구 할 수 없습니다.
여기에 현재 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) {
// It is a 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;
}
}
?>
또한 전화하면
<?php
ini_set('display_errors', false);
?>
PHP는 오류를 표시하지 않습니다. 그렇지 않으면 오류 처리기 전에 오류 텍스트가 클라이언트로 전송됩니다.
여기에있는 대부분의 답변은 불필요하게 장황하기 때문에 다음은 가장 투표가 많은 답변입니다.
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");
치명적인 오류를 일으킬 수있는 코드를 "샌드 박스"할 수 있도록이 기능을 개발했습니다. 클로저에서 발생하는 예외 register_shutdown_function
는 치명적 오류 호출 스택에서 발생하지 않기 때문에이 기능을 사용하면 균일 한 사용 방법을 제공하기 위해 종료해야합니다.
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();
}
치명적인 오류조차도 포착해야하는 특정 상황이 있습니다 (정상적으로 종료하기 전에 약간의 정리를 수행해야하며 죽지 않아야합니다.).
전자 메일을 통해 치명적인 오류를 얻을 수 있도록 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();
}
}