diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 8c9607fcff1c7..b1823e2bd014b 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -342,6 +342,28 @@ ZEND_API void zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *s } } +/* addrefs zvals in an AST recursively */ +ZEND_API void zend_ast_addref(zend_ast *ast) /* {{{ */ +{ + if (ast == NULL) { + return; + } else if (ast->kind == ZEND_AST_ZVAL) { + Z_TRY_ADDREF_P(zend_ast_get_zval(ast)); + } else if (zend_ast_is_list(ast)) { + zend_ast_list *list = zend_ast_get_list(ast); + uint32_t i; + for (i = 0; i < list->children; i++) { + zend_ast_addref(list->child[i]); + } + } else { + uint32_t i, children = zend_ast_get_num_children(ast); + for (i = 0; i < children; i++) { + zend_ast_addref(ast->child[i]); + } + } +} +/* }}} */ + ZEND_API zend_ast *zend_ast_copy(zend_ast *ast) { if (ast == NULL) { diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 2a1582ca24a21..d0e6e856e1286 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -200,6 +200,7 @@ ZEND_API zend_ast *zend_ast_list_add(zend_ast *list, zend_ast *op); ZEND_API void zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope TSRMLS_DC); +ZEND_API void zend_ast_addref(zend_ast *ast); ZEND_API zend_ast *zend_ast_copy(zend_ast *ast); ZEND_API void zend_ast_destroy(zend_ast *ast); ZEND_API void zend_ast_destroy_and_free(zend_ast *ast); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index a5a32298d584a..0de145e2aae67 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6902,8 +6902,24 @@ void zend_compile_conditional(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ uint32_t opnum_jmpz, opnum_jmp, opnum_qm_assign1; if (!true_ast) { - zend_compile_shorthand_conditional(result, ast TSRMLS_CC); - return; + /* if variable is isset()able, rewrite $a ?: $b to empty($a) ? $b : $a */ + if (zend_is_variable(cond_ast) && !zend_is_call(cond_ast)) + { + zend_ast *swap_temp = cond_ast; + + cond_ast = ast->child[0] = zend_ast_create(ZEND_AST_EMPTY, cond_ast); + + true_ast = ast->child[1] = false_ast; + false_ast = ast->child[2] = swap_temp; + + /* avoid any zvals being double-freed due to cond_ast being referenced twice */ + zend_ast_addref(swap_temp); + } + else + { + zend_compile_shorthand_conditional(result, ast TSRMLS_CC); + return; + } } zend_compile_expr(&cond_node, cond_ast TSRMLS_CC); diff --git a/tests/lang/operators/shortTernaryIsset.phpt b/tests/lang/operators/shortTernaryIsset.phpt new file mode 100644 index 0000000000000..381cfe6ee7047 --- /dev/null +++ b/tests/lang/operators/shortTernaryIsset.phpt @@ -0,0 +1,58 @@ +--TEST-- +Test short ternary operator for implicit isset cases +--FILE-- +boo = 7; + +$arr = [ + 2 => 7, + "foo" => "bar", + "foobar" => "", + "qux" => $obj, + "bing" => [ + "bang" + ] +]; + +var_dump($nonexistant_variable ?: 3); +echo PHP_EOL; +var_dump($var ?: 3); +var_dump($var2 ?: 3); +echo PHP_EOL; +var_dump($obj->boo ?: 3); +var_dump($obj->bing ?: 3); +var_dump($arr["qux"]->boo ?: 3); +var_dump($arr["qux"]->bing ?: 3); +echo PHP_EOL; +var_dump($arr[2] ?: 3); +var_dump($arr["foo"] ?: 3); +var_dump($arr["foobar"] ?: 3); +var_dump($arr["qux"] ?: 3); +var_dump($arr["bing"][0] ?: 3); +var_dump($arr["bing"][1] ?: 3); +?> +--EXPECTF-- +int(3) + +int(7) +int(3) + +int(7) +int(3) +int(7) +int(3) + +int(7) +string(3) "bar" +int(3) +object(stdClass)#1 (%d) { + ["boo"]=> + int(7) +} +string(4) "bang" +int(3) \ No newline at end of file