From 51b106b94ea73c5f5d3b9fad4f427798e15ddbf4 Mon Sep 17 00:00:00 2001 From: Matt Harrison Date: Wed, 19 Aug 2026 14:18:51 -0400 Subject: [PATCH 1/6] Fix cached unblaze blocks after state flush Persist unblaze replacement content in compiled components so cached components can restore their replacements after Octane clears static state. Add a regression test covering cached component rendering across state flushes. --- src/Unblaze.php | 10 +++++++--- tests/BladeRendererTest.php | 13 +++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Unblaze.php b/src/Unblaze.php index 2fffbb35..83c00a56 100644 --- a/src/Unblaze.php +++ b/src/Unblaze.php @@ -17,9 +17,13 @@ class Unblaze /** * Store runtime scope data for an @unblaze token. */ - public static function storeScope($token, $scope = []) + public static function storeScope($token, $scope = [], $replacement = null) { static::$unblazeScopes[$token] = $scope; + + if ($replacement !== null) { + static::$unblazeReplacements[$token] = $replacement; + } } /** @@ -52,14 +56,14 @@ public static function processUnblazeDirectives(string $template) $result = preg_replace_callback('/(\[STARTUNBLAZE:([0-9a-zA-Z]+)\])(.*?)(\[ENDUNBLAZE\])/s', function ($matches) use (&$expressionsByToken) { $token = $matches[2]; - $expression = $expressionsByToken[$token]; + $expression = $expressionsByToken[$token] ?: '[]'; $innerContent = $matches[3]; static::$unblazeReplacements[$token] = $innerContent; return '' . '[STARTCOMPILEDUNBLAZE:'.$token.']' - . '<'.'?php \Livewire\Blaze\Unblaze::storeScope("'.$token.'", '.$expression.') ?>' + . '<'.'?php \Livewire\Blaze\Unblaze::storeScope("'.$token.'", '.$expression.', replacement: base64_decode("'.base64_encode($innerContent).'")) ?>' . '[ENDCOMPILEDUNBLAZE:'.$token.']'; }, $result); diff --git a/tests/BladeRendererTest.php b/tests/BladeRendererTest.php index b760328e..ed8df353 100644 --- a/tests/BladeRendererTest.php +++ b/tests/BladeRendererTest.php @@ -7,6 +7,7 @@ use Livewire\Blaze\Parser\Parser; use Livewire\Blaze\Support\AttributeParser; use Livewire\Blaze\Support\Utils; +use Livewire\Blaze\Unblaze; use function Livewire\invade; @@ -62,6 +63,18 @@ ]))); }); +test('processes cached unblaze blocks after request state is flushed', function () { + $path = fixture_path('views/components/foldable/input-unblaze.blade.php'); + $node = app(Parser::class)->parse('')[0]; + $renderer = app(BladeRenderer::class); + + $firstRender = $renderer->render($node, $path); + + Unblaze::flushState(); + + expect($renderer->render($node, $path))->toBe($firstRender); +}); + test('recompiles stale cache of folded components', function () { $dir = sys_get_temp_dir().'/blaze-'.uniqid(); $path = $dir.'/button.blade.php'; From bdecd2028f4db35fb2d36af61e8f452d1b51c788 Mon Sep 17 00:00:00 2001 From: Filip Ganyicz Date: Mon, 24 Aug 2026 01:46:51 +0200 Subject: [PATCH 2/6] Refactor --- src/Unblaze.php | 15 ++++++++++----- tests/BladeRendererTest.php | 12 ------------ tests/IntegrationTest.php | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/Unblaze.php b/src/Unblaze.php index 83c00a56..603bffe7 100644 --- a/src/Unblaze.php +++ b/src/Unblaze.php @@ -17,13 +17,17 @@ class Unblaze /** * Store runtime scope data for an @unblaze token. */ - public static function storeScope($token, $scope = [], $replacement = null) + public static function storeScope($token, $scope = []) { static::$unblazeScopes[$token] = $scope; + } - if ($replacement !== null) { - static::$unblazeReplacements[$token] = $replacement; - } + /** + * Store runtime scope data for an @unblaze token. + */ + public static function storeReplacement(string $token, string $replacement) + { + static::$unblazeReplacements[$token] ??= base64_decode($replacement); } /** @@ -63,7 +67,8 @@ public static function processUnblazeDirectives(string $template) return '' . '[STARTCOMPILEDUNBLAZE:'.$token.']' - . '<'.'?php \Livewire\Blaze\Unblaze::storeScope("'.$token.'", '.$expression.', replacement: base64_decode("'.base64_encode($innerContent).'")) ?>' + . '<'.'?php \Livewire\Blaze\Unblaze::storeScope("'.$token.'", '.$expression.'); ?>' + . '<'.'?php \Livewire\Blaze\Unblaze::storeReplacement("'.$token.'", "'.base64_encode($innerContent).'"); ?>' . '[ENDCOMPILEDUNBLAZE:'.$token.']'; }, $result); diff --git a/tests/BladeRendererTest.php b/tests/BladeRendererTest.php index ed8df353..7c540c4e 100644 --- a/tests/BladeRendererTest.php +++ b/tests/BladeRendererTest.php @@ -63,18 +63,6 @@ ]))); }); -test('processes cached unblaze blocks after request state is flushed', function () { - $path = fixture_path('views/components/foldable/input-unblaze.blade.php'); - $node = app(Parser::class)->parse('')[0]; - $renderer = app(BladeRenderer::class); - - $firstRender = $renderer->render($node, $path); - - Unblaze::flushState(); - - expect($renderer->render($node, $path))->toBe($firstRender); -}); - test('recompiles stale cache of folded components', function () { $dir = sys_get_temp_dir().'/blaze-'.uniqid(); $path = $dir.'/button.blade.php'; diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index 25c902a5..44e81fc8 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -8,6 +8,8 @@ use Livewire\Blaze\Blaze; use Livewire\Blaze\BlazeManager; use Livewire\Blaze\Runtime\BlazeRuntime; +use Livewire\Blaze\Unblaze; +use Illuminate\Support\Facades\View; beforeEach(fn () => Artisan::call('view:clear')); @@ -93,3 +95,16 @@ public function render() ['required' => true] ); })->throwsNoExceptions(); + +test('compiled unblaze blocks can be restored in separate process', function () { + View::share('errors', new \Illuminate\Support\ViewErrorBag); + + // Compile a component with unblaze blocks... + Blade::render(''); + + // Simulate a separate process without stored replacements... + Unblaze::flushState(); + + // Ensure we can restore the compiled unblaze blocks while folding a parent component... + Blade::render(''); +})->throwsNoExceptions(); From 92032e38c085a4151bb7af5470c7d8f921b51442 Mon Sep 17 00:00:00 2001 From: Filip Ganyicz Date: Mon, 24 Aug 2026 01:48:27 +0200 Subject: [PATCH 3/6] Formatting --- src/Unblaze.php | 6 +++--- tests/BladeRendererTest.php | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Unblaze.php b/src/Unblaze.php index 603bffe7..0a993f4c 100644 --- a/src/Unblaze.php +++ b/src/Unblaze.php @@ -60,15 +60,15 @@ public static function processUnblazeDirectives(string $template) $result = preg_replace_callback('/(\[STARTUNBLAZE:([0-9a-zA-Z]+)\])(.*?)(\[ENDUNBLAZE\])/s', function ($matches) use (&$expressionsByToken) { $token = $matches[2]; - $expression = $expressionsByToken[$token] ?: '[]'; + $expression = $expressionsByToken[$token]; $innerContent = $matches[3]; static::$unblazeReplacements[$token] = $innerContent; return '' . '[STARTCOMPILEDUNBLAZE:'.$token.']' - . '<'.'?php \Livewire\Blaze\Unblaze::storeScope("'.$token.'", '.$expression.'); ?>' - . '<'.'?php \Livewire\Blaze\Unblaze::storeReplacement("'.$token.'", "'.base64_encode($innerContent).'"); ?>' + . '<'.'?php \Livewire\Blaze\Unblaze::storeScope("'.$token.'", '.$expression.') ?>' + . '<'.'?php \Livewire\Blaze\Unblaze::storeReplacement("'.$token.'", "'.base64_encode($innerContent).'") ?>' . '[ENDCOMPILEDUNBLAZE:'.$token.']'; }, $result); diff --git a/tests/BladeRendererTest.php b/tests/BladeRendererTest.php index 7c540c4e..b760328e 100644 --- a/tests/BladeRendererTest.php +++ b/tests/BladeRendererTest.php @@ -7,7 +7,6 @@ use Livewire\Blaze\Parser\Parser; use Livewire\Blaze\Support\AttributeParser; use Livewire\Blaze\Support\Utils; -use Livewire\Blaze\Unblaze; use function Livewire\invade; From 71b28f3fcc125aabc07667b7a5e49b4872f60a82 Mon Sep 17 00:00:00 2001 From: Filip Ganyicz Date: Mon, 24 Aug 2026 02:01:13 +0200 Subject: [PATCH 4/6] Improve test --- tests/IntegrationTest.php | 8 +++++--- .../components/foldable/nested-input-unblaze.blade.php | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index 44e81fc8..b9336026 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -100,11 +100,13 @@ public function render() View::share('errors', new \Illuminate\Support\ViewErrorBag); // Compile a component with unblaze blocks... - Blade::render(''); + $nested = Blade::render(''); // Simulate a separate process without stored replacements... Unblaze::flushState(); // Ensure we can restore the compiled unblaze blocks while folding a parent component... - Blade::render(''); -})->throwsNoExceptions(); + $wrapper = Blade::render(''); + + expect($wrapper)->toContain($nested); +}); diff --git a/tests/fixtures/views/components/foldable/nested-input-unblaze.blade.php b/tests/fixtures/views/components/foldable/nested-input-unblaze.blade.php index 0fbe3abd..7b593c8a 100644 --- a/tests/fixtures/views/components/foldable/nested-input-unblaze.blade.php +++ b/tests/fixtures/views/components/foldable/nested-input-unblaze.blade.php @@ -1,5 +1,5 @@ @blaze(fold: true)
- +
From 3db881df34b734470abf650ea35b651648ebf0a4 Mon Sep 17 00:00:00 2001 From: Filip Ganyicz Date: Mon, 24 Aug 2026 02:36:01 +0200 Subject: [PATCH 5/6] Move test --- tests/Folder/UnblazeTest.php | 20 +++++++++++++++++++ tests/IntegrationTest.php | 17 ---------------- .../foldable/nested-input-unblaze.blade.php | 2 +- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/tests/Folder/UnblazeTest.php b/tests/Folder/UnblazeTest.php index bdc7c907..df0823b6 100644 --- a/tests/Folder/UnblazeTest.php +++ b/tests/Folder/UnblazeTest.php @@ -1,9 +1,13 @@ Artisan::call('view:clear')); test('compiles unblaze blocks', function () { $input = ''; @@ -52,3 +56,19 @@ ])) ); }); + +test('processes replacements in compiled files', function () { + $path = fixture_path('views/components/foldable/input-unblaze.blade.php'); + $node = app(Parser::class)->parse('')[0]; + + // Force compilation and store replacements... + $before = app(BladeRenderer::class)->render($node, $path); + + // Flush replacements to simulate a separate process... + Unblaze::flushState(); + + // Ensure we can restore replacements from the compiled file... + $after = app(BladeRenderer::class)->render($node, $path); + + expect($before)->toBe($after); +}); diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index b9336026..25c902a5 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -8,8 +8,6 @@ use Livewire\Blaze\Blaze; use Livewire\Blaze\BlazeManager; use Livewire\Blaze\Runtime\BlazeRuntime; -use Livewire\Blaze\Unblaze; -use Illuminate\Support\Facades\View; beforeEach(fn () => Artisan::call('view:clear')); @@ -95,18 +93,3 @@ public function render() ['required' => true] ); })->throwsNoExceptions(); - -test('compiled unblaze blocks can be restored in separate process', function () { - View::share('errors', new \Illuminate\Support\ViewErrorBag); - - // Compile a component with unblaze blocks... - $nested = Blade::render(''); - - // Simulate a separate process without stored replacements... - Unblaze::flushState(); - - // Ensure we can restore the compiled unblaze blocks while folding a parent component... - $wrapper = Blade::render(''); - - expect($wrapper)->toContain($nested); -}); diff --git a/tests/fixtures/views/components/foldable/nested-input-unblaze.blade.php b/tests/fixtures/views/components/foldable/nested-input-unblaze.blade.php index 7b593c8a..0fbe3abd 100644 --- a/tests/fixtures/views/components/foldable/nested-input-unblaze.blade.php +++ b/tests/fixtures/views/components/foldable/nested-input-unblaze.blade.php @@ -1,5 +1,5 @@ @blaze(fold: true)
- +
From 750f544ea181b0e96d1300770077599ec4b53bef Mon Sep 17 00:00:00 2001 From: Filip Ganyicz Date: Mon, 24 Aug 2026 02:37:24 +0200 Subject: [PATCH 6/6] Formatting --- src/Unblaze.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Unblaze.php b/src/Unblaze.php index 0a993f4c..418d879c 100644 --- a/src/Unblaze.php +++ b/src/Unblaze.php @@ -23,7 +23,7 @@ public static function storeScope($token, $scope = []) } /** - * Store runtime scope data for an @unblaze token. + * Store runtime replacement content for an @unblaze token. */ public static function storeReplacement(string $token, string $replacement) {