Skip to content

[TwigBundle] Enable #[AsTwigFilter], #[AsTwigFunction] and #[AsTwigTest] attributes to configure runtime extensions #52748

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 26, 2025

Conversation

GromNaN
Copy link
Member

@GromNaN GromNaN commented Nov 26, 2023

Q A
Branch? 7.3
Bug fix? no
New feature? yes
Deprecations? no
Issues Fix #50016
License MIT

Integration for new PHP attributes introduced by twigphp/Twig#3916.

@xabbuh xabbuh modified the milestones: 7.1, 7.2 May 15, 2024
@fabpot fabpot modified the milestones: 7.2, 7.3 Nov 20, 2024
@kbond
Copy link
Member

kbond commented Jan 3, 2025

We use the existing empty interface Twig\Extension\RuntimeExtensionInterface to identify services that are registered as Twig...

This shouldn't be required?

@GromNaN

This comment was marked as outdated.

@GromNaN GromNaN force-pushed the twig-attribute branch 3 times, most recently from 8875176 to f3060c0 Compare January 4, 2025 01:27
@GromNaN
Copy link
Member Author

GromNaN commented Mar 6, 2025

PR updated. I added tests for the autoconfiguration. The service is registered as Twig runtime only when there are non-static methods.

Comment on lines +186 to +189
$container->registerAttributeForAutoconfiguration(AsTwigFilter::class, AttributeExtensionPass::autoconfigureFromAttribute(...));
$container->registerAttributeForAutoconfiguration(AsTwigFunction::class, AttributeExtensionPass::autoconfigureFromAttribute(...));
$container->registerAttributeForAutoconfiguration(AsTwigTest::class, AttributeExtensionPass::autoconfigureFromAttribute(...));

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the attribute class is not declared (Older Twig version), this will be skipped.

try {
$attributeReflector = new \ReflectionClass($attributeName);
} catch (\ReflectionException) {
continue;
}

@GromNaN GromNaN force-pushed the twig-attribute branch 2 times, most recently from 07dcb8b to 9adc528 Compare March 6, 2025 17:40
fabpot added a commit to twigphp/Twig that referenced this pull request Mar 24, 2025
…`AsTwigTest` to ease extension development (GromNaN)

This PR was squashed before being merged into the 3.x branch.

Discussion
----------

Create attributes `AsTwigFilter`, `AsTwigFunction` and `AsTwigTest` to ease extension development

One drawback to writing extensions at present is that the declaration of functions/filters/tests is not directly adjacent to the methods. It's worse for runtime extensions because they need to be in 2 different classes. See [`SerializerExtension`](https://github.com/symfony/symfony/blob/7.0/src/Symfony/Bridge/Twig/Extension/SerializerExtension.php) and [`SerializerRuntime`](https://github.com/symfony/symfony/blob/7.0/src/Symfony/Bridge/Twig/Extension/SerializerRuntime.php) as an example.

By using attributes for filters, functions and tests definition, we can make writing extensions more expressive, and use reflection to detect particular options (`needs_environment`, `needs_context`, `is_variadic`).

Example if we implemented the `formatDate` filter: https://github.com/twigphp/Twig/blob/aeeec9a5e907a79e50a6bb78979154599401726e/extra/intl-extra/IntlExtension.php#L392-L395

By using the `AsTwigFilter` attribute, it is not necessary to create the `getFilters()` method. The `needs_environment` option is detected from method signature. The name is still required as the method naming convention (camelCase) doesn't match with Twig naming convention (snake_case).

```php
use Twig\Extension\Attribute\AsTwigFilter;

class IntlExtension
{
    #[AsTwigFilter(name: 'format_date')]
    public function formatDate(Environment $env, $date, ?string $dateFormat = 'medium', string $pattern = '', $timezone = null, string $calendar = 'gregorian', string $locale = null): string
    {
        return $this->formatDateTime($env, $date, $dateFormat, 'none', $pattern, $timezone, $calendar, $locale);
    }
}
```

This approach does not totally replace the current definition of extensions, which is still necessary for advanced needs. It does, however, make for more pleasant reading and writing.

This makes writing lazy-loaded runtime extension the easiest way to create Twig extension in Symfony: symfony/symfony#52748

Related to symfony/symfony#50016

Is there any need to cache the parsing of method attributes? They are only read at compile time, but that can have a performance impact during development or when using dynamic templates.

Commits
-------

5886907 Create attributes `AsTwigFilter`, `AsTwigFunction` and `AsTwigTest` to ease extension development
@fabpot
Copy link
Member

fabpot commented Mar 24, 2025

Twig counter-part has been merged now.

@GromNaN GromNaN requested a review from stof March 24, 2025 21:59
@GromNaN GromNaN force-pushed the twig-attribute branch 2 times, most recently from b41b2ba to 7cc3401 Compare March 25, 2025 08:42
@GromNaN GromNaN added the ❄️ Feature Freeze Important Pull Requests to finish before the next Symfony "feature freeze" label Mar 25, 2025
…wigTest]` attributes to configure runtime extensions
@fabpot
Copy link
Member

fabpot commented Mar 26, 2025

Thank you @GromNaN.

@fabpot fabpot merged commit 0b026f9 into symfony:7.3 Mar 26, 2025
2 checks passed
@GromNaN GromNaN deleted the twig-attribute branch April 28, 2025 08:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature ❄️ Feature Freeze Important Pull Requests to finish before the next Symfony "feature freeze" Status: Reviewed TwigBundle
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[RFC][TwigBundle] Register custom functions & filters using an attribute
7 participants