In a fresh Laravel build, I cannot get overridden IoC bindings to work everywhere in the application.
Suppose a service provider that overrides a core class, e.g. cache:
class NewServiceProvider extends ServiceProvider
{
protected $defer = true;
public function register()
{
$this->app->singleton('cache', function($app) {
return new \stdClass; // demo purpose
});
}
public function provides()
{
return ['cache'];
}
}
The provider is then added at the bottom of app.providers
config.
Now modify routes.php
to the following and go check the result:
Route::get('/', function () {
dd(app('cache'));
});
// Results in an empty stdClass being shown. It works!
However, fire up artisan tinker
and do the same:
$ php artisan tinker
>>> app('cache')
=> Illuminate\Cache\CacheManager
Suddenly the override isn't working anymore...
The same behavior is encountered when processing event listeners...
Is this normal behavior and am I overlooking something? Or is this some kind of bug?
I managed to track down the issue myself.
It seems Artisan loads all deferred providers at once using a key-sorted providers array:
...
"cache" => NewServiceProvider,
"cache.store" => CacheServiceProvider,
...
As you can see, the cache.store
binding calls the built-in CacheServiceProvider
afterwards and hence renders
our cache
binding useless since it contains the binding that we need to override).
So I'm obliged to have NewServiceProvider
extend CacheServiceProvider
, and call parent::register()
to have the providers array converted to:
...
"cache" => NewServiceProvider,
"cache.store" => NewServiceProvider,
...
This seems like the only way to have the overridden cache binding being resolved correctly in Artisan.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With