diff --git a/UPGRADE-4.4.md b/UPGRADE-4.4.md index ac716a4b07..584eb693e2 100644 --- a/UPGRADE-4.4.md +++ b/UPGRADE-4.4.md @@ -60,7 +60,7 @@ DependencyInjection ```php new Definition('%my_class%'); ``` - + DoctrineBridge -------------- * Deprecated injecting `ClassMetadataFactory` in `DoctrineExtractor`, an instance of `EntityManagerInterface` should be @@ -96,7 +96,7 @@ FrameworkBundle * Deprecated `routing.loader.service`, use `routing.loader.container` instead. * Not tagging service route loaders with `routing.route_loader` has been deprecated. * Overriding the methods `KernelTestCase::tearDown()` and `WebTestCase::tearDown()` without the `void` return-type is deprecated. - + HttpClient ---------- @@ -139,7 +139,7 @@ HttpKernel } ``` - As many bundles must be compatible with a range of Symfony versions, the current + As many bundles must be compatible with a range of Symfony versions, the current directory convention is not deprecated yet, but it will be in the future. * Deprecated the second and third argument of `KernelInterface::locateResource` @@ -148,6 +148,7 @@ HttpKernel fallback directories. Resources like service definitions are usually loaded relative to the current directory or with a glob pattern. The fallback directories have never been advocated so you likely do not use those in any app based on the SF Standard or Flex edition. + * Getting the container from a non-booted kernel is deprecated Lock ---- @@ -171,7 +172,7 @@ MonologBridge -------------- * The `RouteProcessor` has been marked final. - + Process ------- @@ -195,14 +196,14 @@ Security * Implementations of `PasswordEncoderInterface` and `UserPasswordEncoderInterface` should add a new `needsRehash()` method * Deprecated returning a non-boolean value when implementing `Guard\AuthenticatorInterface::checkCredentials()`. Please explicitly return `false` to indicate invalid credentials. * Deprecated passing more than one attribute to `AccessDecisionManager::decide()` and `AuthorizationChecker::isGranted()` (and indirectly the `is_granted()` Twig and ExpressionLanguage function) - + **Before** ```php if ($this->authorizationChecker->isGranted(['ROLE_USER', 'ROLE_ADMIN'])) { // ... } ``` - + **After** ```php if ($this->authorizationChecker->isGranted(new Expression("has_role('ROLE_USER') or has_role('ROLE_ADMIN')"))) {} @@ -230,18 +231,18 @@ TwigBridge * Deprecated to pass `$rootDir` and `$fileLinkFormatter` as 5th and 6th argument respectively to the `DebugCommand::__construct()` method, swap the variables position. * Deprecated accepting STDIN implicitly when using the `lint:twig` command, use `lint:twig -` (append a dash) instead to make it explicit. - + TwigBundle ---------- * Deprecated `twig.exception_controller` configuration option, set it to "null" and use `framework.error_controller` instead: - + Before: ```yaml twig: exception_controller: 'App\Controller\MyExceptionController' ``` - + After: ```yaml twig: @@ -250,36 +251,36 @@ TwigBundle framework: error_controller: 'App\Controller\MyExceptionController' ``` - - The new default exception controller will also change the error response content according to + + The new default exception controller will also change the error response content according to https://tools.ietf.org/html/rfc7807 for `json`, `xml`, `atom` and `txt` formats: - + Before: ```json - { - "error": { - "code": 404, - "message": "Sorry, the page you are looking for could not be found" - } + { + "error": { + "code": 404, + "message": "Sorry, the page you are looking for could not be found" + } } ``` - + After: ```json - { + { "title": "Not Found", - "status": 404, + "status": 404, "detail": "Sorry, the page you are looking for could not be found" } ``` - + * Deprecated the `ExceptionController` and `PreviewErrorController` controllers, use `ErrorController` from the HttpKernel component instead * Deprecated all built-in error templates, use the error renderer mechanism of the `ErrorRenderer` component - * Deprecated loading custom error templates in non-html formats. Custom HTML error pages based on Twig keep working as before: + * Deprecated loading custom error templates in non-html formats. Custom HTML error pages based on Twig keep working as before: Before (`templates/bundles/TwigBundle/Exception/error.jsonld.twig`): ```twig - { + { "@id": "https://example.com", "@type": "error", "@context": { @@ -289,7 +290,7 @@ TwigBundle } } ``` - + After (`App\ErrorRenderer\JsonLdErrorRenderer`): ```php class JsonLdErrorRenderer implements ErrorRendererInterface @@ -298,7 +299,7 @@ TwigBundle { return 'jsonld'; } - + public function render(FlattenException $exception): string { return json_encode([ @@ -323,7 +324,7 @@ Validator * Deprecated using anything else than a `string` as the code of a `ConstraintViolation`, a `string` type-hint will be added to the constructor of the `ConstraintViolation` class and to the `ConstraintViolationBuilder::setCode()` method in 5.0. - * Deprecated passing an `ExpressionLanguage` instance as the second argument of `ExpressionValidator::__construct()`. + * Deprecated passing an `ExpressionLanguage` instance as the second argument of `ExpressionValidator::__construct()`. Pass it as the first argument instead. * The `Length` constraint expects the `allowEmptyString` option to be defined when the `min` option is used. @@ -343,7 +344,7 @@ WebServerBundle --------------- * The bundle is deprecated and will be removed in 5.0. - + Yaml ---- diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md index 12a34a85d3..1039e3a5fe 100644 --- a/UPGRADE-5.0.md +++ b/UPGRADE-5.0.md @@ -288,6 +288,7 @@ HttpFoundation use `Symfony\Component\Mime\FileinfoMimeTypeGuesser` instead. * `ApacheRequest` has been removed, use the `Request` class instead. * The third argument of the `HeaderBag::get()` method has been removed, use method `all()` instead. + * Getting the container from a non-booted kernel is not possible anymore. HttpKernel ---------- diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Application.php b/src/Symfony/Bundle/FrameworkBundle/Console/Application.php index 0fdb7ecd44..08595ce917 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Application.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Application.php @@ -174,10 +174,8 @@ class Application extends BaseApplication if ($bundle instanceof Bundle) { try { $bundle->registerCommands($this); - } catch (\Exception $e) { - $this->registrationErrors[] = $e; } catch (\Throwable $e) { - $this->registrationErrors[] = new FatalThrowableError($e); + $this->registrationErrors[] = $e; } } } @@ -192,10 +190,8 @@ class Application extends BaseApplication if (!isset($lazyCommandIds[$id])) { try { $this->add($container->get($id)); - } catch (\Exception $e) { - $this->registrationErrors[] = $e; } catch (\Throwable $e) { - $this->registrationErrors[] = new FatalThrowableError($e); + $this->registrationErrors[] = $e; } } } @@ -211,6 +207,10 @@ class Application extends BaseApplication (new SymfonyStyle($input, $output))->warning('Some commands could not be registered:'); foreach ($this->registrationErrors as $error) { + if (!$error instanceof \Exception) { + $error = new FatalThrowableError($error); + } + $this->doRenderException($error, $output); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php index 01ff87e92e..653d6ff209 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php @@ -125,11 +125,15 @@ abstract class KernelTestCase extends TestCase protected static function ensureKernelShutdown() { if (null !== static::$kernel) { - $container = static::$kernel->getContainer(); - static::$kernel->shutdown(); - static::$booted = false; - if ($container instanceof ResetInterface) { - $container->reset(); + $isBooted = (new \ReflectionClass(static::$kernel))->getProperty('booted'); + $isBooted->setAccessible(true); + if ($isBooted->getValue(static::$kernel)) { + $container = static::$kernel->getContainer(); + static::$kernel->shutdown(); + static::$booted = false; + if ($container instanceof ResetInterface) { + $container->reset(); + } } } static::$container = null; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php index 0d329582b3..14cdef6285 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php @@ -14,6 +14,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app; use Psr\Log\NullLogger; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpKernel\Kernel; @@ -96,4 +97,13 @@ class AppKernel extends Kernel return $parameters; } + + public function getContainer(): ContainerInterface + { + if (!$this->booted) { + throw new \LogicException('Cannot access the container on a non-booted kernel. Did you forget to boot it?'); + } + + return parent::getContainer(); + } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php index 8e622282c2..ca6f16c0d6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\SecurityBundle\Tests\Functional\app; use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpKernel\Kernel; @@ -98,4 +99,13 @@ class AppKernel extends Kernel return $parameters; } + + public function getContainer(): ContainerInterface + { + if (!$this->booted) { + throw new \LogicException('Cannot access the container on a non-booted kernel. Did you forget to boot it?'); + } + + return parent::getContainer(); + } } diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig index b37da94681..a8a5c27365 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig @@ -13,7 +13,7 @@ {% set text %}
Locale | + {% if is_fallback %} +Fallback locale | + {% endif %}Domain | Times used | Message ID | @@ -177,6 +180,9 @@ {% for message in messages %}
---|---|---|---|---|
{{ message.locale }} | + {% if is_fallback %} +{{ message.fallbackLocale|default('-') }} | + {% endif %}{{ message.domain }} | {{ message.count }} |
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig
index ecb559caae..fa2b19e882 100644
--- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig
+++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig
@@ -429,7 +429,7 @@
newToken = (newToken || token);
this.load(
'sfwdt' + token,
- '{{ path("_wdt", { "token": "xxxxxx" }) }}'.replace(/xxxxxx/, newToken),
+ '{{ path("_wdt", { "token": "xxxxxx" })|escape('js') }}'.replace(/xxxxxx/, newToken),
function(xhr, el) {
/* Evaluate in global scope scripts embedded inside the toolbar */
@@ -532,7 +532,7 @@
sfwdt.innerHTML = '\
\
\
- An error occurred while loading the web debug toolbar. Open the web profiler.\
+ An error occurred while loading the web debug toolbar. Open the web profiler.\
\
';
sfwdt.setAttribute('class', 'sf-toolbar sf-error-toolbar');
diff --git a/src/Symfony/Component/Asset/Package.php b/src/Symfony/Component/Asset/Package.php
index e770a17794..b3b1c0be22 100644
--- a/src/Symfony/Component/Asset/Package.php
+++ b/src/Symfony/Component/Asset/Package.php
@@ -68,6 +68,9 @@ class Package implements PackageInterface
return $this->versionStrategy;
}
+ /**
+ * @return bool
+ */
protected function isAbsoluteUrl(string $url)
{
return false !== strpos($url, '://') || '//' === substr($url, 0, 2);
diff --git a/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php b/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php
index 4752737c3e..cc4bf5a554 100644
--- a/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php
+++ b/src/Symfony/Component/Asset/VersionStrategy/JsonManifestVersionStrategy.php
@@ -50,7 +50,7 @@ class JsonManifestVersionStrategy implements VersionStrategyInterface
return $this->getManifestPath($path) ?: $path;
}
- private function getManifestPath(string $path)
+ private function getManifestPath(string $path): ?string
{
if (null === $this->manifestData) {
if (!file_exists($this->manifestPath)) {
diff --git a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php
index 87b99af749..a23f10435f 100644
--- a/src/Symfony/Component/Config/Resource/ReflectionClassResource.php
+++ b/src/Symfony/Component/Config/Resource/ReflectionClassResource.php
@@ -100,7 +100,7 @@ class ReflectionClassResource implements SelfCheckingResourceInterface
} while ($class = $class->getParentClass());
}
- private function computeHash()
+ private function computeHash(): string
{
if (null === $this->classReflector) {
try {
@@ -119,7 +119,7 @@ class ReflectionClassResource implements SelfCheckingResourceInterface
return hash_final($hash);
}
- private function generateSignature(\ReflectionClass $class)
+ private function generateSignature(\ReflectionClass $class): iterable
{
yield $class->getDocComment();
yield (int) $class->isFinal();
diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php
index 7a9508c4ad..4f4fdd271e 100644
--- a/src/Symfony/Component/Console/Application.php
+++ b/src/Symfony/Component/Console/Application.php
@@ -120,7 +120,7 @@ class Application implements ResetInterface
$output = new ConsoleOutput();
}
- $renderException = function ($e) use ($output) {
+ $renderException = function (\Throwable $e) use ($output) {
if (!$e instanceof \Exception) {
$e = class_exists(FatalThrowableError::class) ? new FatalThrowableError($e) : new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
}
@@ -1090,12 +1090,12 @@ class Application implements ResetInterface
/**
* @internal
*/
- public function isSingleCommand()
+ public function isSingleCommand(): bool
{
return $this->singleCommand;
}
- private function splitStringByWidth(string $string, int $width)
+ private function splitStringByWidth(string $string, int $width): array
{
// str_split is not suitable for multi-byte characters, we should use preg_split to get char array properly.
// additionally, array_slice() is not enough as some character has doubled width.
diff --git a/src/Symfony/Component/Console/Command/ListCommand.php b/src/Symfony/Component/Console/Command/ListCommand.php
index 7259b1263b..15c8a1218d 100644
--- a/src/Symfony/Component/Console/Command/ListCommand.php
+++ b/src/Symfony/Component/Console/Command/ListCommand.php
@@ -76,10 +76,7 @@ EOF
]);
}
- /**
- * {@inheritdoc}
- */
- private function createDefinition()
+ private function createDefinition(): InputDefinition
{
return new InputDefinition([
new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'),
diff --git a/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php
index 92f5efdd9b..8b3e182efc 100644
--- a/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php
+++ b/src/Symfony/Component/Console/Descriptor/MarkdownDescriptor.php
@@ -173,7 +173,7 @@ class MarkdownDescriptor extends Descriptor
}
}
- private function getApplicationTitle(Application $application)
+ private function getApplicationTitle(Application $application): string
{
if ('UNKNOWN' !== $application->getName()) {
if ('UNKNOWN' !== $application->getVersion()) {
diff --git a/src/Symfony/Component/Console/EventListener/ErrorListener.php b/src/Symfony/Component/Console/EventListener/ErrorListener.php
index 783c10793f..a34075793e 100644
--- a/src/Symfony/Component/Console/EventListener/ErrorListener.php
+++ b/src/Symfony/Component/Console/EventListener/ErrorListener.php
@@ -77,7 +77,7 @@ class ErrorListener implements EventSubscriberInterface
];
}
- private static function getInputString(ConsoleEvent $event)
+ private static function getInputString(ConsoleEvent $event): ?string
{
$commandName = $event->getCommand() ? $event->getCommand()->getName() : null;
$input = $event->getInput();
diff --git a/src/Symfony/Component/Console/Helper/ProcessHelper.php b/src/Symfony/Component/Console/Helper/ProcessHelper.php
index 44b9179336..944c593957 100644
--- a/src/Symfony/Component/Console/Helper/ProcessHelper.php
+++ b/src/Symfony/Component/Console/Helper/ProcessHelper.php
@@ -129,7 +129,7 @@ class ProcessHelper extends Helper
};
}
- private function escapeString(string $str)
+ private function escapeString(string $str): string
{
return str_replace('<', '\\<', $str);
}
diff --git a/src/Symfony/Component/Console/Helper/ProgressIndicator.php b/src/Symfony/Component/Console/Helper/ProgressIndicator.php
index 70c07f87e8..81cb783ea4 100644
--- a/src/Symfony/Component/Console/Helper/ProgressIndicator.php
+++ b/src/Symfony/Component/Console/Helper/ProgressIndicator.php
@@ -188,7 +188,7 @@ class ProgressIndicator
}, $this->format));
}
- private function determineBestFormat()
+ private function determineBestFormat(): string
{
switch ($this->output->getVerbosity()) {
// OutputInterface::VERBOSITY_QUIET: display is disabled anyway
@@ -215,12 +215,12 @@ class ProgressIndicator
}
}
- private function getCurrentTimeInMilliseconds()
+ private function getCurrentTimeInMilliseconds(): float
{
return round(microtime(true) * 1000);
}
- private static function initPlaceholderFormatters()
+ private static function initPlaceholderFormatters(): array
{
return [
'indicator' => function (self $indicator) {
@@ -238,7 +238,7 @@ class ProgressIndicator
];
}
- private static function initFormats()
+ private static function initFormats(): array
{
return [
'normal' => ' %indicator% %message%',
diff --git a/src/Symfony/Component/Console/Helper/QuestionHelper.php b/src/Symfony/Component/Console/Helper/QuestionHelper.php
index ab80b065c8..80906db4ed 100644
--- a/src/Symfony/Component/Console/Helper/QuestionHelper.php
+++ b/src/Symfony/Component/Console/Helper/QuestionHelper.php
@@ -336,7 +336,7 @@ class QuestionHelper extends Helper
return $fullChoice;
}
- private function mostRecentlyEnteredValue(string $entered)
+ private function mostRecentlyEnteredValue(string $entered): string
{
// Determine the most recent value that the user entered
if (false === strpos($entered, ',')) {
diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php
index f21b5cef46..0a641103d8 100644
--- a/src/Symfony/Component/Console/Helper/Table.php
+++ b/src/Symfony/Component/Console/Helper/Table.php
@@ -456,7 +456,7 @@ class Table
/**
* Renders vertical column separator.
*/
- private function renderColumnSeparator(int $type = self::BORDER_OUTSIDE)
+ private function renderColumnSeparator(int $type = self::BORDER_OUTSIDE): string
{
$borders = $this->style->getBorderChars();
@@ -489,7 +489,7 @@ class Table
/**
* Renders table cell with padding.
*/
- private function renderCell(array $row, int $column, string $cellFormat)
+ private function renderCell(array $row, int $column, string $cellFormat): string
{
$cell = isset($row[$column]) ? $row[$column] : '';
$width = $this->effectiveColumnWidths[$column];
@@ -534,7 +534,7 @@ class Table
$this->numberOfColumns = max($columns);
}
- private function buildTableRows(array $rows)
+ private function buildTableRows(array $rows): TableRows
{
/** @var WrappableOutputFormatterInterface $formatter */
$formatter = $this->output->getFormatter();
@@ -568,7 +568,7 @@ class Table
}
}
- return new TableRows(function () use ($rows, $unmergedRows) {
+ return new TableRows(function () use ($rows, $unmergedRows): \Traversable {
foreach ($rows as $rowKey => $row) {
yield $this->fillCells($row);
@@ -772,7 +772,7 @@ class Table
$this->numberOfColumns = null;
}
- private static function initStyles()
+ private static function initStyles(): array
{
$borderless = new TableStyle();
$borderless
@@ -819,7 +819,7 @@ class Table
];
}
- private function resolveStyle($name)
+ private function resolveStyle($name): TableStyle
{
if ($name instanceof TableStyle) {
return $name;
diff --git a/src/Symfony/Component/Console/Helper/TableStyle.php b/src/Symfony/Component/Console/Helper/TableStyle.php
index 4e69d43603..83719424cf 100644
--- a/src/Symfony/Component/Console/Helper/TableStyle.php
+++ b/src/Symfony/Component/Console/Helper/TableStyle.php
@@ -124,7 +124,7 @@ class TableStyle
*
* @internal
*/
- public function getBorderChars()
+ public function getBorderChars(): array
{
return [
$this->horizontalOutsideBorderChar,
diff --git a/src/Symfony/Component/Console/Style/SymfonyStyle.php b/src/Symfony/Component/Console/Style/SymfonyStyle.php
index 11d8104bd9..eea624e05d 100644
--- a/src/Symfony/Component/Console/Style/SymfonyStyle.php
+++ b/src/Symfony/Component/Console/Style/SymfonyStyle.php
@@ -449,7 +449,7 @@ class SymfonyStyle extends OutputStyle
$this->bufferedOutput->write(substr($message, -4), $newLine, $type);
}
- private function createBlock(iterable $messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = false)
+ private function createBlock(iterable $messages, string $type = null, string $style = null, string $prefix = ' ', bool $padding = false, bool $escape = false): array
{
$indentLength = 0;
$prefixLength = Helper::strlenWithoutDecoration($this->getFormatter(), $prefix);
diff --git a/src/Symfony/Component/Console/Tester/TesterTrait.php b/src/Symfony/Component/Console/Tester/TesterTrait.php
index 2b5aecfee5..73ee0103f9 100644
--- a/src/Symfony/Component/Console/Tester/TesterTrait.php
+++ b/src/Symfony/Component/Console/Tester/TesterTrait.php
@@ -160,6 +160,9 @@ trait TesterTrait
}
}
+ /**
+ * @return resource
+ */
private static function createStream(array $inputs)
{
$stream = fopen('php://memory', 'r+', false);
diff --git a/src/Symfony/Component/Dotenv/Dotenv.php b/src/Symfony/Component/Dotenv/Dotenv.php
index e403bdc0c6..7ad44fa607 100644
--- a/src/Symfony/Component/Dotenv/Dotenv.php
+++ b/src/Symfony/Component/Dotenv/Dotenv.php
@@ -367,7 +367,7 @@ final class Dotenv
}
}
- private function resolveCommands(string $value)
+ private function resolveCommands(string $value): string
{
if (false === strpos($value, '$')) {
return $value;
@@ -414,7 +414,7 @@ final class Dotenv
}, $value);
}
- private function resolveVariables(string $value)
+ private function resolveVariables(string $value): string
{
if (false === strpos($value, '$')) {
return $value;
diff --git a/src/Symfony/Component/ErrorHandler/ErrorHandler.php b/src/Symfony/Component/ErrorHandler/ErrorHandler.php
index f81700b5ad..37047911f7 100644
--- a/src/Symfony/Component/ErrorHandler/ErrorHandler.php
+++ b/src/Symfony/Component/ErrorHandler/ErrorHandler.php
@@ -14,7 +14,6 @@ namespace Symfony\Component\ErrorHandler;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Symfony\Component\ErrorHandler\Exception\FatalErrorException;
-use Symfony\Component\ErrorHandler\Exception\FatalThrowableError;
use Symfony\Component\ErrorHandler\Exception\OutOfMemoryException;
use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext;
use Symfony\Component\ErrorHandler\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
@@ -266,7 +265,7 @@ class ErrorHandler
if ($flush) {
foreach ($this->bootstrappingLogger->cleanLogs() as $log) {
- $type = $log[2]['exception'] instanceof \ErrorException ? $log[2]['exception']->getSeverity() : E_ERROR;
+ $type = ThrowableUtils::getSeverity($log[2]['exception']);
if (!isset($flush[$type])) {
$this->bootstrappingLogger->log($log[0], $log[1], $log[2]);
} elseif ($this->loggers[$type][0]) {
@@ -281,7 +280,7 @@ class ErrorHandler
/**
* Sets a user exception handler.
*
- * @param callable|null $handler A handler that will be called on Exception
+ * @param callable|null $handler A handler that must support \Throwable instances that will be called on Exception
*
* @return callable|null The previous exception handler
*/
@@ -539,57 +538,64 @@ class ErrorHandler
/**
* Handles an exception by logging then forwarding it to another handler.
*
- * @param \Exception|\Throwable $exception An exception to handle
- * @param array $error An array as returned by error_get_last()
+ * @param array $error An array as returned by error_get_last()
*
* @internal
*/
- public function handleException($exception, array $error = null)
+ public function handleException(\Throwable $exception, array $error = null)
{
if (null === $error) {
self::$exitCode = 255;
}
- if (!$exception instanceof \Exception) {
- $exception = new FatalThrowableError($exception);
- }
- $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR;
+
+ $type = ThrowableUtils::getSeverity($exception);
$handlerException = null;
- if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) {
+ if (($this->loggedErrors & $type) || $exception instanceof \Error) {
if (false !== strpos($message = $exception->getMessage(), "class@anonymous\0")) {
$message = $this->parseAnonymousClass($message);
}
+
if ($exception instanceof FatalErrorException) {
- if ($exception instanceof FatalThrowableError) {
- $error = [
- 'type' => $type,
- 'message' => $message,
- 'file' => $exception->getFile(),
- 'line' => $exception->getLine(),
- ];
- } else {
- $message = 'Fatal '.$message;
- }
+ $message = 'Fatal '.$message;
} elseif ($exception instanceof \ErrorException) {
$message = 'Uncaught '.$message;
+ } elseif ($exception instanceof \Error) {
+ $error = [
+ 'type' => $type,
+ 'message' => $message,
+ 'file' => $exception->getFile(),
+ 'line' => $exception->getLine(),
+ ];
+ $message = 'Uncaught Error: '.$message;
} else {
$message = 'Uncaught Exception: '.$message;
}
}
+
if ($this->loggedErrors & $type) {
try {
$this->loggers[$type][0]->log($this->loggers[$type][1], $message, ['exception' => $exception]);
} catch (\Throwable $handlerException) {
}
}
+
+ // temporary until fatal error handlers rework
+ $originalException = $exception;
+ if (!$exception instanceof \Exception) {
+ $exception = new FatalErrorException($exception->getMessage(), $exception->getCode(), $type, $exception->getFile(), $exception->getLine(), null, true, $exception->getTrace());
+ }
+
if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) {
foreach ($this->getFatalErrorHandlers() as $handler) {
if ($e = $handler->handleError($error, $exception)) {
- $exception = $e;
+ $convertedException = $e;
break;
}
}
}
+
+ $exception = $convertedException ?? $originalException;
$exceptionHandler = $this->exceptionHandler;
if ((!\is_array($exceptionHandler) || !$exceptionHandler[0] instanceof self || 'sendPhpResponse' !== $exceptionHandler[1]) && !\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
$this->exceptionHandler = [$this, 'sendPhpResponse'];
diff --git a/src/Symfony/Component/ErrorHandler/Exception/FatalThrowableError.php b/src/Symfony/Component/ErrorHandler/Exception/FatalThrowableError.php
index a690c83597..460d3e1ae8 100644
--- a/src/Symfony/Component/ErrorHandler/Exception/FatalThrowableError.php
+++ b/src/Symfony/Component/ErrorHandler/Exception/FatalThrowableError.php
@@ -11,6 +11,8 @@
namespace Symfony\Component\ErrorHandler\Exception;
+use Symfony\Component\ErrorHandler\ThrowableUtils;
+
/**
* Fatal Throwable Error.
*
@@ -24,18 +26,10 @@ class FatalThrowableError extends FatalErrorException
{
$this->originalClassName = \get_class($e);
- if ($e instanceof \ParseError) {
- $severity = E_PARSE;
- } elseif ($e instanceof \TypeError) {
- $severity = E_RECOVERABLE_ERROR;
- } else {
- $severity = E_ERROR;
- }
-
\ErrorException::__construct(
$e->getMessage(),
$e->getCode(),
- $severity,
+ ThrowableUtils::getSeverity($e),
$e->getFile(),
$e->getLine(),
$e->getPrevious()
diff --git a/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php b/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php
index 4731f2a2db..94c163a26e 100644
--- a/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php
+++ b/src/Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php
@@ -118,7 +118,7 @@ class DebugClassLoaderTest extends TestCase
/**
* @dataProvider provideDeprecatedSuper
*/
- public function testDeprecatedSuper($class, $super, $type)
+ public function testDeprecatedSuper(string $class, string $super, string $type)
{
set_error_handler(function () { return false; });
$e = error_reporting(0);
@@ -140,7 +140,7 @@ class DebugClassLoaderTest extends TestCase
$this->assertSame($xError, $lastError);
}
- public function provideDeprecatedSuper()
+ public function provideDeprecatedSuper(): array
{
return [
['DeprecatedInterfaceClass', 'DeprecatedInterface', 'implements'],
diff --git a/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php b/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php
index b536627a02..bb694cfc3b 100644
--- a/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php
+++ b/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php
@@ -382,18 +382,19 @@ class ErrorHandlerTest extends TestCase
restore_error_handler();
}
- public function testHandleException()
+ /**
+ * @dataProvider handleExceptionProvider
+ */
+ public function testHandleException(string $expectedMessage, \Throwable $exception)
{
try {
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$handler = ErrorHandler::register();
- $exception = new \Exception('foo');
-
- $logArgCheck = function ($level, $message, $context) {
- $this->assertSame('Uncaught Exception: foo', $message);
+ $logArgCheck = function ($level, $message, $context) use ($expectedMessage, $exception) {
+ $this->assertSame($expectedMessage, $message);
$this->assertArrayHasKey('exception', $context);
- $this->assertInstanceOf(\Exception::class, $context['exception']);
+ $this->assertInstanceOf(\get_class($exception), $context['exception']);
};
$logger
@@ -407,7 +408,7 @@ class ErrorHandlerTest extends TestCase
try {
$handler->handleException($exception);
$this->fail('Exception expected');
- } catch (\Exception $e) {
+ } catch (\Throwable $e) {
$this->assertSame($exception, $e);
}
@@ -422,6 +423,15 @@ class ErrorHandlerTest extends TestCase
}
}
+ public function handleExceptionProvider(): array
+ {
+ return [
+ ['Uncaught Exception: foo', new \Exception('foo')],
+ ['Uncaught Error: bar', new \Error('bar')],
+ ['Uncaught ccc', new \ErrorException('ccc')],
+ ];
+ }
+
public function testBootstrappingLogger()
{
$bootLogger = new BufferingLogger();
@@ -572,7 +582,7 @@ class ErrorHandlerTest extends TestCase
/**
* @dataProvider errorHandlerWhenLoggingProvider
*/
- public function testErrorHandlerWhenLogging($previousHandlerWasDefined, $loggerSetsAnotherHandler, $nextHandlerIsDefined)
+ public function testErrorHandlerWhenLogging(bool $previousHandlerWasDefined, bool $loggerSetsAnotherHandler, bool $nextHandlerIsDefined)
{
try {
if ($previousHandlerWasDefined) {
@@ -612,7 +622,7 @@ class ErrorHandlerTest extends TestCase
}
}
- public function errorHandlerWhenLoggingProvider()
+ public function errorHandlerWhenLoggingProvider(): iterable
{
foreach ([false, true] as $previousHandlerWasDefined) {
foreach ([false, true] as $loggerSetsAnotherHandler) {
diff --git a/src/Symfony/Component/ErrorHandler/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php b/src/Symfony/Component/ErrorHandler/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php
index a84c0dce97..0661d742c0 100644
--- a/src/Symfony/Component/ErrorHandler/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php
+++ b/src/Symfony/Component/ErrorHandler/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php
@@ -41,7 +41,7 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
/**
* @dataProvider provideClassNotFoundData
*/
- public function testHandleClassNotFound($error, $translatedMessage, $autoloader = null)
+ public function testHandleClassNotFound(array $error, string $translatedMessage, callable $autoloader = null)
{
if ($autoloader) {
// Unregister all autoloaders to ensure the custom provided
@@ -67,7 +67,7 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
$this->assertSame($error['line'], $exception->getLine());
}
- public function provideClassNotFoundData()
+ public function provideClassNotFoundData(): array
{
$autoloader = new ComposerClassLoader();
$autoloader->add('Symfony\Component\ErrorHandler\Exception\\', realpath(__DIR__.'/../../Exception'));
diff --git a/src/Symfony/Component/ErrorHandler/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php b/src/Symfony/Component/ErrorHandler/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php
index c24109b1b3..fa8dd42268 100644
--- a/src/Symfony/Component/ErrorHandler/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php
+++ b/src/Symfony/Component/ErrorHandler/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php
@@ -20,7 +20,7 @@ class UndefinedFunctionFatalErrorHandlerTest extends TestCase
/**
* @dataProvider provideUndefinedFunctionData
*/
- public function testUndefinedFunction($error, $translatedMessage)
+ public function testUndefinedFunction(array $error, string $translatedMessage)
{
$handler = new UndefinedFunctionFatalErrorHandler();
$exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line']));
@@ -33,7 +33,7 @@ class UndefinedFunctionFatalErrorHandlerTest extends TestCase
$this->assertSame($error['line'], $exception->getLine());
}
- public function provideUndefinedFunctionData()
+ public function provideUndefinedFunctionData(): array
{
return [
[
diff --git a/src/Symfony/Component/ErrorHandler/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php b/src/Symfony/Component/ErrorHandler/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php
index b91792b440..17414e1eb9 100644
--- a/src/Symfony/Component/ErrorHandler/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php
+++ b/src/Symfony/Component/ErrorHandler/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php
@@ -20,7 +20,7 @@ class UndefinedMethodFatalErrorHandlerTest extends TestCase
/**
* @dataProvider provideUndefinedMethodData
*/
- public function testUndefinedMethod($error, $translatedMessage)
+ public function testUndefinedMethod(array $error, string $translatedMessage)
{
$handler = new UndefinedMethodFatalErrorHandler();
$exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line']));
@@ -32,7 +32,7 @@ class UndefinedMethodFatalErrorHandlerTest extends TestCase
$this->assertSame($error['line'], $exception->getLine());
}
- public function provideUndefinedMethodData()
+ public function provideUndefinedMethodData(): array
{
return [
[
diff --git a/src/Symfony/Component/ErrorHandler/ThrowableUtils.php b/src/Symfony/Component/ErrorHandler/ThrowableUtils.php
new file mode 100644
index 0000000000..5cbe87f493
--- /dev/null
+++ b/src/Symfony/Component/ErrorHandler/ThrowableUtils.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\ErrorHandler;
+
+/**
+ * @internal
+ */
+class ThrowableUtils
+{
+ public static function getSeverity(\Throwable $throwable): int
+ {
+ if ($throwable instanceof \ErrorException) {
+ return $throwable->getSeverity();
+ }
+
+ if ($throwable instanceof \ParseError) {
+ return E_PARSE;
+ }
+
+ if ($throwable instanceof \TypeError) {
+ return E_RECOVERABLE_ERROR;
+ }
+
+ return E_ERROR;
+ }
+}
diff --git a/src/Symfony/Component/ErrorRenderer/Exception/FlattenException.php b/src/Symfony/Component/ErrorRenderer/Exception/FlattenException.php
index 2d345aead8..fe67c3fea4 100644
--- a/src/Symfony/Component/ErrorRenderer/Exception/FlattenException.php
+++ b/src/Symfony/Component/ErrorRenderer/Exception/FlattenException.php
@@ -292,7 +292,7 @@ class FlattenException
return $this;
}
- private function flattenArgs(array $args, int $level = 0, int &$count = 0)
+ private function flattenArgs(array $args, int $level = 0, int &$count = 0): array
{
$result = [];
foreach ($args as $key => $value) {
@@ -328,7 +328,7 @@ class FlattenException
return $result;
}
- private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value)
+ private function getClassNameFromIncomplete(\__PHP_Incomplete_Class $value): string
{
$array = new \ArrayObject($value);
diff --git a/src/Symfony/Component/ErrorRenderer/Tests/ErrorRenderer/HtmlErrorRendererTest.php b/src/Symfony/Component/ErrorRenderer/Tests/ErrorRenderer/HtmlErrorRendererTest.php
index 308733c025..4868d215c4 100644
--- a/src/Symfony/Component/ErrorRenderer/Tests/ErrorRenderer/HtmlErrorRendererTest.php
+++ b/src/Symfony/Component/ErrorRenderer/Tests/ErrorRenderer/HtmlErrorRendererTest.php
@@ -26,7 +26,7 @@ class HtmlErrorRendererTest extends TestCase
$this->assertStringMatchesFormat($expected, $errorRenderer->render($exception));
}
- public function getRenderData()
+ public function getRenderData(): iterable
{
$expectedDebug = <<
diff --git a/src/Symfony/Component/ErrorRenderer/Tests/ErrorRenderer/JsonErrorRendererTest.php b/src/Symfony/Component/ErrorRenderer/Tests/ErrorRenderer/JsonErrorRendererTest.php
index e35d3666db..565bcbd7d5 100644
--- a/src/Symfony/Component/ErrorRenderer/Tests/ErrorRenderer/JsonErrorRendererTest.php
+++ b/src/Symfony/Component/ErrorRenderer/Tests/ErrorRenderer/JsonErrorRendererTest.php
@@ -26,7 +26,7 @@ class JsonErrorRendererTest extends TestCase
$this->assertStringMatchesFormat($expected, $errorRenderer->render($exception));
}
- public function getRenderData()
+ public function getRenderData(): iterable
{
$expectedDebug = << |