-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[DependencyInjection] Optional class for class named services #20264
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
Conversation
I've never used autowiring, so probably the following comment is stupid but ... why configuring autowiring seems so verbose? In my imagination, this feature would work like this: framework:
# everything is autowired
autowire: true
# nothing is autowired (default value)
autowire: false |
@javiereguiluz Great suggestion but I would like to enable autowiring per file (only for services defined in this file): autowire: true
services:
Vendor\Namespace\Class: ~ |
@javiereguiluz we chosen to make autowiring an opt-in feature on a per service basis to prevent BC breaks and to force the developer to think about it before blindly enabling this feature. Also, having such global config will enable autowiring for all services defined by the framework and the bundle and it doesn't look reasonable. I think that it's important to be able to choose which services will be autowired. However, they are bundles out there allowing to enable autowiring for all services. |
continue; | ||
} | ||
|
||
foreach ($container->getOriginalIds($id) as $class) { |
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.
Do we really need the original id here? And why do we expect more variants? Imo. there are only 2 (normalized/lowercased and not normalized/original).
For PHP the case sensitivity doesnt matter anyway, ie. it could be just;
if (class_exists($id, true)) {
$definition->setClass($id);
}
Otherwise, what about using reflection to get the original class name here? Ie. if it's required for setClass
but perhaps is good for cosmetic reasons also.
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.
The problem is with composer autoloader which is case sensitive.
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 see. What about this then;
try {
$definition->setClass((new \ReflectionClass($id))->getName());
} catch (\ReflectionException $e) {
}
edit: probably the same issue right? in terms of autoloading.
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.
More variants <=> more ids are converted to one normalized form.
{ | ||
public function process(ContainerBuilder $container) | ||
{ | ||
foreach ($container->getDefinitions() as $id => $definition) { |
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.
Could be $class => $definition
to clarify.
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.
No. $id
is case insensitive, composer autoloader is case sensitive.
I think I like the idea because naming services is really hard and annoying. So when you only have a single implementation of a class, why not make id == class. For YAML it makes sense to use id -> class because the id is the key. But I wonder if it should be class -> id in XML: <service class="Vendor\Namespace\Class" /> So the ID is optional instead of the class. This might be more logical and it keeps the IDE features working like autocomplete for the class property. |
Same here :) @Tobion should the XML loader then reset the class after it has been set as id, so everything is consistently dealt by this pass? Ie. |
@hason not sure.. but what about out-of-the-box support, right here: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/DependencyInjection/ContainerBuilder.php#L762 No need for a pass. |
I agree with @Tobion here, I personally find service naming tiresome and hate above anything to realise sometimes the naming is not consistent for whatever reason, and that actually at the end of the day my names are just a normalized class name unless there is a reason to have the same class declared as a service multiple times, but in which case naming is usually easier. That said this is unrelated to the auto-wiring feature, or you could imagine making the |
@ro0NL Thanks for your suggestion. |
e1b86ab
to
5f9f981
Compare
rebase needed |
One thing to take into consideration is that right now, the DI make the service id lowercase, i.e.: services:
Foo:
class: App\Foo
foo:
class: foo both will have the same ID I don't think this is a big problem though, but I feel like this limitation should be mentioned somewhere in the doc. |
@theofidry PHP class names are not case sensitive (the PSR-0 autoloading is), so you cannot have both a But as we lowercase ids (as soon as we can), we indeed cannot guess the class from the id (at least not later than the loader level) |
Btw, the XML loader already allows to omit the id, and will generate a random id in this case (which is fine as long as you use this service only as an autowired dependency rather than injecting it or retrieving it manually) |
@nicolas-grekas Rebased. |
…wired definitions (wouterj) This PR was merged into the 3.3-dev branch. Discussion ---------- [DependencyInjection] Added a shortcut method for autowired definitions | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | todo This is a simple proposal to make adding autowired definitions to the `ContainerBuilder` a little easier. Registering autowired services in PHP code was quite verbose at the moment, while the whole point of autowiring is quick service registration. **Before** ```php $container->register('app.twig_extension', AppExtension::class) ->setAutowired(true) ->addTag('twig.extension') ; ``` **After** ```php $container->autowire('app.twig_extension', AppExtension::class) ->addTag('twig.extension') ; ``` With #20264, this will be even nicer: ```php $container->autowire(AppExtension::class) ->addTag('twig.extension') ; ``` Commits ------- 6ef4ce8 Added a shortcut method for autowired definitions
…wired definitions (wouterj) This PR was merged into the 3.3-dev branch. Discussion ---------- [DependencyInjection] Added a shortcut method for autowired definitions | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | todo This is a simple proposal to make adding autowired definitions to the `ContainerBuilder` a little easier. Registering autowired services in PHP code was quite verbose at the moment, while the whole point of autowiring is quick service registration. **Before** ```php $container->register('app.twig_extension', AppExtension::class) ->setAutowired(true) ->addTag('twig.extension') ; ``` **After** ```php $container->autowire('app.twig_extension', AppExtension::class) ->addTag('twig.extension') ; ``` With symfony/symfony#20264, this will be even nicer: ```php $container->autowire(AppExtension::class) ->addTag('twig.extension') ; ``` Commits ------- 6ef4ce8 Added a shortcut method for autowired definitions
@@ -759,6 +759,10 @@ public function setDefinition($id, Definition $definition) | |||
throw new BadMethodCallException('Adding definition to a frozen container is not allowed'); | |||
} | |||
|
|||
if (null === $definition->getClass() && class_exists($id)) { |
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.
The class_exists check will hide typos. Can't we throw instead when the class doesn't exist?
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.
also: should we allow this for abstract classes? and for synthetic services? for ChildDefinition?
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.
What about always calling something like Definition::resolve($id)
and move some logic?
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.
@nicolas-grekas The exception for an unknown (not-set) class is thrown in compiler pass.
Rethinking about this one, shouldn't this logic be handled by PhpDumper instead? |
I missed adding that ContainerBuilder shouldn't change the state of Definition objects IMHO. |
@nicolas-grekas I think the implementation cannot be changed to handle this logic in the PhpDumper actually. As mentioned in #20264 (comment), the original service id is lowercased by the On another hand, regarding #20943, @stof got a point in #20943 (comment). A compiler pass would be more suitable. |
…-grekas) This PR was merged into the 3.3-dev branch. Discussion ---------- [DI] Optional class for named services | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - Continues #20264: - makes the id-to-class mapping context-free (no `class_exist` check), - deals with ChildDefinition (which should not have this rule applied on them), - deprecates FactoryReturnTypePass as discussed on slack and reported in this comment: #19191 (comment) From @hason: > I prefer class named services (in applications) because naming things are too hard: ``` yaml services: Vendor\Namespace\Class: class: Vendor\Namespace\Class autowire: true ``` > This PR solves redundant parameter for class: ``` yaml services: Vendor\Namespace\Class: autowire: true ``` > Inspirations: https://laravel.com/docs/5.3/container, #18268, http://php-di.org/, Commits ------- a18c4b6 [DI] Add tests for class named services 71b17c7 [DI] Optional class for named services
I prefer class named services (in applications) because naming things are too hard:
This PR solves redundant parameter for class:
Inspirations: https://laravel.com/docs/5.3/container, #18268, http://php-di.org/,