[Debug] Handled errors
This commit is contained in:
parent
f72eb34fc4
commit
0279fbfdad
@ -9,6 +9,7 @@
|
||||
<parameter key="debug.stopwatch.class">Symfony\Component\Stopwatch\Stopwatch</parameter>
|
||||
<parameter key="debug.container.dump">%kernel.cache_dir%/%kernel.container_class%.xml</parameter>
|
||||
<parameter key="debug.controller_resolver.class">Symfony\Component\HttpKernel\Controller\TraceableControllerResolver</parameter>
|
||||
<parameter key="debug.fatal_error_exceptions_listener.class">Symfony\Component\HttpKernel\EventListener\FatalErrorExceptionsListener</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
@ -39,5 +40,13 @@
|
||||
<argument>scream</argument>
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
</service>
|
||||
|
||||
<service id="debug.fatal_error_exceptions_listener" class="%debug.fatal_error_exceptions_listener.class%">
|
||||
<tag name="kernel.event_subscriber" />
|
||||
<argument type="collection">
|
||||
<argument type="service" id="http_kernel" on-invalid="null" />
|
||||
<argument>handleFatalErrorException</argument>
|
||||
</argument>
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
@ -21,7 +21,7 @@
|
||||
"symfony/config" : "~2.4",
|
||||
"symfony/event-dispatcher": "~2.5",
|
||||
"symfony/http-foundation": "~2.4",
|
||||
"symfony/http-kernel": "~2.4",
|
||||
"symfony/http-kernel": "~2.5",
|
||||
"symfony/filesystem": "~2.3",
|
||||
"symfony/routing": "~2.2",
|
||||
"symfony/security-core": "~2.4",
|
||||
|
@ -4,7 +4,12 @@ CHANGELOG
|
||||
2.5.0
|
||||
-----
|
||||
|
||||
* added HandledErrorException
|
||||
* added ErrorHandler::setFatalErrorExceptionHandler()
|
||||
* added UndefinedMethodFatalErrorHandler
|
||||
* deprecated ExceptionHandlerInterface
|
||||
* deprecated ContextErrorException
|
||||
* deprecated DummyException
|
||||
|
||||
2.4.0
|
||||
-----
|
||||
|
@ -13,9 +13,9 @@ namespace Symfony\Component\Debug;
|
||||
|
||||
use Psr\Log\LogLevel;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Debug\Exception\ContextErrorException;
|
||||
use Symfony\Component\Debug\Exception\FatalErrorException;
|
||||
use Symfony\Component\Debug\Exception\DummyException;
|
||||
use Symfony\Component\Debug\Exception\HandledErrorException;
|
||||
use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
|
||||
use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
|
||||
use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
|
||||
@ -63,6 +63,8 @@ class ErrorHandler
|
||||
|
||||
private static $stackedErrorLevels = array();
|
||||
|
||||
private static $fatalHandler = false;
|
||||
|
||||
/**
|
||||
* Registers the error handler.
|
||||
*
|
||||
@ -117,7 +119,17 @@ class ErrorHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ContextErrorException When error_reporting returns error
|
||||
* Sets a fatal error exception handler.
|
||||
*
|
||||
* @param callable $handler An handler that will be called on FatalErrorException
|
||||
*/
|
||||
public static function setFatalErrorExceptionHandler($handler)
|
||||
{
|
||||
self::$fatalHandler = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws HandledErrorException When error_reporting returns error
|
||||
*/
|
||||
public function handle($level, $message, $file = 'unknown', $line = 0, $context = array())
|
||||
{
|
||||
@ -152,7 +164,7 @@ class ErrorHandler
|
||||
$exceptionHandler = set_exception_handler('var_dump');
|
||||
restore_exception_handler();
|
||||
|
||||
if (is_array($exceptionHandler) && $exceptionHandler[0] instanceof ExceptionHandlerInterface) {
|
||||
if ($exceptionHandler) {
|
||||
if (self::$stackedErrorLevels) {
|
||||
self::$stackedErrors[] = func_get_args();
|
||||
|
||||
@ -160,22 +172,22 @@ class ErrorHandler
|
||||
}
|
||||
|
||||
$exception = sprintf('%s: %s in %s line %d', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message, $file, $line);
|
||||
$exception = new ContextErrorException($exception, 0, $level, $file, $line, $context);
|
||||
$exceptionHandler[0]->handle($exception);
|
||||
$exception = new HandledErrorException($exception, 0, $level, $file, $line, $context);
|
||||
$exception->handleWith($exceptionHandler);
|
||||
|
||||
// we must stop the PHP script execution, as the exception has
|
||||
// already been dealt with, so, let's throw an exception that
|
||||
// will be caught by a dummy exception handler
|
||||
set_exception_handler(function (\Exception $e) use ($exceptionHandler) {
|
||||
if (!$e instanceof DummyException) {
|
||||
// happens if our dummy exception is caught by a
|
||||
// catch-all from user code, in which case, let's the
|
||||
if (!$e instanceof HandledErrorException && !$e instanceof DummyException) {
|
||||
// happens if our handled exception is caught by a
|
||||
// catch-all from user code, in which case, let the
|
||||
// current handler handle this "new" exception
|
||||
call_user_func($exceptionHandler, $e);
|
||||
}
|
||||
});
|
||||
|
||||
throw new DummyException();
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,6 +268,7 @@ class ErrorHandler
|
||||
public function handleFatal()
|
||||
{
|
||||
$this->reservedMemory = '';
|
||||
gc_collect_cycles();
|
||||
$error = error_get_last();
|
||||
|
||||
while (self::$stackedErrorLevels) {
|
||||
@ -281,16 +294,14 @@ class ErrorHandler
|
||||
self::$loggers['emergency']->emergency($error['message'], $fatal);
|
||||
}
|
||||
|
||||
if (!$this->displayErrors) {
|
||||
return;
|
||||
}
|
||||
if ($this->displayErrors) {
|
||||
// get current exception handler
|
||||
$exceptionHandler = set_exception_handler('var_dump');
|
||||
restore_exception_handler();
|
||||
|
||||
// get current exception handler
|
||||
$exceptionHandler = set_exception_handler('var_dump');
|
||||
restore_exception_handler();
|
||||
|
||||
if (is_array($exceptionHandler) && $exceptionHandler[0] instanceof ExceptionHandlerInterface) {
|
||||
$this->handleFatalError($exceptionHandler[0], $error);
|
||||
if ($exceptionHandler || self::$fatalHandler) {
|
||||
$this->handleFatalError($exceptionHandler, $error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,18 +321,25 @@ class ErrorHandler
|
||||
);
|
||||
}
|
||||
|
||||
private function handleFatalError(ExceptionHandlerInterface $exceptionHandler, array $error)
|
||||
private function handleFatalError($exceptionHandler, array $error)
|
||||
{
|
||||
$level = isset($this->levels[$error['type']]) ? $this->levels[$error['type']] : $error['type'];
|
||||
$message = sprintf('%s: %s in %s line %d', $level, $error['message'], $error['file'], $error['line']);
|
||||
$exception = new FatalErrorException($message, 0, $error['type'], $error['file'], $error['line']);
|
||||
$exception = new FatalErrorException($message, 0, $error['type'], $error['file'], $error['line'], 3);
|
||||
|
||||
foreach ($this->getFatalErrorHandlers() as $handler) {
|
||||
if ($ex = $handler->handleError($error, $exception)) {
|
||||
return $exceptionHandler->handle($ex);
|
||||
$exception = $ex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$exceptionHandler->handle($exception);
|
||||
if ($exceptionHandler) {
|
||||
$exception->handleWith($exceptionHandler);
|
||||
}
|
||||
|
||||
if (self::$fatalHandler) {
|
||||
call_user_func(self::$fatalHandler, $exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,5 +28,6 @@ class ClassNotFoundException extends FatalErrorException
|
||||
$previous->getLine(),
|
||||
$previous->getPrevious()
|
||||
);
|
||||
$this->setTrace($previous->getTrace());
|
||||
}
|
||||
}
|
||||
|
@ -15,22 +15,9 @@ namespace Symfony\Component\Debug\Exception;
|
||||
* Error Exception with Variable Context.
|
||||
*
|
||||
* @author Christian Sciberras <uuf6429@gmail.com>
|
||||
*
|
||||
* @deprecated since version 2.5, to be removed in 3.0.
|
||||
*/
|
||||
class ContextErrorException extends \ErrorException
|
||||
class ContextErrorException extends HandledErrorException
|
||||
{
|
||||
private $context = array();
|
||||
|
||||
public function __construct($message, $code, $severity, $filename, $lineno, $context = array())
|
||||
{
|
||||
parent::__construct($message, $code, $severity, $filename, $lineno);
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Array of variables that existed when the exception occurred
|
||||
*/
|
||||
public function getContext()
|
||||
{
|
||||
return $this->context;
|
||||
}
|
||||
}
|
||||
|
@ -12,9 +12,9 @@
|
||||
namespace Symfony\Component\Debug\Exception;
|
||||
|
||||
/**
|
||||
* Used to stop execution of a PHP script after handling a fatal error.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @deprecated since version 2.5, to be removed in 3.0.
|
||||
*/
|
||||
class DummyException extends \ErrorException
|
||||
{
|
||||
|
@ -14,8 +14,54 @@ namespace Symfony\Component\Debug\Exception;
|
||||
/**
|
||||
* Fatal Error Exception.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Konstanton Myakshin <koc-dp@yandex.ru>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class FatalErrorException extends \ErrorException
|
||||
class FatalErrorException extends HandledErrorException
|
||||
{
|
||||
public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null)
|
||||
{
|
||||
parent::__construct($message, $code, $severity, $filename, $lineno);
|
||||
|
||||
if (null !== $traceOffset) {
|
||||
if (function_exists('xdebug_get_function_stack')) {
|
||||
$trace = xdebug_get_function_stack();
|
||||
if (0 < $traceOffset) {
|
||||
$trace = array_slice($trace, 0, -$traceOffset);
|
||||
}
|
||||
$trace = array_reverse($trace);
|
||||
|
||||
foreach ($trace as $i => $frame) {
|
||||
if (!isset($frame['type'])) {
|
||||
// XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695
|
||||
if (isset($frame['class'])) {
|
||||
$trace[$i]['type'] = '::';
|
||||
}
|
||||
} elseif ('dynamic' === $frame['type']) {
|
||||
$trace[$i]['type'] = '->';
|
||||
} elseif ('static' === $frame['type']) {
|
||||
$trace[$i]['type'] = '::';
|
||||
}
|
||||
|
||||
// XDebug also has a different name for the parameters array
|
||||
if (isset($frame['params']) && !isset($frame['args'])) {
|
||||
$trace[$i]['args'] = $frame['params'];
|
||||
unset($trace[$i]['params']);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$trace = array();
|
||||
}
|
||||
|
||||
$this->setTrace($trace);
|
||||
}
|
||||
}
|
||||
|
||||
protected function setTrace($trace)
|
||||
{
|
||||
$traceReflector = new \ReflectionProperty('Exception', 'trace');
|
||||
$traceReflector->setAccessible(true);
|
||||
$traceReflector->setValue($this, $trace);
|
||||
}
|
||||
}
|
||||
|
@ -172,36 +172,7 @@ class FlattenException
|
||||
|
||||
public function setTraceFromException(\Exception $exception)
|
||||
{
|
||||
$trace = $exception->getTrace();
|
||||
|
||||
if ($exception instanceof FatalErrorException) {
|
||||
if (function_exists('xdebug_get_function_stack')) {
|
||||
$trace = array_slice(array_reverse(xdebug_get_function_stack()), 4);
|
||||
|
||||
foreach ($trace as $i => $frame) {
|
||||
// XDebug pre 2.1.1 doesn't currently set the call type key http://bugs.xdebug.org/view.php?id=695
|
||||
if (!isset($frame['type'])) {
|
||||
$trace[$i]['type'] = '??';
|
||||
}
|
||||
|
||||
if ('dynamic' === $trace[$i]['type']) {
|
||||
$trace[$i]['type'] = '->';
|
||||
} elseif ('static' === $trace[$i]['type']) {
|
||||
$trace[$i]['type'] = '::';
|
||||
}
|
||||
|
||||
// XDebug also has a different name for the parameters array
|
||||
if (isset($frame['params']) && !isset($frame['args'])) {
|
||||
$trace[$i]['args'] = $frame['params'];
|
||||
unset($trace[$i]['params']);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$trace = array_slice(array_reverse($trace), 1);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setTrace($trace, $exception->getFile(), $exception->getLine());
|
||||
$this->setTrace($exception->getTrace(), $exception->getFile(), $exception->getLine());
|
||||
}
|
||||
|
||||
public function setTrace($trace, $file, $line)
|
||||
|
@ -0,0 +1,86 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class HandledErrorException extends \ErrorException
|
||||
{
|
||||
private $handlerOutput = false;
|
||||
private $context = array();
|
||||
|
||||
public function __construct($message, $code, $severity, $filename, $lineno, $context = array())
|
||||
{
|
||||
parent::__construct($message, $code, $severity, $filename, $lineno);
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Array of variables that existed when the exception occurred
|
||||
*/
|
||||
public function getContext()
|
||||
{
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
public function handleWith($exceptionHandler)
|
||||
{
|
||||
$this->handlerOutput = false;
|
||||
ob_start(array($this, 'catchOutput'));
|
||||
call_user_func($exceptionHandler, $this);
|
||||
if (false === $this->handlerOutput) {
|
||||
ob_end_clean();
|
||||
}
|
||||
ob_start(array(__CLASS__, 'flushOutput'));
|
||||
echo $this->handlerOutput;
|
||||
$this->handlerOutput = ob_get_length();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function catchOutput($buffer)
|
||||
{
|
||||
$this->handlerOutput = $buffer;
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function flushOutput($buffer)
|
||||
{
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
public function cleanOutput()
|
||||
{
|
||||
$status = ob_get_status();
|
||||
|
||||
if (isset($status['name']) && __CLASS__.'::flushOutput' === $status['name']) {
|
||||
if ($this->handlerOutput) {
|
||||
// use substr_replace() instead of substr() for mbstring overloading resistance
|
||||
echo substr_replace(ob_get_clean(), '', 0, $this->handlerOutput);
|
||||
} else {
|
||||
ob_end_flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->handlerOutput = 0;
|
||||
$this->cleanOutput();
|
||||
}
|
||||
}
|
@ -28,5 +28,6 @@ class UndefinedFunctionException extends FatalErrorException
|
||||
$previous->getLine(),
|
||||
$previous->getPrevious()
|
||||
);
|
||||
$this->setTrace($previous->getTrace());
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,14 @@ class UndefinedMethodException extends FatalErrorException
|
||||
{
|
||||
public function __construct($message, \ErrorException $previous)
|
||||
{
|
||||
parent::__construct($message, $previous->getCode(), $previous->getSeverity(), $previous->getFile(), $previous->getLine(), $previous->getPrevious());
|
||||
parent::__construct(
|
||||
$message,
|
||||
$previous->getCode(),
|
||||
$previous->getSeverity(),
|
||||
$previous->getFile(),
|
||||
$previous->getLine(),
|
||||
$previous->getPrevious()
|
||||
);
|
||||
$this->setTrace($previous->getTrace());
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,9 @@ class ExceptionHandler implements ExceptionHandlerInterface
|
||||
public function handle(\Exception $exception)
|
||||
{
|
||||
if (class_exists('Symfony\Component\HttpFoundation\Response')) {
|
||||
$this->createResponse($exception)->send();
|
||||
$response = $this->createResponse($exception);
|
||||
$response->sendHeaders();
|
||||
$response->sendContent();
|
||||
} else {
|
||||
$this->sendPhpResponse($exception);
|
||||
}
|
||||
|
@ -15,6 +15,8 @@ namespace Symfony\Component\Debug;
|
||||
* An ExceptionHandler does something useful with an exception.
|
||||
*
|
||||
* @author Andrew Moore <me@andrewmoore.ca>
|
||||
*
|
||||
* @deprecated since version 2.5, to be removed in 3.0.
|
||||
*/
|
||||
interface ExceptionHandlerInterface
|
||||
{
|
||||
|
@ -78,14 +78,14 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Debug\Exception\DummyException
|
||||
* @expectedException \Symfony\Component\Debug\Exception\HandledErrorException
|
||||
*/
|
||||
public function testStacking()
|
||||
{
|
||||
// the ContextErrorException must not be loaded to test the workaround
|
||||
// the HandledErrorException must not be loaded to test the workaround
|
||||
// for https://bugs.php.net/65322.
|
||||
if (class_exists('Symfony\Component\Debug\Exception\ContextErrorException', false)) {
|
||||
$this->markTestSkipped('The ContextErrorException class is already loaded.');
|
||||
if (class_exists('Symfony\Component\Debug\Exception\HandledErrorException', false)) {
|
||||
$this->markTestSkipped('The HandledErrorException class is already loaded.');
|
||||
}
|
||||
|
||||
$exceptionHandler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('handle'));
|
||||
@ -93,7 +93,7 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$that = $this;
|
||||
$exceptionCheck = function ($exception) use ($that) {
|
||||
$that->assertInstanceOf('Symfony\Component\Debug\Exception\ContextErrorException', $exception);
|
||||
$that->assertInstanceOf('Symfony\Component\Debug\Exception\HandledErrorException', $exception);
|
||||
$that->assertEquals(E_STRICT, $exception->getSeverity());
|
||||
$that->assertStringStartsWith(__FILE__, $exception->getFile());
|
||||
$that->assertRegexp('/^Runtime Notice: Declaration/', $exception->getMessage());
|
||||
@ -107,7 +107,7 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase
|
||||
try {
|
||||
// Trigger autoloading + E_STRICT at compile time
|
||||
// which in turn triggers $errorHandler->handle()
|
||||
// that again triggers autoloading for ContextErrorException.
|
||||
// that again triggers autoloading for HandledErrorException.
|
||||
// Error stacking works around the bug above and everything is fine.
|
||||
|
||||
eval('
|
||||
|
@ -12,7 +12,7 @@
|
||||
namespace Symfony\Component\Debug\Tests;
|
||||
|
||||
use Symfony\Component\Debug\ErrorHandler;
|
||||
use Symfony\Component\Debug\Exception\DummyException;
|
||||
use Symfony\Component\Debug\Exception\HandledErrorException;
|
||||
|
||||
/**
|
||||
* ErrorHandlerTest
|
||||
@ -51,7 +51,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$that = $this;
|
||||
$exceptionCheck = function ($exception) use ($that) {
|
||||
$that->assertInstanceOf('Symfony\Component\Debug\Exception\ContextErrorException', $exception);
|
||||
$that->assertInstanceOf('Symfony\Component\Debug\Exception\HandledErrorException', $exception);
|
||||
$that->assertEquals(E_NOTICE, $exception->getSeverity());
|
||||
$that->assertEquals(__FILE__, $exception->getFile());
|
||||
$that->assertRegexp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage());
|
||||
@ -80,7 +80,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
try {
|
||||
self::triggerNotice($this);
|
||||
} catch (DummyException $e) {
|
||||
} catch (HandledErrorException $e) {
|
||||
// if an exception is thrown, the test passed
|
||||
} catch (\Exception $e) {
|
||||
restore_error_handler();
|
||||
@ -213,7 +213,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$m = new \ReflectionMethod($handler, 'handleFatalError');
|
||||
$m->setAccessible(true);
|
||||
$m->invoke($handler, $exceptionHandler, $error);
|
||||
$m->invoke($handler, array($exceptionHandler, 'handle'), $error);
|
||||
|
||||
$this->assertInstanceof($class, $exceptionHandler->e);
|
||||
// class names are case insensitive and PHP/HHVM do not return the same
|
||||
|
@ -11,8 +11,8 @@
|
||||
|
||||
namespace Symfony\Component\HttpKernel\DataCollector;
|
||||
|
||||
use Symfony\Component\Debug\ErrorHandler;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Debug\ErrorHandler;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
namespace Symfony\Component\HttpKernel\EventListener;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpKernel\Debug\ErrorHandler;
|
||||
use Symfony\Component\Debug\ErrorHandler;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
|
@ -0,0 +1,45 @@
|
||||
<?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\HttpKernel\EventListener;
|
||||
|
||||
use Symfony\Component\Debug\ErrorHandler;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
/**
|
||||
* Injects a fatal error exceptions handler into the ErrorHandler.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class FatalErrorExceptionsListener implements EventSubscriberInterface
|
||||
{
|
||||
private $handler = null;
|
||||
|
||||
public function __construct($handler)
|
||||
{
|
||||
if (is_callable($handler)) {
|
||||
$this->handler = $handler;
|
||||
}
|
||||
}
|
||||
|
||||
public function injectHandler()
|
||||
{
|
||||
if ($this->handler) {
|
||||
ErrorHandler::setFatalErrorExceptionHandler($this->handler);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(KernelEvents::REQUEST => 'injectHandler');
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ use Symfony\Component\HttpKernel\Controller\ControllerReference;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Debug\Exception\HandledErrorException;
|
||||
|
||||
/**
|
||||
* Implements the inline rendering strategy where the Request is rendered by the current HTTP kernel.
|
||||
@ -86,10 +87,15 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer
|
||||
} catch (\Exception $e) {
|
||||
// we dispatch the exception event to trigger the logging
|
||||
// the response that comes back is simply ignored
|
||||
if (isset($options['ignore_errors']) && $options['ignore_errors'] && $this->dispatcher) {
|
||||
$event = new GetResponseForExceptionEvent($this->kernel, $request, HttpKernelInterface::SUB_REQUEST, $e);
|
||||
if (isset($options['ignore_errors']) && $options['ignore_errors']) {
|
||||
if ($e instanceof HandledErrorException) {
|
||||
$e->cleanOutput();
|
||||
}
|
||||
if ($this->dispatcher) {
|
||||
$event = new GetResponseForExceptionEvent($this->kernel, $request, HttpKernelInterface::SUB_REQUEST, $e);
|
||||
|
||||
$this->dispatcher->dispatch(KernelEvents::EXCEPTION, $event);
|
||||
$this->dispatcher->dispatch(KernelEvents::EXCEPTION, $event);
|
||||
}
|
||||
}
|
||||
|
||||
// let's clean up the output buffers that were created by the sub-request
|
||||
|
@ -25,6 +25,8 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Debug\Exception\HandledErrorException;
|
||||
use Symfony\Component\Debug\Exception\FatalErrorException;
|
||||
|
||||
/**
|
||||
* HttpKernel notifies events to convert a Request object to a Response one.
|
||||
@ -70,6 +72,9 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
|
||||
|
||||
throw $e;
|
||||
}
|
||||
if ($e instanceof HandledErrorException) {
|
||||
$e->cleanOutput();
|
||||
}
|
||||
|
||||
return $this->handleException($e, $request, $type);
|
||||
}
|
||||
@ -85,6 +90,21 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
|
||||
$this->dispatcher->dispatch(KernelEvents::TERMINATE, new PostResponseEvent($this, $request, $response));
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function handleFatalErrorException(FatalErrorException $exception)
|
||||
{
|
||||
$request = $this->requestStack->getMasterRequest();
|
||||
$response = $this->handleException($exception, $request, self::MASTER_REQUEST);
|
||||
|
||||
$response->sendHeaders();
|
||||
$response->sendContent();
|
||||
|
||||
$this->terminate($request, $response);
|
||||
$exception->cleanOutput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a request to convert it to a response.
|
||||
*
|
||||
|
@ -19,7 +19,7 @@
|
||||
"php": ">=5.3.3",
|
||||
"symfony/event-dispatcher": "~2.1",
|
||||
"symfony/http-foundation": "~2.4",
|
||||
"symfony/debug": "~2.3",
|
||||
"symfony/debug": "~2.5",
|
||||
"psr/log": "~1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
|
Reference in New Issue
Block a user