auraphp / Aura.Di

Dependency Injection System
MIT License
349 stars 63 forks source link

Cannot modify container when locked #118

Closed fredw closed 8 years ago

fredw commented 8 years ago

Hi,

I have a problem/doubt about locked container, I don't understand the functioning of this issue. I'm trying to set after call a get and an exception is thrown:

Fatal error: Uncaught exception 'Aura\Di\Exception\ContainerLocked' with message 'Cannot modify container when locked.' 

I'm trying to do something like this:

$container = (new Aura\Di\ContainerBuilder)->newInstance();

$container->set('object1', new stdClass);
var_dump($container->isLocked());
$container->get('object1');
var_dump($container->isLocked());
$container->set('object3', new stdClass); // Exception thrown
pmjones commented 8 years ago

Hi @fredw -- this has been a subject of some discussion. Short version: calling get() or newInstance() locks the container automatically, because further modifications to $params and $setters after creating a concrete object instance are not honored. Thus, the container is locked to prevent making changes that won't be recognized.

Although the docs say the container will be locked after get/newInstance(), I went back and forth on how to prevent changes to the container that would not be honored. Perhaps the right thing to do is throw an exception when get/newInstance are called and the container is not locked.

To pre-empt one objection: yes, the container should be locked and prevent changes after initial setup. The idea is to do all setup work before creating any objects.

Thoughts?

harikt commented 8 years ago

Related issue https://github.com/auraphp/Aura.Di/issues/107 and discussion in groups https://groups.google.com/d/msg/auraphp/kOunjCihqe4/gLQtqNt6CwAJ

fredw commented 8 years ago

Now I understand the reason of lock, but I have some problems that I don't know how to handle with. I have just read the issue #107, but still don't know how to act.

I'm using Slim as router and I'm implementing middlewares to overwrite the 'errorHandler' service. Some like this:

$container = (new Aura\Di\ContainerBuilder)->newInstance();
$app = new Slim\App($container);
$app->add(new MyMiddleware($app));

// Routes - Iternally Slim calls $this->container->get('router'), 
// which causes the container to be locked
$app->get('/', function () { /* ... */ }); 

// Here all middlewares are invoked
$app->run(); 

Here is my middleware class:

class MyMiddleware
{
    private $app;

    public function __construct(Slim\App $app)
    {
        $this->app = $app;
    }

    public function __invoke($req, $resp, $next)
    {
        // ...
        $container = $this->app->getContainer();
        // Here the exception is thrown, because the container is locked
        $container->set('errorHandler', function () {});
        // ...
    }
}

I realy would like to use Aura.DI, replacing the Pimple which is the standard Slim container. Sorry for post this particular type of question here, but I would like to know the best way to handle this situation.

harikt commented 8 years ago

@fredw move $container->set('errorHandler', function () {}); to outside.

So you can set the container definitions before calling it. That means

$container = (new Aura\Di\ContainerBuilder)->newInstance();

// move all container definitions here

$app = new Slim\App($container);

You don't need to set an error handler on execution MyMiddleware .

I suggest you to visit http://auraphp.com/packages/Aura.Di/config.html and see all examples.

Thank you.

pmjones commented 8 years ago

Yes, @harikt has the right answer here. The intent behind Aura.Di is to use it as a Dependency Injection container, not as a service locator. Among other things, that means you configure the container in advance, and then pull objects out of it. Essentially that means putting your configurations in a config section of the application, not mixing configuration and creation.

Does that help at all?

fredw commented 8 years ago

Thank you for response. I had some misconceptions about Dependency Injection and Service Locator. Now I can understand what I'm doing wrong.