Skip to content

[DI] [DX] Suggest what to do when container fails to autowire an inteface #22148

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

sustmi
Copy link
Contributor

@sustmi sustmi commented Mar 24, 2017

Q A
Branch? master
Bug fix? no
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets none
License MIT
Doc PR none

Lets consider this example files:

class Service1 {
    public function __construct(MyInterface $my) {}
}
class Service2 implements MyInterface {}

If you have the service Service2 defined before Service1 in your services.yml file then everything is OK.
But if you have Service1 first and Service2 second you will get the following exception:

[Symfony\Component\DependencyInjection\Exception\RuntimeException]
Cannot autowire argument $my of method AppBundle\Model\Service1::__construct() for service "my_service1": No services were found matching the "AppBundle\Model\MyInterface" interface and it cannot be auto-registered.

Another example: The same happens if you do not have Service2 in your services.yml and you want to autowire this:

class Service3 {
    // Note that the interface is to be autowired before the service implementing the interface
    public function __construct(MyInterface $my1, Service2 $my2)
}

After this PR the exception message changes to:

[Symfony\Component\DependencyInjection\Exception\RuntimeException]
Cannot autowire argument $my1 of method AppBundle\Model\Service3::__construct() for service "my_service3": No services were found matching the "AppBundle\Model\MyInterface" interface and it cannot be auto-registered. Make sure that a service implementing "AppBundle\Model\MyInterface" interface is registered in the container before registering service "my_service3".

Notice the last sentence. It suggests the developer what is the common case for this mistake and what can be the solution.

UPDATE: The first described example actually does not cause any problem. See https://github.com/sustmi/symfony-standard/tree/autowiring-order-dependent for another example that presents the problem.

@carsonbot carsonbot added Status: Needs Review DependencyInjection DX DX = Developer eXperience (anything that improves the experience of using Symfony) labels Mar 24, 2017
@sustmi sustmi force-pushed the clarify-service-not-found-message-in-autowire-pass branch from 530541b to 7052cc3 Compare March 24, 2017 13:34
@sustmi
Copy link
Contributor Author

sustmi commented Mar 24, 2017

(Sorry for the push-force. There were some typos. Now it should be OK.)

@theofidry
Copy link
Contributor

Isn't the autowiring pass ran much later to avoid those issues?

@stof
Copy link
Member

stof commented Mar 24, 2017

the order of service definition should not matter, as this code runs in a compiler pass, so after the whole file is loaded

@sustmi
Copy link
Contributor Author

sustmi commented Mar 24, 2017

In Symfony 3.2.6 the order of service definition matters. I tested it. You can try it on the examples I posted in PR.

@theofidry
Copy link
Contributor

@sustmi I'm using 3.2.6 and I don't have this issue. Could you try to reproduce it in a fork of the Standard Edition?

@nicolas-grekas
Copy link
Member

Can you please try #22135?
Does it fix your issue?

@nicolas-grekas nicolas-grekas added this to the 3.3 milestone Mar 24, 2017
@sustmi
Copy link
Contributor Author

sustmi commented Mar 24, 2017

@theofidry: I am sorry, the example in the PR is probably a bad one.
However here is a branch with an evidence that autowiring does depend on order of definitions: https://github.com/sustmi/symfony-standard/tree/autowiring-order-dependent

Try running php bin/console server:run. It will fail to compile the container.
Then swap services in services.yml and try it again (str_trans before str_trans2). It will work.

@sustmi
Copy link
Contributor Author

sustmi commented Mar 24, 2017

@nicolas-grekas: When I run the new example (https://github.com/sustmi/symfony-standard/tree/autowiring-order-dependent) on Symfony 3.2.6 the error message is:

Unable to autowire argument of type "AppBundle\Model\RepositoryInterface" for the service "str_trans2". No services were found matching this interface and it cannot be auto-registered.

while when I update Symfony to your PR (d7557cf) the message is following:

Cannot autowire service "str_trans2": no services were found matching the "AppBundle\Model\RepositoryInterface" interface and it cannot be auto-registered for argument $myRepository of method AppBundle\Model\String\StringTransformer2::__construct().

Both does not tell anything that would make developer think about the order of definitions.
But I guess by the other comments that the dependency on order is more a bug than an undocumented feature.
Can anyone confirm that?

…rface

Lets consider this example (two php files):
---
class Service1 {
    public function __construct(MyInterface $my) ...
}

class Service2 implements MyInterface {}
---

If you have the service Service2 defined before Service1 in your services.yml file then everything is OK.
But if you have Service1 first and Service2 second you will get the following exception:
---
[Symfony\Component\DependencyInjection\Exception\RuntimeException]
Cannot autowire argument $my of method AppBundle\Model\Service1::__construct() for service "my_service1": No services were found matching the "AppBundle\Model\MyInterface" interface and it cannot be auto-registered.
---

The same happens if you do not have Service2 in your services.yml and you want to autowire this:
---
class Service3 {
    // Note that the interface is to be autowired before the service implementing the interface
    public function __construct(MyInterface $my1, Service2 $my2)
}
---

After this commit the exception message changes to:
---
[Symfony\Component\DependencyInjection\Exception\RuntimeException]
Cannot autowire argument $my1 of method AppBundle\Model\Service3::__construct() for service "my_service3": No services were found matching the "AppBundle\Model\MyInterface" interface and it cannot be auto-registered. Make sure that a service implementing "AppBundle\Model\MyInterface" interface is registered in the container before registering service "my_service3".
---
Notice the last sentence. It suggests the developer what is the common case for this mistake and what can be the solution.
@sustmi sustmi force-pushed the clarify-service-not-found-message-in-autowire-pass branch from 7052cc3 to 749d14e Compare March 24, 2017 23:40
@sustmi
Copy link
Contributor Author

sustmi commented Mar 24, 2017

Meanwhile I updated the PR for the current master.

@sustmi
Copy link
Contributor Author

sustmi commented Mar 25, 2017

To clarify the issue the problem with wrong order happens when DIC has to auto-register a class and not when all services are registered explicitly in services.yml.

The working example (StringTransformer before StringTransformer2) has following flow:

  1. AutowirePass processes StringTransformer
  2. It finds that it has one dependency - MyRepository
  3. It looks into definitions found in services.yml and does not see this class
  4. It auto-registers MyRepository and then resolves the dependency for StringTransformer
  5. Then AutowirePass processes StringTransformer2
  6. It finds that it has one dependency - RepositoryInterface
  7. It sees that there is an implementation of this interface already registered so it resolves the dependency

The not working example (StringTransformer2 before StringTransformer) has following flow:

  1. AutowirePass processes StringTransformer2
  2. It finds that it has one dependency - RepositoryInterface
  3. It looks into definitions found in services.yml and sees nothing that implements RepositoryInterface so it fails (MyRepository was not auto-registered yet because no service depending on this class was processed yet).

@nicolas-grekas
Copy link
Member

I confirm it's a bug. Not a trivial one to fix I'd say. Can you open an issue please, then I think we can close this PR.

@sustmi
Copy link
Contributor Author

sustmi commented Mar 25, 2017

@nicolas-grekas: I opened a new issue #22162 as you advised and now I am closing this PR.

@sustmi sustmi closed this Mar 25, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
DependencyInjection DX DX = Developer eXperience (anything that improves the experience of using Symfony) Status: Needs Review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants