diff --git a/composer.json b/composer.json index ecf2148e25e..a55d94f2eb9 100644 --- a/composer.json +++ b/composer.json @@ -165,7 +165,7 @@ "toflar/psr6-symfony-http-cache-store": "^4.0", "twig/extra-bundle": "^3.0", "twig/string-extra": "^3.0", - "twig/twig": "^3.8", + "twig/twig": "^3.10.2", "ua-parser/uap-php": "^3.9", "webignition/robots-txt-file": "^3.0", "wikimedia/less.php": "^1.7" @@ -202,8 +202,6 @@ "nikic/php-parser": "4.7.0", "terminal42/contao-ce-access": "<3.0", "thecodingmachine/safe": "<1.2", - "twig/intl-extra": "3.9.0", - "twig/twig": "3.9.0 || 3.10.0 || 3.10.1", "zendframework/zend-code": "<3.3.1" }, "autoload": { diff --git a/core-bundle/composer.json b/core-bundle/composer.json index f6cc3500d96..33e7d47dcaf 100644 --- a/core-bundle/composer.json +++ b/core-bundle/composer.json @@ -151,7 +151,7 @@ "terminal42/service-annotation-bundle": "^1.1", "toflar/cronjob-supervisor": "^2.0", "twig/string-extra": "^3.0", - "twig/twig": "^3.8", + "twig/twig": "^3.10.2", "ua-parser/uap-php": "^3.9", "webignition/robots-txt-file": "^3.0", "wikimedia/less.php": "^1.7" @@ -179,9 +179,7 @@ "contao/manager-plugin": "<2.0 || >=3.0", "doctrine/cache": "<1.10", "terminal42/contao-ce-access": "<3.0", - "thecodingmachine/safe": "<1.2", - "twig/intl-extra": "3.9.0", - "twig/twig": "3.9.0 || 3.10.0 || 3.10.1" + "thecodingmachine/safe": "<1.2" }, "autoload": { "psr-4": { diff --git a/core-bundle/config/routes.yaml b/core-bundle/config/routes.yaml index c7efbf4262e..bdea61137b2 100644 --- a/core-bundle/config/routes.yaml +++ b/core-bundle/config/routes.yaml @@ -11,21 +11,7 @@ contao_backend_redirect: path: '%contao.backend.route_prefix%/' defaults: _scope: backend + _store_referrer: false _controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController::redirectAction route: contao_backend permanent: true - -contao_backend_fallback: - path: '%contao.backend.route_prefix%/{parameters}' - defaults: - _scope: backend - _controller: Symfony\Bundle\FrameworkBundle\Controller\TemplateController - template: '@ContaoCore\Error\backend.html.twig' - context: - template: '@ContaoCore\Error\backend.html.twig' - language: en - statusName: Page Not Found - exception: The requested page does not exist. - statusCode: 404 - requirements: - parameters: .* diff --git a/core-bundle/config/services.yaml b/core-bundle/config/services.yaml index abcad4319dc..1680229c739 100644 --- a/core-bundle/config/services.yaml +++ b/core-bundle/config/services.yaml @@ -579,8 +579,6 @@ services: class: Contao\CoreBundle\Monolog\ContaoTableHandler arguments: - !service_closure '@database_connection' - - debug - - false tags: - { name: monolog.logger, channel: contao } diff --git a/core-bundle/contao/drivers/DC_Table.php b/core-bundle/contao/drivers/DC_Table.php index 3015d48913a..3d9bc486be3 100644 --- a/core-bundle/contao/drivers/DC_Table.php +++ b/core-bundle/contao/drivers/DC_Table.php @@ -1139,7 +1139,7 @@ protected function copyChildren($table, $insertID, $id, $parentId) $this->loadDataContainer($v); $cctable[$v] = $GLOBALS['TL_DCA'][$v]['config']['ctable'] ?? null; - if (!($GLOBALS['TL_DCA'][$v]['config']['doNotCopyRecords'] ?? null) && \strlen($v)) + if (!($GLOBALS['TL_DCA'][$v]['config']['doNotCopyRecords'] ?? null)) { // Consider the dynamic parent table (see #4867) if ($GLOBALS['TL_DCA'][$v]['config']['dynamicPtable'] ?? null) diff --git a/core-bundle/src/Controller/BackendController.php b/core-bundle/src/Controller/BackendController.php index c6281545c74..e0162659737 100644 --- a/core-bundle/src/Controller/BackendController.php +++ b/core-bundle/src/Controller/BackendController.php @@ -44,8 +44,8 @@ public function mainAction(): Response return $controller->run(); } - #[Route('/login', name: 'contao_backend_login')] - #[Route('/login-link', name: 'contao_backend_login_link')] + #[Route('/login', name: 'contao_backend_login', defaults: ['_store_referer' => false])] + #[Route('/login-link', name: 'contao_backend_login_link', defaults: ['_store_referer' => false])] public function loginAction(Request $request): Response { $this->initializeContaoFramework(); @@ -72,13 +72,13 @@ public function loginAction(Request $request): Response /** * Symfony will un-authenticate the user automatically by calling this route. */ - #[Route('/logout', name: 'contao_backend_logout')] + #[Route('/logout', name: 'contao_backend_logout', defaults: ['_store_referer' => false])] public function logoutAction(): RedirectResponse { return $this->redirectToRoute('contao_backend_login'); } - #[Route('/password', name: 'contao_backend_password')] + #[Route('/password', name: 'contao_backend_password', defaults: ['_store_referer' => false])] public function passwordAction(): Response { $this->initializeContaoFramework(); @@ -88,7 +88,7 @@ public function passwordAction(): Response return $controller->run(); } - #[Route('/confirm', name: 'contao_backend_confirm')] + #[Route('/confirm', name: 'contao_backend_confirm', defaults: ['_store_referer' => false])] public function confirmAction(): Response { $this->initializeContaoFramework(); @@ -98,7 +98,7 @@ public function confirmAction(): Response return $controller->run(); } - #[Route('/help', name: 'contao_backend_help')] + #[Route('/help', name: 'contao_backend_help', defaults: ['_store_referer' => false])] public function helpAction(): Response { $this->initializeContaoFramework(); @@ -108,7 +108,7 @@ public function helpAction(): Response return $controller->run(); } - #[Route('/popup', name: 'contao_backend_popup')] + #[Route('/popup', name: 'contao_backend_popup', defaults: ['_store_referer' => false])] public function popupAction(): Response { $this->initializeContaoFramework(); @@ -118,7 +118,7 @@ public function popupAction(): Response return $controller->run(); } - #[Route('/alerts', name: 'contao_backend_alerts')] + #[Route('/alerts', name: 'contao_backend_alerts', defaults: ['_store_referer' => false])] public function alertsAction(): Response { $this->initializeContaoFramework(); @@ -133,7 +133,7 @@ public function alertsAction(): Response * It will determine the current provider URL based on the value, which is usually * read dynamically via JavaScript. */ - #[Route('/picker', name: 'contao_backend_picker')] + #[Route('/picker', name: 'contao_backend_picker', defaults: ['_store_referer' => false])] public function pickerAction(Request $request): RedirectResponse { $extras = []; @@ -156,6 +156,21 @@ public function pickerAction(Request $request): RedirectResponse return new RedirectResponse($picker->getCurrentUrl()); } + #[Route('/{parameters}', name: 'contao_backend_fallback', requirements: ['parameters' => '.*'], defaults: ['_store_referer' => false], priority: -1000)] + public function backendFallback(): Response + { + return $this->render( + '@ContaoCore/Error/backend.html.twig', + [ + 'language' => 'en', + 'statusName' => 'Page Not Found', + 'exception' => 'The requested page does not exist.', + 'template' => '@ContaoCore/Error/backend.html.twig', + ], + new Response('', 404), + ); + } + public static function getSubscribedServices(): array { $services = parent::getSubscribedServices(); diff --git a/core-bundle/src/Controller/BackendPreviewController.php b/core-bundle/src/Controller/BackendPreviewController.php index 57297c0b586..8e68877b658 100644 --- a/core-bundle/src/Controller/BackendPreviewController.php +++ b/core-bundle/src/Controller/BackendPreviewController.php @@ -31,7 +31,7 @@ * requested front end page while ensuring that the /preview.php entry point is * used. When requested, the front end user gets authenticated. */ -#[Route('%contao.backend.route_prefix%', defaults: ['_scope' => 'backend', '_allow_preview' => true])] +#[Route('%contao.backend.route_prefix%/preview', name: 'contao_backend_preview', defaults: ['_scope' => 'backend', '_allow_preview' => true, '_store_referrer' => false])] class BackendPreviewController { public function __construct( @@ -44,7 +44,6 @@ public function __construct( ) { } - #[Route('/preview', name: 'contao_backend_preview')] public function __invoke(Request $request): Response { // Skip the redirect if there is no preview script, otherwise we will end up in diff --git a/core-bundle/src/Controller/BackendPreviewSwitchController.php b/core-bundle/src/Controller/BackendPreviewSwitchController.php index 2ec0f4063bf..90a3bdcb332 100644 --- a/core-bundle/src/Controller/BackendPreviewSwitchController.php +++ b/core-bundle/src/Controller/BackendPreviewSwitchController.php @@ -37,7 +37,7 @@ * - Provide the member usernames for the datalist * - Process the switch action (i.e. log in a specific front end user) */ -#[Route('%contao.backend.route_prefix%', defaults: ['_scope' => 'backend', '_allow_preview' => true])] +#[Route('%contao.backend.route_prefix%/preview_switch', name: 'contao_backend_switch', defaults: ['_scope' => 'backend', '_allow_preview' => true, '_store_referrer' => false])] class BackendPreviewSwitchController { public function __construct( @@ -54,7 +54,6 @@ public function __construct( ) { } - #[Route('/preview_switch', name: 'contao_backend_switch')] public function __invoke(Request $request): Response { $user = $this->security->getUser(); diff --git a/core-bundle/src/EventListener/CsrfTokenCookieSubscriber.php b/core-bundle/src/EventListener/CsrfTokenCookieSubscriber.php index 43b9b9a0170..e955a5d1dc0 100644 --- a/core-bundle/src/EventListener/CsrfTokenCookieSubscriber.php +++ b/core-bundle/src/EventListener/CsrfTokenCookieSubscriber.php @@ -120,6 +120,15 @@ private function requiresCsrf(Request $request, Response $response): bool private function isSessionEmpty(SessionInterface $session): bool { + foreach (headers_list() as $header) { + if ( + str_starts_with($header, "Set-Cookie: {$session->getName()}=") + && !str_starts_with($header, "Set-Cookie: {$session->getName()}=deleted;") + ) { + return false; + } + } + if (!$session->isStarted()) { return true; } diff --git a/core-bundle/src/EventListener/MakeResponsePrivateListener.php b/core-bundle/src/EventListener/MakeResponsePrivateListener.php index bceabb622f8..69d2eee5590 100644 --- a/core-bundle/src/EventListener/MakeResponsePrivateListener.php +++ b/core-bundle/src/EventListener/MakeResponsePrivateListener.php @@ -115,6 +115,15 @@ private function makePrivate(Response $response, string $reason): void private function isSessionEmpty(SessionInterface $session): bool { + foreach (headers_list() as $header) { + if ( + str_starts_with($header, "Set-Cookie: {$session->getName()}=") + && !str_starts_with($header, "Set-Cookie: {$session->getName()}=deleted;") + ) { + return false; + } + } + if (!$session->isStarted()) { return true; } diff --git a/core-bundle/src/EventListener/StoreRefererListener.php b/core-bundle/src/EventListener/StoreRefererListener.php index 6997031be61..d0f6668fcb2 100644 --- a/core-bundle/src/EventListener/StoreRefererListener.php +++ b/core-bundle/src/EventListener/StoreRefererListener.php @@ -91,7 +91,8 @@ private function canModifyBackendSession(Request $request): bool && !$request->query->has('token') && !$request->query->has('state') && 'feRedirect' !== $request->query->get('do') - && 'contao_backend' === $request->attributes->get('_route') + && 'backend' === $request->attributes->get('_scope') + && false !== $request->attributes->get('_store_referrer') && !$request->isXmlHttpRequest(); } diff --git a/core-bundle/src/Security/Voter/BackendAccessVoter.php b/core-bundle/src/Security/Voter/BackendAccessVoter.php index ec9189fb14f..1ec2a6edb56 100644 --- a/core-bundle/src/Security/Voter/BackendAccessVoter.php +++ b/core-bundle/src/Security/Voter/BackendAccessVoter.php @@ -121,7 +121,7 @@ private function hasAccess(mixed $subject, string $field, BackendUser $user): bo // Additionally check the child pages of the mounted pages if ('pagemounts' === $field) { - if (!isset($this->pagemountsCache[$user->id])) { + if (!isset($this->pagemountsCache[$user->id]) || (!empty($this->pagemountsCache[$user->id]) && !array_intersect($subject, $this->pagemountsCache[$user->id]))) { $database = $this->framework->createInstance(Database::class); $this->pagemountsCache[$user->id] = $database->getChildRecords($user->pagemounts, 'tl_page'); } diff --git a/core-bundle/src/Twig/Extension/ContaoExtension.php b/core-bundle/src/Twig/Extension/ContaoExtension.php index 3d1ce37129a..b07b2be4e31 100644 --- a/core-bundle/src/Twig/Extension/ContaoExtension.php +++ b/core-bundle/src/Twig/Extension/ContaoExtension.php @@ -48,10 +48,10 @@ use Twig\Environment; use Twig\Extension\AbstractExtension; use Twig\Extension\CoreExtension; -use Twig\Extension\EscaperExtension; use Twig\Extension\GlobalsInterface; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Node; +use Twig\Runtime\EscaperRuntime; use Twig\TwigFilter; use Twig\TwigFunction; @@ -69,15 +69,10 @@ public function __construct( private readonly ContaoVariable $contaoVariable, ) { $contaoEscaper = new ContaoEscaper(); - $escaperExtension = $environment->getExtension(EscaperExtension::class); - // Forward compatibility with twig/twig >=3.10.0 - if (method_exists($escaperExtension, 'setEnvironment')) { - $escaperExtension->setEnvironment($environment); - } - - $escaperExtension->setEscaper('contao_html', $contaoEscaper->escapeHtml(...)); - $escaperExtension->setEscaper('contao_html_attr', $contaoEscaper->escapeHtmlAttr(...)); + $escaperRuntime = $this->environment->getRuntime(EscaperRuntime::class); + $escaperRuntime->setEscaper('contao_html', $contaoEscaper->escapeHtml(...)); + $escaperRuntime->setEscaper('contao_html_attr', $contaoEscaper->escapeHtmlAttr(...)); // Use our escaper on all templates in the "@Contao" and "@Contao_*" namespaces, // as well as the existing bundle templates we're already shipping. @@ -85,8 +80,8 @@ public function __construct( $this->addContaoEscaperRule('%^@ContaoCore/%'); // Mark classes as safe for HTML that already escape their output themselves - $escaperExtension->addSafeClass(HtmlAttributes::class, ['html', 'contao_html']); - $escaperExtension->addSafeClass(HighlightResult::class, ['html', 'contao_html']); + $escaperRuntime->addSafeClass(HtmlAttributes::class, ['html', 'contao_html']); + $escaperRuntime->addSafeClass(HighlightResult::class, ['html', 'contao_html']); $this->environment->addGlobal( 'request_token', @@ -242,7 +237,9 @@ function (Environment $env, $context, $template, $variables = [], $withContext = public function getFilters(): array { - $escaperFilter = static function (Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false) { + $escaperFilter = static function (Environment $env, $string, string $strategy = 'html', string|null $charset = null, bool $autoescape = false) { + $runtime = $env->getRuntime(EscaperRuntime::class); + if ($string instanceof ChunkedText) { $parts = []; @@ -250,31 +247,36 @@ public function getFilters(): array if (ChunkedText::TYPE_RAW === $type) { $parts[] = $chunk; } else { - $parts[] = twig_escape_filter($env, $chunk, $strategy, $charset); + $parts[] = $runtime->escape($chunk, $strategy, $charset); } } return implode('', $parts); } - return twig_escape_filter($env, $string, $strategy, $charset, $autoescape); + return $runtime->escape($string, $strategy, $charset, $autoescape); }; + /** @see \Twig\Extension\EscaperExtension::escapeFilterIsSafe() */ $twigEscaperFilterIsSafe = static function (Node $filterArgs): array { - $expression = iterator_to_array($filterArgs)[0] ?? null; - - if ($expression instanceof ConstantExpression) { - $value = $expression->getAttribute('value'); + foreach ($filterArgs as $arg) { + if ($arg instanceof ConstantExpression) { + $value = $arg->getAttribute('value'); + + // Our escaper strategy variants that tolerate input encoding are also safe in + // the original context (e.g. for the filter argument 'contao_html' we will + // return ['contao_html', 'html']). + if (\in_array($value, ['contao_html', 'contao_html_attr'], true)) { + return [$value, substr($value, 7)]; + } - // Our escaper strategy variants that tolerate input encoding are also safe in - // the original context (e.g. for the filter argument 'contao_html' we will - // return ['contao_html', 'html']). - if (\in_array($value, ['contao_html', 'contao_html_attr'], true)) { - return [$value, substr($value, 7)]; + return [$value]; } + + return []; } - return twig_escape_filter_is_safe($filterArgs); + return ['html']; }; return [ diff --git a/core-bundle/src/Twig/Interop/ContaoEscaper.php b/core-bundle/src/Twig/Interop/ContaoEscaper.php index ac7a67a048d..2cdbcc11fb2 100644 --- a/core-bundle/src/Twig/Interop/ContaoEscaper.php +++ b/core-bundle/src/Twig/Interop/ContaoEscaper.php @@ -13,7 +13,6 @@ namespace Contao\CoreBundle\Twig\Interop; use Contao\StringUtil; -use Twig\Environment; use Twig\Error\RuntimeError; /** @@ -33,7 +32,7 @@ final class ContaoEscaper * * @see twig_escape_filter */ - public function escapeHtml(Environment $environment, mixed $string, string|null $charset): string + public function escapeHtml(mixed $string, string|null $charset): string { if (null !== $charset && 'UTF-8' !== strtoupper($charset)) { throw new RuntimeError(sprintf('The "contao_html" escape filter does not support the %s charset, use UTF-8 instead.', $charset)); @@ -50,7 +49,7 @@ public function escapeHtml(Environment $environment, mixed $string, string|null * * @see twig_escape_filter */ - public function escapeHtmlAttr(Environment $environment, mixed $string, string|null $charset): string + public function escapeHtmlAttr(mixed $string, string|null $charset): string { if (null !== $charset && 'UTF-8' !== strtoupper($charset)) { throw new RuntimeError(sprintf('The "contao_html_attr" escape filter does not support the %s charset, use UTF-8 instead.', $charset)); diff --git a/core-bundle/tests/EventListener/StoreRefererListenerTest.php b/core-bundle/tests/EventListener/StoreRefererListenerTest.php index 06f833f60b7..fb103426201 100644 --- a/core-bundle/tests/EventListener/StoreRefererListenerTest.php +++ b/core-bundle/tests/EventListener/StoreRefererListenerTest.php @@ -246,6 +246,7 @@ public function testDoesNotStoreTheRefererIfTheBackEndSessionCannotBeModified(): $request->setSession($session); $request->attributes->set('_scope', ContaoCoreBundle::SCOPE_BACKEND); + $request->attributes->set('_store_referrer', false); $listener = $this->getListener($this->createMock(User::class)); $listener($this->getResponseEvent($request)); diff --git a/core-bundle/tests/Twig/Extension/ContaoExtensionTest.php b/core-bundle/tests/Twig/Extension/ContaoExtensionTest.php index 0b7d48a3204..cae6f8b7d2b 100644 --- a/core-bundle/tests/Twig/Extension/ContaoExtensionTest.php +++ b/core-bundle/tests/Twig/Extension/ContaoExtensionTest.php @@ -176,6 +176,11 @@ public function testIncludeFunctionDelegatesToTwigInclude(): void public function testThrowsIfCoreIncludeFunctionIsNotFound(): void { $environment = $this->createMock(Environment::class); + $environment + ->method('getRuntime') + ->willReturn(new EscaperRuntime()) + ; + $environment ->method('getExtension') ->willReturnMap([ @@ -185,14 +190,6 @@ public function testThrowsIfCoreIncludeFunctionIsNotFound(): void ]) ; - // Forward compatibility with twig/twig >=3.10.0 - if (class_exists(EscaperRuntime::class)) { - $environment - ->method('getRuntime') - ->willReturn(new EscaperRuntime()) - ; - } - $extension = new ContaoExtension( $environment, $this->createMock(ContaoFilesystemLoader::class), @@ -379,46 +376,6 @@ public static function provideTemplateNames(): iterable yield 'core-bundle template' => ['@ContaoCore/Image/Studio/figure.html.twig']; } - /** - * We need to adjust some of Twig's core functions (e.g. the escape filter) but - * still delegate to the original implementation for maximum compatibility. This - * test makes sure the function's signatures remains the same and changes to the - * original codebase do not stay unnoticed. - * - * @dataProvider provideTwigFunctionSignatures - */ - public function testContaoUsesCorrectTwigFunctionSignatures(\ReflectionFunction|\ReflectionMethod $reflector, array $expectedParameters): void - { - $parameters = array_map( - static fn (\ReflectionParameter $parameter): array => [ - ($type = $parameter->getType()) instanceof \ReflectionNamedType ? $type->getName() : null, - $parameter->getName(), - ], - $reflector->getParameters(), - ); - - $this->assertSame($parameters, $expectedParameters); - } - - public static function provideTwigFunctionSignatures(): iterable - { - // Backwards compatibility with twig/twig <3.9.0 - new \ReflectionClass(EscaperExtension::class); - - yield [ - new \ReflectionFunction('twig_escape_filter'), - [ - [Environment::class, 'env'], - [null, 'string'], - [null, 'strategy'], - [null, 'charset'], - [null, 'autoescape'], - ], - ]; - - yield [new \ReflectionFunction('twig_escape_filter_is_safe'), [[Node::class, 'filterArgs']]]; - } - /** * @param Environment&MockObject $environment */ @@ -427,6 +384,11 @@ private function getContaoExtension(Environment|null $environment = null, Contao $environment ??= $this->createMock(Environment::class); $filesystemLoader ??= $this->createMock(ContaoFilesystemLoader::class); + $environment + ->method('getRuntime') + ->willReturn(new EscaperRuntime()) + ; + $environment ->method('getExtension') ->willReturnMap([ @@ -435,14 +397,6 @@ private function getContaoExtension(Environment|null $environment = null, Contao ]) ; - // Forward compatibility with twig/twig >=3.10.0 - if (class_exists(EscaperRuntime::class)) { - $environment - ->method('getRuntime') - ->willReturn(new EscaperRuntime()) - ; - } - return new ContaoExtension( $environment, $filesystemLoader, diff --git a/core-bundle/tests/Twig/Interop/ContaoEscaperTest.php b/core-bundle/tests/Twig/Interop/ContaoEscaperTest.php index 270f20ac7b0..7eec2775710 100644 --- a/core-bundle/tests/Twig/Interop/ContaoEscaperTest.php +++ b/core-bundle/tests/Twig/Interop/ContaoEscaperTest.php @@ -21,7 +21,6 @@ use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\Fragment\FragmentHandler; -use Twig\Environment; use Twig\Error\RuntimeError; class ContaoEscaperTest extends TestCase @@ -140,11 +139,11 @@ public function testEscapeHtmlAttrThrowsErrorIfCharsetIsNotUtf8(): void private function invokeEscapeHtml(int|string $input, string|null $charset): string { - return (new ContaoEscaper())->escapeHtml($this->createMock(Environment::class), $input, $charset); + return (new ContaoEscaper())->escapeHtml($input, $charset); } private function invokeEscapeHtmlAttr(int|string $input, string|null $charset): string { - return (new ContaoEscaper())->escapeHtmlAttr($this->createMock(Environment::class), $input, $charset); + return (new ContaoEscaper())->escapeHtmlAttr($input, $charset); } } diff --git a/core-bundle/tests/Twig/Interop/PhpTemplateParentReferenceNodeTest.php b/core-bundle/tests/Twig/Interop/PhpTemplateParentReferenceNodeTest.php index cf11c609d46..a2ebe1e31a3 100644 --- a/core-bundle/tests/Twig/Interop/PhpTemplateParentReferenceNodeTest.php +++ b/core-bundle/tests/Twig/Interop/PhpTemplateParentReferenceNodeTest.php @@ -14,7 +14,6 @@ use Contao\CoreBundle\Tests\TestCase; use Contao\CoreBundle\Twig\Interop\PhpTemplateParentReferenceNode; -use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Environment; @@ -27,15 +26,10 @@ public function testCompilesParentReferenceCode(): void (new PhpTemplateParentReferenceNode())->compile($compiler); $expectedSource = <<<'SOURCE' - echo sprintf('[[TL_PARENT_%s]]', \Contao\CoreBundle\Framework\ContaoFramework::getNonce()); + yield sprintf('[[TL_PARENT_%s]]', \Contao\CoreBundle\Framework\ContaoFramework::getNonce()); SOURCE; - // Forward compatibility with twig/twig >=3.9.0 - if (class_exists(YieldReady::class)) { - $expectedSource = str_replace('echo', 'yield', $expectedSource); - } - $this->assertSame($expectedSource, $compiler->getSource()); } } diff --git a/core-bundle/tests/Twig/Interop/PhpTemplateProxyNodeTest.php b/core-bundle/tests/Twig/Interop/PhpTemplateProxyNodeTest.php index 7edb743ffc3..a2784730cc7 100644 --- a/core-bundle/tests/Twig/Interop/PhpTemplateProxyNodeTest.php +++ b/core-bundle/tests/Twig/Interop/PhpTemplateProxyNodeTest.php @@ -15,7 +15,6 @@ use Contao\CoreBundle\Tests\TestCase; use Contao\CoreBundle\Twig\Extension\ContaoExtension; use Contao\CoreBundle\Twig\Interop\PhpTemplateProxyNode; -use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Environment; @@ -28,7 +27,7 @@ public function testCompilesProxyCode(): void (new PhpTemplateProxyNode(ContaoExtension::class))->compile($compiler); $expectedSource = <<<'SOURCE' - echo $this->extensions["Contao\\CoreBundle\\Twig\\Extension\\ContaoExtension"]->renderLegacyTemplate( + yield $this->extensions["Contao\\CoreBundle\\Twig\\Extension\\ContaoExtension"]->renderLegacyTemplate( $this->getTemplateName(), array_map( function(callable $block) use ($context): string { @@ -47,11 +46,6 @@ function(callable $block) use ($context): string { SOURCE; - // Forward compatibility with twig/twig >=3.9.0 - if (class_exists(YieldReady::class)) { - $expectedSource = str_replace('echo', 'yield', $expectedSource); - } - $this->assertSame($expectedSource, $compiler->getSource()); } } diff --git a/core-bundle/tests/Twig/ResponseContext/AddNodeTest.php b/core-bundle/tests/Twig/ResponseContext/AddNodeTest.php index 04a8c6cdcff..8fbdc4cac22 100644 --- a/core-bundle/tests/Twig/ResponseContext/AddNodeTest.php +++ b/core-bundle/tests/Twig/ResponseContext/AddNodeTest.php @@ -16,7 +16,6 @@ use Contao\CoreBundle\Twig\Extension\ContaoExtension; use Contao\CoreBundle\Twig\ResponseContext\AddNode; use Contao\CoreBundle\Twig\ResponseContext\DocumentLocation; -use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Environment; use Twig\Node\Expression\ConstantExpression; @@ -43,7 +42,7 @@ public function testCompilesAddNode(): void $__contao_document_content = ''; foreach((function () use (&$context, $macros, $blocks) { // line 42 - echo "foobar"; + yield "foobar"; yield ''; })() as $__contao_document_chunk) { $__contao_document_content .= ob_get_contents() . $__contao_document_chunk; @@ -56,11 +55,6 @@ public function testCompilesAddNode(): void SOURCE; - // Forward compatibility with twig/twig >=3.9.0 - if (class_exists(YieldReady::class)) { - $expectedSource = str_replace('echo', 'yield', $expectedSource); - } - $this->assertSame($expectedSource, $compiler->getSource()); } }