laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.42k stars 10.99k forks source link

Defered ServiceProvider is not loaded when accessed through $app['key'] #5292

Closed ibrasho closed 10 years ago

ibrasho commented 10 years ago

I'm not sure if this happened to anyone, but trying to use $app['log'] for example in a register() method of a ServiceProvider with throw a ReflectionException.

The reason is that $app['log'] doesn't call Application::loadDeferredProvider('log') like $app->make('log') does, it just delegate the call to Container::offsetGet().

A proposed solution is to make Application extends ArrayAccess, and ensure that Application::offsetGet() load deferred providers if not loaded already.

Of course, one could use App::make in the end, but I prefer to deal with the container in a consistent manner.

Any other ideas? I have a pull request ready if that's the accepted solution.

JoostK commented 10 years ago

This can't be the cause. Container::offsetGet calls $this->make, so it must go through Application::make.

ibrasho commented 10 years ago

Yes, I've been investigating..

Turns out that $app->deferredProviders doesn't get until all eager providers are registered. Which is why trying to load a deferred provider in the register() method throws an exception.

Any possible solutions?

I'm trying to register a middleware, and I need the Monolog instance.

JoostK commented 10 years ago

You shouldn't load any dependencies from the container from register. You can of course do something like the following:

$this->app['key'] = $this->app->share(function($app)
{
    return new Class($app['log']);
});

Now the container lookup is wrapped in a closure so not immediately executed.

ibrasho commented 10 years ago

I got around this by passing the class name of the middleware, instead of an instance.

This allows the loading to happen after deferred providers have been registered.