New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weird service provider behaviour #2760

Closed
anlutro opened this Issue Nov 19, 2013 · 20 comments

Comments

Projects
None yet
8 participants
@anlutro
Contributor

anlutro commented Nov 19, 2013

I have no idea where to start debugging this and am not sure if it is reproducible, but had the issue myself and someone else had the same problem on IRC so thought I'd open an issue.

Sometimes a service provider simply will not get registered - adding die('debug'); to the register or boot method will not do anything. The only way to get it to load is to change the order of providers in app/config/app.php. Once you've changed it and reloaded the app, you can put the providers back in the order they were before.

This happened to me on Debian, Apache, PHP5.4 Laravel 4.1, and someone else had the same issue on Windows, FastCGI 5.5 Laravel 4.0, so it's not opcache related or PHP version specific.

Opening this issue in case someone has similar problems, and perhaps to find out where to start looking for solutions/ways to reproduce.

@bencorlett

This comment has been minimized.

Show comment
Hide comment
@bencorlett

bencorlett Nov 19, 2013

Contributor

I've had this before. I tracked it down (roughly) to a fatal error occurring before the services JSON cache (in storage/meta) was able to be written or read... It was like a year ago so that may not be exactly, but it was along those lines.

Contributor

bencorlett commented Nov 19, 2013

I've had this before. I tracked it down (roughly) to a fatal error occurring before the services JSON cache (in storage/meta) was able to be written or read... It was like a year ago so that may not be exactly, but it was along those lines.

@lsjroberts

This comment has been minimized.

Show comment
Hide comment
@lsjroberts

lsjroberts Nov 19, 2013

I had the exact same issue today, and it as caused by a fatal error as @bencorlett mentioned. An easy way to check is to run php artisan which will output the error instead of hiding it.

lsjroberts commented Nov 19, 2013

I had the exact same issue today, and it as caused by a fatal error as @bencorlett mentioned. An easy way to check is to run php artisan which will output the error instead of hiding it.

@crynobone

This comment has been minimized.

Show comment
Hide comment
@crynobone

crynobone Nov 20, 2013

Contributor

The only way to get it to load is to change the order of providers in app/config/app.php. Once you've changed it and reloaded the app, you can put the providers back in the order they were before.

You could also run php artisan clear-compiled which would delete the meta file. https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/Console/ClearCompiledCommand.php#L30 (work both in 4.0 and 4.1).

Contributor

crynobone commented Nov 20, 2013

The only way to get it to load is to change the order of providers in app/config/app.php. Once you've changed it and reloaded the app, you can put the providers back in the order they were before.

You could also run php artisan clear-compiled which would delete the meta file. https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/Console/ClearCompiledCommand.php#L30 (work both in 4.0 and 4.1).

@crynobone

This comment has been minimized.

Show comment
Hide comment
@crynobone

crynobone Nov 20, 2013

Contributor

Sometimes a service provider simply will not get registered - adding die('debug'); to the register or boot method will not do anything.

I'm not sure what's the cause since I don't have the replication step. But as far as I know, the meta/services.json file would only be re-evaluated when there is changes to app/config/app.php https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/ProviderRepository.php#L133 and not changes in the service provider itself.

Possible issue:

  1. Changing deferred value.
  2. Adding or removing provides on deferred service provider.
Contributor

crynobone commented Nov 20, 2013

Sometimes a service provider simply will not get registered - adding die('debug'); to the register or boot method will not do anything.

I'm not sure what's the cause since I don't have the replication step. But as far as I know, the meta/services.json file would only be re-evaluated when there is changes to app/config/app.php https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/ProviderRepository.php#L133 and not changes in the service provider itself.

Possible issue:

  1. Changing deferred value.
  2. Adding or removing provides on deferred service provider.
@frodeknutsen

This comment has been minimized.

Show comment
Hide comment
@frodeknutsen

frodeknutsen Nov 20, 2013

I don't think a fatal error is causing it since other service providers are registered and booted just fine.

frodeknutsen commented Nov 20, 2013

I don't think a fatal error is causing it since other service providers are registered and booted just fine.

@anlutro

This comment has been minimized.

Show comment
Hide comment
@anlutro

anlutro Nov 20, 2013

Contributor

I personally didn't have any fatal errors. Everything worked fine, the service provider simply wasn't loading until I changed the order of providers in app/config/app.php.

Contributor

anlutro commented Nov 20, 2013

I personally didn't have any fatal errors. Everything worked fine, the service provider simply wasn't loading until I changed the order of providers in app/config/app.php.

