From 4b96d2b15a1899ffaf04b1ef39ecadd9a9bceec6 Mon Sep 17 00:00:00 2001 From: Sander Muller Date: Sun, 29 Mar 2026 10:13:36 +0200 Subject: [PATCH 1/2] Reduce per-call overhead for self-closing components Use function_exists() guard instead of calling ensureRequired() on every render. After the first render, function_exists() is a C-level check that's faster than a PHP method call + hash table lookup. Also adds a test verifying that self-closing components correctly propagate data to @aware descendants via the data stack. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/Compiler/Compiler.php | 2 +- tests/Compiler/CompilerTest.php | 4 ++-- tests/IntegrationTest.php | 6 ++++++ tests/Memoizer/MemoizerTest.php | 2 +- tests/fixtures/views/components/aware-child.blade.php | 4 ++++ tests/fixtures/views/components/aware-wrapper.blade.php | 3 +++ 6 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 tests/fixtures/views/components/aware-child.blade.php create mode 100644 tests/fixtures/views/components/aware-wrapper.blade.php diff --git a/src/Compiler/Compiler.php b/src/Compiler/Compiler.php index f525116..84fb09a 100644 --- a/src/Compiler/Compiler.php +++ b/src/Compiler/Compiler.php @@ -96,7 +96,7 @@ protected function compileComponentTag(ComponentNode $node, ComponentSource $sou $functionName = ($this->manager->isFolding() ? '__' : '_') . $hash; [$attributesArrayString, $boundKeysArrayString, $originalKeysArrayString] = $this->compileAttributes($node); - $output = '<' . '?php $__blaze->ensureRequired(\'' . $source->path . '\', $__blaze->compiledPath.\'/'. $hash . '.php\'); ?>' . "\n"; + $output = '<' . '?php if (!function_exists(\'' . $functionName . '\')) { $__blaze->ensureRequired(\'' . $source->path . '\', $__blaze->compiledPath.\'/'. $hash . '.php\'); } ?>' . "\n"; if ($node->selfClosing) { $output .= '<' . '?php $__blaze->pushData(' . $attributesArrayString . '); ?>' . "\n"; diff --git a/tests/Compiler/CompilerTest.php b/tests/Compiler/CompilerTest.php index 8d34096..c993503 100644 --- a/tests/Compiler/CompilerTest.php +++ b/tests/Compiler/CompilerTest.php @@ -14,7 +14,7 @@ $hash = Utils::hash($path); expect($compiled->render())->toEqualCollapsingWhitespace(join('', [ - 'ensureRequired(\''. $path .'\', $__blaze->compiledPath.\'/'. $hash .'.php\'); ?> ', + 'ensureRequired(\''. $path .'\', $__blaze->compiledPath.\'/'. $hash .'.php\'); } ?> ', 'pushData([\'type\' => \'text\',\'disabled\' => $disabled]); ?> ', ' \'text\',\'disabled\' => $disabled], [], [\'disabled\'], [], $__this ?? (isset($this) ? $this : null)); ?> ', 'popData(); ?>', @@ -42,7 +42,7 @@ $hash = Utils::hash($path); expect($compiled->render())->toEqualCollapsingWhitespace(join('', [ - 'ensureRequired(\''. $path .'\', $__blaze->compiledPath.\'/'. $hash .'.php\'); ?> ', + 'ensureRequired(\''. $path .'\', $__blaze->compiledPath.\'/'. $hash .'.php\'); } ?> ', ' ', ' ', ' \'mt-8\']; ?> ', diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index c63a08c..8fb6b00 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -85,6 +85,12 @@ public function render() })->assertSee('from-livewire'); }); +test('self-closing component propagates data to @aware descendants', function () { + $html = Blade::render(''); + + expect($html)->toContain('text-blue'); +}); + test('folds and compiles the same component', function () { Blade::render(<<<'BLADE' {{-- Folded --}} diff --git a/tests/Memoizer/MemoizerTest.php b/tests/Memoizer/MemoizerTest.php index 3fcdff5..e9771f0 100644 --- a/tests/Memoizer/MemoizerTest.php +++ b/tests/Memoizer/MemoizerTest.php @@ -22,7 +22,7 @@ '', '', '', - 'ensureRequired(\''. $path .'\', $__blaze->compiledPath.\'/'. $hash .'.php\'); ?> ', + 'ensureRequired(\''. $path .'\', $__blaze->compiledPath.\'/'. $hash .'.php\'); } ?> ', 'pushData([\'src\' => $user->avatar]); ?> ', ' $user->avatar], [], [\'src\'], [], $__this ?? (isset($this) ? $this : null)); ?> ', 'popData(); ?>', diff --git a/tests/fixtures/views/components/aware-child.blade.php b/tests/fixtures/views/components/aware-child.blade.php new file mode 100644 index 0000000..be411c2 --- /dev/null +++ b/tests/fixtures/views/components/aware-child.blade.php @@ -0,0 +1,4 @@ +@blaze +@aware(['color' => 'gray']) + +Hello \ No newline at end of file diff --git a/tests/fixtures/views/components/aware-wrapper.blade.php b/tests/fixtures/views/components/aware-wrapper.blade.php new file mode 100644 index 0000000..30f09f8 --- /dev/null +++ b/tests/fixtures/views/components/aware-wrapper.blade.php @@ -0,0 +1,3 @@ +@blaze + +
\ No newline at end of file From d31efff702aa9956af15b760aaed809dcd63b23e Mon Sep 17 00:00:00 2001 From: Filip Ganyicz Date: Sun, 29 Mar 2026 19:37:10 +0200 Subject: [PATCH 2/2] Use function_exists() guard inside ensureRequired Move the function_exists() check into ensureRequired() so both the compiled caller and resolve() benefit from the C-level guard. Drop the $required hash table which is now redundant. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/Compiler/Compiler.php | 2 +- src/Runtime/BlazeRuntime.php | 14 +++++--------- tests/ComparisonTest.php | 5 +++++ tests/Compiler/CompilerTest.php | 4 ++-- tests/IntegrationTest.php | 6 ------ tests/Memoizer/MemoizerTest.php | 2 +- .../views/components/aware-child.blade.php | 4 ---- .../views/components/aware-wrapper.blade.php | 3 --- .../views/components/nested-aware.blade.php | 3 +++ 9 files changed, 17 insertions(+), 26 deletions(-) delete mode 100644 tests/fixtures/views/components/aware-child.blade.php delete mode 100644 tests/fixtures/views/components/aware-wrapper.blade.php create mode 100644 tests/fixtures/views/components/nested-aware.blade.php diff --git a/src/Compiler/Compiler.php b/src/Compiler/Compiler.php index 84fb09a..2e97d6e 100644 --- a/src/Compiler/Compiler.php +++ b/src/Compiler/Compiler.php @@ -96,7 +96,7 @@ protected function compileComponentTag(ComponentNode $node, ComponentSource $sou $functionName = ($this->manager->isFolding() ? '__' : '_') . $hash; [$attributesArrayString, $boundKeysArrayString, $originalKeysArrayString] = $this->compileAttributes($node); - $output = '<' . '?php if (!function_exists(\'' . $functionName . '\')) { $__blaze->ensureRequired(\'' . $source->path . '\', $__blaze->compiledPath.\'/'. $hash . '.php\'); } ?>' . "\n"; + $output = '<' . '?php $__blaze->ensureRequired(\'' . $source->path . '\', $__blaze->compiledPath.\'/'. $hash . '.php\', \'' . $functionName . '\'); ?>' . "\n"; if ($node->selfClosing) { $output .= '<' . '?php $__blaze->pushData(' . $attributesArrayString . '); ?>' . "\n"; diff --git a/src/Runtime/BlazeRuntime.php b/src/Runtime/BlazeRuntime.php index dd40220..81a0bd4 100644 --- a/src/Runtime/BlazeRuntime.php +++ b/src/Runtime/BlazeRuntime.php @@ -23,7 +23,6 @@ class BlazeRuntime protected ?string $compiledPath = null; protected array $paths = []; - protected array $required = []; protected array $blazed = []; protected array $dataStack = []; @@ -39,11 +38,11 @@ public function __construct( } /** - * Compile a component if its source is newer than the cached output. + * Compile and require a component if its function is not yet defined. */ - public function ensureRequired(string $path, string $compiledPath): void + public function ensureRequired(string $path, string $compiledPath, string $functionName): void { - if (isset($this->required[$compiledPath])) { + if (function_exists($functionName)) { return; } @@ -52,8 +51,6 @@ public function ensureRequired(string $path, string $compiledPath): void } require $compiledPath; - - $this->required[$compiledPath] = true; } /** @@ -77,10 +74,9 @@ public function resolve(string $component): string|false $hash = Utils::hash($path); $compiled = $this->getCompiledPath().'/'.$hash.'.php'; + $functionName = '_'.$hash; - if (! isset($this->required[$path])) { - $this->ensureRequired($path, $compiled); - } + $this->ensureRequired($path, $compiled, $functionName); return $hash; } diff --git a/tests/ComparisonTest.php b/tests/ComparisonTest.php index 165b09c..14fe16f 100644 --- a/tests/ComparisonTest.php +++ b/tests/ComparisonTest.php @@ -33,6 +33,11 @@ BLADE )); +test('aware nested', fn () => compare(<<<'BLADE' + + BLADE +)); + test('foldable aware', fn () => compare(<<<'BLADE' diff --git a/tests/Compiler/CompilerTest.php b/tests/Compiler/CompilerTest.php index c993503..e6f71ef 100644 --- a/tests/Compiler/CompilerTest.php +++ b/tests/Compiler/CompilerTest.php @@ -14,7 +14,7 @@ $hash = Utils::hash($path); expect($compiled->render())->toEqualCollapsingWhitespace(join('', [ - 'ensureRequired(\''. $path .'\', $__blaze->compiledPath.\'/'. $hash .'.php\'); } ?> ', + 'ensureRequired(\''. $path .'\', $__blaze->compiledPath.\'/'. $hash .'.php\', \'_'. $hash .'\'); ?> ', 'pushData([\'type\' => \'text\',\'disabled\' => $disabled]); ?> ', ' \'text\',\'disabled\' => $disabled], [], [\'disabled\'], [], $__this ?? (isset($this) ? $this : null)); ?> ', 'popData(); ?>', @@ -42,7 +42,7 @@ $hash = Utils::hash($path); expect($compiled->render())->toEqualCollapsingWhitespace(join('', [ - 'ensureRequired(\''. $path .'\', $__blaze->compiledPath.\'/'. $hash .'.php\'); } ?> ', + 'ensureRequired(\''. $path .'\', $__blaze->compiledPath.\'/'. $hash .'.php\', \'_'. $hash .'\'); ?> ', ' ', ' ', ' \'mt-8\']; ?> ', diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index 8fb6b00..c63a08c 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -85,12 +85,6 @@ public function render() })->assertSee('from-livewire'); }); -test('self-closing component propagates data to @aware descendants', function () { - $html = Blade::render(''); - - expect($html)->toContain('text-blue'); -}); - test('folds and compiles the same component', function () { Blade::render(<<<'BLADE' {{-- Folded --}} diff --git a/tests/Memoizer/MemoizerTest.php b/tests/Memoizer/MemoizerTest.php index e9771f0..9ac2d1c 100644 --- a/tests/Memoizer/MemoizerTest.php +++ b/tests/Memoizer/MemoizerTest.php @@ -22,7 +22,7 @@ '', '', '', - 'ensureRequired(\''. $path .'\', $__blaze->compiledPath.\'/'. $hash .'.php\'); } ?> ', + 'ensureRequired(\''. $path .'\', $__blaze->compiledPath.\'/'. $hash .'.php\', \'_'. $hash .'\'); ?> ', 'pushData([\'src\' => $user->avatar]); ?> ', ' $user->avatar], [], [\'src\'], [], $__this ?? (isset($this) ? $this : null)); ?> ', 'popData(); ?>', diff --git a/tests/fixtures/views/components/aware-child.blade.php b/tests/fixtures/views/components/aware-child.blade.php deleted file mode 100644 index be411c2..0000000 --- a/tests/fixtures/views/components/aware-child.blade.php +++ /dev/null @@ -1,4 +0,0 @@ -@blaze -@aware(['color' => 'gray']) - -Hello \ No newline at end of file diff --git a/tests/fixtures/views/components/aware-wrapper.blade.php b/tests/fixtures/views/components/aware-wrapper.blade.php deleted file mode 100644 index 30f09f8..0000000 --- a/tests/fixtures/views/components/aware-wrapper.blade.php +++ /dev/null @@ -1,3 +0,0 @@ -@blaze - -
\ No newline at end of file diff --git a/tests/fixtures/views/components/nested-aware.blade.php b/tests/fixtures/views/components/nested-aware.blade.php new file mode 100644 index 0000000..674ece8 --- /dev/null +++ b/tests/fixtures/views/components/nested-aware.blade.php @@ -0,0 +1,3 @@ +@blaze + + \ No newline at end of file