Skip to content

Commit 3b62d66

Browse files
authored
Implement constants in traits (#8888)
RFC: https://wiki.php.net/rfc/constants_in_traits
1 parent a191710 commit 3b62d66

29 files changed

+722
-47
lines changed

Zend/Optimizer/pass1.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx)
164164
if (ce) {
165165
zend_class_constant *cc = zend_hash_find_ptr(
166166
&ce->constants_table, Z_STR(ZEND_OP2_LITERAL(opline)));
167-
if (cc && (ZEND_CLASS_CONST_FLAGS(cc) & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC) {
167+
if (cc && (ZEND_CLASS_CONST_FLAGS(cc) & ZEND_ACC_PPP_MASK) == ZEND_ACC_PUBLIC && !(ce->ce_flags & ZEND_ACC_TRAIT)) {
168168
zval *c = &cc->value;
169169
if (Z_TYPE_P(c) == IS_CONSTANT_AST) {
170170
zend_ast *ast = Z_ASTVAL_P(c);

Zend/tests/enum/traits-constants.phpt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
Enum can use traits having constants
3+
--FILE--
4+
<?php
5+
6+
trait Rectangle {
7+
private const MESSAGE_RECTANGLE = 'Rectangle';
8+
9+
public function shape(): string {
10+
return self::MESSAGE_RECTANGLE;
11+
}
12+
}
13+
14+
enum Suit {
15+
use Rectangle;
16+
17+
case Hearts;
18+
case Diamonds;
19+
case Clubs;
20+
case Spades;
21+
}
22+
23+
echo Suit::Hearts->shape() . PHP_EOL;
24+
echo Suit::Diamonds->shape() . PHP_EOL;
25+
echo Suit::Clubs->shape() . PHP_EOL;
26+
echo Suit::Spades->shape() . PHP_EOL;
27+
28+
?>
29+
--EXPECT--
30+
Rectangle
31+
Rectangle
32+
Rectangle
33+
Rectangle

Zend/tests/traits/constant_001.phpt

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
--TEST--
2+
Trying to access a constant on Trait via a Class
3+
--FILE--
4+
<?php
5+
6+
trait Foo {
7+
public const PUBLIC = 'public';
8+
protected const PROTECTED = 'protected';
9+
private const PRIVATE = 'private';
10+
11+
public function f1(): void {
12+
echo self::PUBLIC, ' via self', PHP_EOL;
13+
echo static::PUBLIC, ' via static', PHP_EOL;
14+
echo $this::PUBLIC, ' via $this', PHP_EOL;
15+
}
16+
}
17+
18+
class Base {
19+
use Foo;
20+
21+
public function f2(): void {
22+
echo self::PRIVATE, ' via self', PHP_EOL;
23+
echo static::PRIVATE, ' via static', PHP_EOL;
24+
}
25+
}
26+
27+
class Derived extends Base {
28+
public function f3(): void {
29+
echo self::PROTECTED, ' via self', PHP_EOL;
30+
echo static::PROTECTED, ' via static', PHP_EOL;
31+
echo parent::PROTECTED, ' via parent', PHP_EOL;
32+
}
33+
}
34+
35+
echo Base::PUBLIC, ' via class name', PHP_EOL;
36+
echo (new Base)::PUBLIC, ' via object', PHP_EOL;
37+
(new Base)->f1();
38+
(new Base)->f2();
39+
echo Derived::PUBLIC, ' via derived class name', PHP_EOL;
40+
echo (new Derived)::PUBLIC, ' via derived class object', PHP_EOL;
41+
(new Derived)->f3();
42+
?>
43+
--EXPECTF--
44+
public via class name
45+
public via object
46+
public via self
47+
public via static
48+
public via $this
49+
private via self
50+
private via static
51+
public via derived class name
52+
public via derived class object
53+
protected via self
54+
protected via static
55+
protected via parent

Zend/tests/traits/constant_002.phpt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
Defining a constant in both trait and its composing class with the same name, visibility, finality and value is allowed
3+
--FILE--
4+
<?php
5+
6+
trait TestTrait1 {
7+
public const A = 42;
8+
}
9+
10+
trait TestTrait2 {
11+
public const A = 42;
12+
}
13+
14+
trait TestTrait3 {
15+
use TestTrait2;
16+
public const A = 42;
17+
}
18+
19+
class ComposingClass1 {
20+
use TestTrait1;
21+
use TestTrait2;
22+
}
23+
24+
class ComposingClass2 {
25+
use TestTrait1;
26+
use TestTrait3;
27+
}
28+
29+
class ComposingClass3 {
30+
use TestTrait1;
31+
use TestTrait3;
32+
public const A = 42;
33+
}
34+
35+
echo ComposingClass1::A, PHP_EOL;
36+
echo ComposingClass2::A, PHP_EOL;
37+
echo ComposingClass3::A, PHP_EOL;
38+
?>
39+
--EXPECTF--
40+
42
41+
42
42+
42

Zend/tests/traits/constant_003.phpt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
Non-final Constants in traits can be overridden in derived classes
3+
--FILE--
4+
<?php
5+
6+
trait Foo {
7+
public const A = 123;
8+
}
9+
10+
class Base {
11+
use Foo;
12+
}
13+
14+
class Derived extends Base {
15+
public const A = 456;
16+
}
17+
18+
echo Derived::A, PHP_EOL;
19+
?>
20+
--EXPECTF--
21+
456

Zend/tests/traits/constant_004.phpt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
Trying to access a constant on trait via the name of trait causes a Fatal error
3+
--FILE--
4+
<?php
5+
trait Foo {
6+
const A = 42;
7+
}
8+
9+
class Bar {
10+
use Foo;
11+
}
12+
13+
echo Foo::A, PHP_EOL;
14+
?>
15+
--EXPECTF--
16+
Fatal error: Uncaught Error: Cannot access trait constant Foo::A directly in %s:%d
17+
Stack trace:
18+
#0 {main}
19+
thrown in %s on line %d

Zend/tests/traits/constant_005.phpt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Conflicting constants in composing classes with different visibility modifiers should result in a fatal error, since this indicates that the code is incompatible.
3+
--FILE--
4+
<?php
5+
6+
trait TestTrait {
7+
public const Constant = 42;
8+
}
9+
10+
echo "PRE-CLASS-GUARD\n";
11+
12+
class ComposingClass {
13+
use TestTrait;
14+
private const Constant = 42;
15+
}
16+
17+
echo "POST-CLASS-GUARD\n";
18+
?>
19+
--EXPECTF--
20+
PRE-CLASS-GUARD
21+
22+
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

Zend/tests/traits/constant_006.phpt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Conflicting constants in composing classes with different values should result in a fatal error, since this indicates that the code is incompatible.
3+
--FILE--
4+
<?php
5+
6+
trait TestTrait {
7+
public const Constant = 123;
8+
}
9+
10+
echo "PRE-CLASS-GUARD\n";
11+
12+
class ComposingClass {
13+
use TestTrait;
14+
public const Constant = 456;
15+
}
16+
17+
echo "POST-CLASS-GUARD\n";
18+
?>
19+
--EXPECTF--
20+
PRE-CLASS-GUARD
21+
22+
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

Zend/tests/traits/constant_007.phpt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Conflicting constants in composing classes with different finality should result in a fatal error, since this indicates that the code is incompatible.
3+
--FILE--
4+
<?php
5+
6+
trait TestTrait {
7+
public const Constant = 42;
8+
}
9+
10+
echo "PRE-CLASS-GUARD\n";
11+
12+
class ComposingClass {
13+
use TestTrait;
14+
public final const Constant = 42;
15+
}
16+
17+
echo "POST-CLASS-GUARD\n";
18+
?>
19+
--EXPECTF--
20+
PRE-CLASS-GUARD
21+
22+
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

Zend/tests/traits/constant_008.phpt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
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.
3+
--FILE--
4+
<?php
5+
6+
trait Trait1 {
7+
public const Constant = 42;
8+
}
9+
10+
trait Trait2 {
11+
private const Constant = 42;
12+
}
13+
14+
echo "PRE-CLASS-GUARD\n";
15+
16+
class TraitsTest {
17+
use Trait1;
18+
use Trait2;
19+
}
20+
21+
echo "POST-CLASS-GUARD\n";
22+
?>
23+
--EXPECTF--
24+
PRE-CLASS-GUARD
25+
26+
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

Zend/tests/traits/constant_009.phpt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
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.
3+
--FILE--
4+
<?php
5+
6+
trait Trait1 {
7+
public const Constant = 123;
8+
}
9+
10+
trait Trait2 {
11+
public const Constant = 456;
12+
}
13+
14+
echo "PRE-CLASS-GUARD\n";
15+
16+
class TraitsTest {
17+
use Trait1;
18+
use Trait2;
19+
}
20+
21+
echo "POST-CLASS-GUARD\n";
22+
?>
23+
--EXPECTF--
24+
PRE-CLASS-GUARD
25+
26+
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

Zend/tests/traits/constant_010.phpt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
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.
3+
--FILE--
4+
<?php
5+
6+
trait TestTrait {
7+
public const Constant = 42;
8+
}
9+
10+
echo "PRE-CLASS-GUARD\n";
11+
12+
class ComposingClass {
13+
use TestTrait;
14+
public final const Constant = 42;
15+
}
16+
17+
echo "POST-CLASS-GUARD\n";
18+
?>
19+
--EXPECTF--
20+
PRE-CLASS-GUARD
21+
22+
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

Zend/tests/traits/constant_011.phpt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
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.
3+
--FILE--
4+
<?php
5+
6+
trait Trait1 {
7+
public const Constant = 42;
8+
}
9+
10+
trait Trait2 {
11+
use Trait1;
12+
private const Constant = 42;
13+
}
14+
?>
15+
--EXPECTF--
16+
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

Zend/tests/traits/constant_012.phpt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
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.
3+
--FILE--
4+
<?php
5+
6+
trait Trait1 {
7+
public const Constant = 123;
8+
}
9+
10+
trait Trait2 {
11+
use Trait1;
12+
public const Constant = 456;
13+
}
14+
?>
15+
--EXPECTF--
16+
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

Zend/tests/traits/constant_013.phpt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
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.
3+
--FILE--
4+
<?php
5+
6+
trait Trait1 {
7+
public const Constant = 123;
8+
}
9+
10+
trait Trait2 {
11+
use Trait1;
12+
public const Constant = 456;
13+
}
14+
?>
15+
--EXPECTF--
16+
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

0 commit comments

Comments
 (0)