4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Debug\Tests;
14 use PHPUnit\Framework\TestCase;
16 use Symfony\Component\Debug\ErrorHandler;
17 use Symfony\Component\Debug\BufferingLogger;
18 use Symfony\Component\Debug\Exception\ContextErrorException;
23 * @author Robert Schönthal <seroscho@googlemail.com>
24 * @author Nicolas Grekas <p@tchwork.com>
26 class ErrorHandlerTest extends TestCase
28 public function testRegister()
30 $handler = ErrorHandler::register();
33 $this->assertInstanceOf('Symfony\Component\Debug\ErrorHandler', $handler);
34 $this->assertSame($handler, ErrorHandler::register());
36 $newHandler = new ErrorHandler();
38 $this->assertSame($newHandler, ErrorHandler::register($newHandler, false));
39 $h = set_error_handler('var_dump');
40 restore_error_handler();
41 $this->assertSame(array($handler, 'handleError'), $h);
44 $this->assertSame($newHandler, ErrorHandler::register($newHandler, true));
45 $h = set_error_handler('var_dump');
46 restore_error_handler();
47 $this->assertSame(array($newHandler, 'handleError'), $h);
48 } catch (\Exception $e) {
51 restore_error_handler();
52 restore_exception_handler();
57 } catch (\Exception $e) {
60 restore_error_handler();
61 restore_exception_handler();
68 public function testNotice()
70 ErrorHandler::register();
73 self::triggerNotice($this);
74 $this->fail('ContextErrorException expected');
75 } catch (ContextErrorException $exception) {
76 // if an exception is thrown, the test passed
77 restore_error_handler();
78 restore_exception_handler();
80 $this->assertEquals(E_NOTICE, $exception->getSeverity());
81 $this->assertEquals(__FILE__, $exception->getFile());
82 $this->assertRegExp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
83 $this->assertArrayHasKey('foobar', $exception->getContext());
85 $trace = $exception->getTrace();
86 $this->assertEquals(__FILE__, $trace[0]['file']);
87 $this->assertEquals('Symfony\Component\Debug\ErrorHandler', $trace[0]['class']);
88 $this->assertEquals('handleError', $trace[0]['function']);
89 $this->assertEquals('->', $trace[0]['type']);
91 $this->assertEquals(__FILE__, $trace[1]['file']);
92 $this->assertEquals(__CLASS__, $trace[1]['class']);
93 $this->assertEquals('triggerNotice', $trace[1]['function']);
94 $this->assertEquals('::', $trace[1]['type']);
96 $this->assertEquals(__FILE__, $trace[1]['file']);
97 $this->assertEquals(__CLASS__, $trace[2]['class']);
98 $this->assertEquals(__FUNCTION__, $trace[2]['function']);
99 $this->assertEquals('->', $trace[2]['type']);
100 } catch (\Exception $e) {
101 restore_error_handler();
102 restore_exception_handler();
108 // dummy function to test trace in error handler.
109 private static function triggerNotice($that)
111 // dummy variable to check for in error handler.
113 $that->assertSame('', $foo.$foo.$bar);
116 public function testConstruct()
119 $handler = ErrorHandler::register();
120 $handler->throwAt(3, true);
121 $this->assertEquals(3 | E_RECOVERABLE_ERROR | E_USER_ERROR, $handler->throwAt(0));
123 restore_error_handler();
124 restore_exception_handler();
125 } catch (\Exception $e) {
126 restore_error_handler();
127 restore_exception_handler();
133 public function testDefaultLogger()
136 $handler = ErrorHandler::register();
138 $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
140 $handler->setDefaultLogger($logger, E_NOTICE);
141 $handler->setDefaultLogger($logger, array(E_USER_NOTICE => LogLevel::CRITICAL));
144 E_DEPRECATED => array(null, LogLevel::INFO),
145 E_USER_DEPRECATED => array(null, LogLevel::INFO),
146 E_NOTICE => array($logger, LogLevel::WARNING),
147 E_USER_NOTICE => array($logger, LogLevel::CRITICAL),
148 E_STRICT => array(null, LogLevel::WARNING),
149 E_WARNING => array(null, LogLevel::WARNING),
150 E_USER_WARNING => array(null, LogLevel::WARNING),
151 E_COMPILE_WARNING => array(null, LogLevel::WARNING),
152 E_CORE_WARNING => array(null, LogLevel::WARNING),
153 E_USER_ERROR => array(null, LogLevel::CRITICAL),
154 E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL),
155 E_COMPILE_ERROR => array(null, LogLevel::CRITICAL),
156 E_PARSE => array(null, LogLevel::CRITICAL),
157 E_ERROR => array(null, LogLevel::CRITICAL),
158 E_CORE_ERROR => array(null, LogLevel::CRITICAL),
160 $this->assertSame($loggers, $handler->setLoggers(array()));
162 restore_error_handler();
163 restore_exception_handler();
164 } catch (\Exception $e) {
165 restore_error_handler();
166 restore_exception_handler();
172 public function testHandleError()
175 $handler = ErrorHandler::register();
176 $handler->throwAt(0, true);
177 $this->assertFalse($handler->handleError(0, 'foo', 'foo.php', 12, array()));
179 restore_error_handler();
180 restore_exception_handler();
182 $handler = ErrorHandler::register();
183 $handler->throwAt(3, true);
184 $this->assertFalse($handler->handleError(4, 'foo', 'foo.php', 12, array()));
186 restore_error_handler();
187 restore_exception_handler();
189 $handler = ErrorHandler::register();
190 $handler->throwAt(3, true);
192 $handler->handleError(4, 'foo', 'foo.php', 12, array());
193 } catch (\ErrorException $e) {
194 $this->assertSame('Parse Error: foo', $e->getMessage());
195 $this->assertSame(4, $e->getSeverity());
196 $this->assertSame('foo.php', $e->getFile());
197 $this->assertSame(12, $e->getLine());
200 restore_error_handler();
201 restore_exception_handler();
203 $handler = ErrorHandler::register();
204 $handler->throwAt(E_USER_DEPRECATED, true);
205 $this->assertFalse($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array()));
207 restore_error_handler();
208 restore_exception_handler();
210 $handler = ErrorHandler::register();
211 $handler->throwAt(E_DEPRECATED, true);
212 $this->assertFalse($handler->handleError(E_DEPRECATED, 'foo', 'foo.php', 12, array()));
214 restore_error_handler();
215 restore_exception_handler();
217 $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
220 $warnArgCheck = function ($logLevel, $message, $context) use ($that) {
221 $that->assertEquals('info', $logLevel);
222 $that->assertEquals('foo', $message);
223 $that->assertArrayHasKey('type', $context);
224 $that->assertEquals($context['type'], E_USER_DEPRECATED);
225 $that->assertArrayHasKey('stack', $context);
226 $that->assertInternalType('array', $context['stack']);
230 ->expects($this->once())
232 ->will($this->returnCallback($warnArgCheck))
235 $handler = ErrorHandler::register();
236 $handler->setDefaultLogger($logger, E_USER_DEPRECATED);
237 $this->assertTrue($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array()));
239 restore_error_handler();
240 restore_exception_handler();
242 $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
245 $logArgCheck = function ($level, $message, $context) use ($that) {
246 $that->assertEquals('Undefined variable: undefVar', $message);
247 $that->assertArrayHasKey('type', $context);
248 $that->assertEquals($context['type'], E_NOTICE);
252 ->expects($this->once())
254 ->will($this->returnCallback($logArgCheck))
257 $handler = ErrorHandler::register();
258 $handler->setDefaultLogger($logger, E_NOTICE);
259 $handler->screamAt(E_NOTICE);
263 restore_error_handler();
264 restore_exception_handler();
265 } catch (\Exception $e) {
266 restore_error_handler();
267 restore_exception_handler();
273 public function testHandleUserError()
276 $handler = ErrorHandler::register();
277 $handler->throwAt(0, true);
280 $x = new \Exception('Foo');
283 $f = new Fixtures\ToStringThrower($x);
284 $f .= ''; // Trigger $f->__toString()
285 } catch (\Exception $e) {
288 $this->assertSame($x, $e);
290 restore_error_handler();
291 restore_exception_handler();
292 } catch (\Exception $e) {
293 restore_error_handler();
294 restore_exception_handler();
300 public function testHandleDeprecation()
303 $logArgCheck = function ($level, $message, $context) use ($that) {
304 $that->assertEquals(LogLevel::INFO, $level);
305 $that->assertArrayHasKey('level', $context);
306 $that->assertEquals(E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED, $context['level']);
307 $that->assertArrayHasKey('stack', $context);
310 $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
312 ->expects($this->once())
314 ->will($this->returnCallback($logArgCheck))
317 $handler = new ErrorHandler();
318 $handler->setDefaultLogger($logger);
319 @$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, array());
322 public function testHandleException()
325 $handler = ErrorHandler::register();
327 $exception = new \Exception('foo');
329 $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
332 $logArgCheck = function ($level, $message, $context) use ($that) {
333 $that->assertEquals('Uncaught Exception: foo', $message);
334 $that->assertArrayHasKey('type', $context);
335 $that->assertEquals($context['type'], E_ERROR);
339 ->expects($this->exactly(2))
341 ->will($this->returnCallback($logArgCheck))
344 $handler->setDefaultLogger($logger, E_ERROR);
347 $handler->handleException($exception);
348 $this->fail('Exception expected');
349 } catch (\Exception $e) {
350 $this->assertSame($exception, $e);
354 $handler->setExceptionHandler(function ($e) use ($exception, $that) {
355 $that->assertSame($exception, $e);
358 $handler->handleException($exception);
360 restore_error_handler();
361 restore_exception_handler();
362 } catch (\Exception $e) {
363 restore_error_handler();
364 restore_exception_handler();
370 public function testErrorStacking()
373 $handler = ErrorHandler::register();
374 $handler->screamAt(E_USER_WARNING);
376 $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
379 ->expects($this->exactly(2))
382 array($this->equalTo(LogLevel::WARNING), $this->equalTo('Dummy log')),
383 array($this->equalTo(LogLevel::DEBUG), $this->equalTo('Silenced warning'))
387 $handler->setDefaultLogger($logger, array(E_USER_WARNING => LogLevel::WARNING));
389 ErrorHandler::stackErrors();
390 @trigger_error('Silenced warning', E_USER_WARNING);
391 $logger->log(LogLevel::WARNING, 'Dummy log');
392 ErrorHandler::unstackErrors();
394 restore_error_handler();
395 restore_exception_handler();
396 } catch (\Exception $e) {
397 restore_error_handler();
398 restore_exception_handler();
404 public function testBootstrappingLogger()
406 $bootLogger = new BufferingLogger();
407 $handler = new ErrorHandler($bootLogger);
410 E_DEPRECATED => array($bootLogger, LogLevel::INFO),
411 E_USER_DEPRECATED => array($bootLogger, LogLevel::INFO),
412 E_NOTICE => array($bootLogger, LogLevel::WARNING),
413 E_USER_NOTICE => array($bootLogger, LogLevel::WARNING),
414 E_STRICT => array($bootLogger, LogLevel::WARNING),
415 E_WARNING => array($bootLogger, LogLevel::WARNING),
416 E_USER_WARNING => array($bootLogger, LogLevel::WARNING),
417 E_COMPILE_WARNING => array($bootLogger, LogLevel::WARNING),
418 E_CORE_WARNING => array($bootLogger, LogLevel::WARNING),
419 E_USER_ERROR => array($bootLogger, LogLevel::CRITICAL),
420 E_RECOVERABLE_ERROR => array($bootLogger, LogLevel::CRITICAL),
421 E_COMPILE_ERROR => array($bootLogger, LogLevel::CRITICAL),
422 E_PARSE => array($bootLogger, LogLevel::CRITICAL),
423 E_ERROR => array($bootLogger, LogLevel::CRITICAL),
424 E_CORE_ERROR => array($bootLogger, LogLevel::CRITICAL),
427 $this->assertSame($loggers, $handler->setLoggers(array()));
429 $handler->handleError(E_DEPRECATED, 'Foo message', __FILE__, 123, array());
430 $expectedLog = array(LogLevel::INFO, 'Foo message', array('type' => E_DEPRECATED, 'file' => __FILE__, 'line' => 123, 'level' => error_reporting()));
432 $logs = $bootLogger->cleanLogs();
433 unset($logs[0][2]['stack']);
435 $this->assertSame(array($expectedLog), $logs);
437 $bootLogger->log($expectedLog[0], $expectedLog[1], $expectedLog[2]);
439 $mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
440 $mockLogger->expects($this->once())
442 ->with(LogLevel::WARNING, 'Foo message', $expectedLog[2]);
444 $handler->setLoggers(array(E_DEPRECATED => array($mockLogger, LogLevel::WARNING)));
447 public function testHandleFatalError()
450 $handler = ErrorHandler::register();
459 $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
462 $logArgCheck = function ($level, $message, $context) use ($that) {
463 $that->assertEquals('Fatal Parse Error: foo', $message);
464 $that->assertArrayHasKey('type', $context);
465 $that->assertEquals($context['type'], E_PARSE);
469 ->expects($this->once())
471 ->will($this->returnCallback($logArgCheck))
474 $handler->setDefaultLogger($logger, E_PARSE);
476 $handler->handleFatalError($error);
478 restore_error_handler();
479 restore_exception_handler();
480 } catch (\Exception $e) {
481 restore_error_handler();
482 restore_exception_handler();
491 public function testHandleErrorException()
493 $exception = new \Error("Class 'Foo' not found");
495 $handler = new ErrorHandler();
496 $handler->setExceptionHandler(function () use (&$args) {
497 $args = func_get_args();
500 $handler->handleException($exception);
502 $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]);
503 $this->assertStringStartsWith("Attempted to load class \"Foo\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage());
506 public function testHandleFatalErrorOnHHVM()
509 $handler = ErrorHandler::register();
511 $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
513 ->expects($this->once())
516 $this->equalTo(LogLevel::CRITICAL),
517 $this->equalTo('Fatal Error: foo'),
518 $this->equalTo(array(
523 'stack' => array(456),
528 $handler->setDefaultLogger($logger, E_ERROR);
531 'type' => E_ERROR + 0x1000000, // This error level is used by HHVM for fatal errors
535 'context' => array(123),
536 'backtrace' => array(456),
539 call_user_func_array(array($handler, 'handleError'), $error);
540 $handler->handleFatalError($error);
542 restore_error_handler();
543 restore_exception_handler();
544 } catch (\Exception $e) {
545 restore_error_handler();
546 restore_exception_handler();
555 public function testLegacyInterface()
558 $handler = ErrorHandler::register(0);
559 $this->assertFalse($handler->handle(0, 'foo', 'foo.php', 12, array()));
561 restore_error_handler();
562 restore_exception_handler();
564 $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
567 $logArgCheck = function ($level, $message, $context) use ($that) {
568 $that->assertEquals('Undefined variable: undefVar', $message);
569 $that->assertArrayHasKey('type', $context);
570 $that->assertEquals($context['type'], E_NOTICE);
574 ->expects($this->once())
576 ->will($this->returnCallback($logArgCheck))
579 $handler = ErrorHandler::register(E_NOTICE);
580 @$handler->setLogger($logger, 'scream');
584 restore_error_handler();
585 restore_exception_handler();
586 } catch (\Exception $e) {
587 restore_error_handler();
588 restore_exception_handler();