Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[FrameworkBundle] call setContainer() for autowired controllers #23239

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
Jul 3, 2017

Conversation

xabbuh
Copy link
Member

@xabbuh xabbuh commented Jun 20, 2017

Q A
Branch? 3.3
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #23200, FriendsOfSymfony/FOSRestBundle#1719
License MIT
Doc PR

Previously, you either did not use controllers as services or you explicitly wired everything yourself. In case of a non-service controller the FrameworkBundle took care of calling setContainer() inside the instantiateController() method:

protected function instantiateController($class)
{
    $controller = parent::instantiateController($class);

    if ($controller instanceof ContainerAwareInterface) {
        $controller->setContainer($this->container);
    }
    if ($controller instanceof AbstractController && null !== $previousContainer = $controller->setContainer($this->container)) {
        $controller->setContainer($previousContainer);
    }

    return $controller;
}

With the new autowired controllers as services this is no longer happening as controllers do not need to be instantiated anymore (the container already returns fully built objects).

@GuilhemN
Copy link
Contributor

Isn't this missing AbstractController support?

@jvasseur
Copy link
Contributor

IMO it should be the role of the DI component to call the setContainer method, maybe by adding this magic method call behind the autoconfigure option.

@robfrawley
Copy link
Contributor

Why doesn't AbstractController implement ContainerAwareInterface?

@nicolas-grekas
Copy link
Member

Because PSR 11 isn't compatible with it.

@xabbuh
Copy link
Member Author

xabbuh commented Jun 20, 2017

The PR now handles the AbstractController class too now.

@ogizanagi
Copy link
Contributor

ogizanagi commented Jun 20, 2017

Have you already tried with the DI Extra Bundle ?
I doubt it'll work (for those using the bundle. I don't really get other broken cases), unless I missed something, as it replaces the controller resolver used and the implementation does not make use of the parent. Perhaps @bogdaniel or @trappar could confirm.

@mvrhov
Copy link

mvrhov commented Jun 21, 2017

Why do we need to inject container if the controller is autowired? If one is changing the controllers to new autowired way it should expect to remove the direct access to the container along the way.
IMO it this should be marked as deprecated since 3.4 and removed in 4.0

@ogizanagi
Copy link
Contributor

ogizanagi commented Jun 21, 2017

I agree. It also feels weird injecting it at runtime in a service. In #23200 (comment) I suggested another fix allowing the setContainer call to be properly autowired, which I guess should also work for those using the JMSDIExtraBundle. Having a controller as service with the whole container injection kind of defeat the purpose of defining it as a service. Still, there are real issues which needs to be fixed, as proven in the related issues.

@vincentpazeller
Copy link

I think the container can still be useful in a controller even with autowiring. For instance autowiring cannot be used when the service to be called is only known at runtime (from the db for instance):

public function myAction(){
$dynamicServiceName = ... 
$dynamicService = $this->container->get($dynamicServiceName)
...
}

Of course, using the container that way is not usually a good practice, but I believe that in some situations it can help reduce code size...

@xabbuh
Copy link
Member Author

xabbuh commented Jun 21, 2017

I just checked again and this is only an issue I was able to reproduce with routes registered by FOSRestBundle. The routes that are being registered then reference the controller with a pattern like AppBundle\Controller\UserController:getAction while routes registered with Symfony's annotation loader have two colons (and only in this case instantiateController() will be called and thus injecting the container).

@trappar
Copy link

trappar commented Jun 21, 2017

@ogizanagi I am 99% sure you're right about this not fixing the issues with the DI Extra Bundle, but that's probably something that needs to be addressed in that bundle, not here.

@stof
Copy link
Member

stof commented Jun 21, 2017

yeah, if JMSDIExtraBundle replaces the whole resolver without reusing the Symfony implementation through decoration or inheritance, it means they have to update their bundle each time Symfony adds a new feature. This is their issue.

@ogizanagi
Copy link
Contributor

ogizanagi commented Jun 21, 2017

But the same goes for fos rest: is it normal to use the single colon notation usually used for controller as services ?

@stof
Copy link
Member

stof commented Jun 21, 2017

@ogizanagi IIRC, maybe they wanted to define the route for container as a service.

@xabbuh
Copy link
Member Author

xabbuh commented Jun 21, 2017

@ogizanagi Actually it's one colon for services and two for controller classes that must be instantiated by the controller resolver.

@stof
Copy link
Member

stof commented Jun 21, 2017

@xabbuh no. 2 colons is not for services. It is for normal callables (as it is the syntax used for callables in PHP)

@xabbuh
Copy link
Member Author

xabbuh commented Jun 21, 2017

see FriendsOfSymfony/FOSRestBundle#1721 which could also be a fix

However, I still think that the main inconsistency is that the ControllerResolver from the FrameworkBundle is sometimes calling setContainer() even if the controller is registered as a service, but that whether or not this is done depends on the notation of the _controller attribute.

@xabbuh
Copy link
Member Author

xabbuh commented Jun 21, 2017

@stof good catch, I fixed my comment accordingly

