Skip to content

Fix Livewire smart keys poisoning - #205

Merged
ganyicz merged 11 commits into
mainfrom
filip/fix-livewire-smart-keys
Aug 23, 2026
Merged

Fix Livewire smart keys poisoning#205
ganyicz merged 11 commits into
mainfrom
filip/fix-livewire-smart-keys

Conversation

@ganyicz

@ganyicz ganyicz commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

The scenario

When the following template is compiled and rendered in the same request, the Livewire component is assigned a corrupted wire:key when smart wire keys are enabled:

@if (false)
    <flux:select.option :value="$value">...</flux:select.option>
@endif

<livewire:probe />

When the view is first rendered: lw-2253758600-0-BLAZE_PLACEHOLDER_0_
During subsequent renders: lw-2253758600-0

The problem

Livewire keeps track of last rendered wire:key using a precompiler and a static cache.

During compilation, it prefixes each wire:key with a php block that stores its value:

<div <?php SupportCompiledWireKeys::$currentLoop['key'] = 'foo'; ?>wire:key="foo">

This value is then appended to the key of the next Livewire component.

The problem is that this also happens during isolated rendering, which poisons the static cache.


Let's take the original example:

@if (false)
    <flux:select.option :value="$value">...</flux:select.option>
@endif

<livewire:probe />

1. During folding:

<flux:select.option :value="$value"> is folded with <flux:select.option value="BLAZE_PLACEHOLDER_0_">

Because the value prop is internally used as wire:key, Livewire will store its value in SupportCompiledWireKeys::$currentLoop['key'] during isolated rendering.

2. After folding:

{{-- The $currentLoop['key'] is already set to 'BLAZE_PLACEHOLDER_0_' from isolated render  --}}

@if (false) {{-- This never executes --}}
    <option <?php SupportCompiledWireKeys::$currentLoop['key'] = $value; ?>wire:key="{{ $value }}">...</option>
@endif

{{-- $currentLoop['key'] is still set to 'BLAZE_PLACEHOLDER_0_'  --}}
<livewire:probe />

Result: The Livewire component gets wire key lw-2253758600-0-BLAZE_PLACEHOLDER_0_

3. On subsequent render:

{{-- The $currentLoop['key'] is null --}}

@if (false) {{-- This never executes --}}
    <option <?php SupportCompiledWireKeys::$currentLoop['key'] = $value; ?>wire:key="{{ $value }}">...</option>
@endif

{{-- $currentLoop['key'] is still null --}}
<livewire:probe />

Result: The Livewire component gets wire key lw-2253758600-0

The solution

The solution is to disable smart key compilation as well as all Livewire precompiles during folding as Livewire components shouldn't be folded. This PR also makes that explicit by aborting fold when Livewire components are detected.

Fixes #204

@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Benchmark Result: Default

Attempt Blade Blaze Change
#1 368.85ms 14.73ms 96%
#2 359.71ms 15.17ms 95.8%
#3 361.85ms 15.02ms 95.8%
#4 361.60ms 15.13ms 95.8%
#5 359.32ms 14.91ms 95.9%
#6 365.49ms 14.95ms 95.9%
#7 368.88ms 14.67ms 96%
#8 378.67ms 14.99ms 96%
#9 360.11ms 14.61ms 95.9%
#10 360.39ms 14.74ms 95.9%
Snapshot 366.20ms 14.83ms 96%
Result 361.73ms (~) 14.93ms (~) 95.9% (~)

Median of 10 attempts, 5000 iterations x 10 rounds, 47.86s total

To run a specific benchmark, comment /benchmark <name>
attributes, aware, class, default, forwarding, merge, named-slots, no-attributes, slot, compilation

@ganyicz
ganyicz marked this pull request as draft August 15, 2026 17:45
@ganyicz

ganyicz commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator Author

/benchmark compilation

@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Result: Compilation

Attempt Blade Blaze Change
#1 2.30ms 21.50ms -834.8%
#2 2.28ms 21.72ms -852.6%
#3 2.28ms 21.78ms -855.3%
#4 2.28ms 21.56ms -845.6%
#5 2.24ms 21.53ms -861.2%
#6 2.31ms 21.73ms -840.7%
#7 2.28ms 21.54ms -844.7%
#8 2.22ms 21.62ms -873.9%
#9 2.21ms 21.45ms -870.6%
#10 2.26ms 21.65ms -858%
Snapshot 2.27ms 21.79ms -859.9%
Result 2.28ms (~) 21.59ms (~) -846.9% (+13%)

Median of 10 attempts, 5000 iterations x 10 rounds, 5.3s total

To run a specific benchmark, comment /benchmark <name>
attributes, aware, class, default, forwarding, merge, named-slots, no-attributes, slot, compilation

@ganyicz
ganyicz marked this pull request as ready for review August 23, 2026 21:24
@ganyicz
ganyicz merged commit e50a534 into main Aug 23, 2026
8 checks passed
@ganyicz
ganyicz deleted the filip/fix-livewire-smart-keys branch August 23, 2026 22:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compile-time fold executes Livewire's wire:key runtime statement, poisoning SupportCompiledWireKeys for subsequent mounts

1 participant