@bencorlett

This comment has been minimized.

Show comment
Hide comment
@bencorlett

bencorlett Nov 20, 2013

Contributor

Did your app still show routes or did it return a blank screen (until you changed the order)?
On 20 Nov 2013, at 7:14 pm, Andreas Lutro notifications@github.com wrote:

I personally didn't have any fatal errors. Everything worked fine, the service provider simply wasn't loading until I changed the order of providers in app/config/app.php.


Reply to this email directly or view it on GitHub.

Contributor

bencorlett commented Nov 20, 2013

Did your app still show routes or did it return a blank screen (until you changed the order)?
On 20 Nov 2013, at 7:14 pm, Andreas Lutro notifications@github.com wrote:

I personally didn't have any fatal errors. Everything worked fine, the service provider simply wasn't loading until I changed the order of providers in app/config/app.php.


Reply to this email directly or view it on GitHub.

@frodeknutsen

This comment has been minimized.

Show comment
Hide comment
@frodeknutsen

frodeknutsen Nov 20, 2013

I don't have any routes in my service provider yet, but I'm providing a few drivers for Auth and Cache. Since the provider is never registered/booted the application fails with an exception saying the driver is not supported. At this point, the service provider architecture seems a bit flaky to me. Just randomly changing the order of the providers array causes my service provider to register/boot as normal. But just a commit from another developer can cause this provider to not boot again.

frodeknutsen commented Nov 20, 2013

I don't have any routes in my service provider yet, but I'm providing a few drivers for Auth and Cache. Since the provider is never registered/booted the application fails with an exception saying the driver is not supported. At this point, the service provider architecture seems a bit flaky to me. Just randomly changing the order of the providers array causes my service provider to register/boot as normal. But just a commit from another developer can cause this provider to not boot again.

@frodeknutsen

This comment has been minimized.

Show comment
Hide comment
@frodeknutsen

frodeknutsen Nov 20, 2013

So far none of these methods fixes the problem:

  • Deleting the manifest file (storage/meta/services.json)
  • artisan clear-compiled
  • artisan optimize
  • artisan dump-autoload
  • composer update
  • composer dump-autoload

But as said earlier, randomly changing the order of the providers array fixes it.

frodeknutsen commented Nov 20, 2013

So far none of these methods fixes the problem:

  • Deleting the manifest file (storage/meta/services.json)
  • artisan clear-compiled
  • artisan optimize
  • artisan dump-autoload
  • composer update
  • composer dump-autoload

But as said earlier, randomly changing the order of the providers array fixes it.

@franzliedke

This comment has been minimized.

Show comment
Hide comment
@franzliedke

franzliedke Nov 20, 2013

Contributor

So you're registering a new auth driver in the register() method? That belongs in boot(), because only then you can be sure that the "auth" service has already been registered. If you keep the code in the register() method, you'll have to make sure the order is correct...

Contributor

franzliedke commented Nov 20, 2013

So you're registering a new auth driver in the register() method? That belongs in boot(), because only then you can be sure that the "auth" service has already been registered. If you keep the code in the register() method, you'll have to make sure the order is correct...

@frodeknutsen

This comment has been minimized.

Show comment
Hide comment
@frodeknutsen

frodeknutsen Nov 20, 2013

No, the auth/cache drivers are loaded in boot().

frodeknutsen commented Nov 20, 2013

No, the auth/cache drivers are loaded in boot().

@franzliedke

This comment has been minimized.

Show comment
Hide comment
@franzliedke

franzliedke Nov 20, 2013

Contributor

Then what do you have in the register() method?

Contributor

franzliedke commented Nov 20, 2013

Then what do you have in the register() method?

@frodeknutsen

This comment has been minimized.

Show comment
Hide comment
@frodeknutsen

frodeknutsen Nov 20, 2013

$this->app['new.service.thingy'] = $this->app->share(function() { return new ThingyDingy; });

frodeknutsen commented Nov 20, 2013

$this->app['new.service.thingy'] = $this->app->share(function() { return new ThingyDingy; });

@frodeknutsen

This comment has been minimized.

Show comment
Hide comment
@frodeknutsen

frodeknutsen Nov 20, 2013

Basicly just registering the services. The provides() reflects these services. I've done this after the book, so it should be just fine. The problem is that my service provider is just randomly not being executed.

frodeknutsen commented Nov 20, 2013

Basicly just registering the services. The provides() reflects these services. I've done this after the book, so it should be just fine. The problem is that my service provider is just randomly not being executed.

