From 203e0935947fe37814b3ee428fa5e6782457a2cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erhan=20=C3=9CRG=C3=9CN?= <405591@ogr.ktu.edu.tr> Date: Wed, 4 Mar 2026 01:48:51 +0300 Subject: [PATCH 1/2] fix: use event-based cache invalidation for mid-request view clears Instead of adding a file_exists() check to the hot path (which caused ~40% performance regression), listen for the view:clear command via CommandFinished event and reset both BlazeRuntime's in-memory compilation cache and Laravel's Component view string cache when compiled files are actually deleted. This preserves zero-overhead in-memory caching during normal requests while correctly handling mid-request cache clears (e.g. optimize:clear). Fixes #96 --- src/BlazeServiceProvider.php | 17 +++++++++++++++++ src/Runtime/BlazeRuntime.php | 11 +++++++++++ tests/IntegrationTest.php | 13 +++++++++++-- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/BlazeServiceProvider.php b/src/BlazeServiceProvider.php index f4901ed9..3e75f5cc 100644 --- a/src/BlazeServiceProvider.php +++ b/src/BlazeServiceProvider.php @@ -4,9 +4,12 @@ use Livewire\Blaze\Compiler\Profiler; use Livewire\Blaze\Runtime\BlazeRuntime; +use Illuminate\Console\Events\CommandFinished; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Blade; +use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\View; +use Illuminate\View\Component; class BlazeServiceProvider extends ServiceProvider { @@ -45,6 +48,7 @@ public function boot(): void $this->registerBladeMacros(); $this->interceptBladeCompilation(); $this->registerDebuggerMiddleware(); + $this->clearCompiledCacheOnViewClear(); } /** @@ -148,4 +152,17 @@ protected function registerDebuggerMiddleware(): void } }); } + + /** + * Reset BlazeRuntime's in-memory cache when compiled views are deleted. + */ + protected function clearCompiledCacheOnViewClear(): void + { + Event::listen(CommandFinished::class, function (CommandFinished $event) { + if ($event->command === 'view:clear') { + $this->app->make(BlazeRuntime::class)->clearCompiled(); + Component::flushCache(); + } + }); + } } diff --git a/src/Runtime/BlazeRuntime.php b/src/Runtime/BlazeRuntime.php index d0982b25..1c441ebc 100644 --- a/src/Runtime/BlazeRuntime.php +++ b/src/Runtime/BlazeRuntime.php @@ -181,6 +181,17 @@ public function popData(): void array_pop($this->slotsStack); } + /** + * Clear the in-memory compilation cache. + * + * Called when compiled view files are deleted (e.g. via artisan view:clear) + * so that subsequent resolve() calls trigger recompilation. + */ + public function clearCompiled(): void + { + $this->compiled = []; + } + /** * Walk the data stack to find a value for @aware, checking slots before data at each level. */ diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index e85c18b9..69d06662 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -2,10 +2,13 @@ use Illuminate\Container\Container; use Illuminate\Contracts\View\Engine; +use Illuminate\Foundation\Testing\WithConsoleEvents; use Illuminate\Support\Facades\Artisan; use Livewire\Blaze\Blaze; use Livewire\Blaze\BlazeManager; +uses(WithConsoleEvents::class); + beforeEach(fn () => Artisan::call('view:clear')); test('renders components', function () { @@ -14,14 +17,14 @@ test('renders components with blaze off', function () { Blaze::disable(); - + view('mix')->render(); })->throwsNoExceptions(); test('renders components with blaze off and debug mode on', function () { Blaze::disable(); Blaze::debug(); - + view('mix')->render(); })->throwsNoExceptions(); @@ -47,6 +50,12 @@ public function get($path, array $data = []): string { view('mix')->render(); })->throwsNoExceptions(); +test('clearing views mid-request does not crash re-render', function () { + view('mix')->render(); + Artisan::call('view:clear'); + view('mix')->render(); +})->throwsNoExceptions(); + test('supports antlers engine', function () { // Statamic serializes all view data, we need to make sure // we don't inject BlazeRuntime which is not serializable. From 8847b7fbf9a2785e308fca9d56425f1c9a3774b2 Mon Sep 17 00:00:00 2001 From: Filip Ganyicz Date: Thu, 5 Mar 2026 15:43:33 +0100 Subject: [PATCH 2/2] Formatting --- src/BlazeServiceProvider.php | 9 +++++---- src/Runtime/BlazeRuntime.php | 32 ++++++++++++++++---------------- tests/IntegrationTest.php | 14 ++++++++------ 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/BlazeServiceProvider.php b/src/BlazeServiceProvider.php index 4184258c..ced25445 100644 --- a/src/BlazeServiceProvider.php +++ b/src/BlazeServiceProvider.php @@ -49,7 +49,7 @@ public function boot(): void $this->registerBladeMacros(); $this->interceptBladeCompilation(); $this->registerDebuggerMiddleware(); - $this->clearCompiledCacheOnViewClear(); + $this->registerViewClearListener(); $this->registerOctaneListener(); } @@ -171,13 +171,14 @@ protected function registerDebuggerMiddleware(): void } /** - * Reset BlazeRuntime's in-memory cache when compiled views are deleted. + * Register a listener for the view:clear command to flush the compiled cache. */ - protected function clearCompiledCacheOnViewClear(): void + protected function registerViewClearListener(): void { Event::listen(CommandFinished::class, function (CommandFinished $event) { if ($event->command === 'view:clear') { - $this->app->make(BlazeRuntime::class)->clearCompiled(); + $this->app->make(BlazeRuntime::class)->flushCompiled(); + Component::flushCache(); } }); diff --git a/src/Runtime/BlazeRuntime.php b/src/Runtime/BlazeRuntime.php index 7ce1f4a3..1c960e26 100644 --- a/src/Runtime/BlazeRuntime.php +++ b/src/Runtime/BlazeRuntime.php @@ -181,17 +181,6 @@ public function popData(): void array_pop($this->slotsStack); } - /** - * Clear the in-memory compilation cache. - * - * Called when compiled view files are deleted (e.g. via artisan view:clear) - * so that subsequent resolve() calls trigger recompilation. - */ - public function clearCompiled(): void - { - $this->compiled = []; - } - /** * Walk the data stack to find a value for @aware, checking slots before data at each level. */ @@ -244,11 +233,6 @@ public function processPassthroughContent(string $method, string $content): stri return $content; } - private function getCompiledPath(): string - { - return $this->compiledPath ??= config('view.compiled'); - } - /** * Set the application instance (used by Octane to swap in the sandbox). */ @@ -269,6 +253,14 @@ public function flushState(): void $this->slotsStack = []; } + /** + * Clear the in-memory compilation cache. + */ + public function flushCompiled(): void + { + $this->compiled = []; + } + /** * Lazy-load properties whose canonical values are set after BlazeRuntime is constructed * ($errors by middleware, compiledPath by parallel testing infrastructure). @@ -281,4 +273,12 @@ public function __get(string $name): mixed default => throw new \InvalidArgumentException("Property {$name} does not exist"), }; } + + /** + * Get the compiled path. + */ + private function getCompiledPath(): string + { + return $this->compiledPath ??= config('view.compiled'); + } } diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index 69d06662..d8b291f6 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -28,6 +28,14 @@ view('mix')->render(); })->throwsNoExceptions(); +test('renders components after clearing views', function () { + view('mix')->render(); + + Artisan::call('view:clear'); + + view('mix')->render(); +})->throwsNoExceptions(); + test('supports php engine', function () { view('php-view')->render(); })->throwsNoExceptions(); @@ -50,12 +58,6 @@ public function get($path, array $data = []): string { view('mix')->render(); })->throwsNoExceptions(); -test('clearing views mid-request does not crash re-render', function () { - view('mix')->render(); - Artisan::call('view:clear'); - view('mix')->render(); -})->throwsNoExceptions(); - test('supports antlers engine', function () { // Statamic serializes all view data, we need to make sure // we don't inject BlazeRuntime which is not serializable.