====== PHP RFC: Trailing Boolean Operators ======
* Version: 0.9
* Date: 2026-02-02
* Author: Len Woodward, len@artisan.build
* Status: Draft
* 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.
=== Other Languages Support This ===
**JavaScript** and **Ruby** already support trailing boolean operators naturally:
* **JavaScript**: Due to Automatic Semicolon Insertion (ASI), a line ending with ''&&'' or ''||'' is grammatically incomplete, so no semicolon is inserted and the expression continues on the next line. [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar|MDN: Lexical Grammar]]
* **Ruby**: "Ruby expressions and statements are terminated at the end of a line unless the statement is obviously incomplete—for example if the last token on a line is an operator." [[https://rubystyle.guide/|Ruby Style Guide]]
PHP 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.
==== 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
===== 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]]
* [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar|MDN: JavaScript Lexical Grammar (ASI)]]
* [[https://rubystyle.guide/|Ruby Style Guide]]
===== Rejected Features =====
//To be updated based on mailing list discussion.//
===== Changelog =====
* 2026-02-02: Initial RFC draft created