2011-03-05 19:23:35 +00:00
< ? php
/*
* This file is part of the Symfony package .
*
2011-03-06 11:40:06 +00:00
* ( c ) Fabien Potencier < fabien @ symfony . com >
2011-03-05 19:23:35 +00:00
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
2013-03-20 17:09:46 +00:00
namespace Symfony\Component\Debug\Tests ;
2011-03-05 19:23:35 +00:00
2014-05-26 11:29:25 +01:00
use Psr\Log\LogLevel ;
2013-03-20 17:09:46 +00:00
use Symfony\Component\Debug\ErrorHandler ;
2015-08-12 14:54:08 +01:00
use Symfony\Component\Debug\BufferingLogger ;
2014-04-27 10:44:26 +01:00
use Symfony\Component\Debug\Exception\ContextErrorException ;
2011-03-05 19:23:35 +00:00
/**
2014-12-21 17:00:50 +00:00
* ErrorHandlerTest .
2011-03-05 19:23:35 +00:00
*
* @ author Robert Schönthal < seroscho @ googlemail . com >
2014-05-26 11:29:25 +01:00
* @ author Nicolas Grekas < p @ tchwork . com >
2011-03-05 19:23:35 +00:00
*/
class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
{
2014-09-27 08:00:54 +01:00
public function testRegister ()
{
$handler = ErrorHandler :: register ();
try {
$this -> assertInstanceOf ( 'Symfony\Component\Debug\ErrorHandler' , $handler );
2014-10-27 13:40:25 +00:00
$this -> assertSame ( $handler , ErrorHandler :: register ());
$newHandler = new ErrorHandler ();
$this -> assertSame ( $newHandler , ErrorHandler :: register ( $newHandler , false ));
$h = set_error_handler ( 'var_dump' );
restore_error_handler ();
$this -> assertSame ( array ( $handler , 'handleError' ), $h );
2014-09-27 08:00:54 +01:00
try {
2014-10-27 13:40:25 +00:00
$this -> assertSame ( $newHandler , ErrorHandler :: register ( $newHandler , true ));
$h = set_error_handler ( 'var_dump' );
2014-09-27 08:00:54 +01:00
restore_error_handler ();
2014-10-27 13:40:25 +00:00
$this -> assertSame ( array ( $newHandler , 'handleError' ), $h );
} catch ( \Exception $e ) {
}
restore_error_handler ();
restore_exception_handler ();
if ( isset ( $e )) {
throw $e ;
2014-09-27 08:00:54 +01:00
}
} catch ( \Exception $e ) {
}
restore_error_handler ();
restore_exception_handler ();
if ( isset ( $e )) {
throw $e ;
}
}
2013-11-05 20:12:21 +00:00
public function testNotice ()
{
ErrorHandler :: register ();
2013-11-28 16:41:31 +00:00
try {
self :: triggerNotice ( $this );
2014-04-27 10:44:26 +01:00
$this -> fail ( 'ContextErrorException expected' );
} catch ( ContextErrorException $exception ) {
2013-11-28 16:41:31 +00:00
// if an exception is thrown, the test passed
2014-04-27 10:44:26 +01:00
$this -> assertEquals ( E_NOTICE , $exception -> getSeverity ());
$this -> assertEquals ( __FILE__ , $exception -> getFile ());
2015-07-07 20:01:23 +01:00
$this -> assertRegExp ( '/^Notice: Undefined variable: (foo|bar)/' , $exception -> getMessage ());
2014-04-27 10:44:26 +01:00
$this -> assertArrayHasKey ( 'foobar' , $exception -> getContext ());
$trace = $exception -> getTrace ();
$this -> assertEquals ( __FILE__ , $trace [ 0 ][ 'file' ]);
$this -> assertEquals ( 'Symfony\Component\Debug\ErrorHandler' , $trace [ 0 ][ 'class' ]);
2014-05-26 11:29:25 +01:00
$this -> assertEquals ( 'handleError' , $trace [ 0 ][ 'function' ]);
2014-04-27 10:44:26 +01:00
$this -> assertEquals ( '->' , $trace [ 0 ][ 'type' ]);
$this -> assertEquals ( __FILE__ , $trace [ 1 ][ 'file' ]);
$this -> assertEquals ( __CLASS__ , $trace [ 1 ][ 'class' ]);
$this -> assertEquals ( 'triggerNotice' , $trace [ 1 ][ 'function' ]);
$this -> assertEquals ( '::' , $trace [ 1 ][ 'type' ]);
2014-05-26 11:16:49 +01:00
$this -> assertEquals ( __FILE__ , $trace [ 1 ][ 'file' ]);
2014-04-27 10:44:26 +01:00
$this -> assertEquals ( __CLASS__ , $trace [ 2 ][ 'class' ]);
2014-05-26 11:16:49 +01:00
$this -> assertEquals ( __FUNCTION__ , $trace [ 2 ][ 'function' ]);
2014-04-27 10:44:26 +01:00
$this -> assertEquals ( '->' , $trace [ 2 ][ 'type' ]);
2015-09-28 01:26:11 +01:00
} finally {
2013-12-11 15:16:31 +00:00
restore_error_handler ();
2014-05-26 11:29:25 +01:00
restore_exception_handler ();
2013-11-28 16:41:31 +00:00
}
2013-08-07 14:15:14 +01:00
}
2011-04-15 20:12:02 +01:00
2013-11-05 20:12:21 +00:00
// dummy function to test trace in error handler.
2013-11-28 10:16:43 +00:00
private static function triggerNotice ( $that )
{
2013-11-05 20:12:21 +00:00
// dummy variable to check for in error handler.
$foobar = 123 ;
2013-11-28 10:16:43 +00:00
$that -> assertSame ( '' , $foo . $foo . $bar );
2013-08-07 14:15:14 +01:00
}
2011-04-15 20:12:02 +01:00
2011-03-05 19:23:35 +00:00
public function testConstruct ()
{
2013-12-11 15:16:31 +00:00
try {
2014-10-27 13:40:25 +00:00
$handler = ErrorHandler :: register ();
$handler -> throwAt ( 3 , true );
$this -> assertEquals ( 3 | E_RECOVERABLE_ERROR | E_USER_ERROR , $handler -> throwAt ( 0 ));
2015-09-28 01:26:11 +01:00
} finally {
2014-05-26 11:29:25 +01:00
restore_error_handler ();
restore_exception_handler ();
}
}
public function testDefaultLogger ()
{
try {
$handler = ErrorHandler :: register ();
2011-04-15 20:12:02 +01:00
2014-05-26 11:29:25 +01:00
$logger = $this -> getMock ( 'Psr\Log\LoggerInterface' );
2011-04-15 20:12:02 +01:00
2014-05-26 11:29:25 +01:00
$handler -> setDefaultLogger ( $logger , E_NOTICE );
$handler -> setDefaultLogger ( $logger , array ( E_USER_NOTICE => LogLevel :: CRITICAL ));
$loggers = array (
2014-11-04 14:29:39 +00:00
E_DEPRECATED => array ( null , LogLevel :: INFO ),
E_USER_DEPRECATED => array ( null , LogLevel :: INFO ),
2015-04-02 15:50:50 +01:00
E_NOTICE => array ( $logger , LogLevel :: WARNING ),
2014-11-04 14:29:39 +00:00
E_USER_NOTICE => array ( $logger , LogLevel :: CRITICAL ),
2015-04-02 15:50:50 +01:00
E_STRICT => array ( null , LogLevel :: WARNING ),
2014-11-04 14:29:39 +00:00
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 ),
2015-04-09 08:43:58 +01:00
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 ),
2014-05-26 11:29:25 +01:00
);
$this -> assertSame ( $loggers , $handler -> setLoggers ( array ()));
2015-09-28 01:26:11 +01:00
} finally {
2013-12-11 15:16:31 +00:00
restore_error_handler ();
2014-05-26 11:29:25 +01:00
restore_exception_handler ();
2013-12-11 15:16:31 +00:00
}
2011-03-05 19:23:35 +00:00
}
2014-05-26 11:29:25 +01:00
public function testHandleError ()
2011-03-05 19:23:35 +00:00
{
2013-12-11 15:16:31 +00:00
try {
2014-10-27 13:40:25 +00:00
$handler = ErrorHandler :: register ();
$handler -> throwAt ( 0 , true );
2014-05-26 11:29:25 +01:00
$this -> assertFalse ( $handler -> handleError ( 0 , 'foo' , 'foo.php' , 12 , array ()));
2011-05-05 07:46:20 +01:00
2013-12-11 15:16:31 +00:00
restore_error_handler ();
2014-05-26 11:29:25 +01:00
restore_exception_handler ();
2011-05-05 07:46:20 +01:00
2014-10-27 13:40:25 +00:00
$handler = ErrorHandler :: register ();
$handler -> throwAt ( 3 , true );
2014-05-26 11:29:25 +01:00
$this -> assertFalse ( $handler -> handleError ( 4 , 'foo' , 'foo.php' , 12 , array ()));
2011-04-15 20:12:02 +01:00
2013-12-11 15:16:31 +00:00
restore_error_handler ();
2014-05-26 11:29:25 +01:00
restore_exception_handler ();
2011-04-15 20:12:02 +01:00
2014-10-27 13:40:25 +00:00
$handler = ErrorHandler :: register ();
$handler -> throwAt ( 3 , true );
2013-12-11 15:16:31 +00:00
try {
2014-05-26 11:29:25 +01:00
$handler -> handleError ( 4 , 'foo' , 'foo.php' , 12 , array ());
2013-12-11 15:16:31 +00:00
} catch ( \ErrorException $e ) {
2014-05-26 11:29:25 +01:00
$this -> assertSame ( 'Parse Error: foo' , $e -> getMessage ());
2014-05-26 11:16:49 +01:00
$this -> assertSame ( 4 , $e -> getSeverity ());
2013-12-11 15:16:31 +00:00
$this -> assertSame ( 'foo.php' , $e -> getFile ());
$this -> assertSame ( 12 , $e -> getLine ());
}
2011-03-05 19:23:35 +00:00
2013-12-11 15:16:31 +00:00
restore_error_handler ();
2014-05-26 11:29:25 +01:00
restore_exception_handler ();
2012-12-01 11:25:00 +00:00
2014-10-27 13:40:25 +00:00
$handler = ErrorHandler :: register ();
$handler -> throwAt ( E_USER_DEPRECATED , true );
2014-05-26 11:29:25 +01:00
$this -> assertFalse ( $handler -> handleError ( E_USER_DEPRECATED , 'foo' , 'foo.php' , 12 , array ()));
2012-12-01 11:25:00 +00:00
2013-12-11 15:16:31 +00:00
restore_error_handler ();
2014-05-26 11:29:25 +01:00
restore_exception_handler ();
2012-12-01 11:25:00 +00:00
2014-10-27 13:40:25 +00:00
$handler = ErrorHandler :: register ();
$handler -> throwAt ( E_DEPRECATED , true );
2014-05-26 11:29:25 +01:00
$this -> assertFalse ( $handler -> handleError ( E_DEPRECATED , 'foo' , 'foo.php' , 12 , array ()));
2012-12-01 11:25:00 +00:00
2013-12-11 15:16:31 +00:00
restore_error_handler ();
2014-05-26 11:29:25 +01:00
restore_exception_handler ();
2012-12-01 11:25:00 +00:00
2013-12-11 15:16:31 +00:00
$logger = $this -> getMock ( 'Psr\Log\LoggerInterface' );
2012-12-07 11:47:14 +00:00
2014-12-03 22:04:33 +00:00
$warnArgCheck = function ( $logLevel , $message , $context ) {
$this -> assertEquals ( 'info' , $logLevel );
$this -> assertEquals ( 'foo' , $message );
$this -> assertArrayHasKey ( 'type' , $context );
$this -> assertEquals ( $context [ 'type' ], E_USER_DEPRECATED );
$this -> assertArrayHasKey ( 'stack' , $context );
$this -> assertInternalType ( 'array' , $context [ 'stack' ]);
2013-12-11 15:16:31 +00:00
};
2012-12-07 11:47:14 +00:00
2013-12-11 15:16:31 +00:00
$logger
-> expects ( $this -> once ())
2014-05-26 11:29:25 +01:00
-> method ( 'log' )
2013-12-11 15:16:31 +00:00
-> will ( $this -> returnCallback ( $warnArgCheck ))
;
2012-12-01 11:25:00 +00:00
2014-10-27 13:40:25 +00:00
$handler = ErrorHandler :: register ();
2014-05-26 11:29:25 +01:00
$handler -> setDefaultLogger ( $logger , E_USER_DEPRECATED );
$this -> assertTrue ( $handler -> handleError ( E_USER_DEPRECATED , 'foo' , 'foo.php' , 12 , array ()));
restore_error_handler ();
restore_exception_handler ();
$logger = $this -> getMock ( 'Psr\Log\LoggerInterface' );
2014-12-03 22:04:33 +00:00
$logArgCheck = function ( $level , $message , $context ) {
$this -> assertEquals ( 'Undefined variable: undefVar' , $message );
$this -> assertArrayHasKey ( 'type' , $context );
$this -> assertEquals ( $context [ 'type' ], E_NOTICE );
2014-05-26 11:29:25 +01:00
};
$logger
-> expects ( $this -> once ())
-> method ( 'log' )
-> will ( $this -> returnCallback ( $logArgCheck ))
;
2014-10-27 13:40:25 +00:00
$handler = ErrorHandler :: register ();
2014-05-26 11:29:25 +01:00
$handler -> setDefaultLogger ( $logger , E_NOTICE );
$handler -> screamAt ( E_NOTICE );
unset ( $undefVar );
@ $undefVar ++ ;
restore_error_handler ();
restore_exception_handler ();
} catch ( \Exception $e ) {
restore_error_handler ();
restore_exception_handler ();
throw $e ;
}
}
2015-06-22 18:39:18 +01:00
public function testHandleUserError ()
{
try {
$handler = ErrorHandler :: register ();
$handler -> throwAt ( 0 , true );
$e = null ;
$x = new \Exception ( 'Foo' );
try {
$f = new Fixtures\ToStringThrower ( $x );
$f .= '' ; // Trigger $f->__toString()
} catch ( \Exception $e ) {
}
$this -> assertSame ( $x , $e );
2015-09-28 01:26:11 +01:00
} finally {
2015-06-22 18:39:18 +01:00
restore_error_handler ();
restore_exception_handler ();
2014-05-26 11:29:25 +01:00
}
}
2015-08-29 11:54:54 +01:00
public function testHandleDeprecation ()
{
2015-09-01 11:10:08 +01:00
$logArgCheck = function ( $level , $message , $context ) {
$this -> assertEquals ( LogLevel :: INFO , $level );
$this -> assertArrayHasKey ( 'level' , $context );
$this -> assertEquals ( E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED , $context [ 'level' ]);
$this -> assertArrayHasKey ( 'stack' , $context );
2015-08-29 11:54:54 +01:00
};
$logger = $this -> getMock ( 'Psr\Log\LoggerInterface' );
$logger
-> expects ( $this -> once ())
-> method ( 'log' )
-> will ( $this -> returnCallback ( $logArgCheck ))
;
$handler = new ErrorHandler ();
$handler -> setDefaultLogger ( $logger );
@ $handler -> handleError ( E_USER_DEPRECATED , 'Foo deprecation' , __FILE__ , __LINE__ , array ());
}
2014-05-26 11:29:25 +01:00
public function testHandleException ()
{
try {
$handler = ErrorHandler :: register ();
$exception = new \Exception ( 'foo' );
$logger = $this -> getMock ( 'Psr\Log\LoggerInterface' );
2015-04-02 08:55:37 +01:00
$logArgCheck = function ( $level , $message , $context ) {
2014-12-03 22:04:33 +00:00
$this -> assertEquals ( 'Uncaught Exception: foo' , $message );
$this -> assertArrayHasKey ( 'type' , $context );
$this -> assertEquals ( $context [ 'type' ], E_ERROR );
2014-05-26 11:29:25 +01:00
};
$logger
-> expects ( $this -> exactly ( 2 ))
-> method ( 'log' )
-> will ( $this -> returnCallback ( $logArgCheck ))
;
$handler -> setDefaultLogger ( $logger , E_ERROR );
try {
$handler -> handleException ( $exception );
$this -> fail ( 'Exception expected' );
} catch ( \Exception $e ) {
$this -> assertSame ( $exception , $e );
}
2014-12-03 22:04:33 +00:00
$handler -> setExceptionHandler ( function ( $e ) use ( $exception ) {
$this -> assertSame ( $exception , $e );
2014-05-26 11:29:25 +01:00
});
$handler -> handleException ( $exception );
2015-09-28 01:26:11 +01:00
} finally {
2014-05-26 11:29:25 +01:00
restore_error_handler ();
restore_exception_handler ();
}
}
2015-06-16 17:24:54 +01:00
public function testErrorStacking ()
{
try {
$handler = ErrorHandler :: register ();
$handler -> screamAt ( E_USER_WARNING );
$logger = $this -> getMock ( 'Psr\Log\LoggerInterface' );
$logger
-> expects ( $this -> exactly ( 2 ))
-> method ( 'log' )
-> withConsecutive (
array ( $this -> equalTo ( LogLevel :: WARNING ), $this -> equalTo ( 'Dummy log' )),
array ( $this -> equalTo ( LogLevel :: DEBUG ), $this -> equalTo ( 'Silenced warning' ))
)
;
$handler -> setDefaultLogger ( $logger , array ( E_USER_WARNING => LogLevel :: WARNING ));
ErrorHandler :: stackErrors ();
@ trigger_error ( 'Silenced warning' , E_USER_WARNING );
$logger -> log ( LogLevel :: WARNING , 'Dummy log' );
ErrorHandler :: unstackErrors ();
2015-09-28 01:26:11 +01:00
} finally {
2015-06-16 17:24:54 +01:00
restore_error_handler ();
restore_exception_handler ();
}
}
2015-08-12 14:54:08 +01:00
public function testBootstrappingLogger ()
{
$bootLogger = new BufferingLogger ();
$handler = new ErrorHandler ( $bootLogger );
$loggers = array (
E_DEPRECATED => array ( $bootLogger , LogLevel :: INFO ),
E_USER_DEPRECATED => array ( $bootLogger , LogLevel :: INFO ),
E_NOTICE => array ( $bootLogger , LogLevel :: WARNING ),
E_USER_NOTICE => array ( $bootLogger , LogLevel :: WARNING ),
E_STRICT => array ( $bootLogger , LogLevel :: WARNING ),
E_WARNING => array ( $bootLogger , LogLevel :: WARNING ),
E_USER_WARNING => array ( $bootLogger , LogLevel :: WARNING ),
E_COMPILE_WARNING => array ( $bootLogger , LogLevel :: WARNING ),
E_CORE_WARNING => array ( $bootLogger , LogLevel :: WARNING ),
E_USER_ERROR => array ( $bootLogger , LogLevel :: CRITICAL ),
E_RECOVERABLE_ERROR => array ( $bootLogger , LogLevel :: CRITICAL ),
E_COMPILE_ERROR => array ( $bootLogger , LogLevel :: CRITICAL ),
E_PARSE => array ( $bootLogger , LogLevel :: CRITICAL ),
E_ERROR => array ( $bootLogger , LogLevel :: CRITICAL ),
E_CORE_ERROR => array ( $bootLogger , LogLevel :: CRITICAL ),
);
$this -> assertSame ( $loggers , $handler -> setLoggers ( array ()));
$handler -> handleError ( E_DEPRECATED , 'Foo message' , __FILE__ , 123 , array ());
$expectedLog = array ( LogLevel :: INFO , 'Foo message' , array ( 'type' => E_DEPRECATED , 'file' => __FILE__ , 'line' => 123 , 'level' => error_reporting ()));
$logs = $bootLogger -> cleanLogs ();
unset ( $logs [ 0 ][ 2 ][ 'stack' ]);
$this -> assertSame ( array ( $expectedLog ), $logs );
$bootLogger -> log ( $expectedLog [ 0 ], $expectedLog [ 1 ], $expectedLog [ 2 ]);
$mockLogger = $this -> getMock ( 'Psr\Log\LoggerInterface' );
$mockLogger -> expects ( $this -> once ())
-> method ( 'log' )
-> with ( LogLevel :: WARNING , 'Foo message' , $expectedLog [ 2 ]);
$handler -> setLoggers ( array ( E_DEPRECATED => array ( $mockLogger , LogLevel :: WARNING )));
}
2014-05-26 11:29:25 +01:00
public function testHandleFatalError ()
{
try {
$handler = ErrorHandler :: register ();
$error = array (
'type' => E_PARSE ,
'message' => 'foo' ,
'file' => 'bar' ,
'line' => 123 ,
);
$logger = $this -> getMock ( 'Psr\Log\LoggerInterface' );
2014-12-03 22:04:33 +00:00
$logArgCheck = function ( $level , $message , $context ) {
$this -> assertEquals ( 'Fatal Parse Error: foo' , $message );
$this -> assertArrayHasKey ( 'type' , $context );
2015-05-12 16:48:43 +01:00
$this -> assertEquals ( $context [ 'type' ], E_PARSE );
2014-05-26 11:29:25 +01:00
};
$logger
-> expects ( $this -> once ())
-> method ( 'log' )
-> will ( $this -> returnCallback ( $logArgCheck ))
;
2015-04-25 16:02:32 +01:00
$handler -> setDefaultLogger ( $logger , E_PARSE );
2014-05-26 11:29:25 +01:00
$handler -> handleFatalError ( $error );
restore_error_handler ();
restore_exception_handler ();
} catch ( \Exception $e ) {
restore_error_handler ();
restore_exception_handler ();
throw $e ;
}
}
2016-05-09 20:43:14 +01:00
/**
* @ requires PHP 7
*/
public function testHandleErrorException ()
{
$exception = new \Error ( " Class 'Foo' not found " );
$handler = new ErrorHandler ();
$handler -> setExceptionHandler ( function () use ( & $args ) {
$args = func_get_args ();
});
$handler -> handleException ( $exception );
$this -> assertInstanceOf ( 'Symfony\Component\Debug\Exception\ClassNotFoundException' , $args [ 0 ]);
2016-06-06 12:49:15 +01:00
$this -> assertStringStartsWith ( " Attempted to load class \" Foo \" from the global namespace. \n Did you forget a \" use \" statement " , $args [ 0 ] -> getMessage ());
2016-05-09 20:43:14 +01:00
}
2015-06-12 22:50:21 +01:00
public function testHandleFatalErrorOnHHVM ()
{
try {
$handler = ErrorHandler :: register ();
$logger = $this -> getMock ( 'Psr\Log\LoggerInterface' );
$logger
-> expects ( $this -> once ())
-> method ( 'log' )
-> with (
2015-06-18 14:03:50 +01:00
$this -> equalTo ( LogLevel :: CRITICAL ),
2015-06-12 22:50:21 +01:00
$this -> equalTo ( 'Fatal Error: foo' ),
$this -> equalTo ( array (
'type' => 1 ,
'file' => 'bar' ,
'line' => 123 ,
'level' => - 1 ,
'stack' => array ( 456 ),
))
)
;
$handler -> setDefaultLogger ( $logger , E_ERROR );
$error = array (
'type' => E_ERROR + 0x1000000 , // This error level is used by HHVM for fatal errors
'message' => 'foo' ,
'file' => 'bar' ,
'line' => 123 ,
'context' => array ( 123 ),
'backtrace' => array ( 456 ),
);
call_user_func_array ( array ( $handler , 'handleError' ), $error );
$handler -> handleFatalError ( $error );
2015-09-28 01:26:11 +01:00
} finally {
2015-06-12 22:50:21 +01:00
restore_error_handler ();
restore_exception_handler ();
}
}
2013-12-12 16:06:35 +00:00
}