Yaffs site version 1.1
[yaffs-website] / vendor / symfony / debug / Tests / ErrorHandlerTest.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\Debug\Tests;
13
14 use PHPUnit\Framework\TestCase;
15 use Psr\Log\LogLevel;
16 use Symfony\Component\Debug\ErrorHandler;
17 use Symfony\Component\Debug\BufferingLogger;
18 use Symfony\Component\Debug\Exception\ContextErrorException;
19
20 /**
21  * ErrorHandlerTest.
22  *
23  * @author Robert Schönthal <seroscho@googlemail.com>
24  * @author Nicolas Grekas <p@tchwork.com>
25  */
26 class ErrorHandlerTest extends TestCase
27 {
28     public function testRegister()
29     {
30         $handler = ErrorHandler::register();
31
32         try {
33             $this->assertInstanceOf('Symfony\Component\Debug\ErrorHandler', $handler);
34             $this->assertSame($handler, ErrorHandler::register());
35
36             $newHandler = new ErrorHandler();
37
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);
42
43             try {
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) {
49             }
50
51             restore_error_handler();
52             restore_exception_handler();
53
54             if (isset($e)) {
55                 throw $e;
56             }
57         } catch (\Exception $e) {
58         }
59
60         restore_error_handler();
61         restore_exception_handler();
62
63         if (isset($e)) {
64             throw $e;
65         }
66     }
67
68     public function testNotice()
69     {
70         ErrorHandler::register();
71
72         try {
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();
79
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());
84
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']);
90
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']);
95
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();
103
104             throw $e;
105         }
106     }
107
108     // dummy function to test trace in error handler.
109     private static function triggerNotice($that)
110     {
111         // dummy variable to check for in error handler.
112         $foobar = 123;
113         $that->assertSame('', $foo.$foo.$bar);
114     }
115
116     public function testConstruct()
117     {
118         try {
119             $handler = ErrorHandler::register();
120             $handler->throwAt(3, true);
121             $this->assertEquals(3 | E_RECOVERABLE_ERROR | E_USER_ERROR, $handler->throwAt(0));
122
123             restore_error_handler();
124             restore_exception_handler();
125         } catch (\Exception $e) {
126             restore_error_handler();
127             restore_exception_handler();
128
129             throw $e;
130         }
131     }
132
133     public function testDefaultLogger()
134     {
135         try {
136             $handler = ErrorHandler::register();
137
138             $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
139
140             $handler->setDefaultLogger($logger, E_NOTICE);
141             $handler->setDefaultLogger($logger, array(E_USER_NOTICE => LogLevel::CRITICAL));
142
143             $loggers = array(
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),
159             );
160             $this->assertSame($loggers, $handler->setLoggers(array()));
161
162             restore_error_handler();
163             restore_exception_handler();
164         } catch (\Exception $e) {
165             restore_error_handler();
166             restore_exception_handler();
167
168             throw $e;
169         }
170     }
171
172     public function testHandleError()
173     {
174         try {
175             $handler = ErrorHandler::register();
176             $handler->throwAt(0, true);
177             $this->assertFalse($handler->handleError(0, 'foo', 'foo.php', 12, array()));
178
179             restore_error_handler();
180             restore_exception_handler();
181
182             $handler = ErrorHandler::register();
183             $handler->throwAt(3, true);
184             $this->assertFalse($handler->handleError(4, 'foo', 'foo.php', 12, array()));
185
186             restore_error_handler();
187             restore_exception_handler();
188
189             $handler = ErrorHandler::register();
190             $handler->throwAt(3, true);
191             try {
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());
198             }
199
200             restore_error_handler();
201             restore_exception_handler();
202
203             $handler = ErrorHandler::register();
204             $handler->throwAt(E_USER_DEPRECATED, true);
205             $this->assertFalse($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array()));
206
207             restore_error_handler();
208             restore_exception_handler();
209
210             $handler = ErrorHandler::register();
211             $handler->throwAt(E_DEPRECATED, true);
212             $this->assertFalse($handler->handleError(E_DEPRECATED, 'foo', 'foo.php', 12, array()));
213
214             restore_error_handler();
215             restore_exception_handler();
216
217             $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
218
219             $that = $this;
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']);
227             };
228
229             $logger
230                 ->expects($this->once())
231                 ->method('log')
232                 ->will($this->returnCallback($warnArgCheck))
233             ;
234
235             $handler = ErrorHandler::register();
236             $handler->setDefaultLogger($logger, E_USER_DEPRECATED);
237             $this->assertTrue($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array()));
238
239             restore_error_handler();
240             restore_exception_handler();
241
242             $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
243
244             $that = $this;
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);
249             };
250
251             $logger
252                 ->expects($this->once())
253                 ->method('log')
254                 ->will($this->returnCallback($logArgCheck))
255             ;
256
257             $handler = ErrorHandler::register();
258             $handler->setDefaultLogger($logger, E_NOTICE);
259             $handler->screamAt(E_NOTICE);
260             unset($undefVar);
261             @$undefVar++;
262
263             restore_error_handler();
264             restore_exception_handler();
265         } catch (\Exception $e) {
266             restore_error_handler();
267             restore_exception_handler();
268
269             throw $e;
270         }
271     }
272
273     public function testHandleUserError()
274     {
275         try {
276             $handler = ErrorHandler::register();
277             $handler->throwAt(0, true);
278
279             $e = null;
280             $x = new \Exception('Foo');
281
282             try {
283                 $f = new Fixtures\ToStringThrower($x);
284                 $f .= ''; // Trigger $f->__toString()
285             } catch (\Exception $e) {
286             }
287
288             $this->assertSame($x, $e);
289
290             restore_error_handler();
291             restore_exception_handler();
292         } catch (\Exception $e) {
293             restore_error_handler();
294             restore_exception_handler();
295
296             throw $e;
297         }
298     }
299
300     public function testHandleDeprecation()
301     {
302         $that = $this;
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);
308         };
309
310         $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
311         $logger
312             ->expects($this->once())
313             ->method('log')
314             ->will($this->returnCallback($logArgCheck))
315         ;
316
317         $handler = new ErrorHandler();
318         $handler->setDefaultLogger($logger);
319         @$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, array());
320     }
321
322     public function testHandleException()
323     {
324         try {
325             $handler = ErrorHandler::register();
326
327             $exception = new \Exception('foo');
328
329             $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
330
331             $that = $this;
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);
336             };
337
338             $logger
339                 ->expects($this->exactly(2))
340                 ->method('log')
341                 ->will($this->returnCallback($logArgCheck))
342             ;
343
344             $handler->setDefaultLogger($logger, E_ERROR);
345
346             try {
347                 $handler->handleException($exception);
348                 $this->fail('Exception expected');
349             } catch (\Exception $e) {
350                 $this->assertSame($exception, $e);
351             }
352
353             $that = $this;
354             $handler->setExceptionHandler(function ($e) use ($exception, $that) {
355                 $that->assertSame($exception, $e);
356             });
357
358             $handler->handleException($exception);
359
360             restore_error_handler();
361             restore_exception_handler();
362         } catch (\Exception $e) {
363             restore_error_handler();
364             restore_exception_handler();
365
366             throw $e;
367         }
368     }
369
370     public function testErrorStacking()
371     {
372         try {
373             $handler = ErrorHandler::register();
374             $handler->screamAt(E_USER_WARNING);
375
376             $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
377
378             $logger
379                 ->expects($this->exactly(2))
380                 ->method('log')
381                 ->withConsecutive(
382                     array($this->equalTo(LogLevel::WARNING), $this->equalTo('Dummy log')),
383                     array($this->equalTo(LogLevel::DEBUG), $this->equalTo('Silenced warning'))
384                 )
385             ;
386
387             $handler->setDefaultLogger($logger, array(E_USER_WARNING => LogLevel::WARNING));
388
389             ErrorHandler::stackErrors();
390             @trigger_error('Silenced warning', E_USER_WARNING);
391             $logger->log(LogLevel::WARNING, 'Dummy log');
392             ErrorHandler::unstackErrors();
393
394             restore_error_handler();
395             restore_exception_handler();
396         } catch (\Exception $e) {
397             restore_error_handler();
398             restore_exception_handler();
399
400             throw $e;
401         }
402     }
403
404     public function testBootstrappingLogger()
405     {
406         $bootLogger = new BufferingLogger();
407         $handler = new ErrorHandler($bootLogger);
408
409         $loggers = array(
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),
425         );
426
427         $this->assertSame($loggers, $handler->setLoggers(array()));
428
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()));
431
432         $logs = $bootLogger->cleanLogs();
433         unset($logs[0][2]['stack']);
434
435         $this->assertSame(array($expectedLog), $logs);
436
437         $bootLogger->log($expectedLog[0], $expectedLog[1], $expectedLog[2]);
438
439         $mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
440         $mockLogger->expects($this->once())
441             ->method('log')
442             ->with(LogLevel::WARNING, 'Foo message', $expectedLog[2]);
443
444         $handler->setLoggers(array(E_DEPRECATED => array($mockLogger, LogLevel::WARNING)));
445     }
446
447     public function testHandleFatalError()
448     {
449         try {
450             $handler = ErrorHandler::register();
451
452             $error = array(
453                 'type' => E_PARSE,
454                 'message' => 'foo',
455                 'file' => 'bar',
456                 'line' => 123,
457             );
458
459             $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
460
461             $that = $this;
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);
466             };
467
468             $logger
469                 ->expects($this->once())
470                 ->method('log')
471                 ->will($this->returnCallback($logArgCheck))
472             ;
473
474             $handler->setDefaultLogger($logger, E_PARSE);
475
476             $handler->handleFatalError($error);
477
478             restore_error_handler();
479             restore_exception_handler();
480         } catch (\Exception $e) {
481             restore_error_handler();
482             restore_exception_handler();
483
484             throw $e;
485         }
486     }
487
488     /**
489      * @requires PHP 7
490      */
491     public function testHandleErrorException()
492     {
493         $exception = new \Error("Class 'Foo' not found");
494
495         $handler = new ErrorHandler();
496         $handler->setExceptionHandler(function () use (&$args) {
497             $args = func_get_args();
498         });
499
500         $handler->handleException($exception);
501
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());
504     }
505
506     public function testHandleFatalErrorOnHHVM()
507     {
508         try {
509             $handler = ErrorHandler::register();
510
511             $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
512             $logger
513                 ->expects($this->once())
514                 ->method('log')
515                 ->with(
516                     $this->equalTo(LogLevel::CRITICAL),
517                     $this->equalTo('Fatal Error: foo'),
518                     $this->equalTo(array(
519                         'type' => 1,
520                         'file' => 'bar',
521                         'line' => 123,
522                         'level' => -1,
523                         'stack' => array(456),
524                     ))
525                 )
526             ;
527
528             $handler->setDefaultLogger($logger, E_ERROR);
529
530             $error = array(
531                 'type' => E_ERROR + 0x1000000, // This error level is used by HHVM for fatal errors
532                 'message' => 'foo',
533                 'file' => 'bar',
534                 'line' => 123,
535                 'context' => array(123),
536                 'backtrace' => array(456),
537             );
538
539             call_user_func_array(array($handler, 'handleError'), $error);
540             $handler->handleFatalError($error);
541
542             restore_error_handler();
543             restore_exception_handler();
544         } catch (\Exception $e) {
545             restore_error_handler();
546             restore_exception_handler();
547
548             throw $e;
549         }
550     }
551
552     /**
553      * @group legacy
554      */
555     public function testLegacyInterface()
556     {
557         try {
558             $handler = ErrorHandler::register(0);
559             $this->assertFalse($handler->handle(0, 'foo', 'foo.php', 12, array()));
560
561             restore_error_handler();
562             restore_exception_handler();
563
564             $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
565
566             $that = $this;
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);
571             };
572
573             $logger
574                 ->expects($this->once())
575                 ->method('log')
576                 ->will($this->returnCallback($logArgCheck))
577             ;
578
579             $handler = ErrorHandler::register(E_NOTICE);
580             @$handler->setLogger($logger, 'scream');
581             unset($undefVar);
582             @$undefVar++;
583
584             restore_error_handler();
585             restore_exception_handler();
586         } catch (\Exception $e) {
587             restore_error_handler();
588             restore_exception_handler();
589
590             throw $e;
591         }
592     }
593 }