From 10ad5ad527e7371d7d3627b6b2e5c96a99ccf6d8 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 7 Dec 2024 08:58:47 +0100 Subject: [PATCH 1/4] fix translation lint compatibility with the PseudoLocalizationTranslator --- .../Compiler/TranslationLintCommandPass.php | 33 +++++++++++++++++++ FrameworkBundle.php | 3 ++ 2 files changed, 36 insertions(+) create mode 100644 DependencyInjection/Compiler/TranslationLintCommandPass.php diff --git a/DependencyInjection/Compiler/TranslationLintCommandPass.php b/DependencyInjection/Compiler/TranslationLintCommandPass.php new file mode 100644 index 000000000..4756795d1 --- /dev/null +++ b/DependencyInjection/Compiler/TranslationLintCommandPass.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Translation\TranslatorBagInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +final class TranslationLintCommandPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container): void + { + if (!$container->hasDefinition('console.command.translation_lint') || !$container->has('translator')) { + return; + } + + $translatorClass = $container->getParameterBag()->resolveValue($container->findDefinition('translator')->getClass()); + + if (!is_subclass_of($translatorClass, TranslatorInterface::class) || !is_subclass_of($translatorClass, TranslatorBagInterface::class)) { + $container->removeDefinition('console.command.translation_lint'); + } + } +} diff --git a/FrameworkBundle.php b/FrameworkBundle.php index a1eb059bb..dc7080892 100644 --- a/FrameworkBundle.php +++ b/FrameworkBundle.php @@ -19,6 +19,7 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RemoveUnusedSessionMarshallingHandlerPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerRealRefPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerWeakRefPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationLintCommandPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\VirtualRequestStackPass; use Symfony\Component\Cache\Adapter\ApcuAdapter; @@ -149,6 +150,8 @@ public function build(ContainerBuilder $container): void $this->addCompilerPassIfExists($container, AddConstraintValidatorsPass::class); $this->addCompilerPassIfExists($container, AddValidatorInitializersPass::class); $this->addCompilerPassIfExists($container, AddConsoleCommandPass::class, PassConfig::TYPE_BEFORE_REMOVING); + // must be registered before the AddConsoleCommandPass + $container->addCompilerPass(new TranslationLintCommandPass(), PassConfig::TYPE_BEFORE_REMOVING, 10); // must be registered as late as possible to get access to all Twig paths registered in // twig.template_iterator definition $this->addCompilerPassIfExists($container, TranslatorPass::class, PassConfig::TYPE_BEFORE_OPTIMIZATION, -32); From a8e3976e4e8a567bf27c79113861230dbda48fd3 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 15 Dec 2024 11:26:42 +0100 Subject: [PATCH 2/4] don't require fake notifier transports to be installed as non-dev dependencies --- DependencyInjection/FrameworkExtension.php | 26 +++++++++++++--------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/DependencyInjection/FrameworkExtension.php b/DependencyInjection/FrameworkExtension.php index 36984e739..aed6bdefa 100644 --- a/DependencyInjection/FrameworkExtension.php +++ b/DependencyInjection/FrameworkExtension.php @@ -122,6 +122,8 @@ use Symfony\Component\Mime\MimeTypeGuesserInterface; use Symfony\Component\Mime\MimeTypes; use Symfony\Component\Notifier\Bridge as NotifierBridge; +use Symfony\Component\Notifier\Bridge\FakeChat\FakeChatTransportFactory; +use Symfony\Component\Notifier\Bridge\FakeSms\FakeSmsTransportFactory; use Symfony\Component\Notifier\ChatterInterface; use Symfony\Component\Notifier\Notifier; use Symfony\Component\Notifier\Recipient\Recipient; @@ -2812,8 +2814,6 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\Engagespot\EngagespotTransportFactory::class => 'notifier.transport_factory.engagespot', NotifierBridge\Esendex\EsendexTransportFactory::class => 'notifier.transport_factory.esendex', NotifierBridge\Expo\ExpoTransportFactory::class => 'notifier.transport_factory.expo', - NotifierBridge\FakeChat\FakeChatTransportFactory::class => 'notifier.transport_factory.fake-chat', - NotifierBridge\FakeSms\FakeSmsTransportFactory::class => 'notifier.transport_factory.fake-sms', NotifierBridge\Firebase\FirebaseTransportFactory::class => 'notifier.transport_factory.firebase', NotifierBridge\FortySixElks\FortySixElksTransportFactory::class => 'notifier.transport_factory.forty-six-elks', NotifierBridge\FreeMobile\FreeMobileTransportFactory::class => 'notifier.transport_factory.free-mobile', @@ -2891,20 +2891,26 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ $container->removeDefinition($classToServices[NotifierBridge\Mercure\MercureTransportFactory::class]); } - if (ContainerBuilder::willBeAvailable('symfony/fake-chat-notifier', NotifierBridge\FakeChat\FakeChatTransportFactory::class, ['symfony/framework-bundle', 'symfony/notifier', 'symfony/mailer'])) { - $container->getDefinition($classToServices[NotifierBridge\FakeChat\FakeChatTransportFactory::class]) - ->replaceArgument(0, new Reference('mailer')) - ->replaceArgument(1, new Reference('logger')) + // don't use ContainerBuilder::willBeAvailable() as these are not needed in production + if (class_exists(FakeChatTransportFactory::class)) { + $container->getDefinition('notifier.transport_factory.fake-chat') + ->replaceArgument(0, new Reference('mailer', ContainerBuilder::NULL_ON_INVALID_REFERENCE)) + ->replaceArgument(1, new Reference('logger', ContainerBuilder::NULL_ON_INVALID_REFERENCE)) ->addArgument(new Reference('event_dispatcher', ContainerBuilder::NULL_ON_INVALID_REFERENCE)) ->addArgument(new Reference('http_client', ContainerBuilder::NULL_ON_INVALID_REFERENCE)); + } else { + $container->removeDefinition('notifier.transport_factory.fake-chat'); } - if (ContainerBuilder::willBeAvailable('symfony/fake-sms-notifier', NotifierBridge\FakeSms\FakeSmsTransportFactory::class, ['symfony/framework-bundle', 'symfony/notifier', 'symfony/mailer'])) { - $container->getDefinition($classToServices[NotifierBridge\FakeSms\FakeSmsTransportFactory::class]) - ->replaceArgument(0, new Reference('mailer')) - ->replaceArgument(1, new Reference('logger')) + // don't use ContainerBuilder::willBeAvailable() as these are not needed in production + if (class_exists(FakeSmsTransportFactory::class)) { + $container->getDefinition('notifier.transport_factory.fake-sms') + ->replaceArgument(0, new Reference('mailer', ContainerBuilder::NULL_ON_INVALID_REFERENCE)) + ->replaceArgument(1, new Reference('logger', ContainerBuilder::NULL_ON_INVALID_REFERENCE)) ->addArgument(new Reference('event_dispatcher', ContainerBuilder::NULL_ON_INVALID_REFERENCE)) ->addArgument(new Reference('http_client', ContainerBuilder::NULL_ON_INVALID_REFERENCE)); + } else { + $container->removeDefinition('notifier.transport_factory.fake-sms'); } if (isset($config['admin_recipients'])) { From 6f21a400a8d1a8e644a759042ef59d68ebf8469a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 16 Dec 2024 16:09:00 +0100 Subject: [PATCH 3/4] require the writer to implement getFormats() in the translation:extract --- Command/TranslationUpdateCommand.php | 4 +++ .../Compiler/TranslationUpdateCommandPass.php | 31 +++++++++++++++++++ FrameworkBundle.php | 2 ++ 3 files changed, 37 insertions(+) create mode 100644 DependencyInjection/Compiler/TranslationUpdateCommandPass.php diff --git a/Command/TranslationUpdateCommand.php b/Command/TranslationUpdateCommand.php index 91e7e3349..0ffe6a949 100644 --- a/Command/TranslationUpdateCommand.php +++ b/Command/TranslationUpdateCommand.php @@ -64,6 +64,10 @@ public function __construct(TranslationWriterInterface $writer, TranslationReade { parent::__construct(); + if (!method_exists($writer, 'getFormats')) { + throw new \InvalidArgumentException(sprintf('The writer class "%s" does not implement the "getFormats()" method.', $writer::class)); + } + $this->writer = $writer; $this->reader = $reader; $this->extractor = $extractor; diff --git a/DependencyInjection/Compiler/TranslationUpdateCommandPass.php b/DependencyInjection/Compiler/TranslationUpdateCommandPass.php new file mode 100644 index 000000000..7542191d0 --- /dev/null +++ b/DependencyInjection/Compiler/TranslationUpdateCommandPass.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class TranslationUpdateCommandPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container): void + { + if (!$container->hasDefinition('console.command.translation_extract')) { + return; + } + + $translationWriterClass = $container->getParameterBag()->resolveValue($container->findDefinition('translation.writer')->getClass()); + + if (!method_exists($translationWriterClass, 'getFormats')) { + $container->removeDefinition('console.command.translation_extract'); + } + } +} diff --git a/FrameworkBundle.php b/FrameworkBundle.php index 0da10da9d..c371d10db 100644 --- a/FrameworkBundle.php +++ b/FrameworkBundle.php @@ -20,6 +20,7 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RemoveUnusedSessionMarshallingHandlerPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerRealRefPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TestServiceContainerWeakRefPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslationUpdateCommandPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\VirtualRequestStackPass; use Symfony\Component\Cache\Adapter\ApcuAdapter; @@ -193,6 +194,7 @@ public function build(ContainerBuilder $container) // must be registered after MonologBundle's LoggerChannelPass $container->addCompilerPass(new ErrorLoggerCompilerPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32); $container->addCompilerPass(new VirtualRequestStackPass()); + $container->addCompilerPass(new TranslationUpdateCommandPass(), PassConfig::TYPE_BEFORE_REMOVING); if ($container->getParameter('kernel.debug')) { $container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 2); From 17d8ae2e7aa77154f942e8ac48849ac718b0963f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 19 Dec 2024 14:05:52 +0100 Subject: [PATCH 4/4] relax assertions on generated hashes --- Tests/Functional/FragmentTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Functional/FragmentTest.php b/Tests/Functional/FragmentTest.php index 6d8966a17..48d5c327a 100644 --- a/Tests/Functional/FragmentTest.php +++ b/Tests/Functional/FragmentTest.php @@ -50,6 +50,6 @@ public function testGenerateFragmentUri() $client = self::createClient(['test_case' => 'Fragment', 'root_config' => 'config.yml', 'debug' => true]); $client->request('GET', '/fragment_uri'); - $this->assertSame('/_fragment?_hash=CCRGN2D%2FoAJbeGz%2F%2FdoH3bNSPwLCrmwC1zAYCGIKJ0E%3D&_path=_format%3Dhtml%26_locale%3Den%26_controller%3DSymfony%255CBundle%255CFrameworkBundle%255CTests%255CFunctional%255CBundle%255CTestBundle%255CController%255CFragmentController%253A%253AindexAction', $client->getResponse()->getContent()); + $this->assertMatchesRegularExpression('#/_fragment\?_hash=.+&_path=_format%3Dhtml%26_locale%3Den%26_controller%3DSymfony%255CBundle%255CFrameworkBundle%255CTests%255CFunctional%255CBundle%255CTestBundle%255CController%255CFragmentController%253A%253AindexAction$#', $client->getResponse()->getContent()); } }