diff --git a/UPGRADE-6.4.md b/UPGRADE-6.4.md index 8dc706e851565..3ad780886dd92 100644 --- a/UPGRADE-6.4.md +++ b/UPGRADE-6.4.md @@ -152,6 +152,7 @@ HttpKernel * [BC break] Add native return types to `TraceableEventDispatcher` and to `MergeExtensionConfigurationPass` * Deprecate `Kernel::stripComments()` * Deprecate `UriSigner`, use `UriSigner` from the HttpFoundation component instead + * Deprecate `FileLinkFormatter`, use `FileLinkFormatter` from the ErrorHandler component instead Messenger --------- diff --git a/src/Symfony/Bridge/Twig/Command/DebugCommand.php b/src/Symfony/Bridge/Twig/Command/DebugCommand.php index 43e4d9c9f12c6..31edd0a5b4cc9 100644 --- a/src/Symfony/Bridge/Twig/Command/DebugCommand.php +++ b/src/Symfony/Bridge/Twig/Command/DebugCommand.php @@ -22,8 +22,8 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\Finder\Finder; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Twig\Environment; use Twig\Loader\ChainLoader; use Twig\Loader\FilesystemLoader; diff --git a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php index d6bb18e43b0c0..2160b70df401e 100644 --- a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php @@ -11,7 +11,7 @@ namespace Symfony\Bridge\Twig\Extension; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php index 38983cbd697f1..ae7cf786daa1d 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php @@ -13,7 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Extension\CodeExtension; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; class CodeExtensionTest extends TestCase { diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php index 6ba01bf4d67f3..ab4793b685f8a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php @@ -21,7 +21,7 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\DependencyInjection\Attribute\Target; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; /** * A console command for autowiring information. diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php index 87482e9e5d5bc..8d4e38a29438f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php @@ -22,7 +22,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouterInterface; diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index 8a4f812deeb04..b0fe7f2ac0e34 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -26,8 +26,8 @@ use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Helper/DescriptorHelper.php b/src/Symfony/Bundle/FrameworkBundle/Console/Helper/DescriptorHelper.php index 1f17c999424d3..47d69fef46cb6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Helper/DescriptorHelper.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Helper/DescriptorHelper.php @@ -16,7 +16,7 @@ use Symfony\Bundle\FrameworkBundle\Console\Descriptor\TextDescriptor; use Symfony\Bundle\FrameworkBundle\Console\Descriptor\XmlDescriptor; use Symfony\Component\Console\Helper\DescriptorHelper as BaseDescriptorHelper; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; /** * @author Jean-François Simon diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.php index b4649182b18f7..af6ca27dcabf0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/debug_prod.php @@ -11,8 +11,8 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\HttpKernel\Debug\ErrorHandlerConfigurator; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Symfony\Component\HttpKernel\EventListener\DebugHandlersListener; return static function (ContainerConfigurator $container) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/TextDescriptorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/TextDescriptorTest.php index 2a3c166158deb..2404706d0589a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/TextDescriptorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/TextDescriptorTest.php @@ -12,7 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor; use Symfony\Bundle\FrameworkBundle\Console\Descriptor\TextDescriptor; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\Routing\Route; class TextDescriptorTest extends AbstractDescriptorTestCase diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.php b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.php index 85c64f268b576..7b28de9c40ac2 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/profiler.php @@ -17,7 +17,7 @@ use Symfony\Bundle\WebProfilerBundle\Csp\ContentSecurityPolicyHandler; use Symfony\Bundle\WebProfilerBundle\Csp\NonceGenerator; use Symfony\Bundle\WebProfilerBundle\Twig\WebProfilerExtension; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\VarDumper\Dumper\HtmlDumper; return static function (ContainerConfigurator $container) { diff --git a/src/Symfony/Bundle/WebProfilerBundle/composer.json b/src/Symfony/Bundle/WebProfilerBundle/composer.json index 5858c765e41a3..b220b784f9f05 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/composer.json +++ b/src/Symfony/Bundle/WebProfilerBundle/composer.json @@ -18,8 +18,8 @@ "require": { "php": ">=8.1", "symfony/config": "^5.4|^6.0|^7.0", - "symfony/framework-bundle": "^5.4|^6.0|^7.0", - "symfony/http-kernel": "^6.3|^7.0", + "symfony/framework-bundle": "^6.2|^7.0", + "symfony/http-kernel": "^6.4|^7.0", "symfony/routing": "^5.4|^6.0|^7.0", "symfony/twig-bundle": "^5.4|^6.0|^7.0", "twig/twig": "^2.13|^3.0.4" diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/FileLinkFormatter.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/FileLinkFormatter.php new file mode 100644 index 0000000000000..29fcf835b42fc --- /dev/null +++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/FileLinkFormatter.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ErrorHandler\ErrorRenderer; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * Formats debug file links. + * + * @author Jérémy Romey + * + * @final + */ +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) + { + $fileLinkFormat ??= $_ENV['SYMFONY_IDE'] ?? $_SERVER['SYMFONY_IDE'] ?? ''; + + if (!\is_array($f = $fileLinkFormat)) { + $f = (ErrorRendererInterface::IDE_LINK_FORMATS[$f] ?? $f) ?: \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f#L%l'; + $i = strpos($f, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: \strlen($f); + $fileLinkFormat = [substr($f, 0, $i)] + preg_split('/&([^>]++)>/', substr($f, $i), -1, \PREG_SPLIT_DELIM_CAPTURE); + } + + $this->fileLinkFormat = $fileLinkFormat; + $this->requestStack = $requestStack; + $this->baseDir = $baseDir; + $this->urlFormat = $urlFormat; + } + + /** + * @return string|false + */ + public function format(string $file, int $line): string|bool + { + if ($fmt = $this->getFileLinkFormat()) { + for ($i = 1; isset($fmt[$i]); ++$i) { + if (str_starts_with($file, $k = $fmt[$i++])) { + $file = substr_replace($file, $fmt[$i], 0, \strlen($k)); + break; + } + } + + return strtr($fmt[0], ['%f' => $file, '%l' => $line]); + } + + return false; + } + + /** + * @internal + */ + public function __sleep(): array + { + $this->fileLinkFormat = $this->getFileLinkFormat(); + + return ['fileLinkFormat']; + } + + /** + * @internal + */ + public static function generateUrlFormat(UrlGeneratorInterface $router, string $routeName, string $queryString): ?string + { + try { + return $router->generate($routeName).$queryString; + } catch (\Throwable) { + return null; + } + } + + private function getFileLinkFormat(): array|false + { + if ($this->fileLinkFormat) { + return $this->fileLinkFormat; + } + + if ($this->requestStack && $this->baseDir && $this->urlFormat) { + $request = $this->requestStack->getMainRequest(); + + if ($request instanceof Request && (!$this->urlFormat instanceof \Closure || $this->urlFormat = ($this->urlFormat)())) { + return [ + $request->getSchemeAndHttpHost().$this->urlFormat, + $this->baseDir.\DIRECTORY_SEPARATOR, '', + ]; + } + } + + return false; + } +} + +if (!class_exists(\Symfony\Component\HttpKernel\Debug\FileLinkFormatter::class, false)) { + class_alias(FileLinkFormatter::class, \Symfony\Component\HttpKernel\Debug\FileLinkFormatter::class); +} diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php index c9c45714867c8..4e2a99b808767 100644 --- a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php +++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php @@ -15,7 +15,6 @@ use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Symfony\Component\HttpKernel\Log\DebugLoggerConfigurator; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\HtmlDumper; @@ -37,7 +36,7 @@ class HtmlErrorRenderer implements ErrorRendererInterface private bool|\Closure $debug; private string $charset; - private string|array|FileLinkFormatter|false $fileLinkFormat; + private FileLinkFormatter $fileLinkFormat; private ?string $projectDir; private string|\Closure $outputBuffer; private ?LoggerInterface $logger; @@ -52,10 +51,7 @@ public function __construct(bool|callable $debug = false, string $charset = null { $this->debug = \is_bool($debug) ? $debug : $debug(...); $this->charset = $charset ?: (\ini_get('default_charset') ?: 'UTF-8'); - $fileLinkFormat ??= $_ENV['SYMFONY_IDE'] ?? $_SERVER['SYMFONY_IDE'] ?? null; - $this->fileLinkFormat = \is_string($fileLinkFormat) - ? (ErrorRendererInterface::IDE_LINK_FORMATS[$fileLinkFormat] ?? $fileLinkFormat ?: false) - : ($fileLinkFormat ?: \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: false); + $this->fileLinkFormat = $fileLinkFormat instanceof FileLinkFormatter ? $fileLinkFormat : new FileLinkFormatter($fileLinkFormat); $this->projectDir = $projectDir; $this->outputBuffer = \is_string($outputBuffer) ? $outputBuffer : $outputBuffer(...); $this->logger = $logger; @@ -210,15 +206,6 @@ private function getFileRelative(string $file): ?string return null; } - private function getFileLink(string $file, int $line): string|false - { - if ($fmt = $this->fileLinkFormat) { - return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line); - } - - return false; - } - /** * Formats a file path. * @@ -242,11 +229,9 @@ private function formatFile(string $file, int $line, string $text = null): strin $text .= ' at line '.$line; } - if (false !== $link = $this->getFileLink($file, $line)) { - return sprintf('%s', $this->escape($link), $text); - } + $link = $this->fileLinkFormat->format($file, $line); - return $text; + return sprintf('%s', $this->escape($link), $text); } /** diff --git a/src/Symfony/Component/HttpKernel/Tests/Debug/FileLinkFormatterTest.php b/src/Symfony/Component/ErrorHandler/Tests/ErrorRenderer/FileLinkFormatterTest.php similarity index 66% rename from src/Symfony/Component/HttpKernel/Tests/Debug/FileLinkFormatterTest.php rename to src/Symfony/Component/ErrorHandler/Tests/ErrorRenderer/FileLinkFormatterTest.php index 9348612ee0cfd..a5f6330679ff9 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Debug/FileLinkFormatterTest.php +++ b/src/Symfony/Component/ErrorHandler/Tests/ErrorRenderer/FileLinkFormatterTest.php @@ -9,12 +9,12 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel\Tests\Debug; +namespace Symfony\Component\ErrorHandler\Tests\ErrorRenderer; use PHPUnit\Framework\TestCase; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; class FileLinkFormatterTest extends TestCase { @@ -80,4 +80,44 @@ public function testSerialize() { $this->assertInstanceOf(FileLinkFormatter::class, unserialize(serialize(new FileLinkFormatter()))); } + + /** + * @dataProvider providePathMappings + */ + public function testIdeFileLinkFormatWithPathMappingParameters($mappings) + { + $params = array_reduce($mappings, function ($c, $m) { + return "$c&".implode('>', $m); + }, ''); + $sut = new FileLinkFormatter("vscode://file/%f:%l$params"); + foreach ($mappings as $mapping) { + $fileGuest = $mapping['guest'].'file.php'; + $fileHost = $mapping['host'].'file.php'; + $this->assertSame("vscode://file/$fileHost:3", $sut->format($fileGuest, 3)); + } + } + + public static function providePathMappings() + { + yield 'single path mapping' => [ + [ + [ + 'guest' => '/var/www/app/', + 'host' => '/user/name/project/', + ], + ], + ]; + yield 'multiple path mapping' => [ + [ + [ + 'guest' => '/var/www/app/', + 'host' => '/user/name/project/', + ], + [ + 'guest' => '/var/www/app2/', + 'host' => '/user/name/project2/', + ], + ], + ]; + } } diff --git a/src/Symfony/Component/ErrorHandler/Tests/ErrorRenderer/HtmlErrorRendererTest.php b/src/Symfony/Component/ErrorHandler/Tests/ErrorRenderer/HtmlErrorRendererTest.php index 6680b95a0cc3d..3ca3d9769f690 100644 --- a/src/Symfony/Component/ErrorHandler/Tests/ErrorRenderer/HtmlErrorRendererTest.php +++ b/src/Symfony/Component/ErrorHandler/Tests/ErrorRenderer/HtmlErrorRendererTest.php @@ -54,4 +54,47 @@ public static function getRenderData(): iterable $expectedNonDebug, ]; } + + /** + * @dataProvider provideFileLinkFormats + */ + public function testFileLinkFormat(\ErrorException $exception, string $fileLinkFormat, bool $withSymfonyIde, string $expected) + { + if ($withSymfonyIde) { + $_ENV['SYMFONY_IDE'] = $fileLinkFormat; + } + $errorRenderer = new HtmlErrorRenderer(true, null, $withSymfonyIde ? null : $fileLinkFormat); + + $this->assertStringContainsString($expected, $errorRenderer->render($exception)->getAsString()); + } + + public static function provideFileLinkFormats(): iterable + { + $exception = new \ErrorException('Notice', 0, \E_USER_NOTICE); + + yield 'file link format set as known IDE with SYMFONY_IDE' => [ + $exception, + 'vscode', + true, + 'href="vscode://file/'.__DIR__, + ]; + yield 'file link format set as a raw format with SYMFONY_IDE' => [ + $exception, + 'phpstorm://open?file=%f&line=%l', + true, + 'href="phpstorm://open?file='.__DIR__, + ]; + yield 'file link format set as known IDE without SYMFONY_IDE' => [ + $exception, + 'vscode', + false, + 'href="vscode://file/'.__DIR__, + ]; + yield 'file link format set as a raw format without SYMFONY_IDE' => [ + $exception, + 'phpstorm://open?file=%f&line=%l', + false, + 'href="phpstorm://open?file='.__DIR__, + ]; + } } diff --git a/src/Symfony/Component/Form/Command/DebugCommand.php b/src/Symfony/Component/Form/Command/DebugCommand.php index 4a142e2965e44..a256511261f11 100644 --- a/src/Symfony/Component/Form/Command/DebugCommand.php +++ b/src/Symfony/Component/Form/Command/DebugCommand.php @@ -21,11 +21,12 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\Form\Console\Helper\DescriptorHelper; use Symfony\Component\Form\Extension\Core\CoreExtension; use Symfony\Component\Form\FormRegistryInterface; use Symfony\Component\Form\FormTypeInterface; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; +use Symfony\Component\HttpKernel\Debug\FileLinkFormatter as LegacyFileLinkFormatter; /** * A console command for retrieving information about form types. @@ -40,9 +41,9 @@ class DebugCommand extends Command private array $types; private array $extensions; private array $guessers; - private ?FileLinkFormatter $fileLinkFormatter; + private FileLinkFormatter|LegacyFileLinkFormatter|null $fileLinkFormatter; - public function __construct(FormRegistryInterface $formRegistry, array $namespaces = ['Symfony\Component\Form\Extension\Core\Type'], array $types = [], array $extensions = [], array $guessers = [], FileLinkFormatter $fileLinkFormatter = null) + public function __construct(FormRegistryInterface $formRegistry, array $namespaces = ['Symfony\Component\Form\Extension\Core\Type'], array $types = [], array $extensions = [], array $guessers = [], FileLinkFormatter|LegacyFileLinkFormatter $fileLinkFormatter = null) { parent::__construct(); diff --git a/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php b/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php index c4a2db27a0810..ce84562e1b96c 100644 --- a/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php @@ -13,8 +13,9 @@ use Symfony\Component\Console\Helper\Dumper; use Symfony\Component\Console\Helper\TableSeparator; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\Form\ResolvedFormTypeInterface; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; +use Symfony\Component\HttpKernel\Debug\FileLinkFormatter as LegacyFileLinkFormatter; use Symfony\Component\OptionsResolver\OptionsResolver; /** @@ -24,9 +25,9 @@ */ class TextDescriptor extends Descriptor { - private ?FileLinkFormatter $fileLinkFormatter; + private FileLinkFormatter|LegacyFileLinkFormatter|null $fileLinkFormatter; - public function __construct(FileLinkFormatter $fileLinkFormatter = null) + public function __construct(FileLinkFormatter|LegacyFileLinkFormatter $fileLinkFormatter = null) { $this->fileLinkFormatter = $fileLinkFormatter; } diff --git a/src/Symfony/Component/Form/Console/Helper/DescriptorHelper.php b/src/Symfony/Component/Form/Console/Helper/DescriptorHelper.php index 355fb95989a36..5944d8e18c2be 100644 --- a/src/Symfony/Component/Form/Console/Helper/DescriptorHelper.php +++ b/src/Symfony/Component/Form/Console/Helper/DescriptorHelper.php @@ -12,9 +12,10 @@ namespace Symfony\Component\Form\Console\Helper; use Symfony\Component\Console\Helper\DescriptorHelper as BaseDescriptorHelper; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\Form\Console\Descriptor\JsonDescriptor; use Symfony\Component\Form\Console\Descriptor\TextDescriptor; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; +use Symfony\Component\HttpKernel\Debug\FileLinkFormatter as LegacyFileLinkFormatter; /** * @author Yonel Ceruto @@ -23,7 +24,7 @@ */ class DescriptorHelper extends BaseDescriptorHelper { - public function __construct(FileLinkFormatter $fileLinkFormatter = null) + public function __construct(FileLinkFormatter|LegacyFileLinkFormatter $fileLinkFormatter = null) { $this ->register('txt', new TextDescriptor($fileLinkFormatter)) diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index fa58ba8e52fb0..bf5291721dac7 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -14,6 +14,7 @@ CHANGELOG * Deprecate `Kernel::stripComments()` * Support the `!` character at the beginning of a string as a negation operator in the url filter of the profiler * Deprecate `UriSigner`, use `UriSigner` from the HttpFoundation component instead + * Deprecate `FileLinkFormatter`, use `FileLinkFormatter` from the ErrorHandler component instead 6.3 --- diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php index 37f51ae3353b5..4f518e7bbc2f2 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php @@ -11,10 +11,10 @@ namespace Symfony\Component\HttpKernel\DataCollector; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Symfony\Component\Stopwatch\Stopwatch; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Cloner\VarCloner; diff --git a/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php b/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php index fcb100859f64d..5313660fbc4bc 100644 --- a/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php +++ b/src/Symfony/Component/HttpKernel/Debug/FileLinkFormatter.php @@ -11,102 +11,17 @@ namespace Symfony\Component\HttpKernel\Debug; -use Symfony\Component\ErrorHandler\ErrorRenderer\ErrorRendererInterface; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter as ErrorHandlerFileLinkFormatter; -/** - * Formats debug file links. - * - * @author Jérémy Romey - * - * @final - */ -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) - { - $fileLinkFormat ??= $_ENV['SYMFONY_IDE'] ?? $_SERVER['SYMFONY_IDE'] ?? ''; - - if (!\is_array($f = $fileLinkFormat)) { - $f = (ErrorRendererInterface::IDE_LINK_FORMATS[$f] ?? $f) ?: \ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f#L%l'; - $i = strpos($f, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: \strlen($f); - $fileLinkFormat = [substr($f, 0, $i)] + preg_split('/&([^>]++)>/', substr($f, $i), -1, \PREG_SPLIT_DELIM_CAPTURE); - } - - $this->fileLinkFormat = $fileLinkFormat; - $this->requestStack = $requestStack; - $this->baseDir = $baseDir; - $this->urlFormat = $urlFormat; - } - - /** - * @return string|false - */ - public function format(string $file, int $line): string|bool - { - if ($fmt = $this->getFileLinkFormat()) { - for ($i = 1; isset($fmt[$i]); ++$i) { - if (str_starts_with($file, $k = $fmt[$i++])) { - $file = substr_replace($file, $fmt[$i], 0, \strlen($k)); - break; - } - } +trigger_deprecation('symfony/http-kernel', '6.4', 'The "%s" class is deprecated, use "%s" instead.', FileLinkFormatter::class, ErrorHandlerFileLinkFormatter::class); - return strtr($fmt[0], ['%f' => $file, '%l' => $line]); - } - - return false; - } - - /** - * @internal - */ - public function __sleep(): array - { - $this->fileLinkFormat = $this->getFileLinkFormat(); - - return ['fileLinkFormat']; - } +class_exists(ErrorHandlerFileLinkFormatter::class); +if (false) { /** - * @internal + * @deprecated since Symfony 6.4, use FileLinkFormatter from the ErrorHandle component instead */ - public static function generateUrlFormat(UrlGeneratorInterface $router, string $routeName, string $queryString): ?string - { - try { - return $router->generate($routeName).$queryString; - } catch (\Throwable) { - return null; - } - } - - private function getFileLinkFormat(): array|false + class FileLinkFormatter { - if ($this->fileLinkFormat) { - return $this->fileLinkFormat; - } - - if ($this->requestStack && $this->baseDir && $this->urlFormat) { - $request = $this->requestStack->getMainRequest(); - - if ($request instanceof Request && (!$this->urlFormat instanceof \Closure || $this->urlFormat = ($this->urlFormat)())) { - return [ - $request->getSchemeAndHttpHost().$this->urlFormat, - $this->baseDir.\DIRECTORY_SEPARATOR, '', - ]; - } - } - - return false; } } diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php index 0262ff8db6b99..e55af09fe5a85 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -12,11 +12,11 @@ namespace Symfony\Component\HttpKernel\Tests\DataCollector; use PHPUnit\Framework\TestCase; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Symfony\Component\VarDumper\Cloner\Data; use Symfony\Component\VarDumper\Dumper\CliDumper; use Symfony\Component\VarDumper\Server\Connection; diff --git a/src/Symfony/Component/HttpKernel/UriSigner.php b/src/Symfony/Component/HttpKernel/UriSigner.php index e11ff6af1dc4f..877d832e9dae2 100644 --- a/src/Symfony/Component/HttpKernel/UriSigner.php +++ b/src/Symfony/Component/HttpKernel/UriSigner.php @@ -13,7 +13,7 @@ use Symfony\Component\HttpFoundation\UriSigner as HttpFoundationUriSigner; -trigger_deprecation('symfony/dependency-injection', '6.4', 'The "%s" class is deprecated, use "%s" instead.', UriSigner::class, HttpFoundationUriSigner::class); +trigger_deprecation('symfony/http-kernel', '6.4', 'The "%s" class is deprecated, use "%s" instead.', UriSigner::class, HttpFoundationUriSigner::class); class_exists(HttpFoundationUriSigner::class); diff --git a/src/Symfony/Component/VarDumper/Dumper/ContextProvider/SourceContextProvider.php b/src/Symfony/Component/VarDumper/Dumper/ContextProvider/SourceContextProvider.php index 790285c97e5ac..8923e203ebdc6 100644 --- a/src/Symfony/Component/VarDumper/Dumper/ContextProvider/SourceContextProvider.php +++ b/src/Symfony/Component/VarDumper/Dumper/ContextProvider/SourceContextProvider.php @@ -11,7 +11,8 @@ namespace Symfony\Component\VarDumper\Dumper\ContextProvider; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; +use Symfony\Component\HttpKernel\Debug\FileLinkFormatter as LegacyFileLinkFormatter; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\HtmlDumper; use Symfony\Component\VarDumper\VarDumper; @@ -30,7 +31,7 @@ final class SourceContextProvider implements ContextProviderInterface private ?string $projectDir; private ?FileLinkFormatter $fileLinkFormatter; - public function __construct(string $charset = null, string $projectDir = null, FileLinkFormatter $fileLinkFormatter = null, int $limit = 9) + public function __construct(string $charset = null, string $projectDir = null, FileLinkFormatter|LegacyFileLinkFormatter $fileLinkFormatter = null, int $limit = 9) { $this->charset = $charset; $this->projectDir = $projectDir; diff --git a/src/Symfony/Component/VarDumper/VarDumper.php b/src/Symfony/Component/VarDumper/VarDumper.php index 2e1dad116cdd9..cfc6b2807b4da 100644 --- a/src/Symfony/Component/VarDumper/VarDumper.php +++ b/src/Symfony/Component/VarDumper/VarDumper.php @@ -11,9 +11,9 @@ namespace Symfony\Component\VarDumper; +use Symfony\Component\ErrorHandler\ErrorRenderer\FileLinkFormatter; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Symfony\Component\VarDumper\Caster\ReflectionCaster; use Symfony\Component\VarDumper\Cloner\VarCloner; use Symfony\Component\VarDumper\Dumper\CliDumper;