diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bcb016d21a6..2a9e828e5df 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,11 @@ on: permissions: read-all +defaults: + run: + # Simulate an interactive terminal with color support + shell: script -q -e -c "export TERM=xterm; bash {0}" + jobs: rector: name: Rector @@ -18,12 +23,17 @@ jobs: coverage: none - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + show-progress: false - - name: Install the dependencies - run: | - composer install --no-interaction --no-progress - composer bin rector install --no-interaction --no-progress + - name: Install Contao + uses: ramsey/composer-install@v3 + + - name: Install Rector + uses: ramsey/composer-install@v3 + with: + working-directory: vendor-bin/rector - name: Run Rector run: vendor-bin/rector/vendor/bin/rector --dry-run --no-progress-bar @@ -40,12 +50,17 @@ jobs: coverage: none - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + show-progress: false - - name: Install the dependencies - run: | - composer install --no-interaction --no-progress - composer bin ecs install --no-interaction --no-progress + - name: Install Contao + uses: ramsey/composer-install@v3 + + - name: Install ECS + uses: ramsey/composer-install@v3 + with: + working-directory: vendor-bin/ecs - name: Run ECS run: vendor-bin/ecs/vendor/bin/ecs check --no-progress-bar @@ -62,12 +77,17 @@ jobs: coverage: none - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + show-progress: false - - name: Install the dependencies - run: | - composer install --no-interaction --no-progress - composer bin ecs install --no-interaction --no-progress + - name: Install Contao + uses: ramsey/composer-install@v3 + + - name: Install ECS + uses: ramsey/composer-install@v3 + with: + working-directory: vendor-bin/ecs - name: Run ECS run: | @@ -86,12 +106,17 @@ jobs: coverage: none - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + show-progress: false - - name: Install the dependencies - run: | - composer install --no-interaction --no-progress - composer bin phpstan install --no-interaction --no-progress + - name: Install Contao + uses: ramsey/composer-install@v3 + + - name: Install PHPStan + uses: ramsey/composer-install@v3 + with: + working-directory: vendor-bin/phpstan - name: Run PHPStan run: vendor-bin/phpstan/vendor/bin/phpstan analyse --no-progress @@ -101,14 +126,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + show-progress: false - name: Run YAMLlint - run: | - python3 -m pip install --upgrade pip - python3 -m pip install --upgrade setuptools wheel - python3 -m pip install --upgrade yamllint - /home/runner/.local/bin/yamllint . + run: yamllint --format=github . service-linter: name: Service linter @@ -122,12 +145,17 @@ jobs: coverage: none - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + show-progress: false - - name: Install the dependencies - run: | - composer install --no-interaction --no-progress - composer bin service-linter install --no-interaction --no-progress + - name: Install Contao + uses: ramsey/composer-install@v3 + + - name: Install the service linter + uses: ramsey/composer-install@v3 + with: + working-directory: vendor-bin/service-linter - name: Run the service linter run: vendor-bin/service-linter/bin/lint-service-ids @@ -144,19 +172,27 @@ jobs: coverage: none - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 + show-progress: false - - name: Install the dependencies - run: | - composer install --no-interaction --no-progress - composer -dcore-bundle install --no-interaction --no-progress - composer bin depcheck install --no-interaction --no-progress + - name: Install Contao + uses: ramsey/composer-install@v3 + + - name: Install depcheck + uses: ramsey/composer-install@v3 + with: + working-directory: vendor-bin/depcheck - name: Check for unknown symbols (contao/contao) run: vendor-bin/depcheck/vendor/bin/composer-dependency-analyser --config=depcheck.php + - name: Install the core bundle + uses: ramsey/composer-install@v3 + with: + working-directory: core-bundle + - name: Check for unknown symbols (contao/core-bundle) run: vendor-bin/depcheck/vendor/bin/composer-dependency-analyser --config=depcheck.php --composer-json=core-bundle/composer.json @@ -181,10 +217,12 @@ jobs: mysql -uroot -proot -e "CREATE database contao_test" - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + show-progress: false - - name: Install the dependencies - run: composer install --no-interaction --no-progress + - name: Install Contao + uses: ramsey/composer-install@v3 - name: Run the unit tests run: vendor/bin/phpunit @@ -211,10 +249,12 @@ jobs: coverage: none - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + show-progress: false - - name: Install the dependencies - run: composer install --no-interaction --no-progress + - name: Install Contao + uses: ramsey/composer-install@v3 - name: Run the unit tests in reverse order run: vendor/bin/phpunit --order-by=reverse --extensions Contao\\CoreBundle\\Tests\\PhpunitExtension\\GlobalStateWatcher @@ -237,10 +277,14 @@ jobs: mysql -uroot -proot -e "CREATE database contao_test" - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + show-progress: false - - name: Install the dependencies - run: composer install --ignore-platform-req=php+ --no-interaction --no-progress + - name: Install Contao + uses: ramsey/composer-install@v3 + with: + composer-options: --ignore-platform-req=php+ - name: Run the unit tests run: vendor/bin/phpunit @@ -267,10 +311,14 @@ jobs: mysql -uroot -proot -e "CREATE database contao_test" - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + show-progress: false - - name: Install the dependencies - run: composer update --prefer-lowest --prefer-stable --no-interaction --no-progress + - name: Install Contao + uses: ramsey/composer-install@v3 + with: + dependency-versions: lowest - name: Run the unit tests run: vendor/bin/phpunit @@ -297,7 +345,9 @@ jobs: coverage: none - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + show-progress: false - name: Test the single bundles run: | @@ -337,12 +387,17 @@ jobs: coverage: none - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + show-progress: false - - name: Install the dependencies - run: | - composer install --no-interaction --no-progress - composer bin monorepo-tools install --no-interaction --no-progress + - name: Install Contao + uses: ramsey/composer-install@v3 + + - name: Install the monorepo tools + uses: ramsey/composer-install@v3 + with: + working-directory: vendor-bin/monorepo-tools - name: Validate the composer.json files run: vendor-bin/monorepo-tools/vendor/bin/monorepo-tools composer-json --validate diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 82a9157764c..09efe832906 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -10,6 +10,11 @@ on: permissions: read-all +defaults: + run: + # Simulate an interactive terminal with color support + shell: script -q -e -c "export TERM=xterm; bash {0}" + jobs: coverage: name: Codecov @@ -23,16 +28,18 @@ jobs: coverage: pcov - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + show-progress: false - - name: Install the dependencies - run: composer install --no-interaction --no-progress + - name: Install Contao + uses: ramsey/composer-install@v3 - name: Generate the coverage report run: php -d pcov.enabled=1 vendor/bin/phpunit --testsuite=coverage --coverage-clover=clover.xml - name: Upload the coverage report - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: files: ./clover.xml fail_ci_if_error: true diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 604f89cee13..3f6d8f93397 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -13,8 +13,8 @@ jobs: runs-on: ubuntu-latest continue-on-error: true steps: - - name: Assign the PR - uses: toshimaru/auto-author-assign@v1.4.0 + - name: Assign the author + uses: toshimaru/auto-author-assign@v2.1.0 - name: Assign the milestone uses: zoispag/action-assign-milestone@v1 diff --git a/.github/workflows/split.yml b/.github/workflows/split.yml index 4af23f3f984..f3b191d391a 100644 --- a/.github/workflows/split.yml +++ b/.github/workflows/split.yml @@ -9,6 +9,11 @@ on: permissions: read-all +defaults: + run: + # Simulate an interactive terminal with color support + shell: script -q -e -c "export TERM=xterm; bash {0}" + jobs: monorepo-split: name: Monorepo split @@ -22,16 +27,18 @@ jobs: coverage: none - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 + with: + show-progress: false - name: Cache the monorepo split - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: .monorepo-split-cache key: dev-${GITHUB_REF##*/} - name: Install the dependencies - run: composer global require contao/monorepo-tools:dev-main + run: composer global require contao/monorepo-tools:^0.2 - name: Split the monorepo run: $HOME/.composer/vendor/bin/monorepo-tools split ${GITHUB_REF##*/} ${{ github.event.forced && '--force-push' || '' }} diff --git a/composer.json b/composer.json index d2df07f888e..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", "zendframework/zend-code": "<3.3.1" }, "autoload": { diff --git a/core-bundle/composer.json b/core-bundle/composer.json index 42c2b1cba14..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" + "thecodingmachine/safe": "<1.2" }, "autoload": { "psr-4": { diff --git a/core-bundle/contao/templates/twig/component/_picture.html.twig b/core-bundle/contao/templates/twig/component/_picture.html.twig index 940291fedd8..8adea47869f 100644 --- a/core-bundle/contao/templates/twig/component/_picture.html.twig +++ b/core-bundle/contao/templates/twig/component/_picture.html.twig @@ -27,15 +27,15 @@ {# Just an image (no sources) #} {% block image %} {% set img = figure.image.img %} - {% set defineProportions = img.width|default(false) and img.height|default(false) %} + {% set define_proportions = img.width|default(false) and img.height|default(false) %} {% set img_attributes = attrs() .set('src', img.src) .set('alt', figure.hasMetadata ? figure.metadata.alt|insert_tag : '') .setIfExists('title', figure.hasMetadata ? figure.metadata.title|insert_tag : null) .setIfExists('srcset', img.srcset is defined and img.srcset != img.src ? img.srcset : null) .setIfExists('sizes', img.sizes|default) - .setIfExists('width', defineProportions ? img.width : null) - .setIfExists('height', defineProportions ? img.height : null) + .setIfExists('width', define_proportions ? img.width : null) + .setIfExists('height', define_proportions ? img.height : null) .setIfExists('loading', img.loading|default) .addClass(img.class|default) .mergeWith(figure.options.img_attr|default) @@ -53,14 +53,14 @@ {% block sources %} {% for source in figure.image.sources %} {% block source %} - {% set defineProportions = source.width|default(false) and source.height|default(false) %} + {% set define_proportions = source.width|default(false) and source.height|default(false) %} {% set source_attributes = attrs() .set('srcset', source.srcset) .setIfExists('sizes', source.sizes|default) .setIfExists('media', source.media|default) .setIfExists('type', source.type|default) - .setIfExists('width', defineProportions ? source.width : null) - .setIfExists('height', defineProportions ? source.height : null) + .setIfExists('width', define_proportions ? source.width : null) + .setIfExists('height', define_proportions ? source.height : null) .mergeWith(figure.options.picture_attr|default) .mergeWith(source_attributes|default) %} diff --git a/core-bundle/contao/templates/twig/content_element/description_list.html.twig b/core-bundle/contao/templates/twig/content_element/description_list.html.twig index 03d63f055e6..fc4cfa1457a 100644 --- a/core-bundle/contao/templates/twig/content_element/description_list.html.twig +++ b/core-bundle/contao/templates/twig/content_element/description_list.html.twig @@ -1,14 +1,14 @@ {% extends "@Contao/content_element/_base.html.twig" %} {% block content %} - + {% for description in descriptions %} {% block description %} {% if description.term is not empty %} - {{ description.term|insert_tag_raw }} + {{ description.term|insert_tag_raw }} {% endif %} {% if description.details is not empty %} - {{ description.details|insert_tag_raw }} + {{ description.details|insert_tag_raw }} {% endif %} {% endblock %} {% endfor %} diff --git a/core-bundle/src/Twig/Extension/ContaoExtension.php b/core-bundle/src/Twig/Extension/ContaoExtension.php index 6fbd6bbc99e..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; @@ -70,9 +70,9 @@ public function __construct( ) { $contaoEscaper = new ContaoEscaper(); - $escaperExtension = $environment->getExtension(EscaperExtension::class); - $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. @@ -80,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', @@ -237,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 = []; @@ -245,43 +247,36 @@ public function getFilters(): array if (ChunkedText::TYPE_RAW === $type) { $parts[] = $chunk; } else { - // Forward compatibility with twig/twig 4 - if (method_exists(EscaperExtension::class, 'escape')) { - $parts[] = EscaperExtension::escape($env, $chunk, $strategy, $charset); - } else { - $parts[] = twig_escape_filter($env, $chunk, $strategy, $charset); - } + $parts[] = $runtime->escape($chunk, $strategy, $charset); } } return implode('', $parts); } - // Forward compatibility with twig/twig 4 - if (method_exists(EscaperExtension::class, 'escape')) { - return EscaperExtension::escape($env, $string, $strategy, $charset, $autoescape); - } - - 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 { - // 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 ( - ($expression = iterator_to_array($filterArgs)[0] ?? null) instanceof ConstantExpression - && \in_array($value = $expression->getAttribute('value'), ['contao_html', 'contao_html_attr'], true) - ) { - return [$value, substr($value, 7)]; - } + 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)]; + } + + return [$value]; + } - // Backwards compatibility with twig/twig <3.9 - if (\function_exists('twig_escape_filter_is_safe')) { - return twig_escape_filter_is_safe($filterArgs); + return []; } - return EscaperExtension::escapeFilterIsSafe($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/templates/Collector/contao.html.twig b/core-bundle/templates/Collector/contao.html.twig index 79504386ea1..f4cbd6cb52b 100644 --- a/core-bundle/templates/Collector/contao.html.twig +++ b/core-bundle/templates/Collector/contao.html.twig @@ -37,7 +37,7 @@ {% endset %} - {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: true, name: 'contao', additional_classes: (((collector.summary.preview) ? 'sf-toolbar-status-yellow ' : '') ~ 'sf-toolbar-block-right') }) }} + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', {link: true, name: 'contao', additional_classes: (((collector.summary.preview) ? 'sf-toolbar-status-yellow ' : '') ~ 'sf-toolbar-block-right')}) }} {% endblock %} {% block menu %} diff --git a/core-bundle/templates/Frontend/preview_toolbar_base_js.html.twig b/core-bundle/templates/Frontend/preview_toolbar_base_js.html.twig index bbf07c02a48..010f77ac9d0 100644 --- a/core-bundle/templates/Frontend/preview_toolbar_base_js.html.twig +++ b/core-bundle/templates/Frontend/preview_toolbar_base_js.html.twig @@ -1,6 +1,6 @@ {% do csp_handler.addSource('img-src', 'data:') %} -{% set styleNonce = csp_handler.getNonce('style-src') %} - +{% set style_nonce = csp_handler.getNonce('style-src') %} + .cto-toolbar { font-family: -apple-system,system-ui,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif; font-weight: 400; @@ -208,8 +208,8 @@ } -{% set scriptNonce = csp_handler.getNonce('script-src') %} - +{% set script_nonce = csp_handler.getNonce('script-src') %} + (function () { const toolbar = document.createElement('div'); toolbar.classList.add('cto-toolbar'); diff --git a/core-bundle/templates/Image/Studio/_macros.html.twig b/core-bundle/templates/Image/Studio/_macros.html.twig index 1deeb0be5ac..c4142308677 100644 --- a/core-bundle/templates/Image/Studio/_macros.html.twig +++ b/core-bundle/templates/Image/Studio/_macros.html.twig @@ -40,7 +40,6 @@ : #} - {# Build a
including a picture and - if available - a caption from Studio\Figure data. @@ -81,14 +80,14 @@ {%- if figure.image.sources -%} {% for source in figure.image.sources %} - {%- set defineProportions = source.width|default(false) and source.height|default(false) -%} + {%- set define_proportions = source.width|default(false) and source.height|default(false) -%} {%- set base_attributes = { 'srcset': source.srcset, 'sizes': source.sizes|default(null), 'media': source.media|default(null), 'type': source.type|default(null), - 'width': defineProportions ? source.width : null, - 'height': defineProportions ? source.height : null, + 'width': define_proportions ? source.width : null, + 'height': define_proportions ? source.height : null, } -%} {%- endfor %} @@ -108,7 +107,7 @@ {% set img_attributes = figure.options.img_attr|default({})|merge(options.img_attr|default({})) %} {% set img = figure.image.img %} - {% set defineProportions = img.width|default(false) and img.height|default(false) %} + {% set define_proportions = img.width|default(false) and img.height|default(false) %} {% set base_attributes = { 'src': img.src, @@ -116,8 +115,8 @@ 'title': figure.hasMetadata ? (figure.metadata.title ?: null) : null, 'srcset': img.srcset is defined and img.srcset != img.src ? img.srcset : null, 'sizes': img.sizes|default(null), - 'width': defineProportions ? img.width : null, - 'height': defineProportions ? img.height : null, + 'width': define_proportions ? img.width : null, + 'height': define_proportions ? img.height : null, 'loading': img.loading|default(null), 'class': img.class|default(null), } %} diff --git a/core-bundle/templates/Image/Studio/figure.html.twig b/core-bundle/templates/Image/Studio/figure.html.twig index f995e918475..027ae090b0e 100644 --- a/core-bundle/templates/Image/Studio/figure.html.twig +++ b/core-bundle/templates/Image/Studio/figure.html.twig @@ -1,5 +1,5 @@ {% import "@ContaoCore/Image/Studio/_macros.html.twig" as studio %} {{- studio.figure(figure, figure.options|default({})|merge({ - attr: { class: ('image_container ' ~ figure.options.attr.class|default(''))|trim } + attr: {class: ('image_container ' ~ figure.options.attr.class|default(''))|trim} })) -}} diff --git a/core-bundle/tests/Twig/Extension/ContaoExtensionTest.php b/core-bundle/tests/Twig/Extension/ContaoExtensionTest.php index 7ffc6033451..cae6f8b7d2b 100644 --- a/core-bundle/tests/Twig/Extension/ContaoExtensionTest.php +++ b/core-bundle/tests/Twig/Extension/ContaoExtensionTest.php @@ -44,6 +44,7 @@ use Twig\Node\Node; use Twig\Node\TextNode; use Twig\NodeTraverser; +use Twig\Runtime\EscaperRuntime; use Twig\Source; use Twig\TwigFilter; use Twig\TwigFunction; @@ -175,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([ @@ -370,60 +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 - { - // Make sure the functions outside the class scope are loaded - new \ReflectionClass(EscaperExtension::class); - - // Forward compatibility with twig/twig 4 - if (method_exists(EscaperExtension::class, 'escape')) { - $escape = new \ReflectionMethod(EscaperExtension::class, 'escape'); - } else { - $escape = new \ReflectionFunction('twig_escape_filter'); - } - - yield [ - $escape, - [ - [Environment::class, 'env'], - [null, 'string'], - [null, 'strategy'], - [null, 'charset'], - [null, 'autoescape'], - ], - ]; - - // Backwards compatibility with twig/twig <3.9 - if (\function_exists('twig_escape_filter_is_safe')) { - $escapeIsSafe = new \ReflectionFunction('twig_escape_filter_is_safe'); - } else { - $escapeIsSafe = new \ReflectionMethod(EscaperExtension::class, 'escapeFilterIsSafe'); - } - - yield [$escapeIsSafe, [[Node::class, 'filterArgs']]]; - } - /** * @param Environment&MockObject $environment */ @@ -432,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([ 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()); } }