]> granicus.if.org Git - php/commitdiff
Deprecate left-associative ternary
authorNikita Popov <nikita.ppv@gmail.com>
Tue, 9 Apr 2019 09:04:13 +0000 (11:04 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Tue, 7 May 2019 12:06:29 +0000 (14:06 +0200)
Deprecate nesting ternary operators without explicit parentheses.

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

UPGRADING
Zend/tests/bug72944.phpt
Zend/tests/ternary_associativity.phpt [new file with mode: 0644]
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_language_parser.y

index 4c0f7a6ca1615a8ef30df07b19727f72e9f75e10..3f6dcb742784ee524494aaa7987b3ad62691c30b 100644 (file)
--- a/UPGRADING
+++ b/UPGRADING
@@ -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.
index 5f494beb40dc9c40ceae0af427425781c2408252..0ee8bd62c6f73ac757394e082b4fcdccc8317232 100644 (file)
@@ -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--
diff --git a/Zend/tests/ternary_associativity.phpt b/Zend/tests/ternary_associativity.phpt
new file mode 100644 (file)
index 0000000..3232651
--- /dev/null
@@ -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
index 6e9b42151fc7fbea750e5e2c80403e66ad1b1874..71d3bc769b85fc156ded4dcc0eb1485f644b8118 100644 (file)
@@ -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;
index e03408a5725b4c2ab22224c82a59e8ece626358a..7aa83ee3b6b68a36f3c9c8e64a2eb4d5ba26dd38 100644 (file)
@@ -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)
index 55376ede3d62476c239ec9f06ad7a3ba757a3b7e..770a027fdac7aa642cb3e852f384c8edcc1099b7 100644 (file)
@@ -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); }