8000 [FrameworkBundle] Controller resolver doesn't inject container for invokable controller (no specified method in route) · Issue #31271 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[FrameworkBundle] Controller resolver doesn't inject container for invokable controller (no specified method in route) #31271

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

Closed
andrew-demb opened this issue Apr 26, 2019 · 10 comments

Comments

@andrew-demb
Copy link
Contributor
andrew-demb commented Apr 26, 2019

Symfony version(s) affected: 3.4.26

Description

Got fatal Call to a member function has() on null due to not injected container (service locator) in controller, extended from Symfony\Bundle\FrameworkBundle\Controller\AbstractController.
Actual when in route I use DI service id for invokable controller.

How to reproduce

Create controller

<?php

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class IndexController extends AbstractController
{
    public function __invoke()
    {
        $hasRouter = $this->has('router');

        $response = $hasRouter ? 'router exists' : 'router not found';

        return new Response($response);
    }
}

Register in services:

services:
    app.index_controller:
        class: IndexController
        public: true

Use in route:

index:
    path: /
    controller: app.index_controller

Go to / path.

Possible Solution
Change if statement to work with route without :-notation, and not only with array-callables.

if (1 === substr_count($controller, ':') && \is_array($resolvedController)) {

Workaround for me:

index:
    path: /
    controller: app.index_controller:__invoke

Additional context

@xabbuh
Copy link
Member
xabbuh commented Apr 26, 2019

If your controller is registered as a service, you either need to add the setContainer() call explicitly or mark your service as autowired. Both does not seem to be the case here.

@andrew-demb
Copy link
Contributor Author
andrew-demb commented Apr 26, 2019

But why I meet another behavior for a configured controller in route with ...:__invoke?

Let's make behavior consistent.
UPD: or more clear the process, when the container will be injected automatically.

@Tobion
Copy link
Contributor
Tobion commented May 6, 2019

Closing as this is a new feature that has been implemented for consistency in #26085 already

@Tobion Tobion closed this as completed May 6, 2019
@Tobion
Copy link
Contributor
Tobion commented May 6, 2019

Beware that auto-injection of the container is also deprecated in #27462. So you define it as a service subscriber, e.g. by setting autoconfigure: true

@andrew-demb
Copy link
Contributor Author

Thanks for explanation.

@andrew-demb
Copy link
Contributor Author

I cannot avoid the problem with not injected container for invokable controllers, even if I follow docs recommendations (add autoconfigure or manual tag with controller.service_arguments & container.service_subscriber).

I create reproduce app with more details here https://github.com/andrew-demb/sf-31271-inject-container-in-invokable-controller.

@andrew-demb
Copy link
Contributor Author

Problem can be solved in my side by three ways:

  1. add autowire: true to controller services;
  2. add calls: [ ['setContainer', '@service_container'] ];
  3. in route definition always specify method controller:__invoke for fwbundle controller resolver.

@andrew-demb
Copy link
Contributor Author

In my opinion issue should be reopened, because following docs doesn't clear way to use invokable controllers without investigation about autowire or controller resolver workflow.

@Tobion
Copy link
Contributor
Tobion commented May 28, 2019

@andrew-demb setting autowire and autoconfigure both to true seems like the right solution because the AbstractController::setContainer need to be wired for the service subscriber to do it's work. See

if ($value instanceof Reference && $this->serviceLocator && \in_array((string) $value, [ContainerInterface::class, ServiceProviderInterface::class], true)) {
return new Reference($this->serviceLocator);
}

Which docs, that you are talking about, are wrong?

@andrew-demb
Copy link
Contributor Author
andrew-demb commented May 28, 2019

@Tobion, you won, I cannot prevent adding autowire or manually add method call in DI.

Sorry for "falsy"-issue. Reasons weren't clear for me before.

References to docs, where I would like to see mentions about required autowire or explicit method call with setContainer.
https://symfony.com/doc/current/security/form_login.html#form-login-setup
https://symfony.com/doc/current/service_container/3.3-di-changes.html#controllers-are-registered-as-services

P. S. Is in the sentence below explained about required container injection? (https://symfony.com/doc/current/service_container/3.3-di-changes.html#step-2-using-class-service-id-s)
"NOTE
If you get rid of deprecations and make your controllers extend from AbstractController instead of Controller, you can skip the rest of this step because AbstractController doesn't provide a container where you can get the services from. All services need to be injected as explained in the step 5 of this article."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants
0