Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[DependencyInjection] Private services are not always private in dumped container #19117

Copy link
Copy link
Closed
@hhamon

Description

@hhamon
Issue body actions

While delivering a training on Symfony 3.1 last week, my students and I stumbled upon an unexpected behavior. Some private services we defined were public in the end in the final dumped container. It was possible to request them outside of the container thanks to its get() method.

Consider the following scenarii.

Use Case 1: Orphan Private Service is Removed

services:
    app.foo:
        class: Foo
        public: false

This use case works as expected. An orphan private service is always removed from the final dumped container. It can never be requested to the container.

Use Case 2 : Private Service Referenced Once is Inlined

services:
    app.bar:
        class: Bar
        arguments: ['@app.foo']
    app.foo:
        class: Foo
        public: false

This use case also works as expected. The private app.foo service is inlined in the generated getApp_BarService() method in the compiled container. The app.foo service cannot be requested outside of the container because it's inlined.

Use Case 3 : Private Service Referenced More Than Once is Shared and Public

This is the problem! Consider the following definition.

services:
    app.baz:
        class: Baz
        arguments: ['@app.foo']
    app.bar:
        class: Bar
        arguments: ['@app.foo']
    app.foo:
        class: Foo
        public: false

In this situation, the private service app.foo is referenced twice by two public services. As such the compiled container generates a protected getApp_FooService() method that will create the app.foo service on demand when its requested with the Container::get() method. The app.foo service reference will be stored in the $services array of the dumped container.

It's also possible to request this service outside of the container:

$container->get('app.foo');

The final dumped container looks like the following in my Symfony 3.1 SE project:

<?php

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\LogicException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;

/**
 * appDevDebugProjectContainer.
 *
 * This class has been auto-generated
 * by the Symfony Dependency Injection Component.
 */
class appDevDebugProjectContainer extends Container
{
    private $parameters;
    private $targetDirs = array();

    /**
     * Constructor.
     */
    public function __construct()
    {
        $dir = __DIR__;
        for ($i = 1; $i <= 5; ++$i) {
            $this->targetDirs[$i] = $dir = dirname($dir);
        }
        $this->parameters = $this->getDefaultParameters();

        $this->services = array();
        $this->methodMap = array(
            'annotation_reader' => 'getAnnotationReaderService',
            'app.bar' => 'getApp_BarService',
            'app.baz' => 'getApp_BazService',
            'app.foo' => 'getApp_FooService',
            // ...
        );
        // ...
    }

    // ...

    /**
     * Gets the 'app.foo' service.
     *
     * This service is shared.
     * This method always returns the same instance of the service.
     *
     * This service is private.
     * If you want to be able to request this service from the container directly,
     * make it public, otherwise you might end up with broken code.
     *
     * @return \Foo A Foo instance.
     */
    protected function getApp_FooService()
    {
        return $this->services['app.foo'] = new \Foo();
    }

    /**
     * Gets the 'app.bar' service.
     *
     * This service is shared.
     * This method always returns the same instance of the service.
     *
     * @return \Bar A Bar instance.
     */
    protected function getApp_BarService()
    {
        return $this->services['app.bar'] = new \Bar($this->get('app.foo'));
    }

    /**
     * Gets the 'app.baz' service.
     *
     * This service is shared.
     * This method always returns the same instance of the service.
     *
     * @return \Baz A Baz instance.
     */
    protected function getApp_BazService()
    {
        return $this->services['app.baz'] = new \Baz($this->get('app.foo'));
    }
}

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

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.