From bc49f1e59653a4c4daccabf436e1fd6690742e5e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 23 May 2023 17:24:39 +0200 Subject: [PATCH 01/28] [7.0] Bump to PHP 8.2 minimum --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 9f6ea75..bd1f93d 100644 --- a/composer.json +++ b/composer.json @@ -16,13 +16,13 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^5.4|^6.0|^7.0" + "symfony/var-dumper": "^6.4|^7.0" }, "require-dev": { - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/serializer": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { From 6347bcd597187b6b4215f3a87321928f6c20ed94 Mon Sep 17 00:00:00 2001 From: Frank Fiebig Date: Tue, 4 Jul 2023 11:21:50 +0200 Subject: [PATCH 02/28] [Lock] 7.0 remove deprecations in Lock Component --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d753991..6a5e8fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +6.4 +--- + + * `FlattenExceptionNormalizer` no longer implements `ContextAwareNormalizerInterface` + 6.3 --- From dc149e12c93fd3d4f05dbd69b44d6b78650229c9 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 4 Jul 2023 14:50:59 +0200 Subject: [PATCH 03/28] [7.0] Remove remaining deprecated code paths --- Resources/bin/patch-type-declarations | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/bin/patch-type-declarations b/Resources/bin/patch-type-declarations index f24c996..5be1db4 100755 --- a/Resources/bin/patch-type-declarations +++ b/Resources/bin/patch-type-declarations @@ -19,7 +19,7 @@ if (\in_array('-h', $argv) || \in_array('--help', $argv)) { ' Patches type declarations based on "@return" PHPDoc and triggers deprecations for', ' incompatible method declarations.', '', - ' This assists you to make your package compatible with Symfony 6, but it can be used', + ' This assists you to make your package compatible with Symfony 7, but it can be used', ' for any class/package.', '', ' Available configuration via environment variables:', From f1c1d003bdfd680c7bf6de792028af011312396a Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 2 Jul 2023 23:52:21 +0200 Subject: [PATCH 04/28] [Components] Convert to native return types --- BufferingLogger.php | 5 +---- DebugClassLoader.php | 10 ++++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/BufferingLogger.php b/BufferingLogger.php index b33e079..6790974 100644 --- a/BufferingLogger.php +++ b/BufferingLogger.php @@ -40,10 +40,7 @@ public function __sleep(): array throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } - /** - * @return void - */ - public function __wakeup() + public function __wakeup(): void { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); } diff --git a/DebugClassLoader.php b/DebugClassLoader.php index 16af2d0..edadb27 100644 --- a/DebugClassLoader.php +++ b/DebugClassLoader.php @@ -104,6 +104,10 @@ class DebugClassLoader '__toString' => 'string', '__debugInfo' => 'array', '__serialize' => 'array', + '__set' => 'void', + '__unset' => 'void', + '__unserialize' => 'void', + '__wakeup' => 'void', ]; /** @@ -551,9 +555,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array $forcePatchTypes = $this->patchTypes['force']; if ($canAddReturnType = null !== $forcePatchTypes && !str_contains($method->getFileName(), \DIRECTORY_SEPARATOR.'vendor'.\DIRECTORY_SEPARATOR)) { - if ('void' !== (self::MAGIC_METHODS[$method->name] ?? 'void')) { - $this->patchTypes['force'] = $forcePatchTypes ?: 'docblock'; - } + $this->patchTypes['force'] = $forcePatchTypes ?: 'docblock'; $canAddReturnType = 2 === (int) $forcePatchTypes || false !== stripos($method->getFileName(), \DIRECTORY_SEPARATOR.'Tests'.\DIRECTORY_SEPARATOR) @@ -592,7 +594,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array continue; } - if (isset($doc['return']) || 'void' !== (self::MAGIC_METHODS[$method->name] ?? 'void')) { + if (isset($doc['return'])) { $this->setReturnType($doc['return'] ?? self::MAGIC_METHODS[$method->name], $method->class, $method->name, $method->getFileName(), $parent, $method->getReturnType()); if (isset(self::$returnTypes[$class][$method->name][0]) && $canAddReturnType) { From 1cea1a290654d9c96da8dcf8aa6f556fb22fae0d Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 21 Jul 2023 15:36:26 +0200 Subject: [PATCH 05/28] Add types to public and protected properties --- Exception/SilencedErrorContext.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Exception/SilencedErrorContext.php b/Exception/SilencedErrorContext.php index ac19e63..a3190da 100644 --- a/Exception/SilencedErrorContext.php +++ b/Exception/SilencedErrorContext.php @@ -18,7 +18,7 @@ */ class SilencedErrorContext implements \JsonSerializable { - public $count = 1; + public int $count = 1; private int $severity; private string $file; From ea1ac0828579d08c6363c41512775ab4e120937c Mon Sep 17 00:00:00 2001 From: Julian Krzefski Date: Thu, 9 Nov 2023 13:15:54 +0100 Subject: [PATCH 06/28] Do not use hyphens in exception message --- Resources/views/exception.html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/views/exception.html.php b/Resources/views/exception.html.php index 31554a4..e5471dc 100644 --- a/Resources/views/exception.html.php +++ b/Resources/views/exception.html.php @@ -15,7 +15,7 @@
-

formatFileFromText(nl2br($exceptionMessage)); ?>

+

formatFileFromText(nl2br($exceptionMessage)); ?>

include('assets/images/symfony-ghost.svg.php'); ?> From 60087ccbf6e73b903e6dce54ae60978b75e94f17 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 8 Dec 2023 15:23:08 +0100 Subject: [PATCH 07/28] Fx README files --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 12c0bfa..68904dd 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ The ErrorHandler component provides tools to manage errors and ease debugging PH Getting Started --------------- -``` -$ composer require symfony/error-handler +```bash +composer require symfony/error-handler ``` ```php From d71bd112a1caccf37679de57adfaa63517dcdc18 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Wed, 27 Dec 2023 22:18:42 +0100 Subject: [PATCH 08/28] Fix typo --- DebugClassLoader.php | 6 +++--- Tests/DebugClassLoaderTest.php | 2 +- Tests/ErrorEnhancer/ClassNotFoundErrorEnhancerTest.php | 2 +- Tests/ErrorEnhancer/UndefinedFunctionErrorEnhancerTest.php | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DebugClassLoader.php b/DebugClassLoader.php index edadb27..4ca2360 100644 --- a/DebugClassLoader.php +++ b/DebugClassLoader.php @@ -158,13 +158,13 @@ public function __construct(callable $classLoader) $test = realpath($dir.$test); if (false === $test || false === $i) { - // filesystem is case sensitive + // filesystem is case-sensitive self::$caseCheck = 0; } elseif (str_ends_with($test, $file)) { - // filesystem is case insensitive and realpath() normalizes the case of characters + // filesystem is case-insensitive and realpath() normalizes the case of characters self::$caseCheck = 1; } elseif ('Darwin' === \PHP_OS_FAMILY) { - // on MacOSX, HFS+ is case insensitive but realpath() doesn't normalize the case of characters + // on MacOSX, HFS+ is case-insensitive but realpath() doesn't normalize the case of characters self::$caseCheck = 2; } else { // filesystem case checks failed, fallback to disabling them diff --git a/Tests/DebugClassLoaderTest.php b/Tests/DebugClassLoaderTest.php index ac08e69..6a910d7 100644 --- a/Tests/DebugClassLoaderTest.php +++ b/Tests/DebugClassLoaderTest.php @@ -87,7 +87,7 @@ public function testFileCaseMismatch() $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Case mismatch between class and real file names'); if (!file_exists(__DIR__.'/Fixtures/CaseMismatch.php')) { - $this->markTestSkipped('Can only be run on case insensitive filesystems'); + $this->markTestSkipped('Can only be run on case-insensitive filesystems'); } class_exists(Fixtures\CaseMismatch::class, true); diff --git a/Tests/ErrorEnhancer/ClassNotFoundErrorEnhancerTest.php b/Tests/ErrorEnhancer/ClassNotFoundErrorEnhancerTest.php index 72ee199..38b0423 100644 --- a/Tests/ErrorEnhancer/ClassNotFoundErrorEnhancerTest.php +++ b/Tests/ErrorEnhancer/ClassNotFoundErrorEnhancerTest.php @@ -156,7 +156,7 @@ public function testEnhanceWithFatalError() public function testCannotRedeclareClass() { if (!file_exists(__DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP')) { - $this->markTestSkipped('Can only be run on case insensitive filesystems'); + $this->markTestSkipped('Can only be run on case-insensitive filesystems'); } require_once __DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP'; diff --git a/Tests/ErrorEnhancer/UndefinedFunctionErrorEnhancerTest.php b/Tests/ErrorEnhancer/UndefinedFunctionErrorEnhancerTest.php index 547e333..f9474f7 100644 --- a/Tests/ErrorEnhancer/UndefinedFunctionErrorEnhancerTest.php +++ b/Tests/ErrorEnhancer/UndefinedFunctionErrorEnhancerTest.php @@ -28,7 +28,7 @@ public function testEnhance(string $originalMessage, string $enhancedMessage) $error = $enhancer->enhance(new \Error($originalMessage)); $this->assertInstanceOf(UndefinedFunctionError::class, $error); - // class names are case insensitive and PHP do not return the same + // class names are case-insensitive and PHP do not return the same $this->assertSame(strtolower($enhancedMessage), strtolower($error->getMessage())); $this->assertSame(realpath(__FILE__), $error->getFile()); $this->assertSame($expectedLine, $error->getLine()); From 03b35b60af82df4ea2cb2fd66d7bf9997ff875e6 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Wed, 27 Dec 2023 13:57:19 +0100 Subject: [PATCH 09/28] DX: re-apply CS --- DebugClassLoader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DebugClassLoader.php b/DebugClassLoader.php index 4ca2360..b7ed729 100644 --- a/DebugClassLoader.php +++ b/DebugClassLoader.php @@ -184,7 +184,7 @@ public function getClassLoader(): callable public static function enable(): void { // Ensures we don't hit https://bugs.php.net/42098 - class_exists(\Symfony\Component\ErrorHandler\ErrorHandler::class); + class_exists(ErrorHandler::class); class_exists(\Psr\Log\LogLevel::class); if (!\is_array($functions = spl_autoload_functions())) { From 7bac4a133259157b1ee732438797d4ebfb0c362d Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 8 Jan 2024 08:27:24 +0100 Subject: [PATCH 10/28] [Dotenv][ErrorHandler][EventDispatcher] Use CPP --- Error/FatalError.php | 14 ++++++++------ ErrorRenderer/FileLinkFormatter.php | 14 ++++++-------- ErrorRenderer/HtmlErrorRenderer.php | 14 ++++++++------ ErrorRenderer/SerializerErrorRenderer.php | 10 ++++++---- Exception/SilencedErrorContext.php | 18 +++++++----------- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Error/FatalError.php b/Error/FatalError.php index a1fd5a9..e0cf2f5 100644 --- a/Error/FatalError.php +++ b/Error/FatalError.php @@ -13,17 +13,19 @@ class FatalError extends \Error { - private array $error; - /** * @param array $error An array as returned by error_get_last() */ - public function __construct(string $message, int $code, array $error, int $traceOffset = null, bool $traceArgs = true, array $trace = null) - { + public function __construct( + string $message, + int $code, + private array $error, + int $traceOffset = null, + bool $traceArgs = true, + array $trace = null, + ) { parent::__construct($message, $code); - $this->error = $error; - if (null !== $trace) { if (!$traceArgs) { foreach ($trace as &$frame) { diff --git a/ErrorRenderer/FileLinkFormatter.php b/ErrorRenderer/FileLinkFormatter.php index 28adaf3..570debb 100644 --- a/ErrorRenderer/FileLinkFormatter.php +++ b/ErrorRenderer/FileLinkFormatter.php @@ -25,15 +25,16 @@ class FileLinkFormatter { private array|false $fileLinkFormat; - private ?RequestStack $requestStack = null; - private ?string $baseDir = null; - private \Closure|string|null $urlFormat; /** * @param string|\Closure $urlFormat the URL format, or a closure that returns it on-demand */ - public function __construct(string|array $fileLinkFormat = null, RequestStack $requestStack = null, string $baseDir = null, string|\Closure $urlFormat = null) - { + public function __construct( + string|array $fileLinkFormat = null, + private ?RequestStack $requestStack = null, + private ?string $baseDir = null, + private null|string|\Closure $urlFormat = null, + ) { $fileLinkFormat ??= $_ENV['SYMFONY_IDE'] ?? $_SERVER['SYMFONY_IDE'] ?? ''; if (!\is_array($f = $fileLinkFormat)) { @@ -43,9 +44,6 @@ public function __construct(string|array $fileLinkFormat = null, RequestStack $r } $this->fileLinkFormat = $fileLinkFormat; - $this->requestStack = $requestStack; - $this->baseDir = $baseDir; - $this->urlFormat = $urlFormat; } public function format(string $file, int $line): string|false diff --git a/ErrorRenderer/HtmlErrorRenderer.php b/ErrorRenderer/HtmlErrorRenderer.php index 7ae5947..23ebd7f 100644 --- a/ErrorRenderer/HtmlErrorRenderer.php +++ b/ErrorRenderer/HtmlErrorRenderer.php @@ -37,9 +37,7 @@ class HtmlErrorRenderer implements ErrorRendererInterface private bool|\Closure $debug; private string $charset; private FileLinkFormatter $fileLinkFormat; - private ?string $projectDir; private string|\Closure $outputBuffer; - private ?LoggerInterface $logger; private static string $template = 'views/error.html.php'; @@ -47,14 +45,18 @@ class HtmlErrorRenderer implements ErrorRendererInterface * @param bool|callable $debug The debugging mode as a boolean or a callable that should return it * @param string|callable $outputBuffer The output buffer as a string or a callable that should return it */ - public function __construct(bool|callable $debug = false, string $charset = null, string|FileLinkFormatter $fileLinkFormat = null, string $projectDir = null, string|callable $outputBuffer = '', LoggerInterface $logger = null) - { + public function __construct( + bool|callable $debug = false, + string $charset = null, + string|FileLinkFormatter $fileLinkFormat = null, + private ?string $projectDir = null, + string|callable $outputBuffer = '', + private ?LoggerInterface $logger = null, + ) { $this->debug = \is_bool($debug) ? $debug : $debug(...); $this->charset = $charset ?: (\ini_get('default_charset') ?: 'UTF-8'); $this->fileLinkFormat = $fileLinkFormat instanceof FileLinkFormatter ? $fileLinkFormat : new FileLinkFormatter($fileLinkFormat); - $this->projectDir = $projectDir; $this->outputBuffer = \is_string($outputBuffer) ? $outputBuffer : $outputBuffer(...); - $this->logger = $logger; } public function render(\Throwable $exception): FlattenException diff --git a/ErrorRenderer/SerializerErrorRenderer.php b/ErrorRenderer/SerializerErrorRenderer.php index 1f286b7..88d4c90 100644 --- a/ErrorRenderer/SerializerErrorRenderer.php +++ b/ErrorRenderer/SerializerErrorRenderer.php @@ -24,7 +24,6 @@ */ class SerializerErrorRenderer implements ErrorRendererInterface { - private SerializerInterface $serializer; private string|\Closure $format; private ErrorRendererInterface $fallbackErrorRenderer; private bool|\Closure $debug; @@ -34,9 +33,12 @@ class SerializerErrorRenderer implements ErrorRendererInterface * formats not supported by Request::getMimeTypes() should be given as mime types * @param bool|callable $debug The debugging mode as a boolean or a callable that should return it */ - public function __construct(SerializerInterface $serializer, string|callable $format, ErrorRendererInterface $fallbackErrorRenderer = null, bool|callable $debug = false) - { - $this->serializer = $serializer; + public function __construct( + private SerializerInterface $serializer, + string|callable $format, + ErrorRendererInterface $fallbackErrorRenderer = null, + bool|callable $debug = false, + ) { $this->format = \is_string($format) ? $format : $format(...); $this->fallbackErrorRenderer = $fallbackErrorRenderer ?? new HtmlErrorRenderer(); $this->debug = \is_bool($debug) ? $debug : $debug(...); diff --git a/Exception/SilencedErrorContext.php b/Exception/SilencedErrorContext.php index a3190da..b67a2bc 100644 --- a/Exception/SilencedErrorContext.php +++ b/Exception/SilencedErrorContext.php @@ -20,17 +20,13 @@ class SilencedErrorContext implements \JsonSerializable { public int $count = 1; - private int $severity; - private string $file; - private int $line; - private array $trace; - - public function __construct(int $severity, string $file, int $line, array $trace = [], int $count = 1) - { - $this->severity = $severity; - $this->file = $file; - $this->line = $line; - $this->trace = $trace; + public function __construct( + private int $severity, + private string $file, + private int $line, + private array $trace = [], + int $count = 1, + ) { $this->count = $count; } From 794572527f3700727bb5acd9a78b5fdc2a9fa3b2 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Mon, 15 Jan 2024 20:49:54 +0100 Subject: [PATCH 11/28] CS: enable ordered_types.null_adjustment=always_last --- ErrorRenderer/FileLinkFormatter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ErrorRenderer/FileLinkFormatter.php b/ErrorRenderer/FileLinkFormatter.php index 570debb..59fc14e 100644 --- a/ErrorRenderer/FileLinkFormatter.php +++ b/ErrorRenderer/FileLinkFormatter.php @@ -33,7 +33,7 @@ public function __construct( string|array $fileLinkFormat = null, private ?RequestStack $requestStack = null, private ?string $baseDir = null, - private null|string|\Closure $urlFormat = null, + private string|\Closure|null $urlFormat = null, ) { $fileLinkFormat ??= $_ENV['SYMFONY_IDE'] ?? $_SERVER['SYMFONY_IDE'] ?? ''; From 5ba18e5ad0eac919048489abf42de3f36f7ca164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Fri, 23 Feb 2024 17:16:59 +0100 Subject: [PATCH 12/28] [HttpKernel] Increase log level to "error" at least for all PHP errors --- CHANGELOG.md | 5 +++++ ErrorHandler.php | 18 +++++++++--------- Tests/ErrorHandlerTest.php | 28 ++++++++++++++-------------- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a5e8fb..c3037ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.1 +--- + + * Increase log level to "error" at least for all PHP errors + 6.4 --- diff --git a/ErrorHandler.php b/ErrorHandler.php index c096eac..c8c150e 100644 --- a/ErrorHandler.php +++ b/ErrorHandler.php @@ -71,13 +71,13 @@ class ErrorHandler private array $loggers = [ \E_DEPRECATED => [null, LogLevel::INFO], \E_USER_DEPRECATED => [null, LogLevel::INFO], - \E_NOTICE => [null, LogLevel::WARNING], - \E_USER_NOTICE => [null, LogLevel::WARNING], - \E_STRICT => [null, LogLevel::WARNING], - \E_WARNING => [null, LogLevel::WARNING], - \E_USER_WARNING => [null, LogLevel::WARNING], - \E_COMPILE_WARNING => [null, LogLevel::WARNING], - \E_CORE_WARNING => [null, LogLevel::WARNING], + \E_NOTICE => [null, LogLevel::ERROR], + \E_USER_NOTICE => [null, LogLevel::ERROR], + \E_STRICT => [null, LogLevel::ERROR], + \E_WARNING => [null, LogLevel::ERROR], + \E_USER_WARNING => [null, LogLevel::ERROR], + \E_COMPILE_WARNING => [null, LogLevel::ERROR], + \E_CORE_WARNING => [null, LogLevel::ERROR], \E_USER_ERROR => [null, LogLevel::CRITICAL], \E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL], \E_COMPILE_ERROR => [null, LogLevel::CRITICAL], @@ -432,7 +432,7 @@ public function handleError(int $type, string $message, string $file, int $line) return true; } } else { - if (PHP_VERSION_ID < 80303 && str_contains($message, '@anonymous')) { + if (\PHP_VERSION_ID < 80303 && str_contains($message, '@anonymous')) { $backtrace = debug_backtrace(false, 5); for ($i = 1; isset($backtrace[$i]); ++$i) { @@ -448,7 +448,7 @@ public function handleError(int $type, string $message, string $file, int $line) } } - if (false !== strpos($message, "@anonymous\0")) { + if (str_contains($message, "@anonymous\0")) { $message = $this->parseAnonymousClass($message); $logMessage = $this->levels[$type].': '.$message; } diff --git a/Tests/ErrorHandlerTest.php b/Tests/ErrorHandlerTest.php index f216a8f..aa2765d 100644 --- a/Tests/ErrorHandlerTest.php +++ b/Tests/ErrorHandlerTest.php @@ -194,13 +194,13 @@ public function testDefaultLogger() $loggers = [ \E_DEPRECATED => [null, LogLevel::INFO], \E_USER_DEPRECATED => [null, LogLevel::INFO], - \E_NOTICE => [$logger, LogLevel::WARNING], + \E_NOTICE => [$logger, LogLevel::ERROR], \E_USER_NOTICE => [$logger, LogLevel::CRITICAL], - \E_STRICT => [null, LogLevel::WARNING], - \E_WARNING => [null, LogLevel::WARNING], - \E_USER_WARNING => [null, LogLevel::WARNING], - \E_COMPILE_WARNING => [null, LogLevel::WARNING], - \E_CORE_WARNING => [null, LogLevel::WARNING], + \E_STRICT => [null, LogLevel::ERROR], + \E_WARNING => [null, LogLevel::ERROR], + \E_USER_WARNING => [null, LogLevel::ERROR], + \E_COMPILE_WARNING => [null, LogLevel::ERROR], + \E_CORE_WARNING => [null, LogLevel::ERROR], \E_USER_ERROR => [null, LogLevel::CRITICAL], \E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL], \E_COMPILE_ERROR => [null, LogLevel::CRITICAL], @@ -431,13 +431,13 @@ public function testBootstrappingLogger() $loggers = [ \E_DEPRECATED => [$bootLogger, LogLevel::INFO], \E_USER_DEPRECATED => [$bootLogger, LogLevel::INFO], - \E_NOTICE => [$bootLogger, LogLevel::WARNING], - \E_USER_NOTICE => [$bootLogger, LogLevel::WARNING], - \E_STRICT => [$bootLogger, LogLevel::WARNING], - \E_WARNING => [$bootLogger, LogLevel::WARNING], - \E_USER_WARNING => [$bootLogger, LogLevel::WARNING], - \E_COMPILE_WARNING => [$bootLogger, LogLevel::WARNING], - \E_CORE_WARNING => [$bootLogger, LogLevel::WARNING], + \E_NOTICE => [$bootLogger, LogLevel::ERROR], + \E_USER_NOTICE => [$bootLogger, LogLevel::ERROR], + \E_STRICT => [$bootLogger, LogLevel::ERROR], + \E_WARNING => [$bootLogger, LogLevel::ERROR], + \E_USER_WARNING => [$bootLogger, LogLevel::ERROR], + \E_COMPILE_WARNING => [$bootLogger, LogLevel::ERROR], + \E_CORE_WARNING => [$bootLogger, LogLevel::ERROR], \E_USER_ERROR => [$bootLogger, LogLevel::CRITICAL], \E_RECOVERABLE_ERROR => [$bootLogger, LogLevel::CRITICAL], \E_COMPILE_ERROR => [$bootLogger, LogLevel::CRITICAL], @@ -658,7 +658,7 @@ public function testAssertQuietEval() $logs = $logger->cleanLogs(); - $this->assertSame('warning', $logs[0][0]); + $this->assertSame('error', $logs[0][0]); $this->assertSame('Warning: assert(): assert(false) failed', $logs[0][1]); } From 53b04814ac1091e78c0ceec36a080214ecf8911b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 26 Mar 2024 15:39:06 +0100 Subject: [PATCH 13/28] Update the hyphenate character --- Resources/assets/css/exception.css | 2 +- Resources/views/exception.html.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/assets/css/exception.css b/Resources/assets/css/exception.css index 3e6eae5..ed90e6a 100644 --- a/Resources/assets/css/exception.css +++ b/Resources/assets/css/exception.css @@ -132,7 +132,7 @@ table th { background-color: var(--base-2); font-weight: bold; text-align: left; .prewrap { white-space: pre-wrap; } .nowrap { white-space: nowrap; } .newline { display: block; } -.break-long-words { word-wrap: break-word; overflow-wrap: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; min-width: 0; } +.break-long-words { word-wrap: break-word; overflow-wrap: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; hyphenate-character: ''; min-width: 0; } .text-small { font-size: 12px !important; } .text-muted { color: #999; } .text-bold { font-weight: bold; } diff --git a/Resources/views/exception.html.php b/Resources/views/exception.html.php index e5471dc..31554a4 100644 --- a/Resources/views/exception.html.php +++ b/Resources/views/exception.html.php @@ -15,7 +15,7 @@
-

formatFileFromText(nl2br($exceptionMessage)); ?>

+

formatFileFromText(nl2br($exceptionMessage)); ?>

include('assets/images/symfony-ghost.svg.php'); ?> From 64908ae925a52b500fcd1106858a68d163cc07f1 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 20 Jun 2024 17:52:34 +0200 Subject: [PATCH 14/28] Prefix all sprintf() calls --- BufferingLogger.php | 2 +- DebugClassLoader.php | 34 +++++++++---------- ErrorEnhancer/ClassNotFoundErrorEnhancer.php | 4 +-- .../UndefinedFunctionErrorEnhancer.php | 4 +-- .../UndefinedMethodErrorEnhancer.php | 2 +- ErrorRenderer/HtmlErrorRenderer.php | 12 +++---- Tests/Exception/FlattenExceptionTest.php | 2 +- 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/BufferingLogger.php b/BufferingLogger.php index 6790974..e6597b4 100644 --- a/BufferingLogger.php +++ b/BufferingLogger.php @@ -62,7 +62,7 @@ public function __destruct() } } - error_log(sprintf('%s [%s] %s', date(\DateTimeInterface::RFC3339), $level, $message)); + error_log(\sprintf('%s [%s] %s', date(\DateTimeInterface::RFC3339), $level, $message)); } } } diff --git a/DebugClassLoader.php b/DebugClassLoader.php index 5c8672e..6a6338d 100644 --- a/DebugClassLoader.php +++ b/DebugClassLoader.php @@ -332,7 +332,7 @@ private function checkClass(string $class, ?string $file = null): void $name = $refl->getName(); if ($name !== $class && 0 === strcasecmp($name, $class)) { - throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name)); + throw new \RuntimeException(\sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name)); } $deprecations = $this->checkAnnotations($refl, $name); @@ -348,14 +348,14 @@ private function checkClass(string $class, ?string $file = null): void if (!$exists) { if (str_contains($class, '/')) { - throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class)); + throw new \RuntimeException(\sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class)); } - throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file)); + throw new \RuntimeException(\sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file)); } if (self::$caseCheck && $message = $this->checkCase($refl, $file, $class)) { - throw new \RuntimeException(sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', $message[0], $message[1], $message[2])); + throw new \RuntimeException(\sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', $message[0], $message[1], $message[2])); } } @@ -416,7 +416,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array } if (isset(self::$final[$parent])) { - $deprecations[] = sprintf('The "%s" class is considered final%s It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $className); + $deprecations[] = \sprintf('The "%s" class is considered final%s It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $className); } } @@ -429,10 +429,10 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array $type = class_exists($class, false) ? 'class' : (interface_exists($class, false) ? 'interface' : 'trait'); $verb = class_exists($use, false) || interface_exists($class, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses'); - $deprecations[] = sprintf('The "%s" %s %s "%s" that is deprecated%s', $className, $type, $verb, $use, self::$deprecated[$use]); + $deprecations[] = \sprintf('The "%s" %s %s "%s" that is deprecated%s', $className, $type, $verb, $use, self::$deprecated[$use]); } if (isset(self::$internal[$use]) && strncmp($vendor, str_replace('_', '\\', $use), $vendorLen)) { - $deprecations[] = sprintf('The "%s" %s is considered internal%s It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $className); + $deprecations[] = \sprintf('The "%s" %s is considered internal%s It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $className); } if (isset(self::$method[$use])) { if ($refl->isAbstract()) { @@ -458,7 +458,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array } $realName = substr($name, 0, strpos($name, '(')); if (!$refl->hasMethod($realName) || !($methodRefl = $refl->getMethod($realName))->isPublic() || ($static && !$methodRefl->isStatic()) || (!$static && $methodRefl->isStatic())) { - $deprecations[] = sprintf('Class "%s" should implement method "%s::%s%s"%s', $className, ($static ? 'static ' : '').$interface, $name, $returnType ? ': '.$returnType : '', null === $description ? '.' : ': '.$description); + $deprecations[] = \sprintf('Class "%s" should implement method "%s::%s%s"%s', $className, ($static ? 'static ' : '').$interface, $name, $returnType ? ': '.$returnType : '', null === $description ? '.' : ': '.$description); } } } @@ -522,13 +522,13 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array if ($parent && isset(self::$finalMethods[$parent][$method->name])) { [$declaringClass, $message] = self::$finalMethods[$parent][$method->name]; - $deprecations[] = sprintf('The "%s::%s()" method is considered final%s It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $className); + $deprecations[] = \sprintf('The "%s::%s()" method is considered final%s It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $className); } if (isset(self::$internalMethods[$class][$method->name])) { [$declaringClass, $message] = self::$internalMethods[$class][$method->name]; if (strncmp($ns, $declaringClass, $len)) { - $deprecations[] = sprintf('The "%s::%s()" method is considered internal%s It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $className); + $deprecations[] = \sprintf('The "%s::%s()" method is considered internal%s It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $className); } } @@ -547,7 +547,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array foreach (self::$annotatedParameters[$class][$method->name] as $parameterName => $deprecation) { if (!isset($definedParameters[$parameterName]) && !isset($doc['param'][$parameterName])) { - $deprecations[] = sprintf($deprecation, $className); + $deprecations[] = \sprintf($deprecation, $className); } } } @@ -583,7 +583,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array if ('docblock' === $this->patchTypes['force']) { $this->patchMethod($method, $returnType, $declaringFile, $normalizedType); } elseif ('' !== $declaringClass && $this->patchTypes['deprecations']) { - $deprecations[] = sprintf('Method "%s::%s()" might add "%s" as a native return type declaration in the future. Do the same in %s "%s" now to avoid errors or add an explicit @return annotation to suppress this message.', $declaringClass, $method->name, $normalizedType, interface_exists($declaringClass) ? 'implementation' : 'child class', $className); + $deprecations[] = \sprintf('Method "%s::%s()" might add "%s" as a native return type declaration in the future. Do the same in %s "%s" now to avoid errors or add an explicit @return annotation to suppress this message.', $declaringClass, $method->name, $normalizedType, interface_exists($declaringClass) ? 'implementation' : 'child class', $className); } } } @@ -632,7 +632,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array } foreach ($doc['param'] as $parameterName => $parameterType) { if (!isset($definedParameters[$parameterName])) { - self::$annotatedParameters[$class][$method->name][$parameterName] = sprintf('The "%%s::%s()" method will require a new "%s$%s" argument in the next major version of its %s "%s", not defining it is deprecated.', $method->name, $parameterType ? $parameterType.' ' : '', $parameterName, interface_exists($className) ? 'interface' : 'parent class', $className); + self::$annotatedParameters[$class][$method->name][$parameterName] = \sprintf('The "%%s::%s()" method will require a new "%s$%s" argument in the next major version of its %s "%s", not defining it is deprecated.', $method->name, $parameterType ? $parameterType.' ' : '', $parameterName, interface_exists($className) ? 'interface' : 'parent class', $className); } } } @@ -652,7 +652,7 @@ public function checkAnnotations(\ReflectionClass $refl, string $class): array foreach ($parentAndOwnInterfaces as $use) { if (isset(self::${$type}[$use][$r->name]) && !isset($doc['deprecated']) && ('finalConstants' === $type || substr($use, 0, strrpos($use, '\\')) !== substr($use, 0, strrpos($class, '\\')))) { $msg = 'finalConstants' === $type ? '%s" constant' : '$%s" property'; - $deprecations[] = sprintf('The "%s::'.$msg.' is considered final. You should not override it in "%s".', self::${$type}[$use][$r->name], $r->name, $class); + $deprecations[] = \sprintf('The "%s::'.$msg.' is considered final. You should not override it in "%s".', self::${$type}[$use][$r->name], $r->name, $class); } } @@ -893,8 +893,8 @@ private function setReturnType(string $types, string $class, string $method, str } } - $phpType = sprintf($nullable ? (1 < \count($phpTypes) ? '%s|null' : '?%s') : '%s', implode($glue, $phpTypes)); - $docType = sprintf($nullable ? '%s|null' : '%s', implode($glue, $docTypes)); + $phpType = \sprintf($nullable ? (1 < \count($phpTypes) ? '%s|null' : '?%s') : '%s', implode($glue, $phpTypes)); + $docType = \sprintf($nullable ? '%s|null' : '%s', implode($glue, $docTypes)); self::$returnTypes[$class][$method] = [$phpType, $docType, $class, $filename]; } @@ -1026,7 +1026,7 @@ private function patchMethod(\ReflectionMethod $method, string $returnType, stri ++$fileOffset; } - $returnType[$i] = null !== $format ? sprintf($format, $alias) : $alias; + $returnType[$i] = null !== $format ? \sprintf($format, $alias) : $alias; } if ('docblock' === $this->patchTypes['force'] || ('object' === $normalizedType && '7.1' === $this->patchTypes['php'])) { diff --git a/ErrorEnhancer/ClassNotFoundErrorEnhancer.php b/ErrorEnhancer/ClassNotFoundErrorEnhancer.php index b4623cf..fc243a6 100644 --- a/ErrorEnhancer/ClassNotFoundErrorEnhancer.php +++ b/ErrorEnhancer/ClassNotFoundErrorEnhancer.php @@ -34,11 +34,11 @@ public function enhance(\Throwable $error): ?\Throwable if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) { $className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1); $namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex); - $message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix); + $message = \sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix); $tail = ' for another namespace?'; } else { $className = $fullyQualifiedClassName; - $message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className); + $message = \sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className); $tail = '?'; } diff --git a/ErrorEnhancer/UndefinedFunctionErrorEnhancer.php b/ErrorEnhancer/UndefinedFunctionErrorEnhancer.php index 0458c26..e1d54ab 100644 --- a/ErrorEnhancer/UndefinedFunctionErrorEnhancer.php +++ b/ErrorEnhancer/UndefinedFunctionErrorEnhancer.php @@ -47,10 +47,10 @@ public function enhance(\Throwable $error): ?\Throwable if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedFunctionName, '\\')) { $functionName = substr($fullyQualifiedFunctionName, $namespaceSeparatorIndex + 1); $namespacePrefix = substr($fullyQualifiedFunctionName, 0, $namespaceSeparatorIndex); - $message = sprintf('Attempted to call function "%s" from namespace "%s".', $functionName, $namespacePrefix); + $message = \sprintf('Attempted to call function "%s" from namespace "%s".', $functionName, $namespacePrefix); } else { $functionName = $fullyQualifiedFunctionName; - $message = sprintf('Attempted to call function "%s" from the global namespace.', $functionName); + $message = \sprintf('Attempted to call function "%s" from the global namespace.', $functionName); } $candidates = []; diff --git a/ErrorEnhancer/UndefinedMethodErrorEnhancer.php b/ErrorEnhancer/UndefinedMethodErrorEnhancer.php index 80eaec9..e331c17 100644 --- a/ErrorEnhancer/UndefinedMethodErrorEnhancer.php +++ b/ErrorEnhancer/UndefinedMethodErrorEnhancer.php @@ -34,7 +34,7 @@ public function enhance(\Throwable $error): ?\Throwable $className = $matches[1]; $methodName = $matches[2]; - $message = sprintf('Attempted to call an undefined method named "%s" of class "%s".', $methodName, $className); + $message = \sprintf('Attempted to call an undefined method named "%s" of class "%s".', $methodName, $className); if ('' === $methodName || !class_exists($className) || null === $methods = get_class_methods($className)) { // failed to get the class or its methods on which an unknown method was called (for example on an anonymous class) diff --git a/ErrorRenderer/HtmlErrorRenderer.php b/ErrorRenderer/HtmlErrorRenderer.php index 7cb7cd3..16f7588 100644 --- a/ErrorRenderer/HtmlErrorRenderer.php +++ b/ErrorRenderer/HtmlErrorRenderer.php @@ -160,9 +160,9 @@ private function formatArgs(array $args): string $result = []; foreach ($args as $key => $item) { if ('object' === $item[0]) { - $formattedValue = sprintf('object(%s)', $this->abbrClass($item[1])); + $formattedValue = \sprintf('object(%s)', $this->abbrClass($item[1])); } elseif ('array' === $item[0]) { - $formattedValue = sprintf('array(%s)', \is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); + $formattedValue = \sprintf('array(%s)', \is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); } elseif ('null' === $item[0]) { $formattedValue = 'null'; } elseif ('boolean' === $item[0]) { @@ -175,7 +175,7 @@ private function formatArgs(array $args): string $formattedValue = str_replace("\n", '', $this->escape(var_export($item[1], true))); } - $result[] = \is_int($key) ? $formattedValue : sprintf("'%s' => %s", $this->escape($key), $formattedValue); + $result[] = \is_int($key) ? $formattedValue : \sprintf("'%s' => %s", $this->escape($key), $formattedValue); } return implode(', ', $result); @@ -196,7 +196,7 @@ private function abbrClass(string $class): string $parts = explode('\\', $class); $short = array_pop($parts); - return sprintf('%s', $class, $short); + return \sprintf('%s', $class, $short); } private function getFileRelative(string $file): ?string @@ -225,7 +225,7 @@ private function formatFile(string $file, int $line, ?string $text = null): stri $text = $file; if (null !== $rel = $this->getFileRelative($text)) { $rel = explode('/', $rel, 2); - $text = sprintf('%s%s', $this->projectDir, $rel[0], '/'.($rel[1] ?? '')); + $text = \sprintf('%s%s', $this->projectDir, $rel[0], '/'.($rel[1] ?? '')); } } @@ -235,7 +235,7 @@ private function formatFile(string $file, int $line, ?string $text = null): stri $link = $this->fileLinkFormat->format($file, $line); - return sprintf('%s', $this->escape($link), $text); + return \sprintf('%s', $this->escape($link), $text); } /** diff --git a/Tests/Exception/FlattenExceptionTest.php b/Tests/Exception/FlattenExceptionTest.php index a3ff2f6..737c246 100644 --- a/Tests/Exception/FlattenExceptionTest.php +++ b/Tests/Exception/FlattenExceptionTest.php @@ -255,7 +255,7 @@ public function testAnonymousClass() $this->assertSame('Symfony\Component\HttpKernel\Exception\NotFoundHttpException@anonymous', $flattened->getClass()); - $flattened = FlattenException::createFromThrowable(new \Exception(sprintf('Class "%s" blah.', (new class() extends \RuntimeException { + $flattened = FlattenException::createFromThrowable(new \Exception(\sprintf('Class "%s" blah.', (new class() extends \RuntimeException { })::class))); $this->assertSame('Class "RuntimeException@anonymous" blah.', $flattened->getMessage()); From 9faaecf0003801392ab1fdc150fdee45b12ef7c8 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sun, 16 Jun 2024 17:17:26 +0200 Subject: [PATCH 15/28] chore: CS fixes --- DebugClassLoader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DebugClassLoader.php b/DebugClassLoader.php index 6a6338d..fd69b97 100644 --- a/DebugClassLoader.php +++ b/DebugClassLoader.php @@ -1135,7 +1135,7 @@ private function fixReturnStatements(\ReflectionMethod $method, string $returnTy $braces = 0; for (; $i < $end; ++$i) { if (!$inClosure) { - $inClosure = false !== strpos($code[$i], 'function ('); + $inClosure = str_contains($code[$i], 'function ('); } if ($inClosure) { From f97e68e55daa802548aa47b4c6e89db973b63dc5 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 6 Jul 2024 09:57:16 +0200 Subject: [PATCH 16/28] Update .gitattributes --- .gitattributes | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 84c7add..14c3c35 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,3 @@ /Tests export-ignore /phpunit.xml.dist export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore +/.git* export-ignore From 04d41f32dd20bf1350b2d47850c62ba0c3c6653d Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 22 Jul 2024 10:27:43 +0200 Subject: [PATCH 17/28] Use CPP where possible --- ErrorHandler.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ErrorHandler.php b/ErrorHandler.php index fd83eb3..f1d805d 100644 --- a/ErrorHandler.php +++ b/ErrorHandler.php @@ -92,7 +92,6 @@ class ErrorHandler private int $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE private int $loggedErrors = 0; private \Closure $configureException; - private bool $debug; private bool $isRecursive = false; private bool $isRoot = false; @@ -179,8 +178,10 @@ public static function call(callable $function, mixed ...$arguments): mixed } } - public function __construct(?BufferingLogger $bootstrappingLogger = null, bool $debug = false) - { + public function __construct( + ?BufferingLogger $bootstrappingLogger = null, + private bool $debug = false, + ) { if ($bootstrappingLogger) { $this->bootstrappingLogger = $bootstrappingLogger; $this->setDefaultLogger($bootstrappingLogger); @@ -192,7 +193,6 @@ public function __construct(?BufferingLogger $bootstrappingLogger = null, bool $ $e->line = $line ?? $e->line; }, null, new class() extends \Exception { }); - $this->debug = $debug; } /** From 432bb369952795c61ca1def65e078c4a80dad13c Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 26 Jul 2024 15:02:51 +0200 Subject: [PATCH 18/28] [ErrorHandler] Fix `E_STRICT` logging level --- ErrorHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ErrorHandler.php b/ErrorHandler.php index 76c5a9b..c0af370 100644 --- a/ErrorHandler.php +++ b/ErrorHandler.php @@ -181,7 +181,7 @@ public function __construct(?BufferingLogger $bootstrappingLogger = null, bool $ { if (\PHP_VERSION_ID < 80400) { $this->levels[\E_STRICT] = 'Runtime Notice'; - $this->loggers[\E_STRICT] = [null, LogLevel::WARNING]; + $this->loggers[\E_STRICT] = [null, LogLevel::ERROR]; } if ($bootstrappingLogger) { From 3e985c910c25e97f36ea6b397bc1d6d40c776c98 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 5 Aug 2024 09:12:25 +0200 Subject: [PATCH 19/28] Fix multiple CS errors --- Resources/views/exception_full.html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/views/exception_full.html.php b/Resources/views/exception_full.html.php index af04db1..a865b1c 100644 --- a/Resources/views/exception_full.html.php +++ b/Resources/views/exception_full.html.php @@ -1,4 +1,4 @@ - + From 0ee7b0fcbdcaf6bedddaaec82dcd847fb1daab64 Mon Sep 17 00:00:00 2001 From: Roy de Vos Burchart Date: Thu, 1 Aug 2024 17:21:17 +0200 Subject: [PATCH 20/28] Code style change in `@PER-CS2.0` affecting `@Symfony` (parentheses for anonymous classes) --- ErrorHandler.php | 2 +- ErrorRenderer/CliErrorRenderer.php | 2 +- Tests/ErrorHandlerTest.php | 4 ++-- Tests/Exception/FlattenExceptionTest.php | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ErrorHandler.php b/ErrorHandler.php index 0bce60a..a050fee 100644 --- a/ErrorHandler.php +++ b/ErrorHandler.php @@ -194,7 +194,7 @@ public function __construct( $traceReflector->setValue($e, $trace); $e->file = $file ?? $e->file; $e->line = $line ?? $e->line; - }, null, new class() extends \Exception { + }, null, new class extends \Exception { }); } diff --git a/ErrorRenderer/CliErrorRenderer.php b/ErrorRenderer/CliErrorRenderer.php index 04b3edb..c414c83 100644 --- a/ErrorRenderer/CliErrorRenderer.php +++ b/ErrorRenderer/CliErrorRenderer.php @@ -26,7 +26,7 @@ class CliErrorRenderer implements ErrorRendererInterface public function render(\Throwable $exception): FlattenException { $cloner = new VarCloner(); - $dumper = new class() extends CliDumper { + $dumper = new class extends CliDumper { protected function supportsColors(): bool { $outputStream = $this->outputStream; diff --git a/Tests/ErrorHandlerTest.php b/Tests/ErrorHandlerTest.php index e5632ca..46e01ff 100644 --- a/Tests/ErrorHandlerTest.php +++ b/Tests/ErrorHandlerTest.php @@ -333,7 +333,7 @@ public function testHandleError() public function testHandleErrorWithAnonymousClass() { - $anonymousObject = new class() extends \stdClass { + $anonymousObject = new class extends \stdClass { }; $handler = ErrorHandler::register(); @@ -422,7 +422,7 @@ public static function handleExceptionProvider(): array ['Uncaught Exception: foo', new \Exception('foo')], ['Uncaught Exception: foo', new class('foo') extends \RuntimeException { }], - ['Uncaught Exception: foo stdClass@anonymous bar', new \RuntimeException('foo '.(new class() extends \stdClass { + ['Uncaught Exception: foo stdClass@anonymous bar', new \RuntimeException('foo '.(new class extends \stdClass { })::class.' bar')], ['Uncaught Error: bar', new \Error('bar')], ['Uncaught ccc', new \ErrorException('ccc')], diff --git a/Tests/Exception/FlattenExceptionTest.php b/Tests/Exception/FlattenExceptionTest.php index 737c246..c6efb3c 100644 --- a/Tests/Exception/FlattenExceptionTest.php +++ b/Tests/Exception/FlattenExceptionTest.php @@ -245,7 +245,7 @@ public static function stringAndIntDataProvider(): array public function testAnonymousClass() { - $flattened = FlattenException::createFromThrowable(new class() extends \RuntimeException { + $flattened = FlattenException::createFromThrowable(new class extends \RuntimeException { }); $this->assertSame('RuntimeException@anonymous', $flattened->getClass()); @@ -255,7 +255,7 @@ public function testAnonymousClass() $this->assertSame('Symfony\Component\HttpKernel\Exception\NotFoundHttpException@anonymous', $flattened->getClass()); - $flattened = FlattenException::createFromThrowable(new \Exception(\sprintf('Class "%s" blah.', (new class() extends \RuntimeException { + $flattened = FlattenException::createFromThrowable(new \Exception(\sprintf('Class "%s" blah.', (new class extends \RuntimeException { })::class))); $this->assertSame('Class "RuntimeException@anonymous" blah.', $flattened->getMessage()); From 04b8d14d42fb4f11a1265748787c8dafd052d966 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 26 Aug 2024 17:35:30 +0200 Subject: [PATCH 21/28] Use Stringable whenever possible --- BufferingLogger.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BufferingLogger.php b/BufferingLogger.php index e6597b4..cbbe499 100644 --- a/BufferingLogger.php +++ b/BufferingLogger.php @@ -50,7 +50,7 @@ public function __destruct() foreach ($this->logs as [$level, $message, $context]) { if (str_contains($message, '{')) { foreach ($context as $key => $val) { - if (null === $val || \is_scalar($val) || (\is_object($val) && \is_callable([$val, '__toString']))) { + if (null === $val || \is_scalar($val) || $val instanceof \Stringable) { $message = str_replace("{{$key}}", $val, $message); } elseif ($val instanceof \DateTimeInterface) { $message = str_replace("{{$key}}", $val->format(\DateTimeInterface::RFC3339), $message); From 4a831eb99455e10540601d99967ed0fb27185be0 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sat, 31 Aug 2024 00:31:12 +0200 Subject: [PATCH 22/28] CS: re-apply `trailing_comma_in_multiline` --- Tests/DebugClassLoaderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/DebugClassLoaderTest.php b/Tests/DebugClassLoaderTest.php index 6a910d7..4e8d2b3 100644 --- a/Tests/DebugClassLoaderTest.php +++ b/Tests/DebugClassLoaderTest.php @@ -421,7 +421,7 @@ class_exists('Test\\'.OverrideOutsideFinalProperty::class, true); 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\FinalProperty\FinalProperty::$pub" property is considered final. You should not override it in "Symfony\Component\ErrorHandler\Tests\Fixtures\OverrideFinalProperty".', 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\FinalProperty\FinalProperty::$prot" property is considered final. You should not override it in "Symfony\Component\ErrorHandler\Tests\Fixtures\OverrideFinalProperty".', 'The "Symfony\Component\ErrorHandler\Tests\Fixtures\FinalProperty\FinalProperty::$implicitlyFinal" property is considered final. You should not override it in "Symfony\Component\ErrorHandler\Tests\Fixtures\OverrideFinalProperty".', - 'The "Test\Symfony\Component\ErrorHandler\Tests\FinalProperty\OutsideFinalProperty::$final" property is considered final. You should not override it in "Test\Symfony\Component\ErrorHandler\Tests\OverrideOutsideFinalProperty".' + 'The "Test\Symfony\Component\ErrorHandler\Tests\FinalProperty\OutsideFinalProperty::$final" property is considered final. You should not override it in "Test\Symfony\Component\ErrorHandler\Tests\OverrideOutsideFinalProperty".', ], $deprecations); } From 95d878c731759422f505e8505d8df2a5d2595aa8 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 4 Sep 2024 16:39:53 +0200 Subject: [PATCH 23/28] Remove no-op `ReflectionProperty::setAccessible()` calls --- Tests/ErrorHandlerTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/ErrorHandlerTest.php b/Tests/ErrorHandlerTest.php index 46e01ff..5f55cfb 100644 --- a/Tests/ErrorHandlerTest.php +++ b/Tests/ErrorHandlerTest.php @@ -34,7 +34,6 @@ class ErrorHandlerTest extends TestCase protected function tearDown(): void { $r = new \ReflectionProperty(ErrorHandler::class, 'exitCode'); - $r->setAccessible(true); $r->setValue(null, 0); } From ad57ca04868ca877e7919c56d20bdfa8024b54d6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 10 Oct 2024 15:54:54 +0200 Subject: [PATCH 24/28] resolve class constant types when patching return types --- DebugClassLoader.php | 26 +++++++++++++++++++++++- Tests/DebugClassLoaderTest.php | 22 ++++++++++++++++++++ Tests/Fixtures/ReturnType.php | 1 + Tests/Fixtures/ReturnTypeParent.php | 9 ++++++++ Tests/Fixtures/ReturnTypeParentPhp83.php | 23 +++++++++++++++++++++ Tests/Fixtures/ReturnTypePhp83.php | 11 ++++++++++ 6 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 Tests/Fixtures/ReturnTypeParentPhp83.php create mode 100644 Tests/Fixtures/ReturnTypePhp83.php diff --git a/DebugClassLoader.php b/DebugClassLoader.php index fd69b97..b4709ad 100644 --- a/DebugClassLoader.php +++ b/DebugClassLoader.php @@ -849,6 +849,30 @@ private function setReturnType(string $types, string $class, string $method, str $docTypes = []; foreach ($typesMap as $n => $t) { + if (str_contains($n, '::')) { + [$definingClass, $constantName] = explode('::', $n, 2); + $definingClass = match ($definingClass) { + 'self', 'static', 'parent' => $class, + default => $definingClass, + }; + + if (!\defined($definingClass.'::'.$constantName)) { + return; + } + + $constant = new \ReflectionClassConstant($definingClass, $constantName); + + if (\PHP_VERSION_ID >= 80300 && $constantType = $constant->getType()) { + if ($constantType instanceof \ReflectionNamedType) { + $n = $constantType->getName(); + } else { + return; + } + } else { + $n = \gettype($constant->getValue()); + } + } + if ('null' === $n) { $nullable = true; continue; @@ -872,7 +896,7 @@ private function setReturnType(string $types, string $class, string $method, str continue; } - if (!isset($phpTypes[''])) { + if (!isset($phpTypes['']) && !\in_array($n, $phpTypes, true)) { $phpTypes[] = $n; } } diff --git a/Tests/DebugClassLoaderTest.php b/Tests/DebugClassLoaderTest.php index 4e8d2b3..8575f89 100644 --- a/Tests/DebugClassLoaderTest.php +++ b/Tests/DebugClassLoaderTest.php @@ -401,6 +401,26 @@ class_exists('Test\\'.ReturnType::class, true); 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::true()" might add "true" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.', 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::never()" might add "never" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.', 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::null()" might add "null" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.', + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::classConstant()" might add "string" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.', + ], $deprecations); + } + + /** + * @requires PHP >= 8.3 + */ + public function testReturnTypePhp83() + { + $deprecations = []; + set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; }); + $e = error_reporting(E_USER_DEPRECATED); + + class_exists('Test\\'.ReturnTypePhp83::class, true); + + error_reporting($e); + restore_error_handler(); + + $this->assertSame([ + 'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParentPhp83::classConstantWithType()" might add "string" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnTypePhp83" now to avoid errors or add an explicit @return annotation to suppress this message.', ], $deprecations); } @@ -542,6 +562,8 @@ public function ownAbstractBaseMethod() { } }'); } elseif ('Test\\'.ReturnType::class === $class) { return $fixtureDir.\DIRECTORY_SEPARATOR.'ReturnType.php'; + } elseif ('Test\\'.ReturnTypePhp83::class === $class) { + return $fixtureDir.\DIRECTORY_SEPARATOR.'ReturnTypePhp83.php'; } elseif ('Test\\'.Fixtures\OutsideInterface::class === $class) { return $fixtureDir.\DIRECTORY_SEPARATOR.'OutsideInterface.php'; } elseif ('Test\\'.OverrideOutsideFinalProperty::class === $class) { diff --git a/Tests/Fixtures/ReturnType.php b/Tests/Fixtures/ReturnType.php index 1b81380..72570cf 100644 --- a/Tests/Fixtures/ReturnType.php +++ b/Tests/Fixtures/ReturnType.php @@ -51,4 +51,5 @@ public function true() { } public function never() { } public function null() { } public function outsideMethod() { } + public function classConstant() { } } diff --git a/Tests/Fixtures/ReturnTypeParent.php b/Tests/Fixtures/ReturnTypeParent.php index d42c7c8..3803a74 100644 --- a/Tests/Fixtures/ReturnTypeParent.php +++ b/Tests/Fixtures/ReturnTypeParent.php @@ -4,6 +4,8 @@ abstract class ReturnTypeParent extends ReturnTypeGrandParent implements ReturnTypeParentInterface { + const FOO = 'foo'; + /** * @return void */ @@ -254,4 +256,11 @@ public function null() public function notExtended() { } + + /** + * @return self::FOO + */ + public function classConstant() + { + } } diff --git a/Tests/Fixtures/ReturnTypeParentPhp83.php b/Tests/Fixtures/ReturnTypeParentPhp83.php new file mode 100644 index 0000000..c7d5ca8 --- /dev/null +++ b/Tests/Fixtures/ReturnTypeParentPhp83.php @@ -0,0 +1,23 @@ + Date: Sat, 7 Dec 2024 09:35:37 +0100 Subject: [PATCH 25/28] support non-empty-string/non-empty-list when patching return types --- DebugClassLoader.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DebugClassLoader.php b/DebugClassLoader.php index b4709ad..d3435e2 100644 --- a/DebugClassLoader.php +++ b/DebugClassLoader.php @@ -68,12 +68,14 @@ class DebugClassLoader 'iterable' => 'iterable', 'object' => 'object', 'string' => 'string', + 'non-empty-string' => 'string', 'self' => 'self', 'parent' => 'parent', 'mixed' => 'mixed', 'static' => 'static', '$this' => 'static', 'list' => 'array', + 'non-empty-list' => 'array', 'class-string' => 'string', 'never' => 'never', ]; From f344b88b3452afa01152a0b666c30b6fce07bd21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFck=20Piera?= Date: Tue, 5 Nov 2024 15:14:54 +0100 Subject: [PATCH 26/28] [ErrorHandler] Add a command to dump static error pages --- CHANGELOG.md | 5 ++ Command/ErrorDumpCommand.php | 85 ++++++++++++++++++ Tests/Command/ErrorDumpCommandTest.php | 114 +++++++++++++++++++++++++ composer.json | 4 +- 4 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 Command/ErrorDumpCommand.php create mode 100644 Tests/Command/ErrorDumpCommandTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index c3037ad..cd8d07d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.3 +--- + + * Add `error:dump` command + 7.1 --- diff --git a/Command/ErrorDumpCommand.php b/Command/ErrorDumpCommand.php new file mode 100644 index 0000000..95b6f44 --- /dev/null +++ b/Command/ErrorDumpCommand.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ErrorHandler\Command; + +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\ErrorHandler\ErrorRenderer\ErrorRendererInterface; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\WebpackEncoreBundle\Asset\EntrypointLookupInterface; + +/** + * Dump error pages to plain HTML files that can be directly served by a web server. + * + * @author Loïck Piera + */ +#[AsCommand( + name: 'error:dump', + description: 'Dump error pages to plain HTML files that can be directly served by a web server', +)] +final class ErrorDumpCommand extends Command +{ + public function __construct( + private readonly Filesystem $filesystem, + private readonly ErrorRendererInterface $errorRenderer, + private readonly ?EntrypointLookupInterface $entrypointLookup = null, + ) { + parent::__construct(); + } + + protected function configure(): void + { + $this + ->addArgument('path', InputArgument::REQUIRED, 'Path where to dump the error pages in') + ->addArgument('status-codes', InputArgument::IS_ARRAY, 'Status codes to dump error pages for, all of them by default') + ->addOption('force', 'f', InputOption::VALUE_NONE, 'Force directory removal before dumping new error pages') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $path = $input->getArgument('path'); + + $io = new SymfonyStyle($input, $output); + $io->title('Dumping error pages'); + + $this->dump($io, $path, $input->getArgument('status-codes'), (bool) $input->getOption('force')); + $io->success(\sprintf('Error pages have been dumped in "%s".', $path)); + + return Command::SUCCESS; + } + + private function dump(SymfonyStyle $io, string $path, array $statusCodes, bool $force = false): void + { + if (!$statusCodes) { + $statusCodes = array_filter(array_keys(Response::$statusTexts), fn ($statusCode) => $statusCode >= 400); + } + + if ($force || ($this->filesystem->exists($path) && $io->confirm(\sprintf('The "%s" directory already exists. Do you want to remove it before dumping the error pages?', $path), false))) { + $this->filesystem->remove($path); + } + + foreach ($statusCodes as $statusCode) { + // Avoid assets to be included only on the first dumped page + $this->entrypointLookup?->reset(); + + $this->filesystem->dumpFile($path.\DIRECTORY_SEPARATOR.$statusCode.'.html', $this->errorRenderer->render(new HttpException((int) $statusCode))->getAsString()); + } + } +} diff --git a/Tests/Command/ErrorDumpCommandTest.php b/Tests/Command/ErrorDumpCommandTest.php new file mode 100644 index 0000000..83b2bed --- /dev/null +++ b/Tests/Command/ErrorDumpCommandTest.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ErrorHandler\Tests\Command; + +use PHPUnit\Framework\MockObject\MockObject; +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Bundle\TwigBundle\Tests\TestCase; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\ErrorHandler\Command\ErrorDumpCommand; +use Symfony\Component\ErrorHandler\ErrorRenderer\ErrorRendererInterface; +use Symfony\Component\ErrorHandler\Exception\FlattenException; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\WebpackEncoreBundle\Asset\EntrypointLookupInterface; + +class ErrorDumpCommandTest extends TestCase +{ + private string $tmpDir = ''; + + protected function setUp(): void + { + $this->tmpDir = sys_get_temp_dir().'/error_pages'; + + $fs = new Filesystem(); + $fs->remove($this->tmpDir); + } + + public function testDumpPages() + { + $tester = $this->getCommandTester($this->getKernel(), []); + $tester->execute([ + 'path' => $this->tmpDir, + ]); + + $this->assertFileExists($this->tmpDir.\DIRECTORY_SEPARATOR.'404.html'); + $this->assertStringContainsString('Error 404', file_get_contents($this->tmpDir.\DIRECTORY_SEPARATOR.'404.html')); + } + + public function testDumpPagesOnlyForGivenStatusCodes() + { + $fs = new Filesystem(); + $fs->mkdir($this->tmpDir); + $fs->touch($this->tmpDir.\DIRECTORY_SEPARATOR.'test.html'); + + $tester = $this->getCommandTester($this->getKernel()); + $tester->execute([ + 'path' => $this->tmpDir, + 'status-codes' => ['400', '500'], + ]); + + $this->assertFileExists($this->tmpDir.\DIRECTORY_SEPARATOR.'test.html'); + $this->assertFileDoesNotExist($this->tmpDir.\DIRECTORY_SEPARATOR.'404.html'); + + $this->assertFileExists($this->tmpDir.\DIRECTORY_SEPARATOR.'400.html'); + $this->assertStringContainsString('Error 400', file_get_contents($this->tmpDir.\DIRECTORY_SEPARATOR.'400.html')); + } + + public function testForceRemovalPages() + { + $fs = new Filesystem(); + $fs->mkdir($this->tmpDir); + $fs->touch($this->tmpDir.\DIRECTORY_SEPARATOR.'test.html'); + + $tester = $this->getCommandTester($this->getKernel()); + $tester->execute([ + 'path' => $this->tmpDir, + '--force' => true, + ]); + + $this->assertFileDoesNotExist($this->tmpDir.\DIRECTORY_SEPARATOR.'test.html'); + $this->assertFileExists($this->tmpDir.\DIRECTORY_SEPARATOR.'404.html'); + } + + private function getKernel(): MockObject&KernelInterface + { + return $this->createMock(KernelInterface::class); + } + + private function getCommandTester(KernelInterface $kernel): CommandTester + { + $errorRenderer = $this->createStub(ErrorRendererInterface::class); + $errorRenderer + ->method('render') + ->willReturnCallback(function (HttpException $e) { + $exception = FlattenException::createFromThrowable($e); + $exception->setAsString(\sprintf('Error %s', $e->getStatusCode())); + + return $exception; + }) + ; + + $entrypointLookup = $this->createMock(EntrypointLookupInterface::class); + + $application = new Application($kernel); + $application->add(new ErrorDumpCommand( + new Filesystem(), + $errorRenderer, + $entrypointLookup, + )); + + return new CommandTester($application->find('error:dump')); + } +} diff --git a/composer.json b/composer.json index 987a68f..98b9432 100644 --- a/composer.json +++ b/composer.json @@ -21,9 +21,11 @@ "symfony/var-dumper": "^6.4|^7.0" }, "require-dev": { + "symfony/console": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/serializer": "^6.4|^7.0", - "symfony/deprecation-contracts": "^2.5|^3" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/webpack-encore-bundle": "^1.0|^2.0" }, "conflict": { "symfony/deprecation-contracts": "<2.5", From 070e14ba8214e793a989d93c98e28445c0607afe Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 3 Mar 2025 09:24:15 +0100 Subject: [PATCH 27/28] [ErrorHandler] Improve an error message --- ErrorEnhancer/UndefinedFunctionErrorEnhancer.php | 4 ++-- .../ErrorEnhancer/UndefinedFunctionErrorEnhancerTest.php | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ErrorEnhancer/UndefinedFunctionErrorEnhancer.php b/ErrorEnhancer/UndefinedFunctionErrorEnhancer.php index e1d54ab..41d7af3 100644 --- a/ErrorEnhancer/UndefinedFunctionErrorEnhancer.php +++ b/ErrorEnhancer/UndefinedFunctionErrorEnhancer.php @@ -47,10 +47,10 @@ public function enhance(\Throwable $error): ?\Throwable if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedFunctionName, '\\')) { $functionName = substr($fullyQualifiedFunctionName, $namespaceSeparatorIndex + 1); $namespacePrefix = substr($fullyQualifiedFunctionName, 0, $namespaceSeparatorIndex); - $message = \sprintf('Attempted to call function "%s" from namespace "%s".', $functionName, $namespacePrefix); + $message = \sprintf('Attempted to call undefined function "%s" from namespace "%s".', $functionName, $namespacePrefix); } else { $functionName = $fullyQualifiedFunctionName; - $message = \sprintf('Attempted to call function "%s" from the global namespace.', $functionName); + $message = \sprintf('Attempted to call undefined function "%s" from the global namespace.', $functionName); } $candidates = []; diff --git a/Tests/ErrorEnhancer/UndefinedFunctionErrorEnhancerTest.php b/Tests/ErrorEnhancer/UndefinedFunctionErrorEnhancerTest.php index f9474f7..b5a0d91 100644 --- a/Tests/ErrorEnhancer/UndefinedFunctionErrorEnhancerTest.php +++ b/Tests/ErrorEnhancer/UndefinedFunctionErrorEnhancerTest.php @@ -39,19 +39,19 @@ public static function provideUndefinedFunctionData() return [ [ 'Call to undefined function test_namespaced_function()', - "Attempted to call function \"test_namespaced_function\" from the global namespace.\nDid you mean to call \"\\symfony\\component\\errorhandler\\tests\\errorenhancer\\test_namespaced_function\"?", + "Attempted to call undefined function \"test_namespaced_function\" from the global namespace.\nDid you mean to call \"\\symfony\\component\\errorhandler\\tests\\errorenhancer\\test_namespaced_function\"?", ], [ 'Call to undefined function Foo\\Bar\\Baz\\test_namespaced_function()', - "Attempted to call function \"test_namespaced_function\" from namespace \"Foo\\Bar\\Baz\".\nDid you mean to call \"\\symfony\\component\\errorhandler\\tests\\errorenhancer\\test_namespaced_function\"?", + "Attempted to call undefined function \"test_namespaced_function\" from namespace \"Foo\\Bar\\Baz\".\nDid you mean to call \"\\symfony\\component\\errorhandler\\tests\\errorenhancer\\test_namespaced_function\"?", ], [ 'Call to undefined function foo()', - 'Attempted to call function "foo" from the global namespace.', + 'Attempted to call undefined function "foo" from the global namespace.', ], [ 'Call to undefined function Foo\\Bar\\Baz\\foo()', - 'Attempted to call function "foo" from namespace "Foo\Bar\Baz".', + 'Attempted to call undefined function "foo" from namespace "Foo\Bar\Baz".', ], ]; } From 47a96276149f049ba944cbd470f4d17bf42914e3 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Thu, 13 Mar 2025 23:58:12 +0100 Subject: [PATCH 28/28] chore: PHP CS Fixer fixes --- Tests/Command/ErrorDumpCommandTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Tests/Command/ErrorDumpCommandTest.php b/Tests/Command/ErrorDumpCommandTest.php index 83b2bed..670adbd 100644 --- a/Tests/Command/ErrorDumpCommandTest.php +++ b/Tests/Command/ErrorDumpCommandTest.php @@ -15,7 +15,6 @@ use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\TwigBundle\Tests\TestCase; use Symfony\Component\Console\Tester\CommandTester; -use Symfony\Component\DependencyInjection\Container; use Symfony\Component\ErrorHandler\Command\ErrorDumpCommand; use Symfony\Component\ErrorHandler\ErrorRenderer\ErrorRendererInterface; use Symfony\Component\ErrorHandler\Exception\FlattenException;