作为程序员一定要保持良好的睡眠,才能好编程

php全局异常捕获错误处理机制

发布时间:2019-05-22


set_error_handler

error_types 可选择参数

就像error_reporting 的 ini 设置能够控制错误的显示一样, 此参数能够用于屏蔽 error_handler 的触发。 

如果没有该掩码, 无论 error_reporting 是如何设置的, error_handler 都会在每个错误发生时被调用。


error_handler 并不是可以拦截所有的错误,比如下面的几种错误是系统级别的,用户自定义方法拦截不到。

以下级别的错误不能由用户定义的函数来处理: E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,和在 调用 set_error_handler() 函数所在文件中产生的大多数 E_STRICT。


set_error_handler( function ($severity, $message, $file, $line) use($path){
     
	// $log=[];
	// $log[]=$severity;
	// $log[]=$message;
	// $log[]=$file;
	// $log[]=$line;
	// // file_put_contents($path.'/log.txt', implode("\n",$log),FILE_APPEND);
	// file_put_contents($path.'/log.txt', implode("\n",$log)."\n",FILE_APPEND);
 
    throw new ErrorException($message, 0, $severity, $file, $line);
    
});



如果之前有定义过错误处理程序,则返回该程序名称的 string;

如果是内置的错误处理程序,则返回 NULL。 

如果你指定了一个无效的回调函数,同样会返回 NULL。 


如果之前的错误处理程序是一个类的方法,此函数会返回一个带类和方法名的索引数组(indexed array)。


同时注意,在需要时你有责任使用 die()。 如果错误处理程序返回了,脚本将会继续执行发生错误的后一行。


private $levels = array(
    E_DEPRECATED => 'Deprecated',
    E_USER_DEPRECATED => 'User Deprecated',
    E_NOTICE => 'Notice',
    E_USER_NOTICE => 'User Notice',
    E_STRICT => 'Runtime Notice',
    E_WARNING => 'Warning',
    E_USER_WARNING => 'User Warning',
    E_COMPILE_WARNING => 'Compile Warning',
    E_CORE_WARNING => 'Core Warning',
    E_USER_ERROR => 'User Error',
    E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
    E_COMPILE_ERROR => 'Compile Error',
    E_PARSE => 'Parse Error',
    E_ERROR => 'Error',
    E_CORE_ERROR => 'Core Error',
);

private $loggers = array(
    E_DEPRECATED => array(null, LogLevel::INFO),
    E_USER_DEPRECATED => array(null, LogLevel::INFO),
    E_NOTICE => array(null, LogLevel::WARNING),
    E_USER_NOTICE => array(null, LogLevel::WARNING),
    E_STRICT => array(null, LogLevel::WARNING),
    E_WARNING => array(null, LogLevel::WARNING),
    E_USER_WARNING => array(null, LogLevel::WARNING),
    E_COMPILE_WARNING => array(null, LogLevel::WARNING),
    E_CORE_WARNING => array(null, LogLevel::WARNING),
    E_USER_ERROR => array(null, LogLevel::CRITICAL),
    E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL),
    E_COMPILE_ERROR => array(null, LogLevel::CRITICAL),
    E_PARSE => array(null, LogLevel::CRITICAL),
    E_ERROR => array(null, LogLevel::CRITICAL),
    E_CORE_ERROR => array(null, LogLevel::CRITICAL),
);


有第二个参数

 可选择参数

就像error_reporting 的 ini 设置能够控制错误的显示一样, 此参数能够用于屏蔽 error_handler 的触发。 

如果没有该掩码, 无论 error_reporting 是如何设置的, error_handler 都会在每个错误发生时被调用。


下面实例,我要过滤掉notice的错误。


set_error_handler( function ($severity, $message, $file, $line){
    throw new ErrorException($message, 0, $severity, $file, $line);    
},E_NOTICE);


切记一点:

使用了用户自定义 错误 set_error_handler 函数后,  error_get_last() 函数 将获取不到错误信息,

如果还想后续使用error_get_last() 获取错误,需要 在 set_error_handler 最后 强制  使用 return false


 

用户自定义错误示例:

set_error_handler(function ($severity, $message, $file, $line) use($path){

	$errorType = [
            1     => 'E_ERROR',
            2     => 'E_WARNING',
            4     => 'E_PARSE',
            8     => 'E_NOTICE',
            16    => 'E_CORE_ERROR',
            32    => 'E_CORE_WARNING',
            64    => 'E_COMPILE_ERROR',
            128   => 'E_COMPILE_WARNING',
            256   => 'E_USER_ERROR',
            512   => 'E_USER_WARNING',
            1024  => 'E_USER_NOTICE',
            2048  => 'E_STRICT',
            4096  => 'E_RECOVERABLE_ERROR',
            8192  => 'E_DEPRECATED',
            16384 => 'E_USER_DEPRECATED',
        ];

        $errorMsg  = [
            'type'    => $severity,
            'level'   => $errorType[$severity] ?? 'Unknown Error',
            'file'    => $file,
            'line'    => $line,
            'message' => $message,
        ];

        $errorMsg[]='当前error_reporting的值'.error_reporting();
       file_put_contents($path.'/testerror.log',date('Y-m-d H:i:s')."\n".var_export($errorMsg,true)."\n",FILE_APPEND);

       return false;

});




