Skip to content

[Symfony 7.3] Merge AsTwigFilter/AsTwigFunction rules into single rule, add getTests() support - #1027

Merged
TomasVotruba merged 1 commit into
mainfrom
merge-twig-attribute-rules
Aug 11, 2026
Merged

[Symfony 7.3] Merge AsTwigFilter/AsTwigFunction rules into single rule, add getTests() support#1027
TomasVotruba merged 1 commit into
mainfrom
merge-twig-attribute-rules

Conversation

@TomasVotruba

Copy link
Copy Markdown
Member

GetFiltersToAsTwigFilterAttributeRector and GetFunctionsToAsTwigFunctionAttributeRector could never convert a class that declares both getFilters() and getFunctions().

Each rule checks stillRequiresAbstractExtension() before converting - and the sibling method is exactly such a blocker. So the filter rule bailed because getFunctions() was still there, and the function rule bailed because getFilters() was still there. Very common shape in real Twig extensions.

This merges both into GetFiltersAndFunctionsToAsTwigAttributeRector, which collects conversions of getFilters(), getFunctions() and getTests() in one pass, validates them all, and only then removes extends AbstractExtension.

Also adds getTests() -> #[AsTwigTest], with per-method option mapping (#[AsTwigTest] has no isSafe/preservesSafety/preEscape).

Before

final class FormatterExtension extends AbstractExtension
{
    public function getFilters(): array
    {
        return [
            new TwigFilter('simple_array_to_html', $this->simpleArrayToHtml(...), ['is_safe' => ['html']]),
        ];
    }

    public function getFunctions(): array
    {
        return [
            new TwigFunction('normalize_string_value', $this->normalizeStringValue(...)),
            new TwigFunction('simple_array_to_html', $this->simpleArrayToHtml(...), ['is_safe' => ['html']]),
        ];
    }

    public function normalizeStringValue(string $value): string
    {
        return $value;
    }

    public function simpleArrayToHtml(array $values): string
    {
        return implode('<br />', $values);
    }
}

After

final class FormatterExtension
{
    #[AsTwigFunction(name: 'normalize_string_value')]
    public function normalizeStringValue(string $value): string
    {
        return $value;
    }

    #[AsTwigFilter(name: 'simple_array_to_html', isSafe: ['html'])]
    #[AsTwigFunction(name: 'simple_array_to_html', isSafe: ['html'])]
    public function simpleArrayToHtml(array $values): string
    {
        return implode('<br />', $values);
    }
}

Both old rules are removed and replaced in the twig/twig 3.21 composer-based set; their fixtures are merged into the new rule test.

Skip behaviour is unchanged: if any present get*() method cannot be fully converted (closures, injected-service callables, built-in name overrides, GlobalsInterface, token parsers, ...), the whole class is left alone, since extends AbstractExtension must stay.

…FiltersAndFunctionsToAsTwigAttributeRector

Separate rules per get*() method could never convert a class that declares more
than one of them: each rule bailed out in stillRequiresAbstractExtension(),
because the sibling method kept the class bound to AbstractExtension.

The merged rule collects conversions for getFilters(), getFunctions() and
getTests() in a single pass, validates them all, and only then drops
"extends AbstractExtension".

Also adds getTests() -> #[AsTwigTest] support, with per-method option mapping
(#[AsTwigTest] has no isSafe/preservesSafety options).
@TomasVotruba
TomasVotruba merged commit c7a7535 into main Aug 11, 2026
7 checks passed
@TomasVotruba
TomasVotruba deleted the merge-twig-attribute-rules branch August 11, 2026 09:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant