Skip to content

Commit 4af9a2d

Browse files
committed
Add support for trait constants
1 parent 885f273 commit 4af9a2d

6 files changed

+153
-11
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Typed class constants (incompatible composition; traits)
3+
--FILE--
4+
<?php
5+
6+
trait T {
7+
public const ?array CONST1 = [];
8+
}
9+
10+
class C {
11+
use T;
12+
13+
public const CONST1 = [];
14+
}
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: C and T define the same constant (CONST1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Typed class constants (incompatible covariant composition; traits)
3+
--FILE--
4+
<?php
5+
6+
trait T {
7+
public const ?array CONST1 = [];
8+
}
9+
10+
class C {
11+
use T;
12+
13+
public const array CONST1 = [];
14+
}
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: C and T define the same constant (CONST1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Typed class constants (incompatible contravariant composition; traits)
3+
--FILE--
4+
<?php
5+
6+
trait T {
7+
public const array CONST1 = [];
8+
}
9+
10+
class C {
11+
use T;
12+
13+
public const ?array CONST1 = [];
14+
}
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: C and T define the same constant (CONST1) in the composition of C. However, the definition differs and is considered incompatible. Class was composed in %s on line %d
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
Typed class constants (composition; traits)
3+
--FILE--
4+
<?php
5+
6+
const G = new stdClass();
7+
8+
enum E {
9+
case Case1;
10+
}
11+
12+
trait T {
13+
public const int CONST1 = 1;
14+
public const ?array CONST2 = [];
15+
public const E CONST3 = E::Case1;
16+
public const stdClass CONST4 = G;
17+
}
18+
19+
class C {
20+
use T;
21+
22+
public const int CONST1 = 1;
23+
public const ?array CONST2 = [];
24+
public const E CONST3 = E::Case1;
25+
public const stdClass CONST4 = G;
26+
}
27+
28+
var_dump(C::CONST1);
29+
var_dump(C::CONST2);
30+
var_dump(C::CONST3);
31+
var_dump(C::CONST4);
32+
?>
33+
--EXPECT--
34+
int(1)
35+
array(0) {
36+
}
37+
enum(E::Case1)
38+
object(stdClass)#1 (0) {
39+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
Typed class constants (redefinition; interfaces and traits)
3+
--FILE--
4+
<?php
5+
enum E {
6+
case Case1;
7+
}
8+
9+
interface I {
10+
public const E CONST1 = E::Case1;
11+
}
12+
13+
trait T {
14+
public const E CONST1 = E::Case1;
15+
}
16+
17+
class C implements I {
18+
use T;
19+
20+
public const E CONST1 = E::Case1;
21+
}
22+
23+
var_dump(C::CONST1);
24+
?>
25+
--EXPECT--
26+
enum(E::Case1)

Zend/zend_inheritance.c

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2253,8 +2253,24 @@ static zend_class_entry* find_first_constant_definition(zend_class_entry *ce, ze
22532253
}
22542254
/* }}} */
22552255

2256-
static bool do_trait_constant_check(zend_class_entry *ce, zend_class_constant *trait_constant, zend_string *name, zend_class_entry **traits, size_t current_trait) /* {{{ */
2257-
{
2256+
static bool emit_incompatible_trait_constant_error(
2257+
zend_class_entry *ce, zend_class_constant *existing_constant, zend_class_constant *trait_constant, zend_string *name,
2258+
zend_class_entry **traits, size_t current_trait
2259+
) {
2260+
zend_error_noreturn(E_COMPILE_ERROR,
2261+
"%s and %s define the same constant (%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed",
2262+
ZSTR_VAL(find_first_constant_definition(ce, traits, current_trait, name, existing_constant->ce)->name),
2263+
ZSTR_VAL(trait_constant->ce->name),
2264+
ZSTR_VAL(name),
2265+
ZSTR_VAL(ce->name)
2266+
);
2267+
2268+
return false;
2269+
}
2270+
2271+
static bool do_trait_constant_check(
2272+
zend_class_entry *ce, zend_class_constant *trait_constant, zend_string *name, zend_class_entry **traits, size_t current_trait
2273+
) {
22582274
uint32_t flags_mask = ZEND_ACC_PPP_MASK | ZEND_ACC_FINAL;
22592275

22602276
zval *zv = zend_hash_find_known_hash(&ce->constants_table, name);
@@ -2265,21 +2281,28 @@ static bool do_trait_constant_check(zend_class_entry *ce, zend_class_constant *t
22652281

22662282
zend_class_constant *existing_constant = Z_PTR_P(zv);
22672283

2268-
if ((ZEND_CLASS_CONST_FLAGS(trait_constant) & flags_mask) != (ZEND_CLASS_CONST_FLAGS(existing_constant) & flags_mask) ||
2269-
!check_trait_property_or_constant_value_compatibility(ce, &trait_constant->value, &existing_constant->value)) {
2284+
if ((ZEND_CLASS_CONST_FLAGS(trait_constant) & flags_mask) != (ZEND_CLASS_CONST_FLAGS(existing_constant) & flags_mask)) {
2285+
return emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait);
2286+
}
2287+
2288+
if (ZEND_TYPE_IS_SET(trait_constant->type) != ZEND_TYPE_IS_SET(existing_constant->type)) {
2289+
return emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait);
2290+
} else if (ZEND_TYPE_IS_SET(trait_constant->type)) {
2291+
inheritance_status status1 = zend_perform_covariant_type_check(ce, existing_constant->type, traits[current_trait], trait_constant->type);
2292+
inheritance_status status2 = zend_perform_covariant_type_check(traits[current_trait], trait_constant->type, ce, existing_constant->type);
2293+
if (status1 == INHERITANCE_ERROR || status2 == INHERITANCE_ERROR) {
2294+
return emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait);
2295+
}
2296+
}
2297+
2298+
if (!check_trait_property_or_constant_value_compatibility(ce, &trait_constant->value, &existing_constant->value)) {
22702299
/* There is an existing constant of the same name, and it conflicts with the new one, so let's throw a fatal error */
2271-
zend_error_noreturn(E_COMPILE_ERROR,
2272-
"%s and %s define the same constant (%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed",
2273-
ZSTR_VAL(find_first_constant_definition(ce, traits, current_trait, name, existing_constant->ce)->name),
2274-
ZSTR_VAL(trait_constant->ce->name),
2275-
ZSTR_VAL(name),
2276-
ZSTR_VAL(ce->name));
2300+
return emit_incompatible_trait_constant_error(ce, existing_constant, trait_constant, name, traits, current_trait);
22772301
}
22782302

22792303
/* There is an existing constant which is compatible with the new one, so no need to add it */
22802304
return false;
22812305
}
2282-
/* }}} */
22832306

22842307
static void zend_do_traits_constant_binding(zend_class_entry *ce, zend_class_entry **traits) /* {{{ */
22852308
{

0 commit comments

Comments
 (0)