Sheath rule
blade-valid-directive-arguments
#Why
Blade compiles every known directive it meets, arguments or not, sane or not.
Nothing warns at compile time; the page breaks at first render, and the error
points into storage/:
<p>You should @if something is wrong</p> -> if: invalid PHP
<style>@use "sass:math";</style> -> use ; invalid PHP
<form>@method</form> -> echo method_field; fatals at render
@vite -> app(Vite::class)(); ArgumentCountError
@if() -> if(): invalid PHP
@method(/* nothing */) -> method_field(); ArgumentCountError
@lang() -> translator->get(); ArgumentCountError
@include(‘partials.nav’) -> make(‘partials.nav’) fatals at render
@foreach($items) -> ViewCompilationException at render
Most bare occurrences are not Blade at all: prose that happens to say @if,
JSDoc's @error, SCSS's @use/@each, CSS that mentions a directive name.
Blade compiles them anyway. The @@ escape (@@if) hands the text to the
page as written.
Two argument defects get the same treatment because Blade accepts them
silently: smart quotes (‘ ’ “ ”) compile into PHP that fails at first render
with an undefined-constant error, and directive arguments must open their (
on the same line as the directive. A newline in between compiles the bare
directive and renders the parenthesized text as page output.
#Examples
#Bad
<p>You should @if something is wrong check twice.</p>
<style>@use "sass:math";</style>
<form>@method</form>
@vite
@include(‘partials.nav’)
@foreach($items)
<li>Missing the as clause</li>
@endforeach
@if
($condition)
<p>Arguments on the next line are not arguments</p>
@endif
<style>div:empty { color: red } @empty</style>
#Good
@if($user->isAdmin())
<p>Admin</p>
@endif
@foreach($items as $item)
<li>{{ $item }}</li>
@endforeach
@include('partials.nav')
{{-- The escape for prose, CSS, and JS that collide with directive names --}}
<p>You should @@if something is wrong check twice.</p>
<style>@@use "sass:math";</style>
{{-- Bare forms that are legitimate Blade are never reported --}}
@auth
<p>Signed in</p>
@endauth
@once
<script src="widget.js"></script>
@endonce
@csrf
{{-- Unknown names pass through Blade untouched --}}
<p>Email john@stillat.com, or find @john on Slack.</p>
<style>@media (min-width: 640px) { .cards { display: flex; } }</style>
{{-- Blade's argument matcher understands strings, so this is fine --}}
@if($label == ')')
<p>Closing paren</p>
@endif
#What Is Checked
The following checks apply to core directives whose bare form always
miscompiles, including @if,
@foreach, @include, @section, @method, @json, @error, @use, @vite,
@can, and the rest of their families. Supported Laravel families also include
@session, @context, @fragment, @hasstack, @pushif,
@includeisolated, @extendsfirst, and @componentfirst.
- Missing arguments. This is usually a prose, JSDoc, or SCSS/CSS collision, or the newline mistake above.
- Arguments opening on the next line. Blade only attaches arguments
whose
(starts on the same line. - Arguments that are not valid PHP. Validation applies whenever a
known directive supplies them, including directives such as
@authand@guestwhose arguments are optional. Loop syntax such as@for($i = 0; $i < 10; $i++)and@foreach($items as $item)is accepted.
@dd and @dump still require same-line parentheses, but they accept an empty
argument list. Calls such as @dd() and @dump() therefore have valid
arguments. See blade-no-debug for restricting
debug calls.
For @foreach/@forelse specifically, the arguments must satisfy Blade's own
as test, or the view throws Malformed @foreach statement at first
render. Smart quotes outside PHP strings and comments are reported; Unicode
quote characters inside an ordinary quoted PHP string or comment are valid and
are left alone.
Directives that are legitimately bare (@auth, @guest, @once, @csrf,
@production, @php, @verbatim, @else, every @end*, ...) are
never in the requires-arguments set, and unknown directive names are never
reported at all. The bare @lang ... @endlang translation block is also
valid, while the standalone @lang() form is not: it compiles to
translator->get() without the required translation key. When an optional
directive does supply arguments, those arguments must still be valid PHP.
@empty is context-dependent: its bare form is only valid as a @forelse
branch. Anywhere else, including CSS's
div:empty or prose, Blade compiles the forelse empty branch, emitting a stray
endforeach with no loop to close, so a bare @empty outside @forelse is
reported.
@vite requires an entrypoint on the same line. Blade compiles a bare @vite
to a call to Illuminate\Foundation\Vite with zero arguments, and that
invokable requires its $entrypoints argument, so the view throws an
ArgumentCountError when rendered.
#Notes
- An unclosed prose
@ifalso lacks a matching terminator. Escape prose as@@if, and see blade-unclosed-directives for block-pairing requirements. - PHP validation reports only arguments that PHP rejects.
- No auto-fix is available because a bare
@ifmay be prose to escape or a directive missing its condition.
#Related Rules
- blade-unclosed-directives - unpaired block directives
- blade-no-directive-attribute-collision - the same collision in event-listener attributes
- blade-valid-echo-expression - the echo-side twin of this rule