Skip to content

Commit 3c616f5

Browse files
committed
Implement GH-18261: Allow cast to be used in constant expressions
1 parent ac9392b commit 3c616f5

File tree

4 files changed

+118
-0
lines changed

4 files changed

+118
-0
lines changed

UPGRADING

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ PHP 8.5 UPGRADE NOTES
130130
RFC: https://wiki.php.net/rfc/marking_return_value_as_important
131131
. Added asymmetric visibility support for static properties.
132132
RFC: https://wiki.php.net/rfc/static-aviz
133+
. It is now possible to use casts in constant expressions.
133134

134135
- Curl:
135136
. Added support for share handles that are persisted across multiple PHP
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
--TEST--
2+
Constant expressions with cast
3+
--FILE--
4+
<?php
5+
class X {
6+
public int $foo = 3;
7+
}
8+
9+
const T1 = (int) 0.3;
10+
const T2 = (bool) 0.3;
11+
const T3 = (string) [];
12+
const T4 = (object) ["a" => 1];
13+
const T5 = (float) 5;
14+
const T6 = (array) "";
15+
const T7 = (array) var_dump(...);
16+
const T8 = (array) new X;
17+
18+
var_dump(T1, T2, T3, T4, T5, T6, T7, T8);
19+
?>
20+
--EXPECTF--
21+
Warning: Array to string conversion in %s on line %d
22+
int(0)
23+
bool(true)
24+
string(5) "Array"
25+
object(stdClass)#%d (1) {
26+
["a"]=>
27+
int(1)
28+
}
29+
float(5)
30+
array(1) {
31+
[0]=>
32+
string(0) ""
33+
}
34+
array(1) {
35+
[0]=>
36+
object(Closure)#%d (2) {
37+
["function"]=>
38+
string(8) "var_dump"
39+
["parameter"]=>
40+
array(2) {
41+
["$value"]=>
42+
string(10) "<required>"
43+
["$values"]=>
44+
string(10) "<optional>"
45+
}
46+
}
47+
}
48+
array(1) {
49+
["foo"]=>
50+
int(3)
51+
}

Zend/zend_ast.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,71 @@ ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
702702
}
703703
zval_ptr_dtor_nogc(&op1);
704704
break;
705+
case ZEND_AST_CAST:
706+
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
707+
ret = FAILURE;
708+
break;
709+
}
710+
if (ast->attr == Z_TYPE(op1)) {
711+
ZVAL_COPY_VALUE(result, &op1);
712+
} else {
713+
switch (ast->attr) {
714+
case _IS_BOOL:
715+
ZVAL_BOOL(result, zend_is_true(&op1));
716+
zval_ptr_dtor_nogc(&op1);
717+
break;
718+
case IS_LONG:
719+
ZVAL_LONG(result, zval_get_long_func(&op1, false));
720+
zval_ptr_dtor_nogc(&op1);
721+
break;
722+
case IS_DOUBLE:
723+
ZVAL_DOUBLE(result, zval_get_double_func(&op1));
724+
zval_ptr_dtor_nogc(&op1);
725+
break;
726+
case IS_STRING:
727+
ZVAL_STR(result, zval_get_string_func(&op1));
728+
zval_ptr_dtor_nogc(&op1);
729+
break;
730+
case IS_ARRAY:
731+
/* Adapted from VM */
732+
if (Z_TYPE(op1) != IS_OBJECT || Z_OBJCE(op1) == zend_ce_closure) {
733+
if (Z_TYPE(op1) != IS_NULL) {
734+
ZVAL_ARR(result, zend_new_array(1));
735+
zend_hash_index_add_new(Z_ARRVAL_P(result), 0, &op1);
736+
} else {
737+
ZVAL_EMPTY_ARRAY(result);
738+
}
739+
} else {
740+
/* Constant AST only allows building stdClass objects, closures, or user classes;
741+
* all of which should be compatible. */
742+
ZEND_ASSERT(ZEND_STD_BUILD_OBJECT_PROPERTIES_ARRAY_COMPATIBLE(&op1));
743+
744+
/* Optimized version without rebuilding properties HashTable */
745+
ZVAL_ARR(result, zend_std_build_object_properties_array(Z_OBJ(op1)));
746+
zval_ptr_dtor_nogc(&op1);
747+
}
748+
break;
749+
case IS_OBJECT:
750+
/* Adapted from VM */
751+
ZVAL_OBJ(result, zend_objects_new(zend_standard_class_def));
752+
if (Z_TYPE(op1) == IS_ARRAY) {
753+
HashTable *ht = zend_symtable_to_proptable(Z_ARR(op1));
754+
if (GC_FLAGS(ht) & IS_ARRAY_IMMUTABLE) {
755+
/* TODO: try not to duplicate immutable arrays as well ??? */
756+
ht = zend_array_dup(ht);
757+
}
758+
Z_OBJ_P(result)->properties = ht;
759+
zval_ptr_dtor_nogc(&op1);
760+
} else if (Z_TYPE(op1) != IS_NULL) {
761+
HashTable *ht = zend_new_array(1);
762+
Z_OBJ_P(result)->properties = ht;
763+
zend_hash_add_new(ht, ZSTR_KNOWN(ZEND_STR_SCALAR), &op1);
764+
}
765+
break;
766+
EMPTY_SWITCH_DEFAULT_CASE();
767+
}
768+
}
769+
break;
705770
case ZEND_AST_OR:
706771
if (UNEXPECTED(zend_ast_evaluate_ex(&op1, ast->child[0], scope, &short_circuited, ctx) != SUCCESS)) {
707772
ret = FAILURE;

Zend/zend_compile.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11156,6 +11156,7 @@ static bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */
1115611156
|| kind == ZEND_AST_AND || kind == ZEND_AST_OR
1115711157
|| kind == ZEND_AST_UNARY_OP
1115811158
|| kind == ZEND_AST_UNARY_PLUS || kind == ZEND_AST_UNARY_MINUS
11159+
|| kind == ZEND_AST_CAST
1115911160
|| kind == ZEND_AST_CONDITIONAL || kind == ZEND_AST_DIM
1116011161
|| kind == ZEND_AST_ARRAY || kind == ZEND_AST_ARRAY_ELEM
1116111162
|| kind == ZEND_AST_UNPACK

0 commit comments

Comments
 (0)