livewire/livewire: [4.x] Skip Volt functional components during SFC detection
Contributed by joshhanley
Part of the livewire v4.4.7 release
If you're upgrading from Livewire v3.8.10 into the 4.x series, PR #10367 removes a class of confusing crashes: Volt functional components that return file downloads could fail with PHP parse errors coming from Livewire's generated compiled files. The root cause sat in the single-file component (SFC) detection inside src/Finder/Finder.php, whose "light touch" regex matched far more than it should. This change narrows the pattern to the true anonymous-class shape, so real SFCs keep working while ordinary Volt views are left alone. There is no API change and nothing to migrate — but the fix explains errors you may have hit while testing 4.x.
The symptom: a download that dies with a parse error
Consider a perfectly valid Volt functional component:
<?php
$download = function () {
$file = new \SplFileInfo('export.csv');
return response()->download($file->getPathname());
};
?>
<div class="download">
<button wire:click="download">Download</button>
</div>
On affected 4.x builds, clicking that download button could throw a PHP parse error from a generated compiled file — messages like unexpected token "new" or unexpected token "return" — even though the component itself is syntactically fine. The expected behavior is simple: the download starts and the component continues rendering normally.
Why a harmless new plus an HTML class broke things
In Livewire 4, a .blade.php file whose PHP block declares an anonymous component class (new class extends Component { ... }) can act as a single-file component: class and template living in one file. To keep resolution fast, the Finder performs a cheap regex check before it attempts to treat a file as an SFC. The old check was:
// Pattern: <?php followed by 'new' and 'class'
// (with potential attributes/newlines between)
return preg_match('/\<\?php.*new\s+.*class/s', $contents) === 1;
Because the pattern allows arbitrary content between new and class — and the /s modifier lets that span the entire file — two innocent ingredients combined into a false positive: new \SplFileInfo(...) in the PHP block, and the literal text class="download" in the HTML template. The Finder then misidentified the functional Volt file as an SFC and tried to compile functional PHP (assignments, closures, return statements) as an anonymous component class, which predictably fails to parse.
The fix: match new class, allow attributes in between
The detection now requires the actual anonymous-class shape — new immediately followed by class, with word boundaries on both sides — while still permitting PHP attributes between the two keywords:
return preg_match('/\<\?php.*\bnew\s+(?:#\[[^\]]*\]\s*)*class\b/s', $contents) === 1;
This deliberately preserves every legitimate SFC form. Inline attributes still resolve:
new #[Layout('layouts.app')] #[Title('Dashboard')] class extends Component
{
//
};
So do attributes spread across multiple lines:
new
#[Layout('layouts.app')]
class extends Component
{
//
};
The PR adds fixtures and unit tests for both attribute styles, plus a regression test asserting that a Volt functional component is not resolved as a single-file component (it fixes issue #10351).
Upgrade impact: what changes for you
- Coming from v3.8.10: this bug only exists in 4.x, so you were never exposed to it on v3 — but the fix makes the jump safer if you use Volt, especially functional components that instantiate objects or return download responses.
- On an affected 4.x build: upgrade to a release containing #10367 and the errors disappear with no code changes on your side. Running
php artisan view:clearafterwards is a harmless way to evict any stale compiled artifacts from earlier attempts. - Existing SFCs: unaffected. Both the inline and multiline attribute forms are now locked in by tests, so your anonymous-class components keep resolving exactly as before.
- One theoretical edge case: if an SFC places a comment between
newandclass(for examplenew /* note */ class), the tightened check will no longer match and the file will be treated as a regular Blade view. That shape is unusual; keep at most attributes between the two keywords.
Takeaways
- Volt functional components returning file downloads no longer get miscompiled as anonymous component classes in Livewire 4.x.
- SFC detection now requires the
new classshape (optionally with#[...]attributes between them), instead of anynewfollowed by the textclassanywhere in the file. - Valid SFCs with inline or multiline attributes continue to work, and new fixtures plus regression tests cover both forms.
- Upgrading from v3.8.10 requires no code changes for this fix — just move to a 4.x release containing PR #10367, and optionally clear compiled views if you had hit the parse errors before.