Copy link
Contributor

@GuilhemN GuilhemN left a comment

Choose a reason for hiding this comment

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

I think this is the right fix. ContainerAwareInterface is here for something, setContainer must be called by the resolver when it is detected like what does the serializer when detecting NormalizerAwareInterface for instance.

@nicolas-grekas nicolas-grekas added this to the 3.3 milestone Jul 3, 2017
return parent::createController($controller);
$resolvedController = parent::createController($controller);

if (1 === substr_count($controller, ':') && is_array($resolvedController)) {
Copy link
Member

Choose a reason for hiding this comment

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

shouldn't this be done in the parent class instead, which is the one supporting container injection ?

Copy link
Member Author

Choose a reason for hiding this comment

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

IMO that would be inconsistent as the parent class isn't doing this for instances it creates itself either.

Copy link
Member

Choose a reason for hiding this comment

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

hmm indeed. I missed the fact that injection was done in this class in a separate method

@stof
Copy link
Member

stof commented Jul 3, 2017

hmm, actually, can we have a test covering this case to prevent regressions ?

@xabbuh
Copy link
Member Author

xabbuh commented Jul 3, 2017

I'll check

@xabbuh
Copy link
Member Author

xabbuh commented Jul 3, 2017

tests added

@nicolas-grekas
Copy link
Member

Thank you @xabbuh.

@nicolas-grekas nicolas-grekas merged commit 1d07a28 into symfony:3.3 Jul 3, 2017
nicolas-grekas added a commit that referenced this pull request Jul 3, 2017
…llers (xabbuh)

This PR was merged into the 3.3 branch.

Discussion
----------

[FrameworkBundle] call setContainer() for autowired controllers

| Q             | A
| ------------- | ---
| Branch?       | 3.3
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #23200, FriendsOfSymfony/FOSRestBundle#1719
| License       | MIT
| Doc PR        |

Previously, you either did not use controllers as services or you explicitly wired everything yourself. In case of a non-service controller the FrameworkBundle took care of calling `setContainer()` inside the `instantiateController()` method:

```php
protected function instantiateController($class)
{
    $controller = parent::instantiateController($class);

    if ($controller instanceof ContainerAwareInterface) {
        $controller->setContainer($this->container);
    }
    if ($controller instanceof AbstractController && null !== $previousContainer = $controller->setContainer($this->container)) {
        $controller->setContainer($previousContainer);
    }

    return $controller;
}
```

With the new autowired controllers as services this is no longer happening as controllers do not need to be instantiated anymore (the container already returns fully built objects).

Commits
-------

1d07a28 call setContainer() for autowired controllers
@xabbuh xabbuh deleted the issue-23200 branch July 3, 2017 14:52
@fabpot fabpot mentioned this pull request Jul 4, 2017
maff added a commit to pimcore/pimcore that referenced this pull request Jul 4, 2017
…nerAwareInterface (fixes #1666)"

This reverts commit faccea6.

Fixed in symfony/symfony#23239 (Symfony 3.3.3)
fracz added a commit to SUPLA/supla-cloud that referenced this pull request Jul 26, 2017
Upgrade symfony to 3.3.5 to fix the bug:
symfony/symfony#23239
fracz added a commit to SUPLA/supla-cloud that referenced this pull request Jul 26, 2017
Upgrade symfony to 3.3.5 to fix the bug:
symfony/symfony#23239
aquadran pushed a commit to IoTAqua/supla-cloud that referenced this pull request Aug 16, 2017
Upgrade symfony to 3.3.5 to fix the bug:
symfony/symfony#23239
fracz added a commit to SUPLA/supla-cloud that referenced this pull request Aug 17, 2017
Upgrade symfony to 3.3.5 to fix the bug:
symfony/symfony#23239
fracz added a commit to SUPLA/supla-cloud that referenced this pull request Aug 17, 2017
Upgrade symfony to 3.3.5 to fix the bug:
symfony/symfony#23239
fracz added a commit to SUPLA/supla-cloud that referenced this pull request Aug 18, 2017
Upgrade symfony to 3.3.5 to fix the bug:
symfony/symfony#23239
fracz added a commit to SUPLA/supla-cloud that referenced this pull request Aug 18, 2017
Upgrade symfony to 3.3.5 to fix the bug:
symfony/symfony#23239
fracz added a commit to SUPLA/supla-cloud that referenced this pull request Aug 18, 2017
Upgrade symfony to 3.3.5 to fix the bug:
symfony/symfony#23239
fracz added a commit to SUPLA/supla-cloud that referenced this pull request Aug 18, 2017
Upgrade symfony to 3.3.5 to fix the bug:
symfony/symfony#23239
fracz added a commit to SUPLA/supla-cloud that referenced this pull request Aug 18, 2017
Upgrade symfony to 3.3.5 to fix the bug:
symfony/symfony#23239
fracz added a commit to SUPLA/supla-cloud that referenced this pull request Aug 18, 2017
Upgrade symfony to 3.3.5 to fix the bug:
symfony/symfony#23239
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.