diff --git a/src/Symfony/Component/ErrorHandler/ErrorHandler.php b/src/Symfony/Component/ErrorHandler/ErrorHandler.php index 2b0d1184fe..5493e61a63 100644 --- a/src/Symfony/Component/ErrorHandler/ErrorHandler.php +++ b/src/Symfony/Component/ErrorHandler/ErrorHandler.php @@ -21,6 +21,8 @@ use Symfony\Component\ErrorHandler\FatalErrorHandler\ClassNotFoundFatalErrorHand use Symfony\Component\ErrorHandler\FatalErrorHandler\FatalErrorHandlerInterface; use Symfony\Component\ErrorHandler\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; use Symfony\Component\ErrorHandler\FatalErrorHandler\UndefinedMethodFatalErrorHandler; +use Symfony\Component\ErrorRenderer\ErrorRenderer\HtmlErrorRenderer; +use Symfony\Component\ErrorRenderer\Exception\FlattenException; /** * A generic ErrorHandler for the PHP engine. @@ -144,6 +146,8 @@ class ErrorHandler $handler->setExceptionHandler($p); $prev[0]->setExceptionHandler($p); } + } elseif (null === $prev && !\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { + $handler->setExceptionHandler([$handler, 'sendPhpResponse']); } else { $handler->setExceptionHandler($prev); } @@ -320,7 +324,7 @@ class ErrorHandler public function scopeAt(int $levels, bool $replace = false): int { $prev = $this->scopedErrors; - $this->scopedErrors = (int) $levels; + $this->scopedErrors = $levels; if (!$replace) { $this->scopedErrors |= $prev; } @@ -358,7 +362,7 @@ class ErrorHandler public function screamAt(int $levels, bool $replace = false): int { $prev = $this->screamedErrors; - $this->screamedErrors = (int) $levels; + $this->screamedErrors = $levels; if (!$replace) { $this->screamedErrors |= $prev; } @@ -683,6 +687,29 @@ class ErrorHandler } } + /** + * Sends the error associated with the given Exception as a plain PHP response. + * + * As this method is mainly called during Kernel boot, where nothing is yet + * available, the Response content is always HTML. + */ + private function sendPhpResponse(\Throwable $exception) + { + $charset = ini_get('default_charset') ?: 'UTF-8'; + + if (!headers_sent()) { + header('HTTP/1.0 500'); + header(sprintf('Content-Type: text/html; charset=%s', $charset)); + } + + if (class_exists(HtmlErrorRenderer::class)) { + echo (new HtmlErrorRenderer(true))->render(FlattenException::createFromThrowable($exception)); + } else { + $message = htmlspecialchars($exception->getMessage(), ENT_COMPAT | ENT_SUBSTITUTE, $charset); + echo sprintf('%s', $charset, $message); + } + } + /** * Gets the fatal error handlers. * diff --git a/src/Symfony/Component/ErrorHandler/composer.json b/src/Symfony/Component/ErrorHandler/composer.json index 8cd885431e..3ea91bde35 100644 --- a/src/Symfony/Component/ErrorHandler/composer.json +++ b/src/Symfony/Component/ErrorHandler/composer.json @@ -22,6 +22,9 @@ "require-dev": { "symfony/http-kernel": "^3.4|^4.0|^5.0" }, + "suggest": { + "symfony/error-renderer": "For better error rendering" + }, "conflict": { "symfony/http-kernel": "<3.4" },