Skip to content

Commit

Permalink
Deprecate left-associative ternary
Browse files Browse the repository at this point in the history
Deprecate nesting ternary operators without explicit parentheses.

RFC: https://wiki.php.net/rfc/ternary_associativity
  • Loading branch information
nikic committed May 7, 2019
1 parent 68a7578 commit 09ea55c
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 2 deletions.
10 changes: 10 additions & 0 deletions UPGRADING
Expand Up @@ -27,6 +27,16 @@ PHP 7.4 UPGRADE NOTES
. "fn" is now a reserved keyword. In particular it can no longer be used as a
function or class name. It can still be used as a method or class constant
name.
. Nesting ternary operators without explicit parentheses is deprecated:

// Code like
$a ? $b : $c ? $d : $e
// should be replaced by (current interpretation)
($a ? $b : $c) ? $d : $e
// or (likely intended interpretation)
$a ? $b : ($c ? $d : $e)

RFC: https://wiki.php.net/rfc/ternary_associativity

- Curl:
. Attempting to serialize a CURLFile class will now generate an exception.
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/bug72944.phpt
Expand Up @@ -2,7 +2,7 @@
Bug #72944 (Null pointer deref in zval_delref_p).
--FILE--
<?php
"a"== e & $A = $A? 0 : 0 ?:0;
("a"== e & $A = $A? 0 : 0) ?:0;
echo "OK\n";
?>
--EXPECTF--
Expand Down
30 changes: 30 additions & 0 deletions Zend/tests/ternary_associativity.phpt
@@ -0,0 +1,30 @@
--TEST--
Using ternary associativity is deprecated
--FILE--
<?php

1 ? 2 : 3 ? 4 : 5; // deprecated
(1 ? 2 : 3) ? 4 : 5; // ok
1 ? 2 : (3 ? 4 : 5); // ok

// While the associativity of ?: is also incorrect, it will not cause a
// functional difference, only some unnecessary checks.
1 ?: 2 ?: 3; // ok
(1 ?: 2) ?: 3; // ok
1 ?: (2 ?: 3); // ok

1 ?: 2 ? 3 : 4; // deprecated
(1 ?: 2) ? 3 : 4; // ok
1 ?: (2 ? 3 : 4); // ok

1 ? 2 : 3 ?: 4; // deprecated
(1 ? 2 : 3) ?: 4; // ok
1 ? 2 : (3 ?: 4); // ok

?>
--EXPECTF--
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

Deprecated: Unparenthesized `a ?: b ? c : d` is deprecated. Use either `(a ?: b) ? c : d` or `a ?: (b ? c : d)` in %s on line 13

Deprecated: Unparenthesized `a ? b : c ?: d` is deprecated. Use either `(a ? b : c) ?: d` or `a ? b : (c ?: d)` in %s on line 17
24 changes: 24 additions & 0 deletions Zend/zend_compile.c
Expand Up @@ -7373,6 +7373,30 @@ void zend_compile_conditional(znode *result, zend_ast *ast) /* {{{ */
zend_op *opline_qm_assign2;
uint32_t opnum_jmpz, opnum_jmp;

if (cond_ast->kind == ZEND_AST_CONDITIONAL
&& cond_ast->attr != ZEND_PARENTHESIZED_CONDITIONAL) {
if (cond_ast->child[1]) {
if (true_ast) {
zend_error(E_DEPRECATED,
"Unparenthesized `a ? b : c ? d : e` is deprecated. "
"Use either `(a ? b : c) ? d : e` or `a ? b : (c ? d : e)`");
} else {
zend_error(E_DEPRECATED,
"Unparenthesized `a ? b : c ?: d` is deprecated. "
"Use either `(a ? b : c) ?: d` or `a ? b : (c ?: d)`");
}
} else {
if (true_ast) {
zend_error(E_DEPRECATED,
"Unparenthesized `a ?: b ? c : d` is deprecated. "
"Use either `(a ?: b) ? c : d` or `a ?: (b ? c : d)`");
} else {
/* This case is harmless: (a ?: b) ?: c always produces the same result
* as a ?: (b ?: c). */
}
}
}

if (!true_ast) {
zend_compile_shorthand_conditional(result, ast);
return;
Expand Down
3 changes: 3 additions & 0 deletions Zend/zend_compile.h
Expand Up @@ -994,6 +994,9 @@ static zend_always_inline int zend_check_arg_send_type(const zend_function *zf,
#define ZEND_ARRAY_NOT_PACKED (1<<1)
#define ZEND_ARRAY_SIZE_SHIFT 2

/* Attribute for ternary inside parentheses */
#define ZEND_PARENTHESIZED_CONDITIONAL 1

/* For "use" AST nodes and the seen symbol table */
#define ZEND_SYMBOL_CLASS (1<<0)
#define ZEND_SYMBOL_FUNCTION (1<<1)
Expand Down
5 changes: 4 additions & 1 deletion Zend/zend_language_parser.y
Expand Up @@ -961,7 +961,10 @@ expr:
{ $$ = zend_ast_create_binary_op(ZEND_SPACESHIP, $1, $3); }
| expr T_INSTANCEOF class_name_reference
{ $$ = zend_ast_create(ZEND_AST_INSTANCEOF, $1, $3); }
| '(' expr ')' { $$ = $2; }
| '(' expr ')' {
$$ = $2;
if ($$->kind == ZEND_AST_CONDITIONAL) $$->attr = ZEND_PARENTHESIZED_CONDITIONAL;
}
| new_expr { $$ = $1; }
| expr '?' expr ':' expr
{ $$ = zend_ast_create(ZEND_AST_CONDITIONAL, $1, $3, $5); }
Expand Down

0 comments on commit 09ea55c

Please sign in to comment.