Skip to content

ensureCompiled() in-memory cache causes require_once failure when compiled views are cleared mid-request #96

Description

@erhanurgun

Describe the bug

BlazeRuntime::ensureCompiled() uses an in-memory $this->compiled array to skip re-compilation within the same request. However, if compiled view files are deleted mid-request (e.g. via Artisan::call('optimize:clear')), subsequent calls to ensureCompiled() return early based on the stale in-memory cache — without verifying the file still exists on disk. The following require_once then fails with "No such file or directory".

The issue occurs reliably when a Livewire component action triggers cache clearing (e.g. toggling a module that calls optimize:clear) and the component re-renders in the same request.

Component causing the issue

Any Blaze-compiled component used inside a view that re-renders after optimize:clear is called within the same request. In my case, <flux:switch> inside a module settings page:

@foreach($modules as $module)
    @if($module['is_protected'])
        <flux:switch disabled :checked="$module['is_enabled']" />
    @else
        <flux:switch wire:click="toggleModule('{{ $module['name'] }}')" :checked="$module['is_enabled']" />
    @endif
@endforeach

The toggleModule action calls Artisan::call('optimize:clear') which deletes all compiled views, then Livewire re-renders the component in the same request.

Expected behavior

After compiled views are cleared mid-request, ensureCompiled() should detect the missing file and recompile it before require_once executes.

Actual behavior

ensureCompiled() returns immediately at line 48-50 because $this->compiled[$path] is still set from the first render. The compiled file no longer exists on disk, so the require_once on the next line fails:

require_once(.../storage/framework/views/ad784b2e43740a3a648880f501461253.php):
Failed to open stream: No such file or directory

Root cause

In BlazeRuntime::ensureCompiled() (line 46-59):

public function ensureCompiled(string $path, string $compiledPath): void
{
    if (isset($this->compiled[$path])) {
        return; // ← Returns early even if file was deleted
    }

    $this->compiled[$path] = true;

    if (file_exists($compiledPath) && filemtime($path) <= filemtime($compiledPath)) {
        return;
    }

    $this->compiler->compile($path);
}

The $this->compiled guard at line 48 does not verify the compiled file still exists. Once a path is marked as "compiled" during the first render, all subsequent calls skip compilation entirely — even if optimize:clear has deleted the files in between.

Suggested fix

Add a file_exists() check to the in-memory cache guard:

public function ensureCompiled(string $path, string $compiledPath): void
{
    if (isset($this->compiled[$path]) && file_exists($compiledPath)) {
        return;
    }

    $this->compiled[$path] = true;

    if (file_exists($compiledPath) && filemtime($path) <= filemtime($compiledPath)) {
        return;
    }

    $this->compiler->compile($path);
}

This preserves the performance benefit of the in-memory cache (avoiding redundant filemtime calls) while correctly handling the case where files are deleted mid-request.

Steps to reproduce

  1. Create a Livewire component that renders a Blaze-compiled view (e.g. any view using <flux:*> components)
  2. Add an action to the component that calls Artisan::call('optimize:clear') (or view:clear)
  3. After the action, the component re-renders in the same request
  4. The re-render fails with require_once ... Failed to open stream: No such file or directory

Minimal test case:

// In a Livewire component
public function clearAndRerender(): void
{
    Artisan::call('optimize:clear');
    // Component re-renders after this → crash
}

Environment

  • Laravel version: 12.53.0
  • PHP version: 8.4.18
  • Blaze version: 1.0.3

Additional context

This bug surfaces consistently in test suites (both --parallel and sequential) because:

  • Tests start with a clean view cache
  • First render compiles views and populates $this->compiled
  • A Livewire action triggers optimize:clear (e.g. module toggle, settings save)
  • Same test re-renders the component → require_once fails

My current workaround is extending BlazeRuntime and overriding ensureCompiled() with the fix above, then rebinding in AppServiceProvider::register():

$this->app->singleton(BlazeRuntime::class, FixedBlazeRuntime::class);

Activity

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