@anlutro

This comment has been minimized.

Show comment
Hide comment
@anlutro

anlutro Nov 20, 2013

Contributor

I've been able to semi-reproduce this - the problem seems to happen if you have a provider with $defer = true but then you change it to false. You'll need to refresh the services JSON file somehow - artisan clear-compiled does this on my end, but if @frodeknutsen can't get it to register even after clear-compiled, I can't reproduce that step.

Contributor

anlutro commented Nov 20, 2013

I've been able to semi-reproduce this - the problem seems to happen if you have a provider with $defer = true but then you change it to false. You'll need to refresh the services JSON file somehow - artisan clear-compiled does this on my end, but if @frodeknutsen can't get it to register even after clear-compiled, I can't reproduce that step.

@taylorotwell

This comment has been minimized.

Show comment
Hide comment
@taylorotwell

taylorotwell Nov 20, 2013

Member

If you have this problem you probably just need to run artisan clear-compiled or delete storage/meta/services.json. When you re-order the providers array that is essentially just forcing the providers to recompile, similar to if you just deleted the services file.

Member

taylorotwell commented Nov 20, 2013

If you have this problem you probably just need to run artisan clear-compiled or delete storage/meta/services.json. When you re-order the providers array that is essentially just forcing the providers to recompile, similar to if you just deleted the services file.

@frodeknutsen

This comment has been minimized.

Show comment
Hide comment
@frodeknutsen

frodeknutsen Nov 21, 2013

@taylorotwell Is it being cached as well? Because I've tried deleting, and running that command, but the problem still persists.

frodeknutsen commented Nov 21, 2013

@taylorotwell Is it being cached as well? Because I've tried deleting, and running that command, but the problem still persists.

@awei01

This comment has been minimized.

Show comment
Hide comment
@awei01

awei01 Mar 29, 2014

i know this is a stale topic. but i did find a fix for my particular situation. Please refer to my gist here:
https://gist.github.com/awei01/9861436

BarDependencyServiceProvider registers ties an instance of BarDependency to the container.
BrokenFooServiceProvider registers an instance of Foo which requires Bar in Foo's constructor. Bar, in turn, requires an instance of BarDependencyServiceProvider. In BrokenFooServiceProvider, the register function tries to call $this->app['bar.dependency'] outside of the closure method passed to Container@bindShared().

As a result, the ordering of the ServiceProviders is significant within the app.php configuration file.

If you look at WorkingFooServiceProvider, you'll see that the register function creates an instance of the Bar class that is required by Foo within the closure passed to the Container@bindShared() method. Thus, the ordering of the service providers is no longer significant.

Please correct me if I'm wrong, but I think this is what the problem is.

awei01 commented Mar 29, 2014

i know this is a stale topic. but i did find a fix for my particular situation. Please refer to my gist here:
https://gist.github.com/awei01/9861436

BarDependencyServiceProvider registers ties an instance of BarDependency to the container.
BrokenFooServiceProvider registers an instance of Foo which requires Bar in Foo's constructor. Bar, in turn, requires an instance of BarDependencyServiceProvider. In BrokenFooServiceProvider, the register function tries to call $this->app['bar.dependency'] outside of the closure method passed to Container@bindShared().

As a result, the ordering of the ServiceProviders is significant within the app.php configuration file.

If you look at WorkingFooServiceProvider, you'll see that the register function creates an instance of the Bar class that is required by Foo within the closure passed to the Container@bindShared() method. Thus, the ordering of the service providers is no longer significant.

Please correct me if I'm wrong, but I think this is what the problem is.

@anlutro

This comment has been minimized.

Show comment
Hide comment
@anlutro

anlutro Mar 29, 2014

Contributor

You're not supposed to do anything but bind stuff to the container inside register(). Move the $this->app['bar.dependency']; inside the bindShared closure

Contributor

anlutro commented Mar 29, 2014

You're not supposed to do anything but bind stuff to the container inside register(). Move the $this->app['bar.dependency']; inside the bindShared closure

@awei01

This comment has been minimized.

Show comment
Hide comment
@awei01

awei01 Mar 29, 2014

Yea, I know. I was trying to give an example illustrating the broken code and the way I fixed it (which is, as you say, moving $this->app['bar.dependency'] inside the bindShared closure.

awei01 commented Mar 29, 2014

Yea, I know. I was trying to give an example illustrating the broken code and the way I fixed it (which is, as you say, moving $this->app['bar.dependency'] inside the bindShared closure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment