Skip to content

Commit 9f3eab4

Browse files
marcioAlmadabwoebi
authored andcommitted
allow null coalescing (??) on constant expressions
1 parent 8586726 commit 9f3eab4

File tree

6 files changed

+93
-2
lines changed

6 files changed

+93
-2
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
--TEST--
2+
Constant expressions with null coalescing operator ??
3+
--FILE--
4+
<?php
5+
6+
const A = [1 => [[]]];
7+
8+
const T_1 = null ?? A[1]['undefined']['index'] ?? 1;
9+
const T_2 = null ?? A['undefined']['index'] ?? 2;
10+
const T_3 = null ?? A[1][0][2] ?? 3;
11+
const T_4 = A[1][0][2] ?? 4;
12+
13+
var_dump(T_1);
14+
var_dump(T_2);
15+
var_dump(T_3);
16+
var_dump(T_4);
17+
18+
var_dump((function(){ static $var = null ?? A[1]['undefined']['index'] ?? 1; return $var; })());
19+
var_dump((function(){ static $var = null ?? A['undefined']['index'] ?? 2; return $var; })());
20+
var_dump((function(){ static $var = null ?? A[1][0][2] ?? 3; return $var; })());
21+
var_dump((function(){ static $var = A[1][0][2] ?? 4; return $var; })());
22+
23+
var_dump((new class { public $var = null ?? A[1]['undefined']['index'] ?? 1; })->var);
24+
var_dump((new class { public $var = null ?? A['undefined']['index'] ?? 2; })->var);
25+
var_dump((new class { public $var = null ?? A[1][0][2] ?? 3; })->var);
26+
var_dump((new class { public $var = A[1][0][2] ?? 4; })->var);
27+
28+
const D = [][] ?? 1;
29+
30+
?>
31+
--EXPECTF--
32+
int(1)
33+
int(2)
34+
int(3)
35+
int(4)
36+
int(1)
37+
int(2)
38+
int(3)
39+
int(4)
40+
int(1)
41+
int(2)
42+
int(3)
43+
int(4)
44+
45+
Fatal error: Cannot use [] for reading in %s.php on line 25

Zend/zend_ast.c

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,30 @@ ZEND_API int zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *sc
337337
zval_dtor(&op1);
338338
}
339339
break;
340+
case ZEND_AST_COALESCE:
341+
if (ast->child[0]->kind == ZEND_AST_DIM) {
342+
ast->child[0]->attr = ZEND_DIM_IS;
343+
}
344+
345+
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
346+
ret = FAILURE;
347+
break;
348+
}
349+
if (Z_TYPE(op1) > IS_NULL) {
350+
*result = op1;
351+
} else {
352+
if (ast->child[1]->kind == ZEND_AST_DIM) {
353+
ast->child[1]->attr = ZEND_DIM_IS;
354+
}
355+
356+
if (UNEXPECTED(zend_ast_evaluate(result, ast->child[1], scope) != SUCCESS)) {
357+
zval_dtor(&op1);
358+
ret = FAILURE;
359+
break;
360+
}
361+
zval_dtor(&op1);
362+
}
363+
break;
340364
case ZEND_AST_UNARY_PLUS:
341365
if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[0], scope) != SUCCESS)) {
342366
ret = FAILURE;
@@ -385,6 +409,14 @@ ZEND_API int zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *sc
385409
}
386410
break;
387411
case ZEND_AST_DIM:
412+
if (ast->child[1] == NULL) {
413+
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use [] for reading");
414+
}
415+
416+
if (ast->attr == ZEND_DIM_IS && ast->child[0]->kind == ZEND_AST_DIM) {
417+
ast->child[0]->attr = ZEND_DIM_IS;
418+
}
419+
388420
if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) {
389421
ret = FAILURE;
390422
} else if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) {
@@ -393,7 +425,12 @@ ZEND_API int zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *sc
393425
} else {
394426
zval tmp;
395427

396-
zend_fetch_dimension_by_zval(&tmp, &op1, &op2);
428+
if (ast->attr == ZEND_DIM_IS) {
429+
zend_fetch_dimension_by_zval_is(&tmp, &op1, &op2, IS_CONST);
430+
} else {
431+
zend_fetch_dimension_by_zval(&tmp, &op1, &op2);
432+
}
433+
397434
if (UNEXPECTED(Z_ISREF(tmp))) {
398435
ZVAL_DUP(result, Z_REFVAL(tmp));
399436
} else {

Zend/zend_compile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6833,7 +6833,7 @@ zend_bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */
68336833
|| kind == ZEND_AST_CONDITIONAL || kind == ZEND_AST_DIM
68346834
|| kind == ZEND_AST_ARRAY || kind == ZEND_AST_ARRAY_ELEM
68356835
|| kind == ZEND_AST_CONST || kind == ZEND_AST_CLASS_CONST
6836-
|| kind == ZEND_AST_MAGIC_CONST;
6836+
|| kind == ZEND_AST_MAGIC_CONST || kind == ZEND_AST_COALESCE;
68376837
}
68386838
/* }}} */
68396839

Zend/zend_compile.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,8 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name);
889889
#define ZEND_SEND_BY_REF 1
890890
#define ZEND_SEND_PREFER_REF 2
891891

892+
#define ZEND_DIM_IS 1
893+
892894
static zend_always_inline int zend_check_arg_send_type(const zend_function *zf, uint32_t arg_num, uint32_t mask)
893895
{
894896
arg_num--;

Zend/zend_execute.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1912,6 +1912,12 @@ ZEND_API void zend_fetch_dimension_by_zval(zval *result, zval *container, zval *
19121912
zend_fetch_dimension_address_read_R(result, container, dim, IS_TMP_VAR);
19131913
}
19141914

1915+
ZEND_API void zend_fetch_dimension_by_zval_is(zval *result, zval *container, zval *dim, int dim_type)
1916+
{
1917+
zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_IS, 1);
1918+
}
1919+
1920+
19151921
static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type)
19161922
{
19171923
if (container_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) {

Zend/zend_execute.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, con
296296
void zend_verify_abstract_class(zend_class_entry *ce);
297297

298298
ZEND_API void zend_fetch_dimension_by_zval(zval *result, zval *container, zval *dim);
299+
ZEND_API void zend_fetch_dimension_by_zval_is(zval *result, zval *container, zval *dim, int dim_type);
299300

300301
ZEND_API zval* zend_get_compiled_variable_value(const zend_execute_data *execute_data_ptr, uint32_t var);
301302

0 commit comments

Comments
 (0)