From 3e723502b9ef7f941e2032a0717d1d96e40a7ddc Mon Sep 17 00:00:00 2001 From: tornike Date: Tue, 23 Jul 2024 17:35:47 +0400 Subject: [PATCH 1/4] introduce operation scoped service providers --- config/octane.php | 14 ++++++ src/ApplicationFactory.php | 10 ++++- .../ProvidesDefaultConfigurationOptions.php | 1 + .../RegisterOperationServiceProviders.php | 18 ++++++++ src/OctaneProviderRepository.php | 43 +++++++++++++++++++ src/OctaneRegisterProviders.php | 31 +++++++++++++ 6 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 src/Listeners/RegisterOperationServiceProviders.php create mode 100644 src/OctaneProviderRepository.php create mode 100644 src/OctaneRegisterProviders.php diff --git a/config/octane.php b/config/octane.php index 8cfba0114..95529e8dd 100644 --- a/config/octane.php +++ b/config/octane.php @@ -119,6 +119,20 @@ ], ], + /* + |-------------------------------------------------------------------------- + | Operation Scoped ServiceProviders + |-------------------------------------------------------------------------- + | + | Here you can list service providers which should be registered + | on start of each operation, rather than during boot process. + | + */ + + 'op_service_providers' => [ + // + ], + /* |-------------------------------------------------------------------------- | Warm / Flush Bindings diff --git a/src/ApplicationFactory.php b/src/ApplicationFactory.php index c2524706d..950801cec 100644 --- a/src/ApplicationFactory.php +++ b/src/ApplicationFactory.php @@ -61,10 +61,16 @@ protected function getBootstrappers(Application $app): array $method->setAccessible(true); + $bootstrappers = $method->invoke($kernel); + + // Replace RegisterProviders with OctaneRegisterProviders + $replaceIndex = array_search(RegisterProviders::class, $bootstrappers, true); + $bootstrappers = array_replace($bootstrappers, [$replaceIndex => OctaneRegisterProviders::class]); + return $this->injectBootstrapperBefore( - RegisterProviders::class, + OctaneRegisterProviders::class, SetRequestForConsole::class, - $method->invoke($kernel) + $bootstrappers ); } diff --git a/src/Concerns/ProvidesDefaultConfigurationOptions.php b/src/Concerns/ProvidesDefaultConfigurationOptions.php index 36ebe4101..20611b848 100644 --- a/src/Concerns/ProvidesDefaultConfigurationOptions.php +++ b/src/Concerns/ProvidesDefaultConfigurationOptions.php @@ -53,6 +53,7 @@ public static function prepareApplicationForNextOperation(): array \Laravel\Octane\Listeners\FlushMonologState::class, \Laravel\Octane\Listeners\FlushStrCache::class, \Laravel\Octane\Listeners\FlushTranslatorCache::class, + \Laravel\Octane\Listeners\RegisterOperationServiceProviders::class, // First-Party Packages... \Laravel\Octane\Listeners\PrepareInertiaForNextOperation::class, diff --git a/src/Listeners/RegisterOperationServiceProviders.php b/src/Listeners/RegisterOperationServiceProviders.php new file mode 100644 index 000000000..6705cc9fc --- /dev/null +++ b/src/Listeners/RegisterOperationServiceProviders.php @@ -0,0 +1,18 @@ +sandbox->make('config')->get('octane.op_service_providers', []) as $provider) { + $event->sandbox->register($provider); + } + } +} diff --git a/src/OctaneProviderRepository.php b/src/OctaneProviderRepository.php new file mode 100644 index 000000000..97f8610c0 --- /dev/null +++ b/src/OctaneProviderRepository.php @@ -0,0 +1,43 @@ +loadManifest(); + + // First we will load the service manifest, which contains information on all + // service providers registered with the application and which services it + // provides. This is used to know which services are "deferred" loaders. + if ($this->shouldRecompile($manifest, $providers)) { + $manifest = $this->compileManifest($providers); + } + + // Next, we will register events to load the providers for each of the events + // that it has requested. This allows the service provider to defer itself + // while still getting automatically loaded when a certain event occurs. + foreach ($manifest['when'] as $provider => $events) { + $this->registerLoadEvents($provider, $events); + } + + // We will register eagerly loaded providers, just like base ProviderRepository, + // except we will leave out the ones specified in op_service_providers array. + foreach ($manifest['eager'] as $provider) { + if (!in_array($provider, $this->app->make('config')->get('octane.op_service_providers', []))) { + $this->app->register($provider); + } + } + + $this->app->addDeferredServices($manifest['deferred']); + } +} diff --git a/src/OctaneRegisterProviders.php b/src/OctaneRegisterProviders.php new file mode 100644 index 000000000..9907a763a --- /dev/null +++ b/src/OctaneRegisterProviders.php @@ -0,0 +1,31 @@ +bound('config_loaded_from_cache') || + $app->make('config_loaded_from_cache') === false + ) { + $this->mergeAdditionalProviders($app); + } + + $providerRepository = new OctaneProviderRepository($app, new FileSystem, $app->getCachedServicesPath()); + + $app->registerConfiguredProviders($providerRepository); + } +} From 6b7c52fb94744d3ba94ecd46ba7cce8dffbc0975 Mon Sep 17 00:00:00 2001 From: tornike Date: Tue, 30 Jul 2024 16:53:24 +0400 Subject: [PATCH 2/4] firstly register op_service_providers and then boot them --- src/ApplicationFactory.php | 1 - src/Listeners/RegisterOperationServiceProviders.php | 9 ++++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ApplicationFactory.php b/src/ApplicationFactory.php index 950801cec..e5398e642 100644 --- a/src/ApplicationFactory.php +++ b/src/ApplicationFactory.php @@ -63,7 +63,6 @@ protected function getBootstrappers(Application $app): array $bootstrappers = $method->invoke($kernel); - // Replace RegisterProviders with OctaneRegisterProviders $replaceIndex = array_search(RegisterProviders::class, $bootstrappers, true); $bootstrappers = array_replace($bootstrappers, [$replaceIndex => OctaneRegisterProviders::class]); diff --git a/src/Listeners/RegisterOperationServiceProviders.php b/src/Listeners/RegisterOperationServiceProviders.php index 6705cc9fc..bbb865c8b 100644 --- a/src/Listeners/RegisterOperationServiceProviders.php +++ b/src/Listeners/RegisterOperationServiceProviders.php @@ -11,8 +11,15 @@ class RegisterOperationServiceProviders */ public function handle($event): void { + $resolved_providers = []; + foreach ($event->sandbox->make('config')->get('octane.op_service_providers', []) as $provider) { - $event->sandbox->register($provider); + $resolved_providers[] = $event->sandbox->register($provider, boot: false); + } + + // After all service providers have been registered, we will boot them. + foreach ($resolved_providers as $provider) { + $event->sandbox->bootProvider($provider); } } } From 1ffecfe8d53270b884532f41e5d3e28518cbecde Mon Sep 17 00:00:00 2001 From: tornike Date: Fri, 2 Aug 2024 14:59:49 +0400 Subject: [PATCH 3/4] make deferred service providers operation scoped --- src/ApplicationFactory.php | 9 ++++++++- src/Listeners/RegisterOperationServiceProviders.php | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/ApplicationFactory.php b/src/ApplicationFactory.php index e5398e642..b0ae446db 100644 --- a/src/ApplicationFactory.php +++ b/src/ApplicationFactory.php @@ -45,7 +45,14 @@ public function bootstrap(Application $app, array $initialInstances = []): Appli $app->bootstrapWith($this->getBootstrappers($app)); - $app->loadDeferredProviders(); + // We will register all but operation scoped deferred providers right away so + // they are resolved once, during boot. Remaining operation providers will + // be registered with classical approach, during the occasions of need. + foreach ($app->getDeferredServices() as $service => $provider) { + if (!in_array($provider, $app->make('config')->get('octane.op_service_providers', []))) { + $app->loadDeferredProvider($service); + } + } return $app; } diff --git a/src/Listeners/RegisterOperationServiceProviders.php b/src/Listeners/RegisterOperationServiceProviders.php index bbb865c8b..01c16a9c0 100644 --- a/src/Listeners/RegisterOperationServiceProviders.php +++ b/src/Listeners/RegisterOperationServiceProviders.php @@ -14,6 +14,8 @@ public function handle($event): void $resolved_providers = []; foreach ($event->sandbox->make('config')->get('octane.op_service_providers', []) as $provider) { + $provider = $event->sandbox->resolveProvider($provider); + if ($provider->isDeferred()) continue; $resolved_providers[] = $event->sandbox->register($provider, boot: false); } From c2de0cf7f070763dc01779e67e4be0ff707dfec2 Mon Sep 17 00:00:00 2001 From: tornike Date: Fri, 2 Aug 2024 18:23:56 +0400 Subject: [PATCH 4/4] fix: typo in OctaneRegisterProvider --- src/OctaneRegisterProviders.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OctaneRegisterProviders.php b/src/OctaneRegisterProviders.php index 9907a763a..e3a81815f 100644 --- a/src/OctaneRegisterProviders.php +++ b/src/OctaneRegisterProviders.php @@ -4,7 +4,7 @@ use Illuminate\Contracts\Foundation\Application; use Laravel\Octane\OctaneProviderRepository; -use Illuminate\Filesystem\FileSystem; +use Illuminate\Filesystem\Filesystem; use Illuminate\Foundation\Bootstrap\RegisterProviders; class OctaneRegisterProviders extends RegisterProviders @@ -24,7 +24,7 @@ public function bootstrap(Application $app) $this->mergeAdditionalProviders($app); } - $providerRepository = new OctaneProviderRepository($app, new FileSystem, $app->getCachedServicesPath()); + $providerRepository = new OctaneProviderRepository($app, new Filesystem, $app->getCachedServicesPath()); $app->registerConfiguredProviders($providerRepository); }