Skip to content

Facades and the container instance point to different applications after route:cache #61401

Closed

Description

@ysv-a

Laravel Version

13.30.0 and 13.30.1 (13.29.0 is fine)

PHP Version

8.5.10 (also 8.5.9)

Database Driver & Version

Not involved. Reproduced on the default SQLite of a fresh install and on PostgreSQL 17.

Description

Since #61346 (13.30.0) RouteCacheCommand::getFreshApplication() boots a throwaway Application and afterwards restores the facade root to the live application:

Facade::clearResolvedInstances();
Facade::setFacadeApplication($this->laravel);

new Application also calls Container::setInstance($this) (in registerBaseBindings()), and nothing restores that. So after route:cache the process is in a split state: facades resolve against the live app, while Container::getInstance() / app() resolve against the discarded one. Before 13.30 both pointed at the throwaway app, which was equally wrong but self-consistent, so it never showed.

optimize runs view:cache right after route:cache in the same process, and view:cache falls into the split:

  1. ViewClearCommand / ViewCacheCommand resolve the Blade engine through $this->laravel['view.engine.resolver']->resolve('blade'), i.e. on the live app.
  2. The blade engine closure in ViewServiceProvider::registerBladeEngine() builds the engine from Container::getInstance()->make('blade.compiler'), i.e. the throwaway app's compiler.
  3. That resolution fires the afterResolving('blade.compiler') hooks. A package that registers class component namespaces there through the Blade facade (inertia-laravel 3.x does: Blade::componentNamespace('Inertia\\View\\Components', 'inertia')) registers them on the live app's compiler instead.
  4. The compiler that actually compiles has an empty classComponentNamespaces, and the first <x-inertia::...> tag fails:
  views .......................................................... 9.32ms FAIL

In ComponentTagCompiler.php line 315:

  Unable to locate a class or view for component [inertia::head].

php artisan view:cache on its own succeeds (no route:cache ran before it in that process). 13.29.0 succeeds. 13.30.x with getFreshApplication() reverted to the 13.29 body succeeds.

Instrumented run on 13.30.0 (ids from spl_object_id; #4 is the live app, #2531 the throwaway app booted by route:cache):

inertia callback:           providerApp#2531 passedCompiler#4100 facadeApp#4 facadeCompiler#4463
componentClass(inertia::app): compiler#4100 namespaces=[] facadeApp#4 container#2531

The namespace went to compiler #4463, the compilation ran on #4100.

Inertia could dodge this particular symptom by using the compiler instance passed to its callback instead of the facade (reported separately to inertiajs/inertia-laravel), but the split between Facade::getFacadeApplication() and Container::getInstance() is in the framework and affects anything that runs after route:cache in the same process and goes through app() / Container::getInstance().

Steps To Reproduce

composer create-project laravel/laravel repro        # 13.30.1
cd repro
composer require inertiajs/inertia-laravel           # 3.3.1
cat > resources/views/app.blade.php <<'BLADE'
<!DOCTYPE html>
<html>
<head><x-inertia::head /></head>
<body><x-inertia::app /></body>
</html>
BLADE

php artisan optimize        # views FAIL: Unable to locate a class or view for component [inertia::head]
php artisan optimize:clear
php artisan view:cache      # OK

composer require laravel/framework:13.29.0 -W
php artisan optimize        # config / events / routes / views all DONE

Suggested fix

Restore the container instance together with the facade root:

protected function getFreshApplication()
{
    return tap(require $this->laravel->bootstrapPath('app.php'), function ($app) {
        $app->make(ConsoleKernelContract::class)->bootstrap();

        Facade::clearResolvedInstances();
        Facade::setFacadeApplication($this->laravel);
        Container::setInstance($this->laravel);
    });
}

ConfigCacheCommand::getFreshConfiguration() boots a throwaway app the same way and leaves Container::getInstance() on it too; #61350 / #61351 proposed the facade-only restore for it, so whatever lands there should keep the two in sync as well.

Workarounds until then: run config:cache, event:cache, route:cache, view:cache as separate processes instead of optimize, or pin laravel/framework:13.29.0.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions