Implement constants in traits (#8888)

RFC: https://wiki.php.net/rfc/constants_in_traits
This commit is contained in:
sji 2022-08-05 04:08:40 +09:00 committed by GitHub
parent a191710a21
commit 3b62d66098
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 722 additions and 47 deletions

View File

@ -164,7 +164,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
if (ce) {
zend_class_constant *cc = zend_hash_find_ptr(
&ce->constants_table, Z_STR(ZEND_OP2_LITERAL(opline)));
if (cc && (ZEND_CLASS_CONST_FLAGS(cc) & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) {
if (cc && (ZEND_CLASS_CONST_FLAGS(cc) & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC && !(ce->ce_flags & ZEND_ACC_TRAIT)) {
zval *c = &cc->value;
if (Z_TYPE_P(c) == IS_CONSTANT_AST) {
zend_ast *ast = Z_ASTVAL_P(c);

View File

@ -0,0 +1,33 @@
--TEST--
Enum can use traits having constants
--FILE--
<?php
trait Rectangle {
private const MESSAGE_RECTANGLE = 'Rectangle';
public function shape(): string {
return self::MESSAGE_RECTANGLE;
}
}
enum Suit {
use Rectangle;
case Hearts;
case Diamonds;
case Clubs;
case Spades;
}
echo Suit::Hearts->shape() . PHP_EOL;
echo Suit::Diamonds->shape() . PHP_EOL;
echo Suit::Clubs->shape() . PHP_EOL;
echo Suit::Spades->shape() . PHP_EOL;
?>
--EXPECT--
Rectangle
Rectangle
Rectangle
Rectangle

View File

@ -0,0 +1,55 @@
--TEST--
Trying to access a constant on Trait via a Class
--FILE--
<?php
trait Foo {
public const PUBLIC = 'public';
protected const PROTECTED = 'protected';
private const PRIVATE = 'private';
public function f1(): void {
echo self::PUBLIC, ' via self', PHP_EOL;
echo static::PUBLIC, ' via static', PHP_EOL;
echo $this::PUBLIC, ' via $this', PHP_EOL;
}
}
class Base {
use Foo;
public function f2(): void {
echo self::PRIVATE, ' via self', PHP_EOL;
echo static::PRIVATE, ' via static', PHP_EOL;
}
}
class Derived extends Base {
public function f3(): void {
echo self::PROTECTED, ' via self', PHP_EOL;
echo static::PROTECTED, ' via static', PHP_EOL;
echo parent::PROTECTED, ' via parent', PHP_EOL;
}
}
echo Base::PUBLIC, ' via class name', PHP_EOL;
echo (new Base)::PUBLIC, ' via object', PHP_EOL;
(new Base)->f1();
(new Base)->f2();
echo Derived::PUBLIC, ' via derived class name', PHP_EOL;
echo (new Derived)::PUBLIC, ' via derived class object', PHP_EOL;
(new Derived)->f3();
?>
--EXPECTF--
public via class name
public via object
public via self
public via static
public via $this
private via self
private via static
public via derived class name
public via derived class object
protected via self
protected via static
protected via parent

View File

@ -0,0 +1,42 @@
--TEST--
Defining a constant in both trait and its composing class with the same name, visibility, finality and value is allowed
--FILE--
<?php
trait TestTrait1 {
public const A = 42;
}
trait TestTrait2 {
public const A = 42;
}
trait TestTrait3 {
use TestTrait2;
public const A = 42;
}
class ComposingClass1 {
use TestTrait1;
use TestTrait2;
}
class ComposingClass2 {
use TestTrait1;
use TestTrait3;
}
class ComposingClass3 {
use TestTrait1;
use TestTrait3;
public const A = 42;
}
echo ComposingClass1::A, PHP_EOL;
echo ComposingClass2::A, PHP_EOL;
echo ComposingClass3::A, PHP_EOL;
?>
--EXPECTF--
42
42
42

View File

@ -0,0 +1,21 @@
--TEST--
Non-final Constants in traits can be overridden in derived classes
--FILE--
<?php
trait Foo {
public const A = 123;
}
class Base {
use Foo;
}
class Derived extends Base {
public const A = 456;
}
echo Derived::A, PHP_EOL;
?>
--EXPECTF--
456

View File

@ -0,0 +1,19 @@
--TEST--
Trying to access a constant on trait via the name of trait causes a Fatal error
--FILE--
<?php
trait Foo {
const A = 42;
}
class Bar {
use Foo;
}
echo Foo::A, PHP_EOL;
?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot access trait constant Foo::A directly in %s:%d
Stack trace:
#0 {main}
thrown in %s on line %d

View File

@ -0,0 +1,22 @@
--TEST--
Conflicting constants in composing classes with different visibility modifiers should result in a fatal error, since this indicates that the code is incompatible.
--FILE--
<?php
trait TestTrait {
public const Constant = 42;
}
echo "PRE-CLASS-GUARD\n";
class ComposingClass {
use TestTrait;
private const Constant = 42;
}
echo "POST-CLASS-GUARD\n";
?>
--EXPECTF--
PRE-CLASS-GUARD
Fatal error: ComposingClass and TestTrait define the same constant (Constant) in the composition of ComposingClass. However, the definition differs and is considered incompatible. Class was composed in %s on line %d

View File

@ -0,0 +1,22 @@
--TEST--
Conflicting constants in composing classes with different values should result in a fatal error, since this indicates that the code is incompatible.
--FILE--
<?php
trait TestTrait {
public const Constant = 123;
}
echo "PRE-CLASS-GUARD\n";
class ComposingClass {
use TestTrait;
public const Constant = 456;
}
echo "POST-CLASS-GUARD\n";
?>
--EXPECTF--
PRE-CLASS-GUARD
Fatal error: ComposingClass and TestTrait define the same constant (Constant) in the composition of ComposingClass. However, the definition differs and is considered incompatible. Class was composed in %s on line %d

View File

@ -0,0 +1,22 @@
--TEST--
Conflicting constants in composing classes with different finality should result in a fatal error, since this indicates that the code is incompatible.
--FILE--
<?php
trait TestTrait {
public const Constant = 42;
}
echo "PRE-CLASS-GUARD\n";
class ComposingClass {
use TestTrait;
public final const Constant = 42;
}
echo "POST-CLASS-GUARD\n";
?>
--EXPECTF--
PRE-CLASS-GUARD
Fatal error: ComposingClass and TestTrait define the same constant (Constant) in the composition of ComposingClass. However, the definition differs and is considered incompatible. Class was composed in %s on line %d

View File

@ -0,0 +1,26 @@
--TEST--
Conflicting constants in another traits in same composing classes with different visibility modifiers should result in a fatal error, since this indicates that the code is incompatible.
--FILE--
<?php
trait Trait1 {
public const Constant = 42;
}
trait Trait2 {
private const Constant = 42;
}
echo "PRE-CLASS-GUARD\n";
class TraitsTest {
use Trait1;
use Trait2;
}
echo "POST-CLASS-GUARD\n";
?>
--EXPECTF--
PRE-CLASS-GUARD
Fatal error: Trait1 and Trait2 define the same constant (Constant) in the composition of TraitsTest. However, the definition differs and is considered incompatible. Class was composed in %s on line %d

View File

@ -0,0 +1,26 @@
--TEST--
Conflicting constants in another traits in same composing classes with different values should result in a fatal error, since this indicates that the code is incompatible.
--FILE--
<?php
trait Trait1 {
public const Constant = 123;
}
trait Trait2 {
public const Constant = 456;
}
echo "PRE-CLASS-GUARD\n";
class TraitsTest {
use Trait1;
use Trait2;
}
echo "POST-CLASS-GUARD\n";
?>
--EXPECTF--
PRE-CLASS-GUARD
Fatal error: Trait1 and Trait2 define the same constant (Constant) in the composition of TraitsTest. However, the definition differs and is considered incompatible. Class was composed in %s on line %d

View File

@ -0,0 +1,22 @@
--TEST--
Conflicting constants in another traits in same composing classes with different finality should result in a fatal error, since this indicates that the code is incompatible.
--FILE--
<?php
trait TestTrait {
public const Constant = 42;
}
echo "PRE-CLASS-GUARD\n";
class ComposingClass {
use TestTrait;
public final const Constant = 42;
}
echo "POST-CLASS-GUARD\n";
?>
--EXPECTF--
PRE-CLASS-GUARD
Fatal error: ComposingClass and TestTrait define the same constant (Constant) in the composition of ComposingClass. However, the definition differs and is considered incompatible. Class was composed in %s on line %d

View File

@ -0,0 +1,16 @@
--TEST--
Conflicting constants in a trait and another trait using it with different visibility modifiers should result in a fatal error, since this indicates that the code is incompatible.
--FILE--
<?php
trait Trait1 {
public const Constant = 42;
}
trait Trait2 {
use Trait1;
private const Constant = 42;
}
?>
--EXPECTF--
Fatal error: Trait2 and Trait1 define the same constant (Constant) in the composition of Trait2. However, the definition differs and is considered incompatible. Class was composed in %s on line %d

View File

@ -0,0 +1,16 @@
--TEST--
Conflicting constants in a trait and another trait using it with different values should result in a fatal error, since this indicates that the code is incompatible.
--FILE--
<?php
trait Trait1 {
public const Constant = 123;
}
trait Trait2 {
use Trait1;
public const Constant = 456;
}
?>
--EXPECTF--
Fatal error: Trait2 and Trait1 define the same constant (Constant) in the composition of Trait2. However, the definition differs and is considered incompatible. Class was composed in %s on line %d

View File

@ -0,0 +1,16 @@
--TEST--
Conflicting constants in a trait and another trait using it with different finality should result in a fatal error, since this indicates that the code is incompatible.
--FILE--
<?php
trait Trait1 {
public const Constant = 123;
}
trait Trait2 {
use Trait1;
public const Constant = 456;
}
?>
--EXPECTF--
Fatal error: Trait2 and Trait1 define the same constant (Constant) in the composition of Trait2. However, the definition differs and is considered incompatible. Class was composed in %s on line %d

View File

@ -0,0 +1,21 @@
--TEST--
Cannot override a final trait constant in a class derived from the class that uses the trait
--FILE--
<?php
trait TestTrait {
public final const Constant = 123;
}
class ComposingClass {
use TestTrait;
public final const Constant = 123;
}
class DerivedClass extends ComposingClass {
public final const Constant = 456;
}
?>
--EXPECTF--
Fatal error: DerivedClass::Constant cannot override final constant ComposingClass::Constant in %s on line %d

View File

@ -0,0 +1,32 @@
--TEST--
The same name constant of a trait used in a class that inherits a constant defined in a parent can be defined only if they are compatible.
--FILE--
<?php
trait TestTrait1 {
public final const Constant = 123;
}
class BaseClass1 {
public final const Constant = 123;
}
class DerivedClass1 extends BaseClass1 {
use TestTrait1;
}
trait TestTrait2 {
public final const Constant = 123;
}
class BaseClass2 {
public final const Constant = 456;
}
class DerivedClass2 extends BaseClass2 {
use TestTrait2;
}
?>
--EXPECTF--
Fatal error: BaseClass2 and TestTrait2 define the same constant (Constant) in the composition of DerivedClass2. However, the definition differs and is considered incompatible. Class was composed in %s on line %d

View File

@ -0,0 +1,25 @@
--TEST--
Compatibility of values of same name trait constants is checked after their constant expressions are evaluated
--ENV--
ENSURE_CONSTANT_IS_DEFINED_AT_RUNTIME=1
--FILE--
<?php
// Ensure CONSTANT is defined at runtime
if ($_ENV['ENSURE_CONSTANT_IS_DEFINED_AT_RUNTIME']) {
define('CONSTANT', 2);
}
trait TestTrait {
public const Constant = 40 + CONSTANT;
}
class ComposingClass {
use TestTrait;
public const Constant = 42;
}
echo ComposingClass::Constant;
?>
--EXPECTF--
42

View File

@ -0,0 +1,13 @@
--TEST--
Referencing trait constants directly on calling \defined() returns false
--FILE--
<?php
trait TestTrait {
public const Constant = 42;
}
var_dump(\defined('TestTrait::Constant'));
?>
--EXPECTF--
bool(false)

View File

@ -0,0 +1,17 @@
--TEST--
Referencing trait constants directly on calling \constant() causes a fatal error
--FILE--
<?php
trait TestTrait {
public const Constant = 42;
}
var_dump(\constant('TestTrait::Constant'));
?>
--EXPECTF--
Fatal error: Uncaught Error: Cannot access trait constant TestTrait::Constant directly in %s:%d
Stack trace:
#0 %s: constant('TestTrait::Cons...')
#1 {main}
thrown in %s on line %d

View File

@ -0,0 +1,27 @@
--TEST--
Flattened trait constants are retrieved as members of the composing class via Reflection
--FILE--
<?php
trait TestTrait {
public const Constant = 42;
}
class TestClass {
use TestTrait;
}
$reflection = new \ReflectionClass(TestTrait::class);
var_dump($reflection->getConstant('Constant'));
var_dump($reflection->getReflectionConstant('Constant')->getDeclaringClass()->getName());
$reflection = new \ReflectionClass(TestClass::class);
var_dump($reflection->getConstant('Constant'));
var_dump($reflection->getReflectionConstant('Constant')->getDeclaringClass()->getName());
?>
--EXPECTF--
int(42)
string(9) "TestTrait"
int(42)
string(9) "TestClass"

View File

@ -0,0 +1,28 @@
--TEST--
Attributes can be retrieved from trait constants
--FILE--
<?php
class TestAttribute {
public function __construct(public string $value) {}
}
trait TestTrait {
#[TestAttribute(value: 123)]
public const Constant = 42;
}
class TestClass {
use TestTrait;
}
$reflection = new \ReflectionClass(TestTrait::class);
var_dump($reflection->getReflectionConstant('Constant')->getAttributes('TestAttribute')[0]->getArguments()['value']);
$reflection = new \ReflectionClass(TestClass::class);
var_dump($reflection->getReflectionConstant('Constant')->getAttributes('TestAttribute')[0]->getArguments()['value']);
?>
--EXPECTF--
int(123)
int(123)

View File

@ -0,0 +1,24 @@
--TEST--
Doc comments can be retrieved from trait constants
--FILE--
<?php
trait TestTrait {
/** DocComments */
public const Constant = 42;
}
class TestClass {
use TestTrait;
}
$reflection = new \ReflectionClass(TestTrait::class);
var_dump($reflection->getReflectionConstant('Constant')->getDocComment());
$reflection = new \ReflectionClass(TestClass::class);
var_dump($reflection->getReflectionConstant('Constant')->getDocComment());
?>
--EXPECTF--
string(18) "/** DocComments */"
string(18) "/** DocComments */"

View File

@ -1,12 +0,0 @@
--TEST--
Trying to create a constant on Trait
--FILE--
<?php
trait foo {
const a = 1;
}
?>
--EXPECTF--
Fatal error: Traits cannot have constants in %s on line %d

View File

@ -1594,7 +1594,12 @@ static bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *c
/* We don't use zend_verify_const_access because we need to deal with unlinked classes. */
static bool zend_verify_ct_const_access(zend_class_constant *c, zend_class_entry *scope)
{
if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_PUBLIC) {
if (c->ce->ce_flags & ZEND_ACC_TRAIT) {
/* This condition is only met on directly accessing trait constants,
* because the ce is replaced to the class entry of the composing class
* on binding. */
return 0;
} else if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_PUBLIC) {
return 1;
} else if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_PRIVATE) {
return c->ce == scope;
@ -7532,11 +7537,6 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
zend_class_entry *ce = CG(active_class_entry);
uint32_t i, children = list->children;
if ((ce->ce_flags & ZEND_ACC_TRAIT) != 0) {
zend_error_noreturn(E_COMPILE_ERROR, "Traits cannot have constants");
return;
}
for (i = 0; i < children; ++i) {
zend_class_constant *c;
zend_ast *const_ast = list->child[i];

View File

@ -360,6 +360,15 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string *
}
goto failure;
}
if (UNEXPECTED(ce->ce_flags & ZEND_ACC_TRAIT)) {
/** Prevent accessing trait constants directly on cases like \defined() or \constant(), etc. */
if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) {
zend_throw_error(NULL, "Cannot access trait constant %s::%s directly", ZSTR_VAL(class_name), ZSTR_VAL(constant_name));
}
goto failure;
}
ret_constant = &c->value;
}
}

View File

@ -1630,6 +1630,36 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
}
/* }}} */
static zend_always_inline bool check_trait_property_or_constant_value_compatibility(zend_class_entry *ce, zval *op1, zval *op2) /* {{{ */
{
bool is_compatible;
zval op1_tmp, op2_tmp;
/* if any of the values is a constant, we try to resolve it */
if (UNEXPECTED(Z_TYPE_P(op1) == IS_CONSTANT_AST)) {
ZVAL_COPY_OR_DUP(&op1_tmp, op1);
zval_update_constant_ex(&op1_tmp, ce);
op1 = &op1_tmp;
}
if (UNEXPECTED(Z_TYPE_P(op2) == IS_CONSTANT_AST)) {
ZVAL_COPY_OR_DUP(&op2_tmp, op2);
zval_update_constant_ex(&op2_tmp, ce);
op2 = &op2_tmp;
}
is_compatible = fast_is_identical_function(op1, op2);
if (op1 == &op1_tmp) {
zval_ptr_dtor_nogc(&op1_tmp);
}
if (op2 == &op2_tmp) {
zval_ptr_dtor_nogc(&op2_tmp);
}
return is_compatible;
}
/* }}} */
static bool do_inherit_constant_check(
zend_class_entry *ce, zend_class_constant *parent_constant, zend_string *name
) {
@ -2174,7 +2204,103 @@ static void zend_do_traits_method_binding(zend_class_entry *ce, zend_class_entry
}
/* }}} */
static zend_class_entry* find_first_definition(zend_class_entry *ce, zend_class_entry **traits, size_t current_trait, zend_string *prop_name, zend_class_entry *colliding_ce) /* {{{ */
static zend_class_entry* find_first_constant_definition(zend_class_entry *ce, zend_class_entry **traits, size_t current_trait, zend_string *constant_name, zend_class_entry *colliding_ce) /* {{{ */
{
/* This function is used to show the place of the existing conflicting
* definition in error messages when conflicts occur. Since trait constants
* are flattened into the constants table of the composing class, and thus
* we lose information about which constant was defined in which trait, a
* process like this is needed to find the location of the first definition
* of the constant from traits.
*/
size_t i;
if (colliding_ce == ce) {
for (i = 0; i < current_trait; i++) {
if (traits[i]
&& zend_hash_exists(&traits[i]->constants_table, constant_name)) {
return traits[i];
}
}
}
/* Traits don't have it, then the composing class (or trait) itself has it. */
return colliding_ce;
}
/* }}} */
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) /* {{{ */
{
uint32_t flags_mask = ZEND_ACC_PPP_MASK | ZEND_ACC_FINAL;
zval *zv = zend_hash_find_known_hash(&ce->constants_table, name);
if (zv == NULL) {
/* No existing constant of the same name, so this one can be added */
return true;
}
zend_class_constant *existing_constant = Z_PTR_P(zv);
if ((ZEND_CLASS_CONST_FLAGS(trait_constant) & flags_mask) != (ZEND_CLASS_CONST_FLAGS(existing_constant) & flags_mask) ||
!check_trait_property_or_constant_value_compatibility(ce, &trait_constant->value, &existing_constant->value)) {
/* There is an existing constant of the same name, and it conflicts with the new one, so let's throw a fatal error */
zend_error_noreturn(E_COMPILE_ERROR,
"%s and %s define the same constant (%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed",
ZSTR_VAL(find_first_constant_definition(ce, traits, current_trait, name, existing_constant->ce)->name),
ZSTR_VAL(trait_constant->ce->name),
ZSTR_VAL(name),
ZSTR_VAL(ce->name));
}
/* There is an existing constant which is compatible with the new one, so no need to add it */
return false;
}
/* }}} */
static void zend_do_traits_constant_binding(zend_class_entry *ce, zend_class_entry **traits) /* {{{ */
{
size_t i;
for (i = 0; i < ce->num_traits; i++) {
zend_string *constant_name;
zend_class_constant *constant;
if (!traits[i]) {
continue;
}
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&traits[i]->constants_table, constant_name, constant) {
if (do_trait_constant_check(ce, constant, constant_name, traits, i)) {
zend_class_constant *ct = NULL;
ct = zend_arena_alloc(&CG(arena),sizeof(zend_class_constant));
memcpy(ct, constant, sizeof(zend_class_constant));
constant = ct;
if (Z_TYPE(constant->value) == IS_CONSTANT_AST) {
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS;
}
/* Unlike interface implementations and class inheritances,
* access control of the trait constants is done by the scope
* of the composing class. So let's replace the ce here.
*/
constant->ce = ce;
Z_TRY_ADDREF(constant->value);
constant->doc_comment = constant->doc_comment ? zend_string_copy(constant->doc_comment) : NULL;
if (constant->attributes && (!(GC_FLAGS(constant->attributes) & IS_ARRAY_IMMUTABLE))) {
GC_ADDREF(constant->attributes);
}
zend_hash_update_ptr(&ce->constants_table, constant_name, constant);
}
} ZEND_HASH_FOREACH_END();
}
}
/* }}} */
static zend_class_entry* find_first_property_definition(zend_class_entry *ce, zend_class_entry **traits, size_t current_trait, zend_string *prop_name, zend_class_entry *colliding_ce) /* {{{ */
{
size_t i;
@ -2198,7 +2324,6 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent
zend_property_info *colliding_prop;
zend_property_info *new_prop;
zend_string* prop_name;
bool not_compatible;
zval* prop_value;
zend_string *doc_comment;
@ -2220,15 +2345,14 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent
zend_hash_del(&ce->properties_info, prop_name);
flags |= ZEND_ACC_CHANGED;
} else {
bool is_compatible = false;
uint32_t flags_mask = ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC | ZEND_ACC_READONLY;
not_compatible = 1;
if ((colliding_prop->flags & flags_mask) == (flags & flags_mask) &&
property_types_compatible(property_info, colliding_prop) == INHERITANCE_SUCCESS
) {
/* the flags are identical, thus, the properties may be compatible */
zval *op1, *op2;
zval op1_tmp, op2_tmp;
if (flags & ZEND_ACC_STATIC) {
op1 = &ce->default_static_members_table[colliding_prop->offset];
@ -2239,33 +2363,13 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent
op1 = &ce->default_properties_table[OBJ_PROP_TO_NUM(colliding_prop->offset)];
op2 = &traits[i]->default_properties_table[OBJ_PROP_TO_NUM(property_info->offset)];
}
/* if any of the values is a constant, we try to resolve it */
if (UNEXPECTED(Z_TYPE_P(op1) == IS_CONSTANT_AST)) {
ZVAL_COPY_OR_DUP(&op1_tmp, op1);
zval_update_constant_ex(&op1_tmp, ce);
op1 = &op1_tmp;
}
if (UNEXPECTED(Z_TYPE_P(op2) == IS_CONSTANT_AST)) {
ZVAL_COPY_OR_DUP(&op2_tmp, op2);
zval_update_constant_ex(&op2_tmp, ce);
op2 = &op2_tmp;
}
not_compatible = fast_is_not_identical_function(op1, op2);
if (op1 == &op1_tmp) {
zval_ptr_dtor_nogc(&op1_tmp);
}
if (op2 == &op2_tmp) {
zval_ptr_dtor_nogc(&op2_tmp);
}
is_compatible = check_trait_property_or_constant_value_compatibility(ce, op1, op2);
}
if (not_compatible) {
if (!is_compatible) {
zend_error_noreturn(E_COMPILE_ERROR,
"%s and %s define the same property ($%s) in the composition of %s. However, the definition differs and is considered incompatible. Class was composed",
ZSTR_VAL(find_first_definition(ce, traits, i, prop_name, colliding_prop->ce)->name),
ZSTR_VAL(find_first_property_definition(ce, traits, i, prop_name, colliding_prop->ce)->name),
ZSTR_VAL(property_info->ce->name),
ZSTR_VAL(prop_name),
ZSTR_VAL(ce->name));
@ -2322,7 +2426,8 @@ static void zend_do_bind_traits(zend_class_entry *ce, zend_class_entry **traits)
efree(exclude_tables);
}
/* then flatten the properties into it, to, mostly to notify developer about problems */
/* then flatten the constants and properties into it, to, mostly to notify developer about problems */
zend_do_traits_constant_binding(ce, traits);
zend_do_traits_property_binding(ce, traits);
}
/* }}} */

View File

@ -5901,6 +5901,13 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO
ZVAL_UNDEF(EX_VAR(opline->result.var));
HANDLE_EXCEPTION();
}
if (ce->ce_flags & ZEND_ACC_TRAIT) {
zend_throw_error(NULL, "Cannot access trait constant %s::%s directly", ZSTR_VAL(ce->name), Z_STRVAL_P(RT_CONSTANT(opline, opline->op2)));
ZVAL_UNDEF(EX_VAR(opline->result.var));
HANDLE_EXCEPTION();
}
value = &c->value;
// Enums require loading of all class constants to build the backed enum table
if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {

21
Zend/zend_vm_execute.h generated
View File

@ -7143,6 +7143,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS
ZVAL_UNDEF(EX_VAR(opline->result.var));
HANDLE_EXCEPTION();
}
if (ce->ce_flags & ZEND_ACC_TRAIT) {
zend_throw_error(NULL, "Cannot access trait constant %s::%s directly", ZSTR_VAL(ce->name), Z_STRVAL_P(RT_CONSTANT(opline, opline->op2)));
ZVAL_UNDEF(EX_VAR(opline->result.var));
HANDLE_EXCEPTION();
}
value = &c->value;
// Enums require loading of all class constants to build the backed enum table
if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
@ -24704,6 +24711,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_
ZVAL_UNDEF(EX_VAR(opline->result.var));
HANDLE_EXCEPTION();
}
if (ce->ce_flags & ZEND_ACC_TRAIT) {
zend_throw_error(NULL, "Cannot access trait constant %s::%s directly", ZSTR_VAL(ce->name), Z_STRVAL_P(RT_CONSTANT(opline, opline->op2)));
ZVAL_UNDEF(EX_VAR(opline->result.var));
HANDLE_EXCEPTION();
}
value = &c->value;
// Enums require loading of all class constants to build the backed enum table
if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {
@ -33549,6 +33563,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS
ZVAL_UNDEF(EX_VAR(opline->result.var));
HANDLE_EXCEPTION();
}
if (ce->ce_flags & ZEND_ACC_TRAIT) {
zend_throw_error(NULL, "Cannot access trait constant %s::%s directly", ZSTR_VAL(ce->name), Z_STRVAL_P(RT_CONSTANT(opline, opline->op2)));
ZVAL_UNDEF(EX_VAR(opline->result.var));
HANDLE_EXCEPTION();
}
value = &c->value;
// Enums require loading of all class constants to build the backed enum table
if (ce->ce_flags & ZEND_ACC_ENUM && ce->enum_backing_type != IS_UNDEF && ce->type == ZEND_USER_CLASS && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) {