Skip to content

[RFC] Implement constants in traits #8888

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Zend/Optimizer/pass1.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commentary

This part is needed to prevent accessing trait constants directly when opcache is enabled.
The checking is usually done on the handler of ZEND_FETCH_CLASS_CONSTANT at runtime.

zval *c = &cc->value;
if (Z_TYPE_P(c) == IS_CONSTANT_AST) {
zend_ast *ast = Z_ASTVAL_P(c);
Expand Down
33 changes: 33 additions & 0 deletions Zend/tests/enum/traits-constants.phpt
Original file line number Diff line number Diff line change
@@ -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
55 changes: 55 additions & 0 deletions Zend/tests/traits/constant_001.phpt
Original file line number Diff line number Diff line change
@@ -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
42 changes: 42 additions & 0 deletions Zend/tests/traits/constant_002.phpt
Original file line number Diff line number Diff line change
@@ -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
21 changes: 21 additions & 0 deletions Zend/tests/traits/constant_003.phpt
Original file line number Diff line number Diff line change
@@ -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
19 changes: 19 additions & 0 deletions Zend/tests/traits/constant_004.phpt
Original file line number Diff line number Diff line change
@@ -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
22 changes: 22 additions & 0 deletions Zend/tests/traits/constant_005.phpt
Original file line number Diff line number Diff line change
@@ -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
22 changes: 22 additions & 0 deletions Zend/tests/traits/constant_006.phpt
Original file line number Diff line number Diff line change
@@ -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
22 changes: 22 additions & 0 deletions Zend/tests/traits/constant_007.phpt
Original file line number Diff line number Diff line change
@@ -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
26 changes: 26 additions & 0 deletions Zend/tests/traits/constant_008.phpt
Original file line number Diff line number Diff line change
@@ -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
26 changes: 26 additions & 0 deletions Zend/tests/traits/constant_009.phpt
Original file line number Diff line number Diff line change
@@ -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
22 changes: 22 additions & 0 deletions Zend/tests/traits/constant_010.phpt
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions Zend/tests/traits/constant_011.phpt
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions Zend/tests/traits/constant_012.phpt
Original file line number Diff line number Diff line change
@@ -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
16 changes: 16 additions & 0 deletions Zend/tests/traits/constant_013.phpt
Original file line number Diff line number Diff line change
@@ -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
Loading