From cf8d3d617fc72b9a5b3172d5d7647cf3ee227dc2 Mon Sep 17 00:00:00 2001 From: Wendell Adriel Date: Tue, 24 Feb 2026 19:18:01 +0000 Subject: [PATCH 1/5] Fix issue with Browser tests --- src/Runtime/BlazeRuntime.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Runtime/BlazeRuntime.php b/src/Runtime/BlazeRuntime.php index 39a09281..77e7330f 100644 --- a/src/Runtime/BlazeRuntime.php +++ b/src/Runtime/BlazeRuntime.php @@ -20,8 +20,6 @@ class BlazeRuntime public readonly Application $app; public readonly Debugger $debugger; public readonly Compiler $compiler; - protected ViewErrorBag $errors; - public string $compiledPath; protected array $paths = []; @@ -207,7 +205,7 @@ public function getConsumableData(string $key, mixed $default = null): mixed public function __get(string $name): mixed { if ($name === 'errors') { - return $this->errors ??= $this->env->shared('errors') ?? new ViewErrorBag; + return $this->env->shared('errors') ?? new ViewErrorBag; } throw new \InvalidArgumentException("Property {$name} does not exist"); From 949adeb5293011bcd37564b010964f869b9bd454 Mon Sep 17 00:00:00 2001 From: Wendell Adriel Date: Tue, 24 Feb 2026 19:38:47 +0000 Subject: [PATCH 2/5] Update to flush errors property after each request --- src/BlazeServiceProvider.php | 11 +++++++++++ src/Runtime/BlazeRuntime.php | 12 +++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/BlazeServiceProvider.php b/src/BlazeServiceProvider.php index 5dfe6000..f3f9d89f 100644 --- a/src/BlazeServiceProvider.php +++ b/src/BlazeServiceProvider.php @@ -44,6 +44,7 @@ public function boot(): void $this->registerBladeMacros(); $this->interceptViewCacheInvalidation(); $this->interceptBladeCompilation(); + $this->registerRequestTerminatingCallback(); } /** @@ -116,6 +117,16 @@ protected function interceptBladeCompilation(): void }); } + /** + * Flush request-scoped state from the BlazeRuntime singleton between requests. + */ + protected function registerRequestTerminatingCallback(): void + { + $this->app->terminating(function () { + $this->app->make(BlazeRuntime::class)->requestTerminated(); + }); + } + /** * Recompile views when folded component dependencies have changed. */ diff --git a/src/Runtime/BlazeRuntime.php b/src/Runtime/BlazeRuntime.php index 77e7330f..26d20868 100644 --- a/src/Runtime/BlazeRuntime.php +++ b/src/Runtime/BlazeRuntime.php @@ -20,6 +20,8 @@ class BlazeRuntime public readonly Application $app; public readonly Debugger $debugger; public readonly Compiler $compiler; + protected ViewErrorBag $errors; + public string $compiledPath; protected array $paths = []; @@ -199,13 +201,21 @@ public function getConsumableData(string $key, mixed $default = null): mixed return value($default); } + /** + * Flush request-scoped state so the singleton stays fresh across requests. + */ + public function requestTerminated(): void + { + unset($this->errors); + } + /** * Lazy-load $errors since middleware sets them after BlazeRuntime is constructed. */ public function __get(string $name): mixed { if ($name === 'errors') { - return $this->env->shared('errors') ?? new ViewErrorBag; + return $this->errors ??= $this->env->shared('errors') ?? new ViewErrorBag; } throw new \InvalidArgumentException("Property {$name} does not exist"); From 6a808d96694129c82983317e3ab056e1aeed57f9 Mon Sep 17 00:00:00 2001 From: Caleb Porzio Date: Tue, 24 Feb 2026 20:40:24 -0500 Subject: [PATCH 3/5] Fix stale $errors in long-lived processes (Octane, etc.) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove ??= caching on $errors — always read fresh from the view factory. The previous approach (resetting via terminating callback) didn't cover all long-lived environments. Reading fresh costs ~135ns per component, which is negligible since each component reads $errors exactly once. Co-Authored-By: Claude Opus 4.6 --- src/BlazeServiceProvider.php | 11 ----------- src/Runtime/BlazeRuntime.php | 14 +++----------- tests/StaleErrorsTest.php | 25 +++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 22 deletions(-) create mode 100644 tests/StaleErrorsTest.php diff --git a/src/BlazeServiceProvider.php b/src/BlazeServiceProvider.php index f3f9d89f..5dfe6000 100644 --- a/src/BlazeServiceProvider.php +++ b/src/BlazeServiceProvider.php @@ -44,7 +44,6 @@ public function boot(): void $this->registerBladeMacros(); $this->interceptViewCacheInvalidation(); $this->interceptBladeCompilation(); - $this->registerRequestTerminatingCallback(); } /** @@ -117,16 +116,6 @@ protected function interceptBladeCompilation(): void }); } - /** - * Flush request-scoped state from the BlazeRuntime singleton between requests. - */ - protected function registerRequestTerminatingCallback(): void - { - $this->app->terminating(function () { - $this->app->make(BlazeRuntime::class)->requestTerminated(); - }); - } - /** * Recompile views when folded component dependencies have changed. */ diff --git a/src/Runtime/BlazeRuntime.php b/src/Runtime/BlazeRuntime.php index 26d20868..d3caf45a 100644 --- a/src/Runtime/BlazeRuntime.php +++ b/src/Runtime/BlazeRuntime.php @@ -20,7 +20,6 @@ class BlazeRuntime public readonly Application $app; public readonly Debugger $debugger; public readonly Compiler $compiler; - protected ViewErrorBag $errors; public string $compiledPath; @@ -202,20 +201,13 @@ public function getConsumableData(string $key, mixed $default = null): mixed } /** - * Flush request-scoped state so the singleton stays fresh across requests. - */ - public function requestTerminated(): void - { - unset($this->errors); - } - - /** - * Lazy-load $errors since middleware sets them after BlazeRuntime is constructed. + * Lazy-load $errors from the view factory on every access so the singleton + * never serves a stale ViewErrorBag in long-lived processes (Octane, etc.). */ public function __get(string $name): mixed { if ($name === 'errors') { - return $this->errors ??= $this->env->shared('errors') ?? new ViewErrorBag; + return $this->env->shared('errors') ?? new ViewErrorBag; } throw new \InvalidArgumentException("Property {$name} does not exist"); diff --git a/tests/StaleErrorsTest.php b/tests/StaleErrorsTest.php new file mode 100644 index 00000000..8f40cf9f --- /dev/null +++ b/tests/StaleErrorsTest.php @@ -0,0 +1,25 @@ +share('errors', new ViewErrorBag); + + expect($runtime->errors->any())->toBeFalse(); + + // Request #2: form submission fails, middleware shares fresh errors. + $freshErrors = new ViewErrorBag; + $freshErrors->put('default', new MessageBag([ + 'email' => ['These credentials do not match our records.'], + ])); + app('view')->share('errors', $freshErrors); + + // The singleton must return the fresh errors, not the stale empty bag. + expect($runtime->errors->any())->toBeTrue(); +}); From a10af802495452de04224823f4faf99c31124af8 Mon Sep 17 00:00:00 2001 From: Caleb Porzio Date: Tue, 24 Feb 2026 21:48:24 -0500 Subject: [PATCH 4/5] Revert "Fix stale $errors in long-lived processes (Octane, etc.)" This reverts commit 6a808d96694129c82983317e3ab056e1aeed57f9. --- src/BlazeServiceProvider.php | 11 +++++++++++ src/Runtime/BlazeRuntime.php | 14 +++++++++++--- tests/StaleErrorsTest.php | 25 ------------------------- 3 files changed, 22 insertions(+), 28 deletions(-) delete mode 100644 tests/StaleErrorsTest.php diff --git a/src/BlazeServiceProvider.php b/src/BlazeServiceProvider.php index 5dfe6000..f3f9d89f 100644 --- a/src/BlazeServiceProvider.php +++ b/src/BlazeServiceProvider.php @@ -44,6 +44,7 @@ public function boot(): void $this->registerBladeMacros(); $this->interceptViewCacheInvalidation(); $this->interceptBladeCompilation(); + $this->registerRequestTerminatingCallback(); } /** @@ -116,6 +117,16 @@ protected function interceptBladeCompilation(): void }); } + /** + * Flush request-scoped state from the BlazeRuntime singleton between requests. + */ + protected function registerRequestTerminatingCallback(): void + { + $this->app->terminating(function () { + $this->app->make(BlazeRuntime::class)->requestTerminated(); + }); + } + /** * Recompile views when folded component dependencies have changed. */ diff --git a/src/Runtime/BlazeRuntime.php b/src/Runtime/BlazeRuntime.php index d3caf45a..26d20868 100644 --- a/src/Runtime/BlazeRuntime.php +++ b/src/Runtime/BlazeRuntime.php @@ -20,6 +20,7 @@ class BlazeRuntime public readonly Application $app; public readonly Debugger $debugger; public readonly Compiler $compiler; + protected ViewErrorBag $errors; public string $compiledPath; @@ -201,13 +202,20 @@ public function getConsumableData(string $key, mixed $default = null): mixed } /** - * Lazy-load $errors from the view factory on every access so the singleton - * never serves a stale ViewErrorBag in long-lived processes (Octane, etc.). + * Flush request-scoped state so the singleton stays fresh across requests. + */ + public function requestTerminated(): void + { + unset($this->errors); + } + + /** + * Lazy-load $errors since middleware sets them after BlazeRuntime is constructed. */ public function __get(string $name): mixed { if ($name === 'errors') { - return $this->env->shared('errors') ?? new ViewErrorBag; + return $this->errors ??= $this->env->shared('errors') ?? new ViewErrorBag; } throw new \InvalidArgumentException("Property {$name} does not exist"); diff --git a/tests/StaleErrorsTest.php b/tests/StaleErrorsTest.php deleted file mode 100644 index 8f40cf9f..00000000 --- a/tests/StaleErrorsTest.php +++ /dev/null @@ -1,25 +0,0 @@ -share('errors', new ViewErrorBag); - - expect($runtime->errors->any())->toBeFalse(); - - // Request #2: form submission fails, middleware shares fresh errors. - $freshErrors = new ViewErrorBag; - $freshErrors->put('default', new MessageBag([ - 'email' => ['These credentials do not match our records.'], - ])); - app('view')->share('errors', $freshErrors); - - // The singleton must return the fresh errors, not the stale empty bag. - expect($runtime->errors->any())->toBeTrue(); -}); From 74fb81213e332516e1dca3213d7845c5acd3239c Mon Sep 17 00:00:00 2001 From: Caleb Porzio Date: Tue, 24 Feb 2026 22:07:37 -0500 Subject: [PATCH 5/5] wip --- src/BlazeServiceProvider.php | 11 ----------- src/Runtime/BlazeRuntime.php | 14 +++----------- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/src/BlazeServiceProvider.php b/src/BlazeServiceProvider.php index f3f9d89f..5dfe6000 100644 --- a/src/BlazeServiceProvider.php +++ b/src/BlazeServiceProvider.php @@ -44,7 +44,6 @@ public function boot(): void $this->registerBladeMacros(); $this->interceptViewCacheInvalidation(); $this->interceptBladeCompilation(); - $this->registerRequestTerminatingCallback(); } /** @@ -117,16 +116,6 @@ protected function interceptBladeCompilation(): void }); } - /** - * Flush request-scoped state from the BlazeRuntime singleton between requests. - */ - protected function registerRequestTerminatingCallback(): void - { - $this->app->terminating(function () { - $this->app->make(BlazeRuntime::class)->requestTerminated(); - }); - } - /** * Recompile views when folded component dependencies have changed. */ diff --git a/src/Runtime/BlazeRuntime.php b/src/Runtime/BlazeRuntime.php index 26d20868..6d2aec81 100644 --- a/src/Runtime/BlazeRuntime.php +++ b/src/Runtime/BlazeRuntime.php @@ -20,7 +20,6 @@ class BlazeRuntime public readonly Application $app; public readonly Debugger $debugger; public readonly Compiler $compiler; - protected ViewErrorBag $errors; public string $compiledPath; @@ -202,20 +201,13 @@ public function getConsumableData(string $key, mixed $default = null): mixed } /** - * Flush request-scoped state so the singleton stays fresh across requests. - */ - public function requestTerminated(): void - { - unset($this->errors); - } - - /** - * Lazy-load $errors since middleware sets them after BlazeRuntime is constructed. + * Always read $errors fresh from the view factory rather than caching on the + * singleton — a cached value goes stale in long-lived processes (Octane, etc.). */ public function __get(string $name): mixed { if ($name === 'errors') { - return $this->errors ??= $this->env->shared('errors') ?? new ViewErrorBag; + return $this->env->getShared()['errors'] ?? new ViewErrorBag; } throw new \InvalidArgumentException("Property {$name} does not exist");