Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork9.6k
[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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/RuntimeLoaderPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/RuntimeLoaderPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/RuntimeLoaderPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
This shouldn't be required? |
This comment was marked as outdated.
This comment was marked as outdated.
8875176
tof3060c0
ComparePR updated. I added tests for the autoconfiguration. The service is registered as Twig runtime only when there are non-static methods. |
$container->registerAttributeForAutoconfiguration(AsTwigFilter::class, AttributeExtensionPass::autoconfigureFromAttribute(...)); | ||
$container->registerAttributeForAutoconfiguration(AsTwigFunction::class, AttributeExtensionPass::autoconfigureFromAttribute(...)); | ||
$container->registerAttributeForAutoconfiguration(AsTwigTest::class, AttributeExtensionPass::autoconfigureFromAttribute(...)); | ||
There was a problem hiding this comment.
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.
symfony/src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php
Lines 58 to 62 in6a029ec
try { | |
$attributeReflector =new \ReflectionClass($attributeName); | |
}catch (\ReflectionException) { | |
continue; | |
} |
07dcb8b
to9adc528
Compare…`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 developmentOne 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-L395By 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).```phpuse 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#52748Related tosymfony/symfony#50016Is 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
Twig counter-part has been merged now. |
b41b2ba
to7cc3401
Compare…wigTest]` attributes to configure runtime extensions
Thank you@GromNaN. |
0b026f9
intosymfony:7.3Uh oh!
There was an error while loading.Please reload this page.
…with both `ExtensionInterface` and Twig callable attribute (GromNaN)This PR was merged into the 7.3 branch.Discussion----------[TwigBundle] Improve error when autoconfiguring a class with both `ExtensionInterface` and Twig callable attribute| Q | A| ------------- | ---| Branch? | 7.3| Bug fix? | no| New feature? | no| Deprecations? | no| Issues | -| License | MITReported by Javier:> I'm updating Twig extensions to use the `#[AsTwig...]` attributes. I removed the `getFunctions()` method and had this:> ```> class FooExtension extends AbstractExtension> {> #[AsTwigFunction('name_of_function')]> public function foo(): string> {> // ...> }> }> ```> Then I saw this exception: `Unable to register extension "App\Twig\FooExtension" as it is already registered.`> My error is that I forgot to remove extends `AbstractExtension`But I think that Symfony's error exception should be more helpful. A quick example of something that could've helped me: *"When using the AsTwigFunction attribute, your Twig extension cannot extend AbstractExtension"*The ExtensionSet exception is thrown because the same an extension is registered twice:1. It has the `twig.extension` tag autoconfigured because it implements `Twig\Extension\ExtensionInterface`2. An `AttributeExtension` is registered for this class because it as one of the extension attributes (since#52748)Previous exception when `twig` service is instanciated:> LogicException : Unable to register extension "Symfony\Bundle\TwigBundle\Tests\Functional\InvalidExtensionWithAttributes" as it is already registered.New exception during container compilation:> Symfony\Component\DependencyInjection\Exception\LogicException : The class "Symfony\Bundle\TwigBundle\Tests\Functional\InvalidExtensionWithAttributes" cannot both extend "Twig\Extension\AbstractExtension" and use the "#[Twig\Attribute\AsTwigFilter]" attribute on method "funFilter()", choose one or the other.Checking `AbstractExtension` makes the error message more specific to the code, as most extensions extend this class and doesn't implement the interface directly.Commits-------752b41d [TwigBundle] Improve error when autoconfiguring a class with both ExtensionInterface and Twig callable attribute
Uh oh!
There was an error while loading.Please reload this page.
Integration for new PHP attributes introduced bytwigphp/Twig#3916.