Skip to content

Controller using ServiceSubscriberTrait has service locator overwritten by service container #49382

@edsrzf

Description

@edsrzf

Symfony version(s) affected

6.2.5

Description

A controller using ServiceSubscriberTrait is wired up correctly. However, when a request is served, its service locator is overwritten by the service container. This usually causes an error when a service is retrieved from the service container, since the service names are likely to be incompatible.

How to reproduce

Repo demonstrating the issue here: https://github.com/edsrzf/symfony_reproducer

To reproduce:

  • Start the local server with symfony server:start
  • Navigate to http://127.0.0.1:8000
  • See the error: You have requested a non-existent service "App\Controller\Controller::getService". Did you mean this: "App\Controller\Controller"?

Possible Solution

The cause is this bit of code in Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver:

if (null === $previousContainer = $controller->setContainer($this->container)) {
throw new \LogicException(sprintf('"%s" has no container set, did you forget to define it as a service subscriber?', $class));
} else {
$controller->setContainer($previousContainer);
}

when combined with setContainer from ServiceSubscriberTrait:

#[Required]
public function setContainer(ContainerInterface $container): ?ContainerInterface
{
$this->container = $container;
if (method_exists(get_parent_class(self::class) ?: '', __FUNCTION__)) {
return parent::setContainer($container);
}
return null;
}

setContainer is called a total of 3 times in the process of serving a request:

  • Once by the service container, passing in the service locator. It returns null.
  • The first time by ControllerResolver, passing in the service container. The trait sets the container property, and then AbstractController returns the "previous" container property value, which is actually not the previous value. It's the service container.
  • The second time by ControllerResolver, which passes the "previous" value back in. It should be the service locator, but it's actually the service container.

It feels like the fix needs to be inside ServiceSubscriberTrait. Maybe parent::setContainer could be called before setting $this->container?

Additional Context

Workaround

The issue can be worked around by overriding setContainer, skipping the trait's method:

#[Required]
public function setContainer(ContainerInterface $container): ?ContainerInterface
{
    return parent::setContainer($container);
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions