Detailed explanation of the problems encountered when setting delayed loading in Laravel Service Provider development

  • 2021-08-28 19:42:18
  • OfStack

Preface

This paper mainly introduces some problems encountered when setting delayed loading for Laravel Service Provider. All this article is due to the actual project requirements. Recently, when developing laravel-database-logger package, it was found that setting ServiceProvider defer attribute to true will cause middleware registered in register method to be invalid.


class ServiceProvider extends \Illuminate\Support\ServiceProvider
{
 protected $defer = true; 
 public function register()
 {
 $this->mergeConfigFrom(
  __DIR__ . '/../config/config.php', 'ibrand.dblogger'
 );
 $this->app->singleton(DbLogger::class, function ($app) {
  return new DbLogger();
 });
 // When  $defer  Set to  true  Reference in the route when  databaselogger middleware  Errors will be reported and prompted  databaselogger class not found.
 $this->app[\Illuminate\Routing\Router::class]->middleware('databaselogger', Middleware::class);

 } 
 public function provides()
 {
 return [DbLogger::class];
 }
}

When the problem occurs, it is suspected that it is caused by setting the defer attribute to true, and immediately modify the source code to protected $defer = true; Comment out the code, and the result is still a prompt databaselogger class not found. Laravel is not registered for this ServiceProvder

Next, I want to solve this problem and try the following methods:

1. Verify that there is something wrong with your code

Register your own ServiceProvider in the normally registered AppServiceProvider


public function register()
 {
 //
 $this->app->register(\Ibrand\DatabaseLogger\ServiceProvider::class);
 }

Result 1 is normal after registration.

2. Study the source code

providers registration is invalid in config/app. php, but valid in other ServiceProvider, indicating other problems.

Find the method of registerConfiguredProviders by studying the source code of Illuminate\ Foundation\ Application:

Laravel is to read providers contents in config/app. php and load into ProviderRepository in this method.


(new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
     ->load($providers->collapse()->toArray());

The point is $this->getCachedServicesPath()  Through the source found Laravel is based on bootstrap/cache/services. php file to determine how to register ServiceProvider.

At this time, I thought of why I commented before //protected $defer = true; The reason why it is still invalid after the code.

So in order to make the annotated //protected $defer = true; The code is valid and needs to be executed


php artisan clear-compiled 
php artisan optimize

After that, the problem was solved, and the principle of ServiceProvider was further understood.

Therefore, remember: If you are going to use delayed loading of ServiceProvider, it is strictly forbidden to register middleware, route and other series of operations. At the same time, after changing the defer property value, you need to execute php artisan clear-compiled And php artisan optimize To update the ServiceProvider cache.

3. Why is the registration valid in AppServiceProvider?

The willingness is simple, because AppServiceProvider does not delay loading, so executing the register method in AppServiceProvider to register a new ServiceProvider does not delay loading.

Summarize

Careful use of delayed loading ServiceProvider

After you change the defer property value, you need to execute the php artisan clear-compiled And php artisan optimize To update the ServiceProvider cache.

It is strictly forbidden to register middleware and route with delayed loading ServiceProvider.


Related articles: