====== PHP RFC: Trailing Boolean Operators ======
* Version: 1.1
* Date: 2026-02-02
* Author: Len Woodward, len@artisan.build
* Status: Under Discussion
* Target Version: PHP 8.6
===== Introduction =====
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.
isActive() &&
$user->hasPermission('edit') &&
$resource->isEditable() &&
) {
// ...
}
?>
===== Proposal =====
Allow a trailing ''&&'', ''||'', ''and'', or ''or'' at the end of a parenthesized boolean expression:
isActive() &&
$user->hasPermission('edit') &&
$resource->isEditable() &&
) {
// ...
}
?>
The trailing operator is syntactically permitted and ignored. The above is semantically equivalent to:
This follows the same pattern as trailing commas: the parser consumes and discards the trailing token, leaving the AST unchanged.
==== Rationale ====
=== Consistency with Trailing Commas ===
PHP already supports trailing commas in multiple contexts:
* Arrays (since PHP 5.0): ''[1, 2, 3,]''
* Function/method calls (since PHP 7.3): ''foo($a, $b,)''
* Function/method declarations (since PHP 8.0): ''function foo($a, $b,) {}''
* Closure ''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:
- **Cleaner diffs**: Adding or removing a condition only changes one line
- **Easier reordering**: Conditions can be moved without adjusting punctuation
- **Reduced syntax errors**: No need to remember to remove the operator when deleting the last condition
=== Diff Comparison ===
**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.
=== Trailing-Operator Style is Established ===
[[https://www.php-fig.org/psr/psr-12/|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.
=== A Natural Extension ===
Trailing commas have proven valuable in PHP and are now supported across arrays, function calls, function declarations, closure ''use'' statements, and ''match'' arms. This RFC extends the same principle to boolean operators.
=== Why Not Leading Operators? ===
Leading-operator style is valid, but it doesn't solve the problem — it just moves it. Both current styles have a "special" line that requires modification when reordering:
**Leading style — reorder first two conditions:**
if (
- $isAdmin
- || $isEditor
+ $isEditor
+ || $isAdmin
|| $isModerator
) {
Two lines touched to swap two conditions.
**Trailing style (current) — reorder last two conditions:**
if (
$isAdmin ||
- $isEditor ||
- $isModerator
+ $isModerator ||
+ $isEditor
) {
Same problem, different location.
**With trailing operators (proposed) — reorder any two:**
if (
$isAdmin ||
- $isEditor ||
$isModerator ||
+ $isEditor ||
) {
Each condition is a self-contained line. Reordering is a single-line move.
The PEAR coding standard recommends leading operators partly because "it's easier to comment out a line." But this is a //workaround// for the lack of trailing operator support — a workaround that trades one problem (commenting) for another (reordering the first condition). With trailing operators, both commenting //and// reordering become single-line operations:
This RFC doesn't deprecate or discourage leading-operator style — it simply solves the underlying problem that makes //both// current styles require multi-line edits in certain cases.
==== Scope ====
Trailing boolean operators are permitted in:
- **Parenthesized expressions**: ''($a && $b &&)''
- **Control structure conditions**: ''if'', ''elseif'', ''while'', ''do-while'', ''switch''
- **Match expressions**: ''match ($expr) { ... }'' (for consistency)
Trailing boolean operators are NOT permitted in:
- **Bare expressions**: ''$x = $a &&;'' (syntax error)
- **Return statements**: ''return $a &&;'' (syntax error)
- **Array elements**: ''[$a &&]'' (syntax error)
This restriction is natural because trailing operators only make sense in contexts where the expression is already delimited by parentheses.
==== Examples ====
Basic usage in an if statement:
isPaid() &&
$order->isShipped() &&
$order->isDelivered() &&
) {
$order->archive();
}
?>
Using the OR operator:
isAdmin() ||
$user->isEditor() ||
$user->isModerator() ||
);
?>
In a while loop:
valid() &&
$count < $limit &&
) {
process($iterator->current());
$iterator->next();
$count++;
}
?>
With the ''and''/''or'' keywords:
Edge case - nested parentheses:
===== Backward Incompatible Changes =====
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.
===== Proposed PHP Version(s) =====
PHP 8.6
===== RFC Impact =====
==== To the Ecosystem ====
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.
==== To Existing Extensions ====
None. The change is at the parser level only.
==== To SAPIs ====
None.
==== To Opcache ====
None. The change is at the parser level; compiled bytecode is identical with or without the trailing operator.
===== Open Issues =====
None at this time.
===== Future Scope =====
This RFC intentionally does not address:
* Trailing operators in other contexts (e.g., bitwise ''|'', ''&'')
* The ''xor'' operator (less commonly used in multiline expressions)
These could be proposed separately if there is demand.
===== Voting Choices =====
Primary vote requiring a 2/3 majority:
* Yes
* No
* Abstain
===== Patches and Tests =====
Proof of concept implementation: //To be added//
==== Implementation Details ====
The implementation is parser-only. No changes are required to:
* The lexer (tokens already exist)
* The AST node types
* The compiler
* The VM/opcodes
The trailing operator is consumed by the parser and discarded, identical to how trailing commas are handled via ''possible_comma''.
=== Grammar Changes ===
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).
=== Files to Modify ===
- ''Zend/zend_language_parser.y'' — Grammar rules (~10 lines)
- ''Zend/tests/operators/trailing_boolean_operators_basic.phpt'' — Basic functionality tests
- ''Zend/tests/operators/trailing_boolean_operators_error.phpt'' — Error case tests
===== Implementation =====
After the RFC is implemented, this section should contain:
- the version(s) it was merged into
- a link to the git commit(s)
- a link to the PHP manual entry for the feature
===== References =====
* [[https://www.php-fig.org/psr/psr-12/|PSR-12: Extended Coding Style]]
* [[https://wiki.php.net/rfc/trailing-comma-function-calls|PHP RFC: Trailing Commas in Function Calls]]
* [[https://wiki.php.net/rfc/trailing_comma_in_parameter_list|PHP RFC: Trailing Commas in Parameters]]
* [[https://wiki.php.net/rfc/trailing_comma_in_closure_use_list|PHP RFC: Trailing Commas in Closure Use Lists]]
===== Rejected Features =====
//To be updated based on mailing list discussion.//
===== Changelog =====
* 2026-02-03: Removed incorrect claims about JavaScript/Ruby supporting this feature; clarified that this is a novel extension of the trailing comma principle; added "Why Not Leading Operators?" section addressing the main counter-argument
* 2026-02-02: Initial RFC draft created