Throughout the process of upgrading Laravel 10 -> 11, statamic/cms
was also upgraded from 4 -> 5. Since this occurred i've noticed some service providers not booting when the app is bootstrapping only before calls to Kernel files.
For context, the package ylsideas/feature-flags
adds a macro to the Event
facade for skipWithoutFeature
for use in the kernel schedule which was not loaded at the point of the kernel. Tracing through vendor files led me to the vendor/laravel/framework/src/Illuminate/Foundation/Application::boot
method.
When the app is bootstrapping it walks through all service providers and boots them to be accessible. This halts on statamic/cms/src/Providers/AppServiceProvider.php
on the line:
$this->app->make(Schedule::class)->job(new HandleEntrySchedule)->everyMinute()
No exception or throwable is thrown from this line, by catching in a try/catch, but additionally no code execution is performed afterwards which causes further service providers to not be booted. This issue does not persist after the kernel is registered and the macro is then applied.
What could this issue be, and how is it possible to debug when no catchable exception is thrown and no logs produced?
This was caused by a conflict of bootstrapping order between two packages.
The vendor/statamic/cms/src/Providers/AppServiceProvider.php
provider will run first, appending a task to the vendor/laravel/framework/src/Illuminate/Console/Scheduling/Schedule.php
class.
The vendor/ylsideas/feature-flags/src/FeatureFlagsServiceProvider.php
provider runs second and applies a macro for use within app/Console/Kernel.php
.
When the macro is used, because the FeatureFlagsServiceProvider
has not yet been booted, the call to $this->app->make(Schedule::class)
will throw an exception and therefore stop further service providers from being booted.
To fix this issue, within the app/Providers/AppServiceProvider::register
method I manually boot this service provider first:
$this->app->booting(function(Application $app) {
$app->make(FeatureFlagsServiceProvider::class, ['app' => $app])->boot();
});
This then makes the macro available to the facade, preventing any issues in the initial service provider.