Skip to content

Commit 857ef14

Browse files
committed
Newer fixes
1 parent dc3a06f commit 857ef14

25 files changed

+357
-124
lines changed

Zend/tests/enum/name-property.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ array(1) {
3333
string(3) "Bar"
3434
array(2) {
3535
[0]=>
36-
object(ReflectionProperty)#4 (2) {
36+
object(ReflectionProperty)#3 (2) {
3737
["name"]=>
3838
string(4) "name"
3939
["class"]=>
4040
string(6) "IntFoo"
4141
}
4242
[1]=>
43-
object(ReflectionProperty)#5 (2) {
43+
object(ReflectionProperty)#4 (2) {
4444
["name"]=>
4545
string(5) "value"
4646
["class"]=>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
Typed class constants (type mismatch; runtime)
3+
--FILE--
4+
<?php
5+
class A {
6+
public const stdClass&Stringable CONST1 = C;
7+
public const stdClass&Stringable CONST2 = A::CONST1;
8+
}
9+
10+
define("C", new stdClass);
11+
12+
try {
13+
var_dump(A::CONST2);
14+
} catch (TypeError $exception) {
15+
echo $exception->getMessage() . "\n";
16+
}
17+
18+
try {
19+
var_dump(A::CONST2);
20+
} catch (TypeError $exception) {
21+
echo $exception->getMessage() . "\n";
22+
}
23+
24+
try {
25+
var_dump(A::CONST1);
26+
} catch (TypeError $exception) {
27+
echo $exception->getMessage() . "\n";
28+
}
29+
30+
try {
31+
var_dump(A::CONST1);
32+
} catch (TypeError $exception) {
33+
echo $exception->getMessage() . "\n";
34+
}
35+
?>
36+
--EXPECT--
37+
Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable
38+
Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable
39+
Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable
40+
Cannot assign stdClass to class constant A::CONST1 of type stdClass&Stringable
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
Typed class constants (type coercion is unsupported)
3+
--FILE--
4+
<?php
5+
class S {
6+
public function __toString() {
7+
echo "Side effect!\n";
8+
return 'S';
9+
}
10+
}
11+
12+
class A {
13+
public const string S = S;
14+
}
15+
16+
define("S", new S());
17+
18+
try {
19+
var_dump(A::S);
20+
} catch (TypeError $e) {
21+
echo $e->getMessage() . "\n";
22+
}
23+
?>
24+
--EXPECT--
25+
Cannot assign S to class constant A::S of type string

Zend/tests/type_declarations/typed_class_constants_type_success3.phpt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ enum E {
66
public const E CONST1 = E::Foo;
77
public const self CONST2 = E::Foo;
88
public const static CONST3 = E::Foo;
9+
public const static|stdClass CONST4 = E::Foo;
910

1011
case Foo;
1112
}
@@ -15,6 +16,7 @@ class A {
1516
public const E CONST1 = E::CONST1;
1617
public const E CONST2 = E::CONST2;
1718
public const E CONST3 = E::CONST3;
19+
public const E CONST4 = E::CONST4;
1820
}
1921

2022
var_dump(A::ENUM_CONST);
@@ -25,6 +27,8 @@ var_dump(A::CONST2);
2527
var_dump(A::CONST2);
2628
var_dump(A::CONST3);
2729
var_dump(A::CONST3);
30+
var_dump(A::CONST4);
31+
var_dump(A::CONST4);
2832
?>
2933
--EXPECT--
3034
enum(E::Foo)
@@ -35,3 +39,5 @@ enum(E::Foo)
3539
enum(E::Foo)
3640
enum(E::Foo)
3741
enum(E::Foo)
42+
enum(E::Foo)
43+
enum(E::Foo)

Zend/zend_API.c

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,6 +1413,33 @@ static zend_result update_property(zval *val, zend_property_info *prop_info) {
14131413
return zval_update_constant_ex(val, prop_info->ce);
14141414
}
14151415

1416+
zend_result zend_update_class_constant(zend_class_constant *c, zval *value, zend_class_entry *scope)
1417+
{
1418+
if (EXPECTED(!ZEND_TYPE_IS_SET(c->type) || ZEND_TYPE_PURE_MASK(c->type) == MAY_BE_ANY)) {
1419+
return zval_update_constant_ex(value, scope);
1420+
}
1421+
1422+
zval tmp;
1423+
1424+
ZVAL_COPY_OR_DUP(&tmp, value);
1425+
zend_result result = zval_update_constant_ex(&tmp, scope);
1426+
if (result == FAILURE) {
1427+
zval_ptr_dtor(&tmp);
1428+
return FAILURE;
1429+
}
1430+
1431+
if (UNEXPECTED(!zend_verify_class_constant_type(c, &tmp))) {
1432+
zval_ptr_dtor(&tmp);
1433+
return FAILURE;
1434+
}
1435+
1436+
zval_ptr_dtor(value);
1437+
ZVAL_COPY_OR_DUP(value, &tmp);
1438+
zval_ptr_dtor(&tmp);
1439+
1440+
return SUCCESS;
1441+
}
1442+
14161443
ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /* {{{ */
14171444
{
14181445
zend_class_mutable_data *mutable_data = NULL;
@@ -1471,11 +1498,7 @@ ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type) /
14711498
}
14721499

14731500
val = &c->value;
1474-
if (UNEXPECTED(zval_update_constant_ex(val, c->ce) != SUCCESS)) {
1475-
return FAILURE;
1476-
}
1477-
1478-
if (ZEND_TYPE_IS_SET(c->type) && UNEXPECTED(!zend_verify_class_constant_type(c, val))) {
1501+
if (UNEXPECTED(zend_update_class_constant(c, val, c->ce) != SUCCESS)) {
14791502
return FAILURE;
14801503
}
14811504
}
@@ -4556,18 +4579,21 @@ ZEND_API zend_class_constant *zend_declare_typed_class_constant(zend_class_entry
45564579
zval_make_interned_string(value);
45574580
}
45584581

4582+
if (!ZSTR_IS_INTERNED(name)) {
4583+
name = zend_new_interned_string(zend_string_copy(name));
4584+
}
4585+
45594586
if (ce->type == ZEND_INTERNAL_CLASS) {
45604587
c = pemalloc(sizeof(zend_class_constant), 1);
45614588
} else {
45624589
c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant));
45634590
}
4564-
45654591
ZVAL_COPY_VALUE(&c->value, value);
45664592
ZEND_CLASS_CONST_FLAGS(c) = flags;
4567-
c->name = name;
45684593
c->doc_comment = doc_comment;
45694594
c->attributes = NULL;
45704595
c->ce = ce;
4596+
c->name = name;
45714597
c->type = type;
45724598

45734599
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {

Zend/zend_API.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ ZEND_API void zend_declare_class_constant_double(zend_class_entry *ce, const cha
440440
ZEND_API void zend_declare_class_constant_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_length);
441441
ZEND_API void zend_declare_class_constant_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value);
442442

443+
zend_result zend_update_class_constant(zend_class_constant *c, zval *value, zend_class_entry *scope);
443444
ZEND_API zend_result zend_update_class_constants(zend_class_entry *class_type);
444445
ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_type);
445446

Zend/zend_constants.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *
373373
}
374374

375375
MARK_CONSTANT_VISITED(ret_constant);
376-
ret = zval_update_constant_ex(ret_constant, c->ce);
376+
ret = zend_update_class_constant(c, ret_constant, c->ce);
377377
RESET_CONSTANT_VISITED(ret_constant);
378378

379379
if (UNEXPECTED(ret != SUCCESS)) {

Zend/zend_execute.c

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -830,11 +830,6 @@ ZEND_API bool zend_verify_scalar_type_hint(uint32_t type_mask, zval *arg, bool s
830830

831831
ZEND_COLD zend_never_inline void zend_verify_class_constant_type_error(const zend_class_constant *c, const zval *constant)
832832
{
833-
/* we _may_ land here in case reading already errored and runtime cache thus has not been updated (i.e. it contains a valid but unrelated info) */
834-
if (EG(exception)) {
835-
return;
836-
}
837-
838833
zend_string *type_str = zend_type_to_string(c->type);
839834

840835
zend_type_error("Cannot assign %s to class constant %s::%s of type %s",
@@ -931,7 +926,7 @@ static const zend_class_entry *resolve_single_class_type(zend_string *name, cons
931926
}
932927

933928
static zend_always_inline const zend_class_entry *zend_ce_from_type(
934-
const zend_property_info *info, const zend_type *type) {
929+
const zend_class_entry *class_ce, const zend_type *type) {
935930
ZEND_ASSERT(ZEND_TYPE_HAS_NAME(*type));
936931
zend_string *name = ZEND_TYPE_NAME(*type);
937932
if (ZSTR_HAS_CE_CACHE(name)) {
@@ -941,50 +936,52 @@ static zend_always_inline const zend_class_entry *zend_ce_from_type(
941936
}
942937
return ce;
943938
}
944-
return resolve_single_class_type(name, info->ce);
939+
return resolve_single_class_type(name, class_ce);
945940
}
946941

947-
static bool zend_check_intersection_for_property_class_type(zend_type_list *intersection_type_list,
948-
const zend_property_info *info, const zend_class_entry *object_ce)
942+
static bool zend_check_intersection_for_property_or_class_constant_class_type(zend_type_list *intersection_type_list,
943+
const zend_class_entry *class_ce, const zend_class_entry *object_ce)
949944
{
950945
zend_type *list_type;
951946

952947
ZEND_TYPE_LIST_FOREACH(intersection_type_list, list_type) {
953948
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
954-
const zend_class_entry *ce = zend_ce_from_type(info, list_type);
949+
const zend_class_entry *ce = zend_ce_from_type(class_ce, list_type);
955950
if (!ce || !instanceof_function(object_ce, ce)) {
956951
return false;
957952
}
958953
} ZEND_TYPE_LIST_FOREACH_END();
959954
return true;
960955
}
961956

962-
static bool zend_check_and_resolve_property_class_type(
963-
const zend_property_info *info, const zend_class_entry *object_ce) {
964-
if (ZEND_TYPE_HAS_LIST(info->type)) {
957+
static bool zend_check_and_resolve_property_or_class_constant_class_type(
958+
zend_type type, const zend_class_entry *class_ce, const zend_class_entry *object_ce) {
959+
if (ZEND_TYPE_HAS_LIST(type)) {
965960
zend_type *list_type;
966-
if (ZEND_TYPE_IS_INTERSECTION(info->type)) {
967-
return zend_check_intersection_for_property_class_type(
968-
ZEND_TYPE_LIST(info->type), info, object_ce);
961+
if (ZEND_TYPE_IS_INTERSECTION(type)) {
962+
return zend_check_intersection_for_property_or_class_constant_class_type(
963+
ZEND_TYPE_LIST(type), class_ce, object_ce);
969964
} else {
970-
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(info->type), list_type) {
965+
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) {
971966
if (ZEND_TYPE_IS_INTERSECTION(*list_type)) {
972-
if (zend_check_intersection_for_property_class_type(
973-
ZEND_TYPE_LIST(*list_type), info, object_ce)) {
967+
if (zend_check_intersection_for_property_or_class_constant_class_type(
968+
ZEND_TYPE_LIST(*list_type), class_ce, object_ce)) {
974969
return true;
975970
}
976971
continue;
977972
}
978973
ZEND_ASSERT(!ZEND_TYPE_HAS_LIST(*list_type));
979-
const zend_class_entry *ce = zend_ce_from_type(info, list_type);
974+
const zend_class_entry *ce = zend_ce_from_type(class_ce, list_type);
980975
if (ce && instanceof_function(object_ce, ce)) {
981976
return true;
982977
}
983978
} ZEND_TYPE_LIST_FOREACH_END();
984979
return false;
985980
}
981+
} else if ((ZEND_TYPE_PURE_MASK(type) & MAY_BE_STATIC)) {
982+
return instanceof_function(object_ce, class_ce);
986983
} else {
987-
const zend_class_entry *ce = zend_ce_from_type(info, &info->type);
984+
const zend_class_entry *ce = zend_ce_from_type(class_ce, &type);
988985
return ce && instanceof_function(object_ce, ce);
989986
}
990987
}
@@ -997,12 +994,11 @@ static zend_always_inline bool i_zend_check_property_type(const zend_property_in
997994
}
998995

999996
if (ZEND_TYPE_IS_COMPLEX(info->type) && Z_TYPE_P(property) == IS_OBJECT
1000-
&& zend_check_and_resolve_property_class_type(info, Z_OBJCE_P(property))) {
997+
&& zend_check_and_resolve_property_or_class_constant_class_type(info->type, info->ce, Z_OBJCE_P(property))) {
1001998
return 1;
1002999
}
10031000

10041001
uint32_t type_mask = ZEND_TYPE_FULL_MASK(info->type);
1005-
ZEND_ASSERT(!(type_mask & (MAY_BE_CALLABLE|MAY_BE_STATIC)));
10061002
return zend_verify_scalar_type_hint(type_mask, property, strict, 0);
10071003
}
10081004

@@ -1478,15 +1474,31 @@ static ZEND_COLD void zend_verify_missing_return_type(const zend_function *zf)
14781474
zend_verify_return_error(zf, NULL);
14791475
}
14801476

1481-
bool zend_never_inline zend_verify_class_constant_type(const zend_class_constant *c, zval *constant)
1477+
static zend_always_inline bool zend_check_class_constant_type(zend_class_constant *c, zval *constant)
14821478
{
1483-
if (zend_check_type((zend_type *) &c->type, constant, NULL, NULL, /* is_return_type */ 1, /* is_internal_arg */ 0)) {
1479+
ZEND_ASSERT(!Z_ISREF_P(constant));
1480+
if (EXPECTED(ZEND_TYPE_CONTAINS_CODE(c->type, Z_TYPE_P(constant)))) {
1481+
return 1;
1482+
}
1483+
1484+
if (((ZEND_TYPE_PURE_MASK(c->type) & MAY_BE_STATIC) || ZEND_TYPE_IS_COMPLEX(c->type)) && Z_TYPE_P(constant) == IS_OBJECT
1485+
&& zend_check_and_resolve_property_or_class_constant_class_type(c->type, c->ce, Z_OBJCE_P(constant))) {
14841486
return 1;
14851487
}
14861488

1487-
zend_verify_class_constant_type_error(c, constant);
1489+
uint32_t type_mask = ZEND_TYPE_FULL_MASK(c->type);
1490+
ZEND_ASSERT(!(type_mask & (MAY_BE_CALLABLE|MAY_BE_NEVER|MAY_BE_VOID)));
1491+
return zend_verify_scalar_type_hint(type_mask, constant, 1, 0);
1492+
}
14881493

1489-
return 0;
1494+
ZEND_API bool zend_never_inline zend_verify_class_constant_type(zend_class_constant *c, zval *constant)
1495+
{
1496+
if (!zend_check_class_constant_type(c, constant)) {
1497+
zend_verify_class_constant_type_error(c, constant);
1498+
return 0;
1499+
}
1500+
1501+
return 1;
14901502
}
14911503

14921504
static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_use_object_as_array(void)
@@ -3506,7 +3518,7 @@ static zend_always_inline int i_zend_verify_type_assignable_zval(
35063518
}
35073519

35083520
if (ZEND_TYPE_IS_COMPLEX(type) && zv_type == IS_OBJECT
3509-
&& zend_check_and_resolve_property_class_type(info, Z_OBJCE_P(zv))) {
3521+
&& zend_check_and_resolve_property_or_class_constant_class_type(info->type, info->ce, Z_OBJCE_P(zv))) {
35103522
return 1;
35113523
}
35123524

Zend/zend_execute.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ ZEND_API int ZEND_FASTCALL zend_handle_undef_args(zend_execute_data *call);
466466
#define ZEND_CLASS_HAS_READONLY_PROPS(ce) ((ce->ce_flags & ZEND_ACC_HAS_READONLY_PROPS) == ZEND_ACC_HAS_READONLY_PROPS)
467467

468468

469-
bool zend_verify_class_constant_type(const zend_class_constant *c, zval *constant);
469+
ZEND_API bool zend_verify_class_constant_type(zend_class_constant *c, zval *constant);
470470
ZEND_COLD void zend_verify_class_constant_type_error(const zend_class_constant *c, const zval *constant);
471471

472472
ZEND_API bool zend_verify_property_type(const zend_property_info *info, zval *property, bool strict);

Zend/zend_inheritance.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,7 +1359,7 @@ static void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_en
13591359
}
13601360
/* }}} */
13611361

1362-
inheritance_status class_constant_types_compatible(const zend_class_constant *parent, const zend_class_constant *child)
1362+
static inheritance_status class_constant_types_compatible(const zend_class_constant *parent, const zend_class_constant *child)
13631363
{
13641364
if (!ZEND_TYPE_IS_SET(child->type)) {
13651365
return INHERITANCE_ERROR;
@@ -1663,12 +1663,16 @@ static zend_always_inline bool check_trait_property_or_constant_value_compatibil
16631663
/* if any of the values is a constant, we try to resolve it */
16641664
if (UNEXPECTED(Z_TYPE_P(op1) == IS_CONSTANT_AST)) {
16651665
ZVAL_COPY_OR_DUP(&op1_tmp, op1);
1666-
zval_update_constant_ex(&op1_tmp, ce);
1666+
if (UNEXPECTED(zval_update_constant_ex(&op1_tmp, ce) != SUCCESS)) {
1667+
return false;
1668+
}
16671669
op1 = &op1_tmp;
16681670
}
16691671
if (UNEXPECTED(Z_TYPE_P(op2) == IS_CONSTANT_AST)) {
16701672
ZVAL_COPY_OR_DUP(&op2_tmp, op2);
1671-
zval_update_constant_ex(&op2_tmp, ce);
1673+
if (UNEXPECTED(zval_update_constant_ex(&op2_tmp, ce) != SUCCESS)) {
1674+
return false;
1675+
}
16721676
op2 = &op2_tmp;
16731677
}
16741678

0 commit comments

Comments
 (0)