Parentheses On Echo
ID |
php.parentheses_calls_echo |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Code Smell |
Language |
Php |
Tags |
code_smell, readability |
Description
Reports echo(…) written as though echo were a function.
The trigger is deliberately narrow: the whole of echo’s single argument must be one parenthesised
group. That is the form that reads as a call, and the only form where dropping the parentheses is
unconditionally safe. Parentheses that carry meaning are left alone — `echo (1 + 2) * 3; needs them
to group the addition, echo (string) $v; is a cast, echo ($obj)→name(); parenthesises the
receiver of a chain, and echo sprintf('%s', $v); is a real function call.
print is not covered, though it looks the same. Unlike echo it is a real expression that yields
1, so print($x) is not a false claim about the language, and its parentheses can be
load-bearing: print binds looser than almost every operator, so removing them changes what gets
printed.
Rationale
echo is a language construct, not a function. The parentheses parse — as an ordinary
parenthesised expression around the single thing being printed — so the line works, and that is
precisely what makes it misleading: it tells the reader something the language does not support.
Two things follow that a reader who believes it will get wrong. echo takes a comma-separated list
of values, so echo "a", "b"; is valid while echo("a", "b"); is a parse error — and there is no
way to read the second form as the call it resembles. And echo is a statement, so it has no
value: it cannot be used where a function’s result would be expected, not in an expression, not as
a callback, not with array_map. The parentheses invite exactly the edit that will not compile.
<?php
class InvoiceView
{
public function render(array $invoice): void
{
echo("Invoice " . $invoice['id']); // FLAW
echo ($invoice['total'] > 0 ? 'due' : 'settled'); // FLAW
echo 'Invoice ' . $invoice['id']; // OK
echo 'Customer: ', $invoice['customer'], "\n"; // OK - the list form
echo (1 + $invoice['lines']) * 2; // OK - grouping, not a call
echo (string) $invoice['total']; // OK - a cast
echo sprintf('%0.2f', $invoice['total']); // OK - a real call
}
}
Remediation
Drop the parentheses. Where several values are being printed, use echo’s own comma-separated list
rather than concatenating them — it avoids building the joined string at all. Where the value really
is the result of a call, keep the call’s own parentheses and leave `echo bare.
<?php
echo 'Invoice ' . $invoice['id'];
echo 'Invoice ', $invoice['id'], "\n";
echo sprintf('%0.2f', $invoice['total']);
Related rules
php.include_without_parens and php.return_without_parens cover the other language constructs of
this family, and php.avoid_concat_echo covers concatenating what echo would take as a list.