Skip to content

Commit 018139c

Browse files
authored
Improve compile speed (#116)
* Improve fold caching * Add parallel view:cache * Refactor * Refactor * Formatting * Tweaks * Refactor * Fix processes option * Render foldable components with Blaze * Update blaze.php * Update blaze.php * Cache array parser * Cache php parser * Drop legacy test helpers * Cache ComponentSource * Fix stray output buffer * Refactor
1 parent 21cb11a commit 018139c

File tree

17 files changed

+197
-133
lines changed

17 files changed

+197
-133
lines changed

src/BladeRenderer.php

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,17 @@
33
namespace Livewire\Blaze;
44

55
use Illuminate\Contracts\View\Factory;
6+
use Illuminate\Support\Arr;
67
use Illuminate\Support\Facades\File;
78
use Illuminate\View\Compilers\BladeCompiler;
89
use Illuminate\View\Component;
10+
use Illuminate\View\ComponentSlot;
11+
use Livewire\Blaze\Parser\Attribute;
12+
use Livewire\Blaze\Parser\Nodes\ComponentNode;
13+
use Livewire\Blaze\Parser\Nodes\SlotNode;
914
use Livewire\Blaze\Runtime\BlazeRuntime;
15+
use Livewire\Blaze\Support\ComponentSource;
16+
use Livewire\Blaze\Support\Utils;
1017
use ReflectionClass;
1118

1219
/**
@@ -32,7 +39,7 @@ public function getTemporaryCachePath(): string
3239
/**
3340
* Render a Blade template string in isolation by freezing and restoring compiler state.
3441
*/
35-
public function render(string $template): string
42+
public function render(ComponentNode $component, ComponentSource $source): string
3643
{
3744
$temporaryCachePath = $this->getTemporaryCachePath();
3845

@@ -85,21 +92,51 @@ function ($input) {
8592
'slotsStack' => [],
8693
]);
8794

88-
$restoreComponent = $this->freezeObjectProperties(Component::class, [
89-
'bladeViewCache' => [],
90-
]);
95+
$obLevel = ob_get_level();
96+
$hash = Utils::hash($source->path);
97+
$path = $temporaryCachePath . '/' . $hash . '.php';
98+
$fn = '__' . $hash;
99+
100+
$this->manager->startFolding();
91101

92102
try {
93-
$this->manager->startFolding();
103+
if (! file_exists($path)) {
104+
$this->blade->compile($source->path);
105+
}
106+
107+
$attributes = Arr::mapWithKeys($component->attributes, function (Attribute $attribute) {
108+
return [$attribute->name => $attribute->getStaticValue()];
109+
});
110+
111+
$slots = Arr::mapWithKeys($component->children, function (SlotNode $slot) {
112+
return [$slot->name => new ComponentSlot($slot->content())];
113+
});
114+
115+
$this->runtime->pushData($attributes);
116+
$this->runtime->pushSlots($slots);
117+
118+
ob_start();
119+
120+
require_once $path;
94121

95-
$result = $this->blade->render($template, deleteCachedView: true);
122+
$fn(
123+
__blaze: $this->runtime,
124+
__data: $attributes,
125+
__slots: $slots,
126+
);
127+
128+
$result = ltrim(ob_get_clean());
96129
} finally {
130+
while (ob_get_level() > $obLevel) {
131+
ob_end_clean();
132+
}
133+
134+
$this->runtime->popData();
135+
$this->manager->stopFolding();
136+
97137
$restoreCompiler();
98138
$restoreFactory();
99139
$restoreRuntime();
100-
$restoreComponent();
101-
102-
$this->manager->stopFolding();
103140
}
104141

105142
$result = Unblaze::replaceUnblazePrecompiledDirectives($result);

src/BlazeManager.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,6 @@ public function compile(string $template, ?string $path = null): string
126126

127127
$output = $this->blade->restoreRawBlocks($output);
128128

129-
try {
130-
$this->renderer->deleteTemporaryCacheDirectory();
131-
} catch (\Throwable $e) {
132-
//
133-
}
134-
135129
return $output;
136130
}
137131

