2013-03-20 17:09:46 +00:00
< ? php
/*
* This file is part of the Symfony package .
*
* ( c ) Fabien Potencier < fabien @ symfony . com >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Symfony\Component\Debug ;
use Psr\Log\LoggerInterface ;
2013-05-28 18:12:41 +01:00
use Symfony\Component\Debug\Exception\ContextErrorException ;
use Symfony\Component\Debug\Exception\FatalErrorException ;
2013-07-24 05:51:29 +01:00
use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler ;
use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler ;
2013-03-20 17:09:46 +00:00
/**
* ErrorHandler .
*
* @ author Fabien Potencier < fabien @ symfony . com >
2013-04-10 06:14:53 +01:00
* @ author Konstantin Myakshin < koc - dp @ yandex . ru >
2013-03-20 17:09:46 +00:00
*/
class ErrorHandler
{
const TYPE_DEPRECATION = - 100 ;
private $levels = array (
E_WARNING => 'Warning' ,
E_NOTICE => 'Notice' ,
E_USER_ERROR => 'User Error' ,
E_USER_WARNING => 'User Warning' ,
E_USER_NOTICE => 'User Notice' ,
E_STRICT => 'Runtime Notice' ,
E_RECOVERABLE_ERROR => 'Catchable Fatal Error' ,
E_DEPRECATED => 'Deprecated' ,
E_USER_DEPRECATED => 'User Deprecated' ,
E_ERROR => 'Error' ,
E_CORE_ERROR => 'Core Error' ,
E_COMPILE_ERROR => 'Compile Error' ,
E_PARSE => 'Parse' ,
);
private $level ;
private $reservedMemory ;
2013-04-10 06:14:53 +01:00
private $displayErrors ;
/**
* @ var LoggerInterface [] Loggers for channels
*/
private static $loggers = array ();
2013-03-20 17:09:46 +00:00
/**
2013-03-21 07:33:19 +00:00
* Registers the error handler .
2013-03-20 17:09:46 +00:00
*
* @ param integer $level The level at which the conversion to Exception is done ( null to use the error_reporting () value and 0 to disable )
2013-04-10 06:14:53 +01:00
* @ param Boolean $displayErrors Display errors ( for dev environment ) or just log they ( production usage )
2013-03-20 17:09:46 +00:00
*
* @ return The registered error handler
*/
2013-04-10 06:14:53 +01:00
public static function register ( $level = null , $displayErrors = true )
2013-03-20 17:09:46 +00:00
{
$handler = new static ();
$handler -> setLevel ( $level );
2013-04-10 06:14:53 +01:00
$handler -> setDisplayErrors ( $displayErrors );
2013-03-20 17:09:46 +00:00
ini_set ( 'display_errors' , 0 );
set_error_handler ( array ( $handler , 'handle' ));
register_shutdown_function ( array ( $handler , 'handleFatal' ));
$handler -> reservedMemory = str_repeat ( 'x' , 10240 );
return $handler ;
}
public function setLevel ( $level )
{
$this -> level = null === $level ? error_reporting () : $level ;
}
2013-04-10 06:14:53 +01:00
public function setDisplayErrors ( $displayErrors )
{
$this -> displayErrors = $displayErrors ;
}
public static function setLogger ( LoggerInterface $logger , $channel = 'deprecation' )
2013-03-20 17:09:46 +00:00
{
2013-04-10 06:14:53 +01:00
self :: $loggers [ $channel ] = $logger ;
2013-03-20 17:09:46 +00:00
}
/**
2013-05-31 13:57:00 +01:00
* @ throws ContextErrorException When error_reporting returns error
2013-03-20 17:09:46 +00:00
*/
2013-05-31 13:57:00 +01:00
public function handle ( $level , $message , $file = 'unknown' , $line = 0 , $context = array ())
2013-03-20 17:09:46 +00:00
{
if ( 0 === $this -> level ) {
return false ;
}
if ( $level & ( E_USER_DEPRECATED | E_DEPRECATED )) {
2013-04-10 06:14:53 +01:00
if ( isset ( self :: $loggers [ 'deprecation' ])) {
2013-04-07 21:25:23 +01:00
if ( version_compare ( PHP_VERSION , '5.4' , '<' )) {
$stack = array_map (
function ( $row ) {
unset ( $row [ 'args' ]);
2013-07-01 13:24:43 +01:00
2013-04-07 21:25:23 +01:00
return $row ;
},
array_slice ( debug_backtrace ( false ), 0 , 10 )
);
} else {
$stack = debug_backtrace ( DEBUG_BACKTRACE_IGNORE_ARGS , 10 );
}
2013-03-20 17:09:46 +00:00
2013-04-10 06:14:53 +01:00
self :: $loggers [ 'deprecation' ] -> warning ( $message , array ( 'type' => self :: TYPE_DEPRECATION , 'stack' => $stack ));
2013-03-20 17:09:46 +00:00
}
return true ;
}
2013-04-10 06:14:53 +01:00
if ( $this -> displayErrors && error_reporting () & $level && $this -> level & $level ) {
2013-05-31 13:57:00 +01:00
throw new ContextErrorException ( sprintf ( '%s: %s in %s line %d' , isset ( $this -> levels [ $level ]) ? $this -> levels [ $level ] : $level , $message , $file , $line ), 0 , $level , $file , $line , $context );
2013-03-20 17:09:46 +00:00
}
return false ;
}
public function handleFatal ()
{
if ( null === $error = error_get_last ()) {
return ;
}
unset ( $this -> reservedMemory );
$type = $error [ 'type' ];
if ( 0 === $this -> level || ! in_array ( $type , array ( E_ERROR , E_CORE_ERROR , E_COMPILE_ERROR , E_PARSE ))) {
return ;
}
2013-04-10 06:14:53 +01:00
if ( isset ( self :: $loggers [ 'emergency' ])) {
$fatal = array (
'type' => $type ,
'file' => $error [ 'file' ],
'line' => $error [ 'line' ],
);
self :: $loggers [ 'emergency' ] -> emerg ( $error [ 'message' ], $fatal );
}
if ( ! $this -> displayErrors ) {
return ;
}
2013-07-24 05:59:37 +01:00
// get current exception handler
$exceptionHandler = set_exception_handler ( function () {});
restore_exception_handler ();
if ( is_array ( $exceptionHandler ) && $exceptionHandler [ 0 ] instanceof ExceptionHandler ) {
$this -> handleFatalError ( $exceptionHandler [ 0 ], $error );
}
2013-05-28 18:12:41 +01:00
}
2013-07-24 05:51:29 +01:00
public function getFatalErrorHandlers ()
{
return array (
new UndefinedFunctionFatalErrorHandler (),
new ClassNotFoundFatalErrorHandler (),
);
}
2013-07-24 05:59:37 +01:00
private function handleFatalError ( ExceptionHandler $exceptionHandler , array $error )
2013-05-28 18:12:41 +01:00
{
2013-07-24 05:59:37 +01:00
$level = isset ( $this -> levels [ $error [ 'type' ]]) ? $this -> levels [ $error [ 'type' ]] : $error [ 'type' ];
$message = sprintf ( '%s: %s in %s line %d' , $level , $error [ 'message' ], $error [ 'file' ], $error [ 'line' ]);
$exception = new FatalErrorException ( $message , 0 , $error [ 'type' ], $error [ 'file' ], $error [ 'line' ]);
2013-05-28 18:12:41 +01:00
2013-07-24 05:59:37 +01:00
foreach ( $this -> getFatalErrorHandlers () as $handler ) {
if ( $ex = $handler -> handleError ( $error , $exception )) {
return $exceptionHandler -> handle ( $ex );
2013-07-24 04:56:44 +01:00
}
}
2013-07-24 05:59:37 +01:00
$exceptionHandler -> handle ( $exception );
2013-07-24 04:31:30 +01:00
}
2013-03-20 17:09:46 +00:00
}