Skip to content

Commit 09ea55c

Browse files
committed
Deprecate left-associative ternary
Deprecate nesting ternary operators without explicit parentheses. RFC: https://wiki.php.net/rfc/ternary_associativity
1 parent 68a7578 commit 09ea55c

File tree

6 files changed

+72
-2
lines changed

6 files changed

+72
-2
lines changed

UPGRADING

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@ PHP 7.4 UPGRADE NOTES
2727
. "fn" is now a reserved keyword. In particular it can no longer be used as a
2828
function or class name. It can still be used as a method or class constant
2929
name.
30+
. Nesting ternary operators without explicit parentheses is deprecated:
31+
32+
// Code like
33+
$a ? $b : $c ? $d : $e
34+
// should be replaced by (current interpretation)
35+
($a ? $b : $c) ? $d : $e
36+
// or (likely intended interpretation)
37+
$a ? $b : ($c ? $d : $e)
38+
39+
RFC: https://wiki.php.net/rfc/ternary_associativity
3040

3141
- Curl:
3242
. Attempting to serialize a CURLFile class will now generate an exception.

Zend/tests/bug72944.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Bug #72944 (Null pointer deref in zval_delref_p).
33
--FILE--
44
<?php
5-
"a"== e & $A = $A? 0 : 0 ?:0;
5+
("a"== e & $A = $A? 0 : 0) ?:0;
66
echo "OK\n";
77
?>
88
--EXPECTF--

Zend/tests/ternary_associativity.phpt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
Using ternary associativity is deprecated
3+
--FILE--
4+
<?php
5+
6+
1 ? 2 : 3 ? 4 : 5; // deprecated
7+
(1 ? 2 : 3) ? 4 : 5; // ok
8+
1 ? 2 : (3 ? 4 : 5); // ok
9+
10+
// While the associativity of ?: is also incorrect, it will not cause a
11+
// functional difference, only some unnecessary checks.
12+
1 ?: 2 ?: 3; // ok
13+
(1 ?: 2) ?: 3; // ok
14+
1 ?: (2 ?: 3); // ok
15+
16+
1 ?: 2 ? 3 : 4; // deprecated
17+
(1 ?: 2) ? 3 : 4; // ok
18+
1 ?: (2 ? 3 : 4); // ok
19+
20+
1 ? 2 : 3 ?: 4; // deprecated
21+
(1 ? 2 : 3) ?: 4; // ok
22+
1 ? 2 : (3 ?: 4); // ok
23+
24+
?>
25+
--EXPECTF--
26+
Deprecated: Unparenthesized `a ? b : c ? d : e` is deprecated. Use either `(a ? b : c) ? d : e` or `a ? b : (c ? d : e)` in %s on line 3
27+
28+
Deprecated: Unparenthesized `a ?: b ? c : d` is deprecated. Use either `(a ?: b) ? c : d` or `a ?: (b ? c : d)` in %s on line 13
29+
30+
Deprecated: Unparenthesized `a ? b : c ?: d` is deprecated. Use either `(a ? b : c) ?: d` or `a ? b : (c ?: d)` in %s on line 17

Zend/zend_compile.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7373,6 +7373,30 @@ void zend_compile_conditional(znode *result, zend_ast *ast) /* {{{ */
73737373
zend_op *opline_qm_assign2;
73747374
uint32_t opnum_jmpz, opnum_jmp;
73757375

7376+
if (cond_ast->kind == ZEND_AST_CONDITIONAL
7377+
&& cond_ast->attr != ZEND_PARENTHESIZED_CONDITIONAL) {
7378+
if (cond_ast->child[1]) {
7379+
if (true_ast) {
7380+
zend_error(E_DEPRECATED,
7381+
"Unparenthesized `a ? b : c ? d : e` is deprecated. "
7382+
"Use either `(a ? b : c) ? d : e` or `a ? b : (c ? d : e)`");
7383+
} else {
7384+
zend_error(E_DEPRECATED,
7385+
"Unparenthesized `a ? b : c ?: d` is deprecated. "
7386+
"Use either `(a ? b : c) ?: d` or `a ? b : (c ?: d)`");
7387+
}
7388+
} else {
7389+
if (true_ast) {
7390+
zend_error(E_DEPRECATED,
7391+
"Unparenthesized `a ?: b ? c : d` is deprecated. "
7392+
"Use either `(a ?: b) ? c : d` or `a ?: (b ? c : d)`");
7393+
} else {
7394+
/* This case is harmless: (a ?: b) ?: c always produces the same result
7395+
* as a ?: (b ?: c). */
7396+
}
7397+
}
7398+
}
7399+
73767400
if (!true_ast) {
73777401
zend_compile_shorthand_conditional(result, ast);
73787402
return;

Zend/zend_compile.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,9 @@ static zend_always_inline int zend_check_arg_send_type(const zend_function *zf,
994994
#define ZEND_ARRAY_NOT_PACKED (1<<1)
995995
#define ZEND_ARRAY_SIZE_SHIFT 2
996996

997+
/* Attribute for ternary inside parentheses */
998+
#define ZEND_PARENTHESIZED_CONDITIONAL 1
999+
9971000
/* For "use" AST nodes and the seen symbol table */
9981001
#define ZEND_SYMBOL_CLASS (1<<0)
9991002
#define ZEND_SYMBOL_FUNCTION (1<<1)

Zend/zend_language_parser.y

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,10 @@ expr:
961961
{ $$ = zend_ast_create_binary_op(ZEND_SPACESHIP, $1, $3); }
962962
| expr T_INSTANCEOF class_name_reference
963963
{ $$ = zend_ast_create(ZEND_AST_INSTANCEOF, $1, $3); }
964-
| '(' expr ')' { $$ = $2; }
964+
| '(' expr ')' {
965+
$$ = $2;
966+
if ($$->kind == ZEND_AST_CONDITIONAL) $$->attr = ZEND_PARENTHESIZED_CONDITIONAL;
967+
}
965968
| new_expr { $$ = $1; }
966969
| expr '?' expr ':' expr
967970
{ $$ = zend_ast_create(ZEND_AST_CONDITIONAL, $1, $3, $5); }

0 commit comments

Comments
 (0)