This RFC proposes allowing trailing boolean operators (&&, ||, and, or) in parenthesized expressions, mirroring the existing support for trailing commas in arrays, function parameters, and other contexts.
<?php // Currently invalid, proposed to be valid: if ( $user->isActive() && $user->hasPermission('edit') && $resource->isEditable() && ) { // ... } ?>
Allow a trailing &&, ||, and, or or at the end of a parenthesized boolean expression:
<?php $result = ( $a <= $b && $b <= $c && $c <= $a * $b && ); if ( $user->isActive() && $user->hasPermission('edit') && $resource->isEditable() && ) { // ... } ?>
The trailing operator is syntactically permitted and ignored. The above is semantically equivalent to:
<?php $result = ( $a <= $b && $b <= $c && $c <= $a * $b ); ?>
This follows the same pattern as trailing commas: the parser consumes and discards the trailing token, leaving the AST unchanged.
PHP already supports trailing commas in multiple contexts:
[1, 2, 3,]foo($a, $b,)function foo($a, $b,) {}use statements (since PHP 8.0): function() use ($a, $b,) {}match arms (since PHP 8.0)The rationale for trailing commas applies equally to boolean operators:
Without trailing operators (current behavior):
if ( $user->isActive() && - $user->hasPermission('edit') + $user->hasPermission('edit') && + $resource->isEditable() ) {
With trailing operators (proposed):
if ( $user->isActive() && $user->hasPermission('edit') && + $resource->isEditable() && ) {
The trailing operator style produces a one-line diff for adding a condition, compared to a two-line diff that touches an unrelated line.
PSR-12 permits boolean operators at either the beginning or end of lines. Both styles are valid and used in practice. This RFC is not about whether trailing-operator style should exist — it already does. This RFC addresses the friction inherent in that style: the need to modify the final line when adding or removing conditions.
JavaScript and Ruby already support trailing boolean operators naturally:
&& or || is grammatically incomplete, so no semicolon is inserted and the expression continues on the next line. MDN: Lexical GrammarPHP would not be pioneering new territory — it would be providing explicit support for a pattern that other popular languages handle implicitly through their line continuation rules.
Trailing boolean operators are permitted in:
($a && $b &&)if, elseif, while, do-while, switchmatch ($expr) { ... } (for consistency)Trailing boolean operators are NOT permitted in:
$x = $a &&; (syntax error)return $a &&; (syntax error)[$a &&] (syntax error)This restriction is natural because trailing operators only make sense in contexts where the expression is already delimited by parentheses.
Basic usage in an if statement:
<?php if ( $order->isPaid() && $order->isShipped() && $order->isDelivered() && ) { $order->archive(); } ?>
Using the OR operator:
<?php $canAccess = ( $user->isAdmin() || $user->isEditor() || $user->isModerator() || ); ?>
In a while loop:
<?php while ( $iterator->valid() && $count < $limit && ) { process($iterator->current()); $iterator->next(); $count++; } ?>
With the and/or keywords:
<?php $result = ( $a and $b and $c and ); ?>
Edge case - nested parentheses:
<?php $complex = ( ($a && $b &&) || ($c && $d &&) || ); ?>
This is a purely additive change. Code that was previously a syntax error becomes valid. No existing valid code changes meaning.
No backward compatibility breaks are expected.
PHP 8.6
IDEs, language servers, and static analyzers will need to update their parsers to recognize the new syntax. However, since this is purely additive (previously-invalid syntax becoming valid), existing tooling will not break — it will simply report false-positive syntax errors until updated.
Auto-formatters may choose to add or remove trailing operators based on configuration, similar to how they handle trailing commas.
None. The change is at the parser level only.
None.
None. The change is at the parser level; compiled bytecode is identical with or without the trailing operator.
None at this time.
This RFC intentionally does not address:
|, &)xor operator (less commonly used in multiline expressions)These could be proposed separately if there is demand.
Primary vote requiring a 2/3 majority:
Proof of concept implementation: To be added
The implementation is parser-only. No changes are required to:
The trailing operator is consumed by the parser and discarded, identical to how trailing commas are handled via possible_comma.
In Zend/zend_language_parser.y:
Add new rule:
paren_expr:
expr
| expr T_BOOLEAN_AND { $$ = $1; }
| expr T_BOOLEAN_OR { $$ = $1; }
| expr T_LOGICAL_AND { $$ = $1; }
| expr T_LOGICAL_OR { $$ = $1; }
;
Modify existing rules to use paren_expr instead of expr in parenthesized contexts (if, while, do-while, switch, elseif, and general parenthesized expressions).
Zend/zend_language_parser.y — Grammar rules (~10 lines)Zend/tests/operators/trailing_boolean_operators_basic.phpt — Basic functionality testsZend/tests/operators/trailing_boolean_operators_error.phpt — Error case testsAfter the RFC is implemented, this section should contain:
To be updated based on mailing list discussion.