Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork9.7k
[DependencyInjection] Autoconfigurable attributes on methods, properties and parameters#42039
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
ccc99fe toba31f27CompareUh oh!
There was an error while loading.Please reload this page.
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
nicolas-grekas commentedJul 9, 2021
Scanning all methods of all autoconfigured services might be a perf hog on real apps. |
ruudk commentedJul 10, 2021
@nicolas-grekas Not sure how toproperly benchmark this, but I used a Benchmark codeprivate Stopwatch$stopwatch;private int$classes =0;private int$methods =0;publicfunction__destruct() {dump(sprintf('Class attributes took %f ms for %d classes',$this->stopwatch->getEvent('class')->getDuration(),$this->classes ));dump(sprintf('Method attributes took %f ms for %d classes and total of %d methods',$this->stopwatch->getEvent('method')->getDuration(),$this->classes,$this->methods )); }protectedfunctionprocessValue($value,bool$isRoot =false) {// ...$this->stopwatch->start('class'); ++$this->classes;foreach ($reflector->getAttributes()as$attribute) {if ($configurator =$autoconfiguredAttributes[$attribute->getName()] ??null) {$configurator($conditionals,$attribute->newInstance(),$reflector); } }$this->stopwatch->stop('class');$this->stopwatch->start('method');foreach ($reflector->getMethods()as$method) { ++$this->methods;foreach ($method->getAttributes()as$attribute) {if ($configurator =$autoconfiguredAttributes[$attribute->getName()] ??null) {$configurator($conditionals,$attribute->newInstance(),$method); } } }$this->stopwatch->stop('method');// ... } Not sure if this is acceptable and how it can be improved. |
nicolas-grekas commentedJul 10, 2021
Thanks for the bench, this looks acceptable! |
nicolas-grekas left a comment
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.
I'd suggest extending this to properties and parameters.
In order to play safe and optimize perf a bit, I'd suggest doing some reflection in theprocess() method to classify configurators in 4 groups: the ones that work on classes (ie the ones that have only two arguments or that have a 3rd arg that accept ReflectionClass), the ones that work on methods, properties, and parameters. If a group is empty, we would skip scanning for the corresponding type of attributes.
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
ruudk commentedJul 10, 2021 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@nicolas-grekas I applied this feedback and pushed a WIP commit. Since Attributes only work on PHP 8 I thought it would be nice to support Union types when you want to register an attribute for multiple methods. So for my example of the This works, but it will produce a fatal error on PHP 7.4 because it doesn't support unions there. How can we fix that? I'm also not sure how Todo
|
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.phpShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.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/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
nicolas-grekas left a comment
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.
LGTM, I only have 2 minor comments.
Thanks for completing this quickly!
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.php OutdatedShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
6911e60 toc532028Comparec532028 tob449833Compareb449833 to917fcc0Compareruudk commentedAug 27, 2021
Much better! |
fabpot commentedAug 27, 2021
@ruudk Can you work on a PR for the docs, or at least describe the feature with examples in the PR description? Thank you. |
src/Symfony/Component/DependencyInjection/Compiler/AttributeAutoconfigurationPass.phpShow resolvedHide resolved
Uh oh!
There was an error while loading.Please reload this page.
| foreach ($reflector->getAttributes()as$attribute) { | ||
| if ($configurator =$autoconfiguredAttributes[$attribute->getName()] ??null) { | ||
| $configurator($conditionals,$attribute->newInstance(),$reflector); | ||
| $conditionals =$instanceof[$classReflector->getName()] ??newChildDefinition(''); |
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.
I would also put all of this new code into a new method
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.
Will it really make it more clear? I don't agree, but if you give me a name for the method I'll do the change.
Tobion left a comment
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.
I like it now. It will probably have some performance impact on container building. But I think using attributes for this might be the future. So there is no way around it. This will probably replace the marker interface solutions currently in place when there are such attributes as alternatives.
ruudk commentedAug 28, 2021
@fabpot I updated the PR description and added a few examples. Hope this helps! |
fabpot commentedAug 30, 2021
That's perfect, thank you. |
fabpot commentedAug 30, 2021
Thank you@ruudk. |
This PR was merged into the 6.0 branch.Discussion----------remove unneeded eval given PHP 8 in 6.0| Q | A| ------------- | ---| Branch? | 6.0| Bug fix? | no| New feature? | no <!-- please update src/**/CHANGELOG.md files -->| Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->| Tickets || License | MIT| Doc PR |Remove eval() added in#42039 for 6.0Commits-------1a3ec8c remove unneeded eval given PHP 8 in 6.0
ruudk commentedDec 14, 2021 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
@nicolas-grekas This PHPStan annotation doesn't work as we expect it to work
How can we solve this? $container->registerAttributeForAutoconfiguration( WithFlow::class,staticfunction (ChildDefinition$definition,WithFlow$attribute,ReflectionMethod$reflector) :void {$definition->addTag(ServiceTag::WITH_FLOW, ['flow' =>$attribute->getFlowName(), ]); } ); gives this error: |
nicolas-grekas commentedDec 14, 2021 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I'm not sure phpstan supports any simple way to express the closures we support here. Ignoring the error might be just fine. |
Uh oh!
There was an error while loading.Please reload this page.
Introduction
#39897 introduced the possibility auto configure classes that were annotated with attributes:
This works great. But it only works when the attribute is added on the class.
With this PR, it's now possible to also auto configure methods, properties and parameters.
How does it work?
Let's say you have an attribute that targets classes and methods like this:
You have two services that use them:
You can now use
registerAttributeForAutoconfigurationin your extension, together with a union of the types that you want to seach for. In this example, the extension only cares for classes and methods, so it uses\ReflectionClass|\ReflectionMethod $reflector:This will tag
MyServicewithmy.tagand it will tagMyOtherServicewithmy.tag, method: myMethodIf the extension also wants to target the properties that are annotated with attributes, it can either change the union to
\ReflectionClass|\ReflectionMethod|\ReflectionProperty $reflectoror it can just use\Reflector $reflectorand do the switching in the callable.Another example
Let's say you have an attribute like this:
and you use it like this:
you'll get an error saying that
ReflectionMethodis not possible as the attribute only targets classes.