@@ -420,7 +414,7 @@ protected function hasAwareDescendant(ComponentNode|SlotNode $node): bool
420414
{
421415
foreach ($node->children as $child) {
422416
if ($child instanceof ComponentNode) {
423-
$source = new ComponentSource($this->blade->componentNameToPath($child->name));
417+
$source = ComponentSource::for($this->blade->componentNameToPath($child->name));
424418

425419
if (str_ends_with($child->name, 'delegate-component')) {
426420
return true;

src/BlazeServiceProvider.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ public function register(): void
2323
$this->app->singleton(Profiler::class);
2424
$this->app->singleton(BlazeManager::class);
2525

26+
$this->app->singleton(\PhpParser\Parser::class, function () {
27+
return (new \PhpParser\ParserFactory)->createForNewestSupportedVersion();
28+
});
29+
2630
$this->app->alias(BlazeManager::class, Blaze::class);
2731

2832
$this->app->alias(BlazeManager::class, 'blaze');

src/Compiler/ArrayParser.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use PhpParser\Node\Scalar\Int_;
1212
use PhpParser\Node\Scalar\String_;
1313
use PhpParser\Node\Stmt\Expression;
14+
use PhpParser\Parser;
1415
use PhpParser\ParserFactory;
1516

1617
/**
@@ -64,10 +65,8 @@ public static function parse(string $expression): array
6465
*/
6566
protected static function parseToArrayNode(string $expression): Array_
6667
{
67-
$parser = (new ParserFactory)->createForNewestSupportedVersion();
68-
6968
try {
70-
$ast = $parser->parse('<?php ' . $expression . ';');
69+
$ast = app(Parser::class)->parse('<?php ' . $expression . ';');
7170
} catch (\Throwable $e) {
7271
throw new ArrayParserException($expression, $e->getMessage());
7372
}

src/Compiler/Compiler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function compile(Node $node): Node
4343
return new TextNode($this->compileDelegateComponentTag($node));
4444
}
4545

46-
$source = new ComponentSource($this->blade->componentNameToPath($node->name));
46+
$source = ComponentSource::for($this->blade->componentNameToPath($node->name));
4747

4848
if (! $source->exists()) {
4949
return $node;

src/Compiler/Profiler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public function __construct(
3131
*/
3232
public function profile(Node $node, string $componentName, ?string $strategy = null): Node
3333
{
34-
$source = new ComponentSource($this->blade->componentNameToPath($componentName));
34+
$source = ComponentSource::for($this->blade->componentNameToPath($componentName));
3535

3636
if ($strategy === null) {
3737
$isBlade = $node instanceof ComponentNode;

src/Compiler/UseExtractor.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use PhpParser\Node\Stmt\GroupUse;
66
use PhpParser\Node\Stmt\Use_;
7-
use PhpParser\ParserFactory;
7+
use PhpParser\Parser;
88

99
/**
1010
* Extracts use statements from raw PHP blocks in compiled templates.
@@ -24,10 +24,8 @@ public function extract(string $compiled, callable $callback): string
2424
$inner = $isDirective ? $match[2] : $match[1];
2525
$block = '<?php' . $inner;
2626

27-
$parser = (new ParserFactory)->createForNewestSupportedVersion();
28-
2927
try {
30-
$ast = $parser->parse($block);
28+
$ast = app(Parser::class)->parse($block);
3129
} catch (\Throwable) {
3230
return $match[0];
3331
}

src/Folder/Foldable.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class Foldable
2121
{
2222
protected array $attributeByPlaceholder = [];
2323
protected array $slotByPlaceholder = [];
24+
protected int $placeholderIndex = 0;
2425

2526
protected ComponentNode $renderable;
2627
protected string $html;
@@ -51,7 +52,7 @@ public function fold(): string
5152
$this->setupSlots();
5253
$this->mergeAwareProps();
5354

54-
$this->html = $this->renderer->render($this->renderable->render());
55+
$this->html = $this->renderer->render($this->renderable, $this->source);
5556

5657
$this->processUncompiledAttributes();
5758
$this->restorePlaceholders();
@@ -67,7 +68,7 @@ protected function setupAttributes(): void
6768
{
6869
foreach ($this->node->attributes as $key => $attribute) {
6970
if (! $attribute->isStaticValue()) {
70-
$placeholder = 'BLAZE_PLACEHOLDER_' . strtoupper(str()->random());
71+
$placeholder = 'BLAZE_PLACEHOLDER_' . $this->placeholderIndex++ . '_';
7172

7273
$this->attributeByPlaceholder[$placeholder] = $attribute;
7374

@@ -95,7 +96,7 @@ protected function setupSlots(): void
9596

9697
foreach ($this->node->children as $child) {
9798
if ($child instanceof SlotNode) {
98-
$placeholder = 'BLAZE_PLACEHOLDER_' . strtoupper(str()->random());
99+
$placeholder = 'BLAZE_PLACEHOLDER_' . $this->placeholderIndex++ . '_';
99100

100101
$this->slotByPlaceholder[$placeholder] = $child;
101102

@@ -115,7 +116,7 @@ protected function setupSlots(): void
115116

116117
// Synthesize a default slot from loose content when there's not an explicit one
117118
if ($looseContent && ! isset($slots['slot'])) {
118-
$placeholder = 'BLAZE_PLACEHOLDER_' . strtoupper(str()->random());
119+
$placeholder = 'BLAZE_PLACEHOLDER_' . $this->placeholderIndex++ . '_';
119120

120121
$defaultSlot = new SlotNode(
121122
name: 'slot',
@@ -160,7 +161,7 @@ protected function mergeAwareProps(): void
160161
$attribute = $this->node->parentsAttributes[$prop];
161162

162163
if (! $attribute->isStaticValue()) {
163-
$placeholder = 'BLAZE_PLACEHOLDER_' . strtoupper(str()->random());
164+
$placeholder = 'BLAZE_PLACEHOLDER_' . $this->placeholderIndex++ . '_';
164165

165166
$this->attributeByPlaceholder[$placeholder] = $attribute;
166167

@@ -201,7 +202,7 @@ protected function mergeAwareProps(): void
201202
*/
202203
protected function processUncompiledAttributes(): void
203204
{
204-
$this->html = preg_replace_callback('/\[BLAZE_ATTR:(BLAZE_PLACEHOLDER_[A-Z0-9]+)\](\r?\n)?/', function ($matches) {
205+
$this->html = preg_replace_callback('/\[BLAZE_ATTR:(BLAZE_PLACEHOLDER_[0-9]+_)\](\r?\n)?/', function ($matches) {
205206
$attribute = $this->attributeByPlaceholder[$matches[1]];
206207

207208
if ($attribute->bound()) {

src/Folder/Folder.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Livewire\Blaze\BlazeManager;
1616
use Illuminate\Support\Arr;
1717
use Livewire\Blaze\Config;
18+
use Throwable;
1819

1920
/**
2021
* Determines whether a component should be folded and orchestrates the folding process.
@@ -40,7 +41,7 @@ public function fold(Node $node): Node
4041

4142
$component = $node;
4243

43-
$source = new ComponentSource($this->blade->componentNameToPath($component->name));
44+
$source = ComponentSource::for($this->blade->componentNameToPath($component->name));
4445

4546
if (! $source->exists()) {
4647
return $component;
@@ -68,9 +69,9 @@ public function fold(Node $node): Node
6869
));
6970

7071
return new TextNode('<?php ob_start(); ?>' . $html . '<?php echo ltrim(ob_get_clean()); ?>');
71-
} catch (\Exception $e) {
72+
} catch (Throwable $th) {
7273
if ($this->manager->shouldThrow()) {
73-
throw $e;
74+
throw $th;
7475
}
7576

7677
return $node;
@@ -179,7 +180,7 @@ protected function slotHasDynamicAttributes(SlotNode $slot): bool
179180
protected function checkProblematicPatterns(ComponentSource $source): void
180181
{
181182
// @unblaze blocks can contain dynamic content and are excluded from validation
182-
$sourceWithoutUnblaze = preg_replace('/@unblaze.*?@endunblaze/s', '', $source->content);
183+
$sourceWithoutUnblaze = preg_replace('/@unblaze.*?@endunblaze/s', '', $source->content());
183184

184185
$problematicPatterns = [
185186
'@once' => 'forOnce',

src/Memoizer/Memoizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ protected function isMemoizable(Node $node): bool
8181
return false;
8282
}
8383

84-
$source = new ComponentSource($this->blade->componentNameToPath($node->name));
84+
$source = ComponentSource::for($this->blade->componentNameToPath($node->name));
8585

8686
if (! $source->exists()) {
8787
return false;

0 commit comments

Comments
 (0)