set_exception_handler

function showExceptionInfoWithHtml(\Throwable $e): void
{
    $errorType   = get_class($e);
    $errorMsg    = $e->getMessage();
    $code        = $e->getCode();
    $file        = $e->getFile();
    $line        = $e->getLine();
    $traceString = $e->getTraceAsString();
    $traceString = explode('#', $traceString);
    $traceString = implode('<br />#', $traceString);
    $traceString = substr($traceString, 6);

    $htmlTemplate = '<div style="border:1px dodgerblue;font-size:18px;line-height:2.0em;padding:10px;">' . PHP_EOL;
    $htmlTemplate .= '<div><i>Error Type:</i>&nbsp;&nbsp;<b>%s</b></div>' . PHP_EOL;
    $htmlTemplate .= '<div><i>Error Message:</i>&nbsp;&nbsp;<b>%s</b></div>' . PHP_EOL;
    $htmlTemplate .= '<div><i>Error Code:</i>&nbsp;&nbsp;<b style="color:red;">%d</b></div>' . PHP_EOL;
    $htmlTemplate .= '<div><i>File:</i>&nbsp;&nbsp;<b>%s</b>,&nbsp;on&nbsp;line <b style="color:red;">&nbsp;%s</b>.</div>' . PHP_EOL;
    $htmlTemplate .= '<div><div style="margin-top:20px;"><i><b>Stack Trace:</b></i></div>%s</div></div>' . PHP_EOL;
    printf($htmlTemplate, $errorType, $errorMsg, $code, $file, $line, $traceString);
}


set_exception_handler 执行顺序

<?php

function log_exception( Exception $e )
{
    
    try{
    	print_r('后执行...');
    	throw $e;
    }catch(LoadException $e){
		print_r('这是load异常...');
    }catch(SdkException $e){
    	print_r('这是sdk异常...');
    }finally{
    	$message = "Type: " . get_class( $e ) . "; Message: {$e->getMessage()}; File: {$e->getFile()}; Line: {$e->getLine()};";
        file_put_contents( __DIR__ . "/exceptions.log", $message . PHP_EOL, FILE_APPEND );
    }
}

class LoadException extends RuntimeException
{
}
class SdkException extends RuntimeException
{
} 

set_exception_handler( "log_exception" );

try{

	// throw new SdkException('sss');
	throw new mysqli_sql_exception('mysql');
}catch(mysqli_sql_exception $e){
	print_r('mysql错误,有可能没有数据没有插入成功...');
}catch(Exception $e){
	print_r('先执行...');
	throw $e;
}


执行结果:

先执行...后执行...这是sdk异常...


另外说明 Exception  是不能捕获到 mysqli_sql_exception 异常的。






register_shutdown_function



register_shutdown_function(

function () use ($path){

    $endMicroTime = microtime(true);
    $requestInfo  = [
        'project'    => '',
        'uri'        => $_SERVER['REQUEST_URI'],
        'start-time' => date('Y-m-d H:i:s', (int)REQUEST_TIME_START),
        'end-time'   => date('Y-m-d H:i:s', (int)$endMicroTime),
        'run-time'   => (($endMicroTime - REQUEST_TIME_START) * 1000) . 'ms'
    ];

	file_put_contents($path.'/log.txt',var_export($requestInfo,true)."\n",FILE_APPEND);
	 
    // 最后一次错误处理
    if (! empty($errorInfo = error_get_last())) {
        $errorType = [
            1     => 'E_ERROR',
            2     => 'E_WARNING',
            4     => 'E_PARSE',
            8     => 'E_NOTICE',
            16    => 'E_CORE_ERROR',
            32    => 'E_CORE_WARNING',
            64    => 'E_COMPILE_ERROR',
            128   => 'E_COMPILE_WARNING',
            256   => 'E_USER_ERROR',
            512   => 'E_USER_WARNING',
            1024  => 'E_USER_NOTICE',
            2048  => 'E_STRICT',
            4096  => 'E_RECOVERABLE_ERROR',
            8192  => 'E_DEPRECATED',
            16384 => 'E_USER_DEPRECATED',
        ];
         file_put_contents($path.'/log.txt',var_export($errorInfo,true)."\n",FILE_APPEND);
        $errorMsg  = [
            'type'    => $errorInfo['type'],
            'level'   => $errorType[$errorInfo['type']] ?? 'Unknown Error',
            'file'    => $errorInfo['file'],
            'line'    => $errorInfo['line'],
            'message' => $errorInfo['message'],
        ];
       file_put_contents($path.'/log.txt',var_export($errorMsg,true)."\n",FILE_APPEND);
    }

    
});




