[Debug] ensured that a fatal PHP error is actually fatal after being handled by our error handler

This commit is contained in:
Fabien Potencier 2013-11-28 17:41:31 +01:00
parent ad9008e452
commit d7a4cfb0e4
3 changed files with 54 additions and 7 deletions

View File

@ -13,6 +13,7 @@ namespace Symfony\Component\Debug;
use Symfony\Component\Debug\Exception\FatalErrorException;
use Symfony\Component\Debug\Exception\ContextErrorException;
use Symfony\Component\Debug\Exception\DummyException;
use Psr\Log\LoggerInterface;
/**
@ -129,13 +130,29 @@ class ErrorHandler
// Exceptions thrown from error handlers are sometimes not caught by the exception
// handler, so we invoke it directly (https://bugs.php.net/bug.php?id=54275)
$exceptionHandler = set_exception_handler(function() {});
$exceptionHandler = set_exception_handler(function () {});
restore_exception_handler();
if (is_array($exceptionHandler) && $exceptionHandler[0] instanceof ExceptionHandler) {
$exceptionHandler[0]->handle($exception);
return true;
if (!class_exists('Symfony\Component\Debug\Exception\DummyException')) {
require __DIR__.'/Exception/DummyException.php';
}
// we must stop the PHP script execution, as the exception has
// already been dealt with, so, let's throw an exception that
// will be catched by a dummy exception handler
set_exception_handler(function (\Exception $e) use ($exceptionHandler) {
if (!$e instanceof DummyException) {
// happens if our dummy exception is catched by a
// catch-all from user code, in which case, let's the
// current handler handle this "new" exception
call_user_func($exceptionHandler, $e);
}
});
throw new DummyException();
}
}

View File

@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Debug\Exception;
/**
* Used to stop execution of a PHP script after handling a fatal error.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class DummyException extends \ErrorException
{
}

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Debug\Tests;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\Exception\DummyException;
/**
* ErrorHandlerTest
@ -81,11 +82,15 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
$bar = 123;
// trigger compile time error
eval(<<<'PHP'
try {
eval(<<<'PHP'
class _BaseCompileTimeError { function foo() {} }
class _CompileTimeError extends _BaseCompileTimeError { function foo($invalid) {} }
PHP
);
);
} catch (DummyException $e) {
// if an exception is thrown, the test passed
}
restore_error_handler();
restore_exception_handler();
@ -100,7 +105,7 @@ PHP
$exceptionCheck = function($exception) use ($that) {
$that->assertInstanceOf('Symfony\Component\Debug\Exception\ContextErrorException', $exception);
$that->assertEquals(E_NOTICE, $exception->getSeverity());
$that->assertEquals(__LINE__ + 36, $exception->getLine());
$that->assertEquals(__LINE__ + 40, $exception->getLine());
$that->assertEquals(__FILE__, $exception->getFile());
$that->assertRegexp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
$that->assertArrayHasKey('foobar', $exception->getContext());
@ -121,12 +126,16 @@ PHP
$that->assertEquals('->', $trace[2]['type']);
};
$exceptionHandler->expects($this->exactly(3))
$exceptionHandler->expects($this->once())
->method('handle')
->will($this->returnCallback($exceptionCheck));
ErrorHandler::register();
self::triggerNotice($this);
try {
self::triggerNotice($this);
} catch (DummyException $e) {
// if an exception is thrown, the test passed
}
restore_error_handler();
}