diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml
index 2366ac1f0604e..c457e4f903a36 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug.xml
@@ -9,7 +9,7 @@
Symfony\Component\Stopwatch\Stopwatch
%kernel.cache_dir%/%kernel.container_class%.xml
Symfony\Component\HttpKernel\Controller\TraceableControllerResolver
- Symfony\Component\HttpKernel\EventListener\FatalErrorExceptionsListener
+ Symfony\Component\HttpKernel\EventListener\DebugHandlersListener
@@ -41,11 +41,11 @@
-
+
- handleFatalErrorException
+ terminateWithException
diff --git a/src/Symfony/Component/Debug/CHANGELOG.md b/src/Symfony/Component/Debug/CHANGELOG.md
index b128efaaa8913..776468fb7a59e 100644
--- a/src/Symfony/Component/Debug/CHANGELOG.md
+++ b/src/Symfony/Component/Debug/CHANGELOG.md
@@ -4,9 +4,8 @@ CHANGELOG
2.5.0
-----
-* added ErrorHandler::setFatalErrorExceptionHandler()
+* added ExceptionHandler::setHandler()
* added UndefinedMethodFatalErrorHandler
-* deprecated ExceptionHandlerInterface
* deprecated DummyException
2.4.0
diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php
index b62e5b752150f..850f7d9c55376 100644
--- a/src/Symfony/Component/Debug/ErrorHandler.php
+++ b/src/Symfony/Component/Debug/ErrorHandler.php
@@ -15,6 +15,7 @@
use Psr\Log\LoggerInterface;
use Symfony\Component\Debug\Exception\ContextErrorException;
use Symfony\Component\Debug\Exception\FatalErrorException;
+use Symfony\Component\Debug\Exception\OutOfMemoryException;
use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
@@ -53,8 +54,6 @@ class ErrorHandler
private $displayErrors;
- private $caughtOutput = 0;
-
/**
* @var LoggerInterface[] Loggers for channels
*/
@@ -64,8 +63,6 @@ class ErrorHandler
private static $stackedErrorLevels = array();
- private static $fatalHandler = false;
-
/**
* Registers the error handler.
*
@@ -119,16 +116,6 @@ public static function setLogger(LoggerInterface $logger, $channel = 'deprecatio
self::$loggers[$channel] = $logger;
}
- /**
- * 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 ContextErrorException When error_reporting returns error
*/
@@ -284,7 +271,7 @@ public function handleFatal()
throw $exception;
}
- if (!$error || !$this->level || !in_array($error['type'], array(E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE))) {
+ if (!$error || !$this->level || !($error['type'] & (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_PARSE))) {
return;
}
@@ -298,7 +285,7 @@ public function handleFatal()
self::$loggers['emergency']->emergency($error['message'], $fatal);
}
- if ($this->displayErrors && ($exceptionHandler || self::$fatalHandler)) {
+ if ($this->displayErrors && $exceptionHandler) {
$this->handleFatalError($exceptionHandler, $error);
}
}
@@ -327,82 +314,25 @@ 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'], 3);
-
- foreach ($this->getFatalErrorHandlers() as $handler) {
- if ($e = $handler->handleError($error, $exception)) {
- $exception = $e;
- break;
- }
- }
-
- // To be as fail-safe as possible, the FatalErrorException is first handled
- // by the exception handler, then by the fatal error handler. The latter takes
- // precedence and any output from the former is cancelled, if and only if
- // nothing bad happens in this handling path.
-
- $caughtOutput = 0;
-
- if ($exceptionHandler) {
- $this->caughtOutput = false;
- ob_start(array($this, 'catchOutput'));
- try {
- call_user_func($exceptionHandler, $exception);
- } catch (\Exception $e) {
- // Ignore this exception, we have to deal with the fatal error
- }
- if (false === $this->caughtOutput) {
- ob_end_clean();
- }
- if (isset($this->caughtOutput[0])) {
- ob_start(array($this, 'cleanOutput'));
- echo $this->caughtOutput;
- $caughtOutput = ob_get_length();
- }
- $this->caughtOutput = 0;
- }
-
- if (self::$fatalHandler) {
- try {
- call_user_func(self::$fatalHandler, $exception);
-
- if ($caughtOutput) {
- $this->caughtOutput = $caughtOutput;
- }
- } catch (\Exception $e) {
- if (!$caughtOutput) {
- // Neither the exception nor the fatal handler succeeded.
- // Let PHP handle that now.
- throw $exception;
+ if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) {
+ $exception = new OutOfMemoryException($message, 0, $error['type'], $error['file'], $error['line'], 3, false);
+ } else {
+ $exception = new FatalErrorException($message, 0, $error['type'], $error['file'], $error['line'], 3, true);
+
+ foreach ($this->getFatalErrorHandlers() as $handler) {
+ if ($e = $handler->handleError($error, $exception)) {
+ $exception = $e;
+ break;
}
}
}
- }
-
- /**
- * @internal
- */
- public function catchOutput($buffer)
- {
- $this->caughtOutput = $buffer;
- return '';
- }
-
- /**
- * @internal
- */
- public function cleanOutput($buffer)
- {
- if ($this->caughtOutput) {
- // use substr_replace() instead of substr() for mbstring overloading resistance
- $cleanBuffer = substr_replace($buffer, '', 0, $this->caughtOutput);
- if (isset($cleanBuffer[0])) {
- $buffer = $cleanBuffer;
- }
+ try {
+ call_user_func($exceptionHandler, $exception);
+ } catch (\Exception $e) {
+ // The handler failed. Let PHP handle that now.
+ throw $exception;
}
-
- return $buffer;
}
}
diff --git a/src/Symfony/Component/Debug/Exception/FatalErrorException.php b/src/Symfony/Component/Debug/Exception/FatalErrorException.php
index 4e29495f302cb..d5b58468c9c6d 100644
--- a/src/Symfony/Component/Debug/Exception/FatalErrorException.php
+++ b/src/Symfony/Component/Debug/Exception/FatalErrorException.php
@@ -20,7 +20,7 @@
*/
class FatalErrorException extends \ErrorException
{
- public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null)
+ public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null, $traceArgs = true)
{
parent::__construct($message, $code, $severity, $filename, $lineno);
@@ -28,28 +28,32 @@ public function __construct($message, $code, $severity, $filename, $lineno, $tra
if (function_exists('xdebug_get_function_stack')) {
$trace = xdebug_get_function_stack();
if (0 < $traceOffset) {
- $trace = array_slice($trace, 0, -$traceOffset);
+ array_splice($trace, -$traceOffset);
}
- $trace = array_reverse($trace);
- foreach ($trace as $i => $frame) {
+ foreach ($trace as &$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'] = '::';
+ $frame['type'] = '::';
}
} elseif ('dynamic' === $frame['type']) {
- $trace[$i]['type'] = '->';
+ $frame['type'] = '->';
} elseif ('static' === $frame['type']) {
- $trace[$i]['type'] = '::';
+ $frame['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']);
+ if (!$traceArgs) {
+ unset($frame['params'], $frame['args']);
+ } elseif (isset($frame['params']) && !isset($frame['args'])) {
+ $frame['args'] = $frame['params'];
+ unset($frame['params']);
}
}
+
+ unset($frame);
+ $trace = array_reverse($trace);
} else {
$trace = array();
}
diff --git a/src/Symfony/Component/Debug/Exception/OutOfMemoryException.php b/src/Symfony/Component/Debug/Exception/OutOfMemoryException.php
new file mode 100644
index 0000000000000..fec1979836450
--- /dev/null
+++ b/src/Symfony/Component/Debug/Exception/OutOfMemoryException.php
@@ -0,0 +1,21 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Debug\Exception;
+
+/**
+ * Out of memory exception.
+ *
+ * @author Nicolas Grekas
+ */
+class OutOfMemoryException extends FatalErrorException
+{
+}
diff --git a/src/Symfony/Component/Debug/ExceptionHandler.php b/src/Symfony/Component/Debug/ExceptionHandler.php
index 91e904fbe25ce..bfbd78313fb2f 100644
--- a/src/Symfony/Component/Debug/ExceptionHandler.php
+++ b/src/Symfony/Component/Debug/ExceptionHandler.php
@@ -13,6 +13,7 @@
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Debug\Exception\FlattenException;
+use Symfony\Component\Debug\Exception\OutOfMemoryException;
if (!defined('ENT_SUBSTITUTE')) {
define('ENT_SUBSTITUTE', 8);
@@ -29,10 +30,12 @@
*
* @author Fabien Potencier
*/
-class ExceptionHandler implements ExceptionHandlerInterface
+class ExceptionHandler
{
private $debug;
private $charset;
+ private $handler;
+ private $caughtOutput = 0;
public function __construct($debug = true, $charset = 'UTF-8')
{
@@ -56,6 +59,24 @@ public static function register($debug = true)
return $handler;
}
+ /**
+ * Sets a user exception handler.
+ *
+ * @param callable $handler An handler that will be called on Exception
+ *
+ * @return callable|null The previous exception handler if any
+ */
+ public function setHandler($handler)
+ {
+ if (isset($handler) && !is_callable($handler)) {
+ throw new \LogicException('The exception handler must be a valid PHP callable.');
+ }
+ $old = $this->handler;
+ $this->handler = $handler;
+
+ return $old;
+ }
+
/**
* {@inheritdoc}
*
@@ -70,12 +91,55 @@ public static function register($debug = true)
*/
public function handle(\Exception $exception)
{
- if (class_exists('Symfony\Component\HttpFoundation\Response')) {
- $response = $this->createResponse($exception);
- $response->sendHeaders();
- $response->sendContent();
- } else {
+ if ($exception instanceof OutOfMemoryException) {
$this->sendPhpResponse($exception);
+
+ return;
+ }
+
+ // To be as fail-safe as possible, the exception is first handled
+ // by our simple exception handler, then by the user exception handler.
+ // The latter takes precedence and any output from the former is cancelled,
+ // if and only if nothing bad happens in this handling path.
+
+ $caughtOutput = 0;
+
+ $this->caughtOutput = false;
+ ob_start(array($this, 'catchOutput'));
+ try {
+ if (class_exists('Symfony\Component\HttpFoundation\Response')) {
+ $response = $this->createResponse($exception);
+ $response->sendHeaders();
+ $response->sendContent();
+ } else {
+ $this->sendPhpResponse($exception);
+ }
+ } catch (\Exception $e) {
+ // Ignore this $e exception, we have to deal with $exception
+ }
+ if (false === $this->caughtOutput) {
+ ob_end_clean();
+ }
+ if (isset($this->caughtOutput[0])) {
+ ob_start(array($this, 'cleanOutput'));
+ echo $this->caughtOutput;
+ $caughtOutput = ob_get_length();
+ }
+ $this->caughtOutput = 0;
+
+ if (!empty($this->handler)) {
+ try {
+ call_user_func($this->handler, $exception);
+
+ if ($caughtOutput) {
+ $this->caughtOutput = $caughtOutput;
+ }
+ } catch (\Exception $e) {
+ if (!$caughtOutput) {
+ // All handlers failed. Let PHP handle that now.
+ throw $exception;
+ }
+ }
}
}
@@ -317,4 +381,30 @@ private function formatArgs(array $args)
return implode(', ', $result);
}
+
+ /**
+ * @internal
+ */
+ public function catchOutput($buffer)
+ {
+ $this->caughtOutput = $buffer;
+
+ return '';
+ }
+
+ /**
+ * @internal
+ */
+ public function cleanOutput($buffer)
+ {
+ if ($this->caughtOutput) {
+ // use substr_replace() instead of substr() for mbstring overloading resistance
+ $cleanBuffer = substr_replace($buffer, '', 0, $this->caughtOutput);
+ if (isset($cleanBuffer[0])) {
+ $buffer = $cleanBuffer;
+ }
+ }
+
+ return $buffer;
+ }
}
diff --git a/src/Symfony/Component/Debug/ExceptionHandlerInterface.php b/src/Symfony/Component/Debug/ExceptionHandlerInterface.php
deleted file mode 100644
index f1740184c6dfe..0000000000000
--- a/src/Symfony/Component/Debug/ExceptionHandlerInterface.php
+++ /dev/null
@@ -1,29 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Symfony\Component\Debug;
-
-/**
- * An ExceptionHandler does something useful with an exception.
- *
- * @author Andrew Moore
- *
- * @deprecated since version 2.5, to be removed in 3.0.
- */
-interface ExceptionHandlerInterface
-{
- /**
- * Handles an exception.
- *
- * @param \Exception $exception An \Exception instance
- */
- public function handle(\Exception $exception);
-}
diff --git a/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php
new file mode 100644
index 0000000000000..f46ef71208bde
--- /dev/null
+++ b/src/Symfony/Component/HttpKernel/EventListener/DebugHandlersListener.php
@@ -0,0 +1,50 @@
+
+ *
+ * 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\ExceptionHandler;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpKernel\KernelEvents;
+
+/**
+ * Configures the ExceptionHandler.
+ *
+ * @author Nicolas Grekas
+ */
+class DebugHandlersListener implements EventSubscriberInterface
+{
+ private $exceptionHandler;
+
+ public function __construct($exceptionHandler)
+ {
+ if (is_callable($exceptionHandler)) {
+ $this->exceptionHandler = $exceptionHandler;
+ }
+ }
+
+ public function configure()
+ {
+ if ($this->exceptionHandler) {
+ $mainHandler = set_exception_handler('var_dump');
+ restore_exception_handler();
+ if ($mainHandler instanceof ExceptionHandler) {
+ $mainHandler->setHandler($this->exceptionHandler);
+ }
+ $this->exceptionHandler = null;
+ }
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array(KernelEvents::REQUEST => array('configure', 2048));
+ }
+}
diff --git a/src/Symfony/Component/HttpKernel/EventListener/FatalErrorExceptionsListener.php b/src/Symfony/Component/HttpKernel/EventListener/FatalErrorExceptionsListener.php
deleted file mode 100644
index 0677682810ea8..0000000000000
--- a/src/Symfony/Component/HttpKernel/EventListener/FatalErrorExceptionsListener.php
+++ /dev/null
@@ -1,47 +0,0 @@
-
- *
- * 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
- */
-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);
- $this->handler = null;
- }
- }
-
- public static function getSubscribedEvents()
- {
- // Don't register early as e.g. the Router is generally required by the handler
- return array(KernelEvents::REQUEST => array('injectHandler', 8));
- }
-}
diff --git a/src/Symfony/Component/HttpKernel/HttpKernel.php b/src/Symfony/Component/HttpKernel/HttpKernel.php
index c3556972e97d1..68d89c94e9be3 100644
--- a/src/Symfony/Component/HttpKernel/HttpKernel.php
+++ b/src/Symfony/Component/HttpKernel/HttpKernel.php
@@ -25,7 +25,6 @@
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use Symfony\Component\Debug\Exception\FatalErrorException;
/**
* HttpKernel notifies events to convert a Request object to a Response one.
@@ -87,11 +86,16 @@ public function terminate(Request $request, Response $response)
}
/**
+ * @throws \LogicException If the request stack is empty
+ *
* @internal
*/
- public function handleFatalErrorException(FatalErrorException $exception)
+ public function terminateWithException(\Exception $exception)
{
- $request = $this->requestStack->getMasterRequest();
+ if (!$request = $this->requestStack->getMasterRequest()) {
+ throw new \LogicException('Request stack is empty', 0, $exception);
+ }
+
$response = $this->handleException($exception, $request, self::MASTER_REQUEST);
$response->sendHeaders();
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy