phplaravelstatamic

Statamic AppServiceProvider halting app bootstrapping


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?


Solution

  • 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.