整体代码

<?php

define('REQUEST_TIME_START',$_SERVER['REQUEST_TIME_FLOAT']);


error_reporting(-1);
ini_set('display_errors',1);

$path=__DIR__;


set_error_handler( function ($severity, $message, $file, $line) use($path){
     
	// $log=[];
	// $log[]=$severity;
	// $log[]=$message;
	// $log[]=$file;
	// $log[]=$line;
	// // file_put_contents($path.'/log.txt', implode("\n",$log),FILE_APPEND);
	// file_put_contents($path.'/log.txt', implode("\n",$log)."\n",FILE_APPEND);
 
    throw new ErrorException($message, 0, $severity, $file, $line);
    
});

register_shutdown_function(

function () use ($path){




    $endMicroTime = microtime(true);
    $requestInfo  = [
        'project'    => '',
        'uri'        => $_SERVER['REQUEST_URI'],
        'start-time' => date('Y-m-d H:i:s', (int)REQUEST_TIME_START),
        'end-time'   => date('Y-m-d H:i:s', (int)$endMicroTime),
        'run-time'   => (($endMicroTime - REQUEST_TIME_START) * 1000) . 'ms'
    ];

		file_put_contents($path.'/log.txt',var_export($requestInfo,true)."\n",FILE_APPEND);
		// $backtrace=debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT);
		// var_dump($backtrace);
		  var_dump($bbbb);
    // 最后一次错误处理
    if (! empty($errorInfo = error_get_last())) {
        $errorType = [
            1     => 'E_ERROR',
            2     => 'E_WARNING',
            4     => 'E_PARSE',
            8     => 'E_NOTICE',
            16    => 'E_CORE_ERROR',
            32    => 'E_CORE_WARNING',
            64    => 'E_COMPILE_ERROR',
            128   => 'E_COMPILE_WARNING',
            256   => 'E_USER_ERROR',
            512   => 'E_USER_WARNING',
            1024  => 'E_USER_NOTICE',
            2048  => 'E_STRICT',
            4096  => 'E_RECOVERABLE_ERROR',
            8192  => 'E_DEPRECATED',
            16384 => 'E_USER_DEPRECATED',
        ];
         file_put_contents($path.'/log.txt',var_export($errorInfo,true)."\n",FILE_APPEND);
        $errorMsg  = [
            'type'    => $errorInfo['type'],
            'level'   => $errorType[$errorInfo['type']] ?? 'Unknown Error',
            'file'    => $errorInfo['file'],
            'line'    => $errorInfo['line'],
            'message' => $errorInfo['message'],
        ];
       file_put_contents($path.'/log.txt',var_export($errorMsg,true)."\n",FILE_APPEND);
    }
});


try{

	echo 111;
	function a (){
		b();
	}
	function b($name){
		 
	}

	function c(){
		a();
	}


	c();

	//throw new ErrorException('cuole ');

}catch(Exception $e){
	//print_r(debug_backtrace());
	 
	// print_r($e);
	 showExceptionInfoWithHtml($e);
}

function showExceptionInfoWithHtml(\Throwable $e)
{
    $errorType   = get_class($e);
    $errorMsg    = $e->getMessage();
    $code        = $e->getCode();
    $file        = $e->getFile();
    $line        = $e->getLine();
    $traceString = $e->getTraceAsString();
    $traceString = explode('#', $traceString);
    $traceString = implode('<br />#', $traceString);
    $traceString = substr($traceString, 6);

    $htmlTemplate = '<div style="border:1px dodgerblue;font-size:18px;line-height:2.0em;padding:10px;">' . PHP_EOL;
    $htmlTemplate .= '<div><i>Error Type:</i>&nbsp;&nbsp;<b>%s</b></div>' . PHP_EOL;
    $htmlTemplate .= '<div><i>Error Message:</i>&nbsp;&nbsp;<b>%s</b></div>' . PHP_EOL;
    $htmlTemplate .= '<div><i>Error Code:</i>&nbsp;&nbsp;<b style="color:red;">%d</b></div>' . PHP_EOL;
    $htmlTemplate .= '<div><i>File:</i>&nbsp;&nbsp;<b>%s</b>,&nbsp;on&nbsp;line <b style="color:red;">&nbsp;%s</b>.</div>' . PHP_EOL;
    $htmlTemplate .= '<div><div style="margin-top:20px;"><i><b>Stack Trace:</b></i></div>%s</div></div>' . PHP_EOL;
    printf($htmlTemplate, $errorType, $errorMsg, $code, $file, $line, $traceString);
}