| 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 Psr\Log\NullLogger; |
| 17 |
use Symfony\Component\Debug\BufferingLogger; |
| 18 |
use Symfony\Component\Debug\ErrorHandler; |
| 19 |
use Symfony\Component\Debug\Exception\SilencedErrorContext; |
| 20 |
use Symfony\Component\Debug\Tests\Fixtures\ErrorHandlerThatUsesThePreviousOne; |
| 21 |
use Symfony\Component\Debug\Tests\Fixtures\LoggerThatSetAnErrorHandler; |
| 22 |
|
| 23 |
/** |
| 24 |
* ErrorHandlerTest. |
| 25 |
* |
| 26 |
* @author Robert Schönthal <seroscho@googlemail.com> |
| 27 |
* @author Nicolas Grekas <p@tchwork.com> |
| 28 |
*/ |
| 29 |
class ErrorHandlerTest extends TestCase |
| 30 |
{ |
| 31 |
public function testRegister() |
| 32 |
{ |
| 33 |
$handler = ErrorHandler::register(); |
| 34 |
|
| 35 |
try { |
| 36 |
$this->assertInstanceOf('Symfony\Component\Debug\ErrorHandler', $handler); |
| 37 |
$this->assertSame($handler, ErrorHandler::register()); |
| 38 |
|
| 39 |
$newHandler = new ErrorHandler(); |
| 40 |
|
| 41 |
$this->assertSame($handler, ErrorHandler::register($newHandler, false)); |
| 42 |
$h = set_error_handler('var_dump'); |
| 43 |
restore_error_handler(); |
| 44 |
$this->assertSame([$handler, 'handleError'], $h); |
| 45 |
|
| 46 |
try { |
| 47 |
$this->assertSame($newHandler, ErrorHandler::register($newHandler, true)); |
| 48 |
$h = set_error_handler('var_dump'); |
| 49 |
restore_error_handler(); |
| 50 |
$this->assertSame([$newHandler, 'handleError'], $h); |
| 51 |
} catch (\Exception $e) { |
| 52 |
} |
| 53 |
|
| 54 |
restore_error_handler(); |
| 55 |
restore_exception_handler(); |
| 56 |
|
| 57 |
if (isset($e)) { |
| 58 |
throw $e; |
| 59 |
} |
| 60 |
} catch (\Exception $e) { |
| 61 |
} |
| 62 |
|
| 63 |
restore_error_handler(); |
| 64 |
restore_exception_handler(); |
| 65 |
|
| 66 |
if (isset($e)) { |
| 67 |
throw $e; |
| 68 |
} |
| 69 |
} |
| 70 |
|
| 71 |
public function testErrorGetLast() |
| 72 |
{ |
| 73 |
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); |
| 74 |
$handler = ErrorHandler::register(); |
| 75 |
$handler->setDefaultLogger($logger); |
| 76 |
$handler->screamAt(\E_ALL); |
| 77 |
|
| 78 |
try { |
| 79 |
@trigger_error('Hello', \E_USER_WARNING); |
| 80 |
$expected = [ |
| 81 |
'type' => \E_USER_WARNING, |
| 82 |
'message' => 'Hello', |
| 83 |
'file' => __FILE__, |
| 84 |
'line' => __LINE__ - 5, |
| 85 |
]; |
| 86 |
$this->assertSame($expected, error_get_last()); |
| 87 |
} catch (\Exception $e) { |
| 88 |
restore_error_handler(); |
| 89 |
restore_exception_handler(); |
| 90 |
|
| 91 |
throw $e; |
| 92 |
} |
| 93 |
} |
| 94 |
|
| 95 |
public function testNotice() |
| 96 |
{ |
| 97 |
ErrorHandler::register(); |
| 98 |
|
| 99 |
try { |
| 100 |
self::triggerNotice($this); |
| 101 |
$this->fail('ErrorException expected'); |
| 102 |
} catch (\ErrorException $exception) { |
| 103 |
// if an exception is thrown, the test passed |
| 104 |
if (\PHP_VERSION_ID < 80000) { |
| 105 |
$this->assertEquals(\E_NOTICE, $exception->getSeverity()); |
| 106 |
$this->assertMatchesRegularExpression('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage()); |
| 107 |
} else { |
| 108 |
$this->assertEquals(\E_WARNING, $exception->getSeverity()); |
| 109 |
$this->assertMatchesRegularExpression('/^Warning: Undefined variable \$(foo|bar)/', $exception->getMessage()); |
| 110 |
} |
| 111 |
$this->assertEquals(__FILE__, $exception->getFile()); |
| 112 |
|
| 113 |
$trace = $exception->getTrace(); |
| 114 |
|
| 115 |
$this->assertEquals(__FILE__, $trace[0]['file']); |
| 116 |
$this->assertEquals(__CLASS__, $trace[0]['class']); |
| 117 |
$this->assertEquals('triggerNotice', $trace[0]['function']); |
| 118 |
$this->assertEquals('::', $trace[0]['type']); |
| 119 |
|
| 120 |
$this->assertEquals(__FILE__, $trace[0]['file']); |
| 121 |
$this->assertEquals(__CLASS__, $trace[1]['class']); |
| 122 |
$this->assertEquals(__FUNCTION__, $trace[1]['function']); |
| 123 |
$this->assertEquals('->', $trace[1]['type']); |
| 124 |
} finally { |
| 125 |
restore_error_handler(); |
| 126 |
restore_exception_handler(); |
| 127 |
} |
| 128 |
} |
| 129 |
|
| 130 |
// dummy function to test trace in error handler. |
| 131 |
private static function triggerNotice($that) |
| 132 |
{ |
| 133 |
$that->assertSame('', $foo.$foo.$bar); |
| 134 |
} |
| 135 |
|
| 136 |
public function testConstruct() |
| 137 |
{ |
| 138 |
try { |
| 139 |
$handler = ErrorHandler::register(); |
| 140 |
$handler->throwAt(3, true); |
| 141 |
$this->assertEquals(3 | \E_RECOVERABLE_ERROR | \E_USER_ERROR, $handler->throwAt(0)); |
| 142 |
} finally { |
| 143 |
restore_error_handler(); |
| 144 |
restore_exception_handler(); |
| 145 |
} |
| 146 |
} |
| 147 |
|
| 148 |
public function testDefaultLogger() |
| 149 |
{ |
| 150 |
try { |
| 151 |
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); |
| 152 |
$handler = ErrorHandler::register(); |
| 153 |
|
| 154 |
$handler->setDefaultLogger($logger, \E_NOTICE); |
| 155 |
$handler->setDefaultLogger($logger, [\E_USER_NOTICE => LogLevel::CRITICAL]); |
| 156 |
|
| 157 |
$loggers = [ |
| 158 |
\E_DEPRECATED => [null, LogLevel::INFO], |
| 159 |
\E_USER_DEPRECATED => [null, LogLevel::INFO], |
| 160 |
\E_NOTICE => [$logger, LogLevel::WARNING], |
| 161 |
\E_USER_NOTICE => [$logger, LogLevel::CRITICAL], |
| 162 |
\E_STRICT => [null, LogLevel::WARNING], |
| 163 |
\E_WARNING => [null, LogLevel::WARNING], |
| 164 |
\E_USER_WARNING => [null, LogLevel::WARNING], |
| 165 |
\E_COMPILE_WARNING => [null, LogLevel::WARNING], |
| 166 |
\E_CORE_WARNING => [null, LogLevel::WARNING], |
| 167 |
\E_USER_ERROR => [null, LogLevel::CRITICAL], |
| 168 |
\E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL], |
| 169 |
\E_COMPILE_ERROR => [null, LogLevel::CRITICAL], |
| 170 |
\E_PARSE => [null, LogLevel::CRITICAL], |
| 171 |
\E_ERROR => [null, LogLevel::CRITICAL], |
| 172 |
\E_CORE_ERROR => [null, LogLevel::CRITICAL], |
| 173 |
]; |
| 174 |
$this->assertSame($loggers, $handler->setLoggers([])); |
| 175 |
} finally { |
| 176 |
restore_error_handler(); |
| 177 |
restore_exception_handler(); |
| 178 |
} |
| 179 |
} |
| 180 |
|
| 181 |
public function testHandleError() |
| 182 |
{ |
| 183 |
try { |
| 184 |
$handler = ErrorHandler::register(); |
| 185 |
$handler->throwAt(0, true); |
| 186 |
$this->assertFalse($handler->handleError(0, 'foo', 'foo.php', 12, [])); |
| 187 |
|
| 188 |
restore_error_handler(); |
| 189 |
restore_exception_handler(); |
| 190 |
|
| 191 |
$handler = ErrorHandler::register(); |
| 192 |
$handler->throwAt(3, true); |
| 193 |
$this->assertFalse($handler->handleError(4, 'foo', 'foo.php', 12, [])); |
| 194 |
|
| 195 |
restore_error_handler(); |
| 196 |
restore_exception_handler(); |
| 197 |
|
| 198 |
$handler = ErrorHandler::register(); |
| 199 |
$handler->throwAt(3, true); |
| 200 |
try { |
| 201 |
$handler->handleError(4, 'foo', 'foo.php', 12, []); |
| 202 |
} catch (\ErrorException $e) { |
| 203 |
$this->assertSame('Parse Error: foo', $e->getMessage()); |
| 204 |
$this->assertSame(4, $e->getSeverity()); |
| 205 |
$this->assertSame('foo.php', $e->getFile()); |
| 206 |
$this->assertSame(12, $e->getLine()); |
| 207 |
} |
| 208 |
|
| 209 |
restore_error_handler(); |
| 210 |
restore_exception_handler(); |
| 211 |
|
| 212 |
$handler = ErrorHandler::register(); |
| 213 |
$handler->throwAt(\E_USER_DEPRECATED, true); |
| 214 |
$this->assertFalse($handler->handleError(\E_USER_DEPRECATED, 'foo', 'foo.php', 12, [])); |
| 215 |
|
| 216 |
restore_error_handler(); |
| 217 |
restore_exception_handler(); |
| 218 |
|
| 219 |
$handler = ErrorHandler::register(); |
| 220 |
$handler->throwAt(\E_DEPRECATED, true); |
| 221 |
$this->assertFalse($handler->handleError(\E_DEPRECATED, 'foo', 'foo.php', 12, [])); |
| 222 |
|
| 223 |
restore_error_handler(); |
| 224 |
restore_exception_handler(); |
| 225 |
|
| 226 |
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); |
| 227 |
|
| 228 |
$warnArgCheck = function ($logLevel, $message, $context) { |
| 229 |
$this->assertEquals('info', $logLevel); |
| 230 |
$this->assertEquals('User Deprecated: foo', $message); |
| 231 |
$this->assertArrayHasKey('exception', $context); |
| 232 |
$exception = $context['exception']; |
| 233 |
$this->assertInstanceOf(\ErrorException::class, $exception); |
| 234 |
$this->assertSame('User Deprecated: foo', $exception->getMessage()); |
| 235 |
$this->assertSame(\E_USER_DEPRECATED, $exception->getSeverity()); |
| 236 |
}; |
| 237 |
|
| 238 |
$logger |
| 239 |
->expects($this->once()) |
| 240 |
->method('log') |
| 241 |
->willReturnCallback($warnArgCheck) |
| 242 |
; |
| 243 |
|
| 244 |
$handler = ErrorHandler::register(); |
| 245 |
$handler->setDefaultLogger($logger, \E_USER_DEPRECATED); |
| 246 |
$this->assertTrue($handler->handleError(\E_USER_DEPRECATED, 'foo', 'foo.php', 12, [])); |
| 247 |
|
| 248 |
restore_error_handler(); |
| 249 |
restore_exception_handler(); |
| 250 |
|
| 251 |
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); |
| 252 |
|
| 253 |
$line = null; |
| 254 |
$logArgCheck = function ($level, $message, $context) use (&$line) { |
| 255 |
$this->assertArrayHasKey('exception', $context); |
| 256 |
$exception = $context['exception']; |
| 257 |
|
| 258 |
if (\PHP_VERSION_ID < 80000) { |
| 259 |
$this->assertEquals('Notice: Undefined variable: undefVar', $message); |
| 260 |
$this->assertSame(\E_NOTICE, $exception->getSeverity()); |
| 261 |
} else { |
| 262 |
$this->assertEquals('Warning: Undefined variable $undefVar', $message); |
| 263 |
$this->assertSame(\E_WARNING, $exception->getSeverity()); |
| 264 |
} |
| 265 |
$this->assertInstanceOf(SilencedErrorContext::class, $exception); |
| 266 |
$this->assertSame(__FILE__, $exception->getFile()); |
| 267 |
$this->assertSame($line, $exception->getLine()); |
| 268 |
$this->assertNotEmpty($exception->getTrace()); |
| 269 |
$this->assertSame(1, $exception->count); |
| 270 |
}; |
| 271 |
|
| 272 |
$logger |
| 273 |
->expects($this->once()) |
| 274 |
->method('log') |
| 275 |
->willReturnCallback($logArgCheck) |
| 276 |
; |
| 277 |
|
| 278 |
$handler = ErrorHandler::register(); |
| 279 |
if (\PHP_VERSION_ID < 80000) { |
| 280 |
$handler->setDefaultLogger($logger, \E_NOTICE); |
| 281 |
$handler->screamAt(\E_NOTICE); |
| 282 |
} else { |
| 283 |
$handler->setDefaultLogger($logger, \E_WARNING); |
| 284 |
$handler->screamAt(\E_WARNING); |
| 285 |
} |
| 286 |
unset($undefVar); |
| 287 |
$line = __LINE__ + 1; |
| 288 |
@$undefVar++; |
| 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 testHandleUserError() |
| 301 |
{ |
| 302 |
if (\PHP_VERSION_ID >= 70400) { |
| 303 |
$this->markTestSkipped('PHP 7.4 allows __toString to throw exceptions'); |
| 304 |
} |
| 305 |
|
| 306 |
try { |
| 307 |
$handler = ErrorHandler::register(); |
| 308 |
$handler->throwAt(0, true); |
| 309 |
|
| 310 |
$e = null; |
| 311 |
$x = new \Exception('Foo'); |
| 312 |
|
| 313 |
try { |
| 314 |
$f = new Fixtures\ToStringThrower($x); |
| 315 |
$f .= ''; // Trigger $f->__toString() |
| 316 |
} catch (\Exception $e) { |
| 317 |
} |
| 318 |
|
| 319 |
$this->assertSame($x, $e); |
| 320 |
} finally { |
| 321 |
restore_error_handler(); |
| 322 |
restore_exception_handler(); |
| 323 |
} |
| 324 |
} |
| 325 |
|
| 326 |
public function testHandleDeprecation() |
| 327 |
{ |
| 328 |
$logArgCheck = function ($level, $message, $context) { |
| 329 |
$this->assertEquals(LogLevel::INFO, $level); |
| 330 |
$this->assertArrayHasKey('exception', $context); |
| 331 |
$exception = $context['exception']; |
| 332 |
$this->assertInstanceOf(\ErrorException::class, $exception); |
| 333 |
$this->assertSame('User Deprecated: Foo deprecation', $exception->getMessage()); |
| 334 |
}; |
| 335 |
|
| 336 |
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); |
| 337 |
$logger |
| 338 |
->expects($this->once()) |
| 339 |
->method('log') |
| 340 |
->willReturnCallback($logArgCheck) |
| 341 |
; |
| 342 |
|
| 343 |
$handler = new ErrorHandler(); |
| 344 |
$handler->setDefaultLogger($logger); |
| 345 |
@$handler->handleError(\E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, []); |
| 346 |
} |
| 347 |
|
| 348 |
/** |
| 349 |
* @group no-hhvm |
| 350 |
*/ |
| 351 |
public function testHandleException() |
| 352 |
{ |
| 353 |
try { |
| 354 |
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); |
| 355 |
$handler = ErrorHandler::register(); |
| 356 |
|
| 357 |
$exception = new \Exception('foo'); |
| 358 |
|
| 359 |
$logArgCheck = function ($level, $message, $context) { |
| 360 |
$this->assertSame('Uncaught Exception: foo', $message); |
| 361 |
$this->assertArrayHasKey('exception', $context); |
| 362 |
$this->assertInstanceOf(\Exception::class, $context['exception']); |
| 363 |
}; |
| 364 |
|
| 365 |
$logger |
| 366 |
->expects($this->exactly(2)) |
| 367 |
->method('log') |
| 368 |
->willReturnCallback($logArgCheck) |
| 369 |
; |
| 370 |
|
| 371 |
$handler->setDefaultLogger($logger, \E_ERROR); |
| 372 |
|
| 373 |
try { |
| 374 |
$handler->handleException($exception); |
| 375 |
$this->fail('Exception expected'); |
| 376 |
} catch (\Exception $e) { |
| 377 |
$this->assertSame($exception, $e); |
| 378 |
} |
| 379 |
|
| 380 |
$handler->setExceptionHandler(function ($e) use ($exception) { |
| 381 |
$this->assertSame($exception, $e); |
| 382 |
}); |
| 383 |
|
| 384 |
$handler->handleException($exception); |
| 385 |
} finally { |
| 386 |
restore_error_handler(); |
| 387 |
restore_exception_handler(); |
| 388 |
} |
| 389 |
} |
| 390 |
|
| 391 |
/** |
| 392 |
* @group legacy |
| 393 |
*/ |
| 394 |
public function testErrorStacking() |
| 395 |
{ |
| 396 |
try { |
| 397 |
$handler = ErrorHandler::register(); |
| 398 |
$handler->screamAt(\E_USER_WARNING); |
| 399 |
|
| 400 |
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); |
| 401 |
|
| 402 |
$logger |
| 403 |
->expects($this->exactly(2)) |
| 404 |
->method('log') |
| 405 |
->withConsecutive( |
| 406 |
[$this->equalTo(LogLevel::WARNING), $this->equalTo('Dummy log')], |
| 407 |
[$this->equalTo(LogLevel::DEBUG), $this->equalTo('User Warning: Silenced warning')] |
| 408 |
) |
| 409 |
; |
| 410 |
|
| 411 |
$handler->setDefaultLogger($logger, [\E_USER_WARNING => LogLevel::WARNING]); |
| 412 |
|
| 413 |
ErrorHandler::stackErrors(); |
| 414 |
@trigger_error('Silenced warning', \E_USER_WARNING); |
| 415 |
$logger->log(LogLevel::WARNING, 'Dummy log'); |
| 416 |
ErrorHandler::unstackErrors(); |
| 417 |
} finally { |
| 418 |
restore_error_handler(); |
| 419 |
restore_exception_handler(); |
| 420 |
} |
| 421 |
} |
| 422 |
|
| 423 |
public function testBootstrappingLogger() |
| 424 |
{ |
| 425 |
$bootLogger = new BufferingLogger(); |
| 426 |
$handler = new ErrorHandler($bootLogger); |
| 427 |
|
| 428 |
$loggers = [ |
| 429 |
\E_DEPRECATED => [$bootLogger, LogLevel::INFO], |
| 430 |
\E_USER_DEPRECATED => [$bootLogger, LogLevel::INFO], |
| 431 |
\E_NOTICE => [$bootLogger, LogLevel::WARNING], |
| 432 |
\E_USER_NOTICE => [$bootLogger, LogLevel::WARNING], |
| 433 |
\E_STRICT => [$bootLogger, LogLevel::WARNING], |
| 434 |
\E_WARNING => [$bootLogger, LogLevel::WARNING], |
| 435 |
\E_USER_WARNING => [$bootLogger, LogLevel::WARNING], |
| 436 |
\E_COMPILE_WARNING => [$bootLogger, LogLevel::WARNING], |
| 437 |
\E_CORE_WARNING => [$bootLogger, LogLevel::WARNING], |
| 438 |
\E_USER_ERROR => [$bootLogger, LogLevel::CRITICAL], |
| 439 |
\E_RECOVERABLE_ERROR => [$bootLogger, LogLevel::CRITICAL], |
| 440 |
\E_COMPILE_ERROR => [$bootLogger, LogLevel::CRITICAL], |
| 441 |
\E_PARSE => [$bootLogger, LogLevel::CRITICAL], |
| 442 |
\E_ERROR => [$bootLogger, LogLevel::CRITICAL], |
| 443 |
\E_CORE_ERROR => [$bootLogger, LogLevel::CRITICAL], |
| 444 |
]; |
| 445 |
|
| 446 |
$this->assertSame($loggers, $handler->setLoggers([])); |
| 447 |
|
| 448 |
$handler->handleError(\E_DEPRECATED, 'Foo message', __FILE__, 123, []); |
| 449 |
|
| 450 |
$logs = $bootLogger->cleanLogs(); |
| 451 |
|
| 452 |
$this->assertCount(1, $logs); |
| 453 |
$log = $logs[0]; |
| 454 |
$this->assertSame('info', $log[0]); |
| 455 |
$this->assertSame('Deprecated: Foo message', $log[1]); |
| 456 |
$this->assertArrayHasKey('exception', $log[2]); |
| 457 |
$exception = $log[2]['exception']; |
| 458 |
$this->assertInstanceOf(\ErrorException::class, $exception); |
| 459 |
$this->assertSame('Deprecated: Foo message', $exception->getMessage()); |
| 460 |
$this->assertSame(__FILE__, $exception->getFile()); |
| 461 |
$this->assertSame(123, $exception->getLine()); |
| 462 |
$this->assertSame(\E_DEPRECATED, $exception->getSeverity()); |
| 463 |
|
| 464 |
$bootLogger->log(LogLevel::WARNING, 'Foo message', ['exception' => $exception]); |
| 465 |
|
| 466 |
$mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); |
| 467 |
$mockLogger->expects($this->once()) |
| 468 |
->method('log') |
| 469 |
->with(LogLevel::WARNING, 'Foo message', ['exception' => $exception]); |
| 470 |
|
| 471 |
$handler->setLoggers([\E_DEPRECATED => [$mockLogger, LogLevel::WARNING]]); |
| 472 |
} |
| 473 |
|
| 474 |
/** |
| 475 |
* @group no-hhvm |
| 476 |
*/ |
| 477 |
public function testSettingLoggerWhenExceptionIsBuffered() |
| 478 |
{ |
| 479 |
$bootLogger = new BufferingLogger(); |
| 480 |
$handler = new ErrorHandler($bootLogger); |
| 481 |
|
| 482 |
$exception = new \Exception('Foo message'); |
| 483 |
|
| 484 |
$mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); |
| 485 |
$mockLogger->expects($this->once()) |
| 486 |
->method('log') |
| 487 |
->with(LogLevel::CRITICAL, 'Uncaught Exception: Foo message', ['exception' => $exception]); |
| 488 |
|
| 489 |
$handler->setExceptionHandler(function () use ($handler, $mockLogger) { |
| 490 |
$handler->setDefaultLogger($mockLogger); |
| 491 |
}); |
| 492 |
|
| 493 |
$handler->handleException($exception); |
| 494 |
} |
| 495 |
|
| 496 |
/** |
| 497 |
* @group no-hhvm |
| 498 |
*/ |
| 499 |
public function testHandleFatalError() |
| 500 |
{ |
| 501 |
try { |
| 502 |
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); |
| 503 |
$handler = ErrorHandler::register(); |
| 504 |
|
| 505 |
$error = [ |
| 506 |
'type' => \E_PARSE, |
| 507 |
'message' => 'foo', |
| 508 |
'file' => 'bar', |
| 509 |
'line' => 123, |
| 510 |
]; |
| 511 |
|
| 512 |
$logArgCheck = function ($level, $message, $context) { |
| 513 |
$this->assertEquals('Fatal Parse Error: foo', $message); |
| 514 |
$this->assertArrayHasKey('exception', $context); |
| 515 |
$this->assertInstanceOf(\Exception::class, $context['exception']); |
| 516 |
}; |
| 517 |
|
| 518 |
$logger |
| 519 |
->expects($this->once()) |
| 520 |
->method('log') |
| 521 |
->willReturnCallback($logArgCheck) |
| 522 |
; |
| 523 |
|
| 524 |
$handler->setDefaultLogger($logger, \E_PARSE); |
| 525 |
|
| 526 |
$handler->handleFatalError($error); |
| 527 |
|
| 528 |
restore_error_handler(); |
| 529 |
restore_exception_handler(); |
| 530 |
} catch (\Exception $e) { |
| 531 |
restore_error_handler(); |
| 532 |
restore_exception_handler(); |
| 533 |
|
| 534 |
throw $e; |
| 535 |
} |
| 536 |
} |
| 537 |
|
| 538 |
/** |
| 539 |
* @requires PHP 7 |
| 540 |
*/ |
| 541 |
public function testHandleErrorException() |
| 542 |
{ |
| 543 |
$exception = new \Error("Class 'IReallyReallyDoNotExistAnywhereInTheRepositoryISwear' not found"); |
| 544 |
|
| 545 |
$handler = new ErrorHandler(); |
| 546 |
$handler->setExceptionHandler(function () use (&$args) { |
| 547 |
$args = \func_get_args(); |
| 548 |
}); |
| 549 |
|
| 550 |
$handler->handleException($exception); |
| 551 |
|
| 552 |
$this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]); |
| 553 |
$this->assertStringStartsWith("Attempted to load class \"IReallyReallyDoNotExistAnywhereInTheRepositoryISwear\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage()); |
| 554 |
} |
| 555 |
|
| 556 |
/** |
| 557 |
* @group no-hhvm |
| 558 |
*/ |
| 559 |
public function testHandleFatalErrorOnHHVM() |
| 560 |
{ |
| 561 |
if (!\defined('HHVM_VERSION')) { |
| 562 |
$this->markTestSkipped('This test requires HHVM.'); |
| 563 |
} |
| 564 |
|
| 565 |
try { |
| 566 |
$handler = ErrorHandler::register(); |
| 567 |
|
| 568 |
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); |
| 569 |
$logger |
| 570 |
->expects($this->once()) |
| 571 |
->method('log') |
| 572 |
->with( |
| 573 |
$this->equalTo(LogLevel::CRITICAL), |
| 574 |
$this->equalTo('Fatal Error: foo') |
| 575 |
) |
| 576 |
; |
| 577 |
|
| 578 |
$handler->setDefaultLogger($logger, \E_ERROR); |
| 579 |
|
| 580 |
$error = [ |
| 581 |
'type' => \E_ERROR + 0x1000000, // This error level is used by HHVM for fatal errors |
| 582 |
'message' => 'foo', |
| 583 |
'file' => 'bar', |
| 584 |
'line' => 123, |
| 585 |
'context' => [123], |
| 586 |
'backtrace' => [456], |
| 587 |
]; |
| 588 |
|
| 589 |
\call_user_func_array([$handler, 'handleError'], $error); |
| 590 |
$handler->handleFatalError($error); |
| 591 |
} finally { |
| 592 |
restore_error_handler(); |
| 593 |
restore_exception_handler(); |
| 594 |
} |
| 595 |
} |
| 596 |
|
| 597 |
/** |
| 598 |
* @group no-hhvm |
| 599 |
*/ |
| 600 |
public function testCustomExceptionHandler() |
| 601 |
{ |
| 602 |
$this->expectException('Exception'); |
| 603 |
$handler = new ErrorHandler(); |
| 604 |
$handler->setExceptionHandler(function ($e) use ($handler) { |
| 605 |
$handler->handleException($e); |
| 606 |
}); |
| 607 |
|
| 608 |
$handler->handleException(new \Exception()); |
| 609 |
} |
| 610 |
|
| 611 |
/** |
| 612 |
* @dataProvider errorHandlerWhenLoggingProvider |
| 613 |
*/ |
| 614 |
public function testErrorHandlerWhenLogging($previousHandlerWasDefined, $loggerSetsAnotherHandler, $nextHandlerIsDefined) |
| 615 |
{ |
| 616 |
try { |
| 617 |
if ($previousHandlerWasDefined) { |
| 618 |
set_error_handler('count'); |
| 619 |
} |
| 620 |
|
| 621 |
$logger = $loggerSetsAnotherHandler ? new LoggerThatSetAnErrorHandler() : new NullLogger(); |
| 622 |
|
| 623 |
$handler = ErrorHandler::register(); |
| 624 |
$handler->setDefaultLogger($logger); |
| 625 |
|
| 626 |
if ($nextHandlerIsDefined) { |
| 627 |
$handler = ErrorHandlerThatUsesThePreviousOne::register(); |
| 628 |
} |
| 629 |
|
| 630 |
@trigger_error('foo', \E_USER_DEPRECATED); |
| 631 |
@trigger_error('bar', \E_USER_DEPRECATED); |
| 632 |
|
| 633 |
$this->assertSame([$handler, 'handleError'], set_error_handler('var_dump')); |
| 634 |
|
| 635 |
if ($logger instanceof LoggerThatSetAnErrorHandler) { |
| 636 |
$this->assertCount(2, $logger->cleanLogs()); |
| 637 |
} |
| 638 |
|
| 639 |
restore_error_handler(); |
| 640 |
|
| 641 |
if ($previousHandlerWasDefined) { |
| 642 |
restore_error_handler(); |
| 643 |
} |
| 644 |
|
| 645 |
if ($nextHandlerIsDefined) { |
| 646 |
restore_error_handler(); |
| 647 |
} |
| 648 |
} finally { |
| 649 |
restore_error_handler(); |
| 650 |
restore_exception_handler(); |
| 651 |
} |
| 652 |
} |
| 653 |
|
| 654 |
public function errorHandlerWhenLoggingProvider() |
| 655 |
{ |
| 656 |
foreach ([false, true] as $previousHandlerWasDefined) { |
| 657 |
foreach ([false, true] as $loggerSetsAnotherHandler) { |
| 658 |
foreach ([false, true] as $nextHandlerIsDefined) { |
| 659 |
yield [$previousHandlerWasDefined, $loggerSetsAnotherHandler, $nextHandlerIsDefined]; |
| 660 |
} |
| 661 |
} |
| 662 |
} |
| 663 |
} |
| 664 |
} |
| 665 |
|