Security update for Core, with self-updated composer
[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\BufferingLogger;
17 use Symfony\Component\Debug\ErrorHandler;
18 use Symfony\Component\Debug\Exception\SilencedErrorContext;
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($handler, 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('ErrorException expected');
75         } catch (\ErrorException $exception) {
76             // if an exception is thrown, the test passed
77             $this->assertEquals(E_NOTICE, $exception->getSeverity());
78             $this->assertEquals(__FILE__, $exception->getFile());
79             $this->assertRegExp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
80
81             $trace = $exception->getTrace();
82
83             $this->assertEquals(__FILE__, $trace[0]['file']);
84             $this->assertEquals(__CLASS__, $trace[0]['class']);
85             $this->assertEquals('triggerNotice', $trace[0]['function']);
86             $this->assertEquals('::', $trace[0]['type']);
87
88             $this->assertEquals(__FILE__, $trace[0]['file']);
89             $this->assertEquals(__CLASS__, $trace[1]['class']);
90             $this->assertEquals(__FUNCTION__, $trace[1]['function']);
91             $this->assertEquals('->', $trace[1]['type']);
92         } finally {
93             restore_error_handler();
94             restore_exception_handler();
95         }
96     }
97
98     // dummy function to test trace in error handler.
99     private static function triggerNotice($that)
100     {
101         $that->assertSame('', $foo.$foo.$bar);
102     }
103
104     public function testConstruct()
105     {
106         try {
107             $handler = ErrorHandler::register();
108             $handler->throwAt(3, true);
109             $this->assertEquals(3 | E_RECOVERABLE_ERROR | E_USER_ERROR, $handler->throwAt(0));
110         } finally {
111             restore_error_handler();
112             restore_exception_handler();
113         }
114     }
115
116     public function testDefaultLogger()
117     {
118         try {
119             $handler = ErrorHandler::register();
120
121             $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
122
123             $handler->setDefaultLogger($logger, E_NOTICE);
124             $handler->setDefaultLogger($logger, array(E_USER_NOTICE => LogLevel::CRITICAL));
125
126             $loggers = array(
127                 E_DEPRECATED => array(null, LogLevel::INFO),
128                 E_USER_DEPRECATED => array(null, LogLevel::INFO),
129                 E_NOTICE => array($logger, LogLevel::WARNING),
130                 E_USER_NOTICE => array($logger, LogLevel::CRITICAL),
131                 E_STRICT => array(null, LogLevel::WARNING),
132                 E_WARNING => array(null, LogLevel::WARNING),
133                 E_USER_WARNING => array(null, LogLevel::WARNING),
134                 E_COMPILE_WARNING => array(null, LogLevel::WARNING),
135                 E_CORE_WARNING => array(null, LogLevel::WARNING),
136                 E_USER_ERROR => array(null, LogLevel::CRITICAL),
137                 E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL),
138                 E_COMPILE_ERROR => array(null, LogLevel::CRITICAL),
139                 E_PARSE => array(null, LogLevel::CRITICAL),
140                 E_ERROR => array(null, LogLevel::CRITICAL),
141                 E_CORE_ERROR => array(null, LogLevel::CRITICAL),
142             );
143             $this->assertSame($loggers, $handler->setLoggers(array()));
144         } finally {
145             restore_error_handler();
146             restore_exception_handler();
147         }
148     }
149
150     public function testHandleError()
151     {
152         try {
153             $handler = ErrorHandler::register();
154             $handler->throwAt(0, true);
155             $this->assertFalse($handler->handleError(0, 'foo', 'foo.php', 12, array()));
156
157             restore_error_handler();
158             restore_exception_handler();
159
160             $handler = ErrorHandler::register();
161             $handler->throwAt(3, true);
162             $this->assertFalse($handler->handleError(4, 'foo', 'foo.php', 12, array()));
163
164             restore_error_handler();
165             restore_exception_handler();
166
167             $handler = ErrorHandler::register();
168             $handler->throwAt(3, true);
169             try {
170                 $handler->handleError(4, 'foo', 'foo.php', 12, array());
171             } catch (\ErrorException $e) {
172                 $this->assertSame('Parse Error: foo', $e->getMessage());
173                 $this->assertSame(4, $e->getSeverity());
174                 $this->assertSame('foo.php', $e->getFile());
175                 $this->assertSame(12, $e->getLine());
176             }
177
178             restore_error_handler();
179             restore_exception_handler();
180
181             $handler = ErrorHandler::register();
182             $handler->throwAt(E_USER_DEPRECATED, true);
183             $this->assertFalse($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array()));
184
185             restore_error_handler();
186             restore_exception_handler();
187
188             $handler = ErrorHandler::register();
189             $handler->throwAt(E_DEPRECATED, true);
190             $this->assertFalse($handler->handleError(E_DEPRECATED, 'foo', 'foo.php', 12, array()));
191
192             restore_error_handler();
193             restore_exception_handler();
194
195             $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
196
197             $warnArgCheck = function ($logLevel, $message, $context) {
198                 $this->assertEquals('info', $logLevel);
199                 $this->assertEquals('User Deprecated: foo', $message);
200                 $this->assertArrayHasKey('exception', $context);
201                 $exception = $context['exception'];
202                 $this->assertInstanceOf(\ErrorException::class, $exception);
203                 $this->assertSame('User Deprecated: foo', $exception->getMessage());
204                 $this->assertSame(E_USER_DEPRECATED, $exception->getSeverity());
205             };
206
207             $logger
208                 ->expects($this->once())
209                 ->method('log')
210                 ->will($this->returnCallback($warnArgCheck))
211             ;
212
213             $handler = ErrorHandler::register();
214             $handler->setDefaultLogger($logger, E_USER_DEPRECATED);
215             $this->assertTrue($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array()));
216
217             restore_error_handler();
218             restore_exception_handler();
219
220             $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
221
222             $line = null;
223             $logArgCheck = function ($level, $message, $context) use (&$line) {
224                 $this->assertEquals('Notice: Undefined variable: undefVar', $message);
225                 $this->assertArrayHasKey('exception', $context);
226                 $exception = $context['exception'];
227                 $this->assertInstanceOf(SilencedErrorContext::class, $exception);
228                 $this->assertSame(E_NOTICE, $exception->getSeverity());
229                 $this->assertSame(__FILE__, $exception->getFile());
230                 $this->assertSame($line, $exception->getLine());
231                 $this->assertNotEmpty($exception->getTrace());
232                 $this->assertSame(1, $exception->count);
233             };
234
235             $logger
236                 ->expects($this->once())
237                 ->method('log')
238                 ->will($this->returnCallback($logArgCheck))
239             ;
240
241             $handler = ErrorHandler::register();
242             $handler->setDefaultLogger($logger, E_NOTICE);
243             $handler->screamAt(E_NOTICE);
244             unset($undefVar);
245             $line = __LINE__ + 1;
246             @$undefVar++;
247
248             restore_error_handler();
249             restore_exception_handler();
250         } catch (\Exception $e) {
251             restore_error_handler();
252             restore_exception_handler();
253
254             throw $e;
255         }
256     }
257
258     public function testHandleUserError()
259     {
260         try {
261             $handler = ErrorHandler::register();
262             $handler->throwAt(0, true);
263
264             $e = null;
265             $x = new \Exception('Foo');
266
267             try {
268                 $f = new Fixtures\ToStringThrower($x);
269                 $f .= ''; // Trigger $f->__toString()
270             } catch (\Exception $e) {
271             }
272
273             $this->assertSame($x, $e);
274         } finally {
275             restore_error_handler();
276             restore_exception_handler();
277         }
278     }
279
280     public function testHandleDeprecation()
281     {
282         $logArgCheck = function ($level, $message, $context) {
283             $this->assertEquals(LogLevel::INFO, $level);
284             $this->assertArrayHasKey('exception', $context);
285             $exception = $context['exception'];
286             $this->assertInstanceOf(\ErrorException::class, $exception);
287             $this->assertSame('User Deprecated: Foo deprecation', $exception->getMessage());
288         };
289
290         $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
291         $logger
292             ->expects($this->once())
293             ->method('log')
294             ->will($this->returnCallback($logArgCheck))
295         ;
296
297         $handler = new ErrorHandler();
298         $handler->setDefaultLogger($logger);
299         @$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, array());
300     }
301
302     /**
303      * @group no-hhvm
304      */
305     public function testHandleException()
306     {
307         try {
308             $handler = ErrorHandler::register();
309
310             $exception = new \Exception('foo');
311
312             $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
313
314             $logArgCheck = function ($level, $message, $context) {
315                 $this->assertSame('Uncaught Exception: foo', $message);
316                 $this->assertArrayHasKey('exception', $context);
317                 $this->assertInstanceOf(\Exception::class, $context['exception']);
318             };
319
320             $logger
321                 ->expects($this->exactly(2))
322                 ->method('log')
323                 ->will($this->returnCallback($logArgCheck))
324             ;
325
326             $handler->setDefaultLogger($logger, E_ERROR);
327
328             try {
329                 $handler->handleException($exception);
330                 $this->fail('Exception expected');
331             } catch (\Exception $e) {
332                 $this->assertSame($exception, $e);
333             }
334
335             $handler->setExceptionHandler(function ($e) use ($exception) {
336                 $this->assertSame($exception, $e);
337             });
338
339             $handler->handleException($exception);
340         } finally {
341             restore_error_handler();
342             restore_exception_handler();
343         }
344     }
345
346     /**
347      * @group legacy
348      */
349     public function testErrorStacking()
350     {
351         try {
352             $handler = ErrorHandler::register();
353             $handler->screamAt(E_USER_WARNING);
354
355             $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
356
357             $logger
358                 ->expects($this->exactly(2))
359                 ->method('log')
360                 ->withConsecutive(
361                     array($this->equalTo(LogLevel::WARNING), $this->equalTo('Dummy log')),
362                     array($this->equalTo(LogLevel::DEBUG), $this->equalTo('User Warning: Silenced warning'))
363                 )
364             ;
365
366             $handler->setDefaultLogger($logger, array(E_USER_WARNING => LogLevel::WARNING));
367
368             ErrorHandler::stackErrors();
369             @trigger_error('Silenced warning', E_USER_WARNING);
370             $logger->log(LogLevel::WARNING, 'Dummy log');
371             ErrorHandler::unstackErrors();
372         } finally {
373             restore_error_handler();
374             restore_exception_handler();
375         }
376     }
377
378     public function testBootstrappingLogger()
379     {
380         $bootLogger = new BufferingLogger();
381         $handler = new ErrorHandler($bootLogger);
382
383         $loggers = array(
384             E_DEPRECATED => array($bootLogger, LogLevel::INFO),
385             E_USER_DEPRECATED => array($bootLogger, LogLevel::INFO),
386             E_NOTICE => array($bootLogger, LogLevel::WARNING),
387             E_USER_NOTICE => array($bootLogger, LogLevel::WARNING),
388             E_STRICT => array($bootLogger, LogLevel::WARNING),
389             E_WARNING => array($bootLogger, LogLevel::WARNING),
390             E_USER_WARNING => array($bootLogger, LogLevel::WARNING),
391             E_COMPILE_WARNING => array($bootLogger, LogLevel::WARNING),
392             E_CORE_WARNING => array($bootLogger, LogLevel::WARNING),
393             E_USER_ERROR => array($bootLogger, LogLevel::CRITICAL),
394             E_RECOVERABLE_ERROR => array($bootLogger, LogLevel::CRITICAL),
395             E_COMPILE_ERROR => array($bootLogger, LogLevel::CRITICAL),
396             E_PARSE => array($bootLogger, LogLevel::CRITICAL),
397             E_ERROR => array($bootLogger, LogLevel::CRITICAL),
398             E_CORE_ERROR => array($bootLogger, LogLevel::CRITICAL),
399         );
400
401         $this->assertSame($loggers, $handler->setLoggers(array()));
402
403         $handler->handleError(E_DEPRECATED, 'Foo message', __FILE__, 123, array());
404
405         $logs = $bootLogger->cleanLogs();
406
407         $this->assertCount(1, $logs);
408         $log = $logs[0];
409         $this->assertSame('info', $log[0]);
410         $this->assertSame('Deprecated: Foo message', $log[1]);
411         $this->assertArrayHasKey('exception', $log[2]);
412         $exception = $log[2]['exception'];
413         $this->assertInstanceOf(\ErrorException::class, $exception);
414         $this->assertSame('Deprecated: Foo message', $exception->getMessage());
415         $this->assertSame(__FILE__, $exception->getFile());
416         $this->assertSame(123, $exception->getLine());
417         $this->assertSame(E_DEPRECATED, $exception->getSeverity());
418
419         $bootLogger->log(LogLevel::WARNING, 'Foo message', array('exception' => $exception));
420
421         $mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
422         $mockLogger->expects($this->once())
423             ->method('log')
424             ->with(LogLevel::WARNING, 'Foo message', array('exception' => $exception));
425
426         $handler->setLoggers(array(E_DEPRECATED => array($mockLogger, LogLevel::WARNING)));
427     }
428
429     /**
430      * @group no-hhvm
431      */
432     public function testSettingLoggerWhenExceptionIsBuffered()
433     {
434         $bootLogger = new BufferingLogger();
435         $handler = new ErrorHandler($bootLogger);
436
437         $exception = new \Exception('Foo message');
438
439         $mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
440         $mockLogger->expects($this->once())
441             ->method('log')
442             ->with(LogLevel::CRITICAL, 'Uncaught Exception: Foo message', array('exception' => $exception));
443
444         $handler->setExceptionHandler(function () use ($handler, $mockLogger) {
445             $handler->setDefaultLogger($mockLogger);
446         });
447
448         $handler->handleException($exception);
449     }
450
451     /**
452      * @group no-hhvm
453      */
454     public function testHandleFatalError()
455     {
456         try {
457             $handler = ErrorHandler::register();
458
459             $error = array(
460                 'type' => E_PARSE,
461                 'message' => 'foo',
462                 'file' => 'bar',
463                 'line' => 123,
464             );
465
466             $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
467
468             $logArgCheck = function ($level, $message, $context) {
469                 $this->assertEquals('Fatal Parse Error: foo', $message);
470                 $this->assertArrayHasKey('exception', $context);
471                 $this->assertInstanceOf(\Exception::class, $context['exception']);
472             };
473
474             $logger
475                 ->expects($this->once())
476                 ->method('log')
477                 ->will($this->returnCallback($logArgCheck))
478             ;
479
480             $handler->setDefaultLogger($logger, E_PARSE);
481
482             $handler->handleFatalError($error);
483
484             restore_error_handler();
485             restore_exception_handler();
486         } catch (\Exception $e) {
487             restore_error_handler();
488             restore_exception_handler();
489
490             throw $e;
491         }
492     }
493
494     /**
495      * @requires PHP 7
496      */
497     public function testHandleErrorException()
498     {
499         $exception = new \Error("Class 'Foo' not found");
500
501         $handler = new ErrorHandler();
502         $handler->setExceptionHandler(function () use (&$args) {
503             $args = func_get_args();
504         });
505
506         $handler->handleException($exception);
507
508         $this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]);
509         $this->assertStringStartsWith("Attempted to load class \"Foo\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage());
510     }
511
512     /**
513      * @group no-hhvm
514      */
515     public function testHandleFatalErrorOnHHVM()
516     {
517         try {
518             $handler = ErrorHandler::register();
519
520             $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
521             $logger
522                 ->expects($this->once())
523                 ->method('log')
524                 ->with(
525                     $this->equalTo(LogLevel::CRITICAL),
526                     $this->equalTo('Fatal Error: foo')
527                 )
528             ;
529
530             $handler->setDefaultLogger($logger, E_ERROR);
531
532             $error = array(
533                 'type' => E_ERROR + 0x1000000, // This error level is used by HHVM for fatal errors
534                 'message' => 'foo',
535                 'file' => 'bar',
536                 'line' => 123,
537                 'context' => array(123),
538                 'backtrace' => array(456),
539             );
540
541             call_user_func_array(array($handler, 'handleError'), $error);
542             $handler->handleFatalError($error);
543         } finally {
544             restore_error_handler();
545             restore_exception_handler();
546         }
547     }
548 }