summaryrefslogtreecommitdiff
path: root/Zend/tests
diff options
context:
space:
mode:
authorIlija Tovilo <ilija.tovilo@me.com>2020-06-10 23:10:18 +0200
committerIlija Tovilo <ilija.tovilo@me.com>2021-03-17 19:08:03 +0100
commit269c8dac1d56ee85d71ae94d9b28dd7d8e8de7b7 (patch)
tree810ac41b2157ff4e8063f9696f97e1a9d77837c4 /Zend/tests
parenta6fc427b8c51015c16541c112a26dd06bd75e99e (diff)
downloadphp-git-269c8dac1d56ee85d71ae94d9b28dd7d8e8de7b7.tar.gz
Implement enums
RFC: https://wiki.php.net/rfc/enumerations Co-authored-by: Nikita Popov <nikita.ppv@gmail.com> Closes GH-6489.
Diffstat (limited to 'Zend/tests')
-rw-r--r--Zend/tests/enum/__call.phpt29
-rw-r--r--Zend/tests/enum/__callStatic.phpt27
-rw-r--r--Zend/tests/enum/__class__.phpt19
-rw-r--r--Zend/tests/enum/__function__.phpt19
-rw-r--r--Zend/tests/enum/__get.phpt17
-rw-r--r--Zend/tests/enum/__invoke.phpt24
-rw-r--r--Zend/tests/enum/__isset.phpt16
-rw-r--r--Zend/tests/enum/__method__.phpt19
-rw-r--r--Zend/tests/enum/ast-dumper.phpt48
-rw-r--r--Zend/tests/enum/backed-cases-int.phpt26
-rw-r--r--Zend/tests/enum/backed-cases-string.phpt26
-rw-r--r--Zend/tests/enum/backed-duplicate-int.phpt13
-rw-r--r--Zend/tests/enum/backed-duplicate-string.phpt15
-rw-r--r--Zend/tests/enum/backed-from-invalid-int.phpt19
-rw-r--r--Zend/tests/enum/backed-from-invalid-string.phpt21
-rw-r--r--Zend/tests/enum/backed-from-invalid-type.phpt34
-rw-r--r--Zend/tests/enum/backed-from-unknown-hash.phpt17
-rw-r--r--Zend/tests/enum/backed-from.phpt36
-rw-r--r--Zend/tests/enum/backed-int-case-without-value.phpt14
-rw-r--r--Zend/tests/enum/backed-int-const-expr.phpt20
-rw-r--r--Zend/tests/enum/backed-int-const-invalid-expr.phpt14
-rw-r--r--Zend/tests/enum/backed-int.phpt17
-rw-r--r--Zend/tests/enum/backed-invalid.phpt10
-rw-r--r--Zend/tests/enum/backed-mismatch.phpt12
-rw-r--r--Zend/tests/enum/backed-negative-int.phpt21
-rw-r--r--Zend/tests/enum/backed-string-heredoc.phpt30
-rw-r--r--Zend/tests/enum/backed-string.phpt17
-rw-r--r--Zend/tests/enum/backed-tryFrom-casing.phpt15
-rw-r--r--Zend/tests/enum/backed-tryFrom-unknown-hash.phpt17
-rw-r--r--Zend/tests/enum/backed-tryFrom.phpt40
-rw-r--r--Zend/tests/enum/backed-type-no-union.phpt10
-rw-r--r--Zend/tests/enum/basic-methods.phpt21
-rw-r--r--Zend/tests/enum/case-attributes.phpt25
-rw-r--r--Zend/tests/enum/case-in-class.phpt12
-rw-r--r--Zend/tests/enum/cases-refcount.phpt22
-rw-r--r--Zend/tests/enum/comparison.phpt57
-rw-r--r--Zend/tests/enum/constant-aliases.phpt21
-rw-r--r--Zend/tests/enum/constants.phpt14
-rw-r--r--Zend/tests/enum/default-parameter.phpt18
-rw-r--r--Zend/tests/enum/enum-as-constant.phpt22
-rw-r--r--Zend/tests/enum/enum-as-params.phpt35
-rw-r--r--Zend/tests/enum/enum-attributes.phpt23
-rw-r--r--Zend/tests/enum/enum-in-constant.phpt22
-rw-r--r--Zend/tests/enum/enum-in-static-var.phpt21
-rw-r--r--Zend/tests/enum/enum-reserved-non-modifiers.phpt32
-rw-r--r--Zend/tests/enum/enum_exists.phpt41
-rw-r--r--Zend/tests/enum/final.phpt12
-rw-r--r--Zend/tests/enum/implements.phpt34
-rw-r--r--Zend/tests/enum/instanceof-backed-enum.phpt20
-rw-r--r--Zend/tests/enum/instanceof-unitenum.phpt18
-rw-r--r--Zend/tests/enum/instanceof.phpt25
-rw-r--r--Zend/tests/enum/json_encode.phpt59
-rw-r--r--Zend/tests/enum/keyword-no-bc-break.phpt42
-rw-r--r--Zend/tests/enum/keyword-whitespace.phpt16
-rw-r--r--Zend/tests/enum/name-property.phpt50
-rw-r--r--Zend/tests/enum/namespaces.phpt32
-rw-r--r--Zend/tests/enum/no-cases.phpt16
-rw-r--r--Zend/tests/enum/no-class-implements-backed-enum.phpt10
-rw-r--r--Zend/tests/enum/no-class-implements-unit-enum.phpt10
-rw-r--r--Zend/tests/enum/no-clone.phpt18
-rw-r--r--Zend/tests/enum/no-constructors.phpt12
-rw-r--r--Zend/tests/enum/no-destruct.phpt12
-rw-r--r--Zend/tests/enum/no-dynamic-properties.phpt20
-rw-r--r--Zend/tests/enum/no-enum-implements-backed-enum.phpt10
-rw-r--r--Zend/tests/enum/no-enum-implements-unit-enum.phpt10
-rw-r--r--Zend/tests/enum/no-from.phpt16
-rw-r--r--Zend/tests/enum/no-implement-serializable-indirect.phpt24
-rw-r--r--Zend/tests/enum/no-implement-serializable.phpt22
-rw-r--r--Zend/tests/enum/no-name-property.phpt12
-rw-r--r--Zend/tests/enum/no-new-through-reflection.phpt16
-rw-r--r--Zend/tests/enum/no-new.phpt16
-rw-r--r--Zend/tests/enum/no-non-backed-enum-implements-backed-enum.phpt10
-rw-r--r--Zend/tests/enum/no-pass-properties-by-ref.phpt26
-rw-r--r--Zend/tests/enum/no-properties.phpt12
-rw-r--r--Zend/tests/enum/no-return-properties-by-ref.phpt27
-rw-r--r--Zend/tests/enum/no-static-properties.phpt12
-rw-r--r--Zend/tests/enum/no-unsed-value.phpt14
-rw-r--r--Zend/tests/enum/no-unset-propertes.phpt37
-rw-r--r--Zend/tests/enum/no-value-property.phpt12
-rw-r--r--Zend/tests/enum/no-write-properties-through-foreach-reference.phpt22
-rw-r--r--Zend/tests/enum/no-write-properties-through-references.phpt23
-rw-r--r--Zend/tests/enum/no-write-properties.phpt37
-rw-r--r--Zend/tests/enum/non-backed-enum-with-expr-value.phpt12
-rw-r--r--Zend/tests/enum/non-backed-enum-with-int-value.phpt12
-rw-r--r--Zend/tests/enum/non-backed-enum-with-invalid-value.phpt12
-rw-r--r--Zend/tests/enum/non-backed-enum-with-string-value.phpt12
-rw-r--r--Zend/tests/enum/offsetGet-in-const-expr.phpt29
-rw-r--r--Zend/tests/enum/print_r.phpt37
-rw-r--r--Zend/tests/enum/reflectionclass.phpt20
-rw-r--r--Zend/tests/enum/serialization-round-trip.phpt14
-rw-r--r--Zend/tests/enum/serialize.phpt14
-rw-r--r--Zend/tests/enum/spl-object-storage.phpt37
-rw-r--r--Zend/tests/enum/static-methods.phpt28
-rw-r--r--Zend/tests/enum/traits-no-__construct.phpt21
-rw-r--r--Zend/tests/enum/traits-no-cases-method.phpt34
-rw-r--r--Zend/tests/enum/traits-no-forbidden-methods.phpt21
-rw-r--r--Zend/tests/enum/traits-no-properties.phpt25
-rw-r--r--Zend/tests/enum/traits.phpt31
-rw-r--r--Zend/tests/enum/unit-cases.phpt28
-rw-r--r--Zend/tests/enum/unserialize-const.phpt18
-rw-r--r--Zend/tests/enum/unserialize-missing-colon.phpt17
-rw-r--r--Zend/tests/enum/unserialize-non-enum.phpt15
-rw-r--r--Zend/tests/enum/unserialize-non-existent-case.phpt17
-rw-r--r--Zend/tests/enum/unserialize-refcount.phpt45
-rw-r--r--Zend/tests/enum/unserialize.phpt24
-rw-r--r--Zend/tests/enum/value-property-type.phpt20
-rw-r--r--Zend/tests/enum/var_dump-nested.phpt20
-rw-r--r--Zend/tests/enum/var_dump-reference.phpt22
-rw-r--r--Zend/tests/enum/var_export.phpt14
109 files changed, 2411 insertions, 0 deletions
diff --git a/Zend/tests/enum/__call.phpt b/Zend/tests/enum/__call.phpt
new file mode 100644
index 0000000000..b2be5eb040
--- /dev/null
+++ b/Zend/tests/enum/__call.phpt
@@ -0,0 +1,29 @@
+--TEST--
+Enum __call
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+
+ public function __call(string $name, array $args)
+ {
+ return [$name, $args];
+ }
+}
+
+var_dump(Foo::Bar->baz('qux', 'quux'));
+
+?>
+--EXPECT--
+array(2) {
+ [0]=>
+ string(3) "baz"
+ [1]=>
+ array(2) {
+ [0]=>
+ string(3) "qux"
+ [1]=>
+ string(4) "quux"
+ }
+}
diff --git a/Zend/tests/enum/__callStatic.phpt b/Zend/tests/enum/__callStatic.phpt
new file mode 100644
index 0000000000..d3acd38239
--- /dev/null
+++ b/Zend/tests/enum/__callStatic.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Enum __callStatic
+--FILE--
+<?php
+
+enum Foo {
+ public static function __callStatic(string $name, array $args)
+ {
+ return [$name, $args];
+ }
+}
+
+var_dump(Foo::bar('baz', 'qux'));
+
+?>
+--EXPECT--
+array(2) {
+ [0]=>
+ string(3) "bar"
+ [1]=>
+ array(2) {
+ [0]=>
+ string(3) "baz"
+ [1]=>
+ string(3) "qux"
+ }
+}
diff --git a/Zend/tests/enum/__class__.phpt b/Zend/tests/enum/__class__.phpt
new file mode 100644
index 0000000000..0b9a5834e4
--- /dev/null
+++ b/Zend/tests/enum/__class__.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Enum __CLASS__
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+
+ public function printClass()
+ {
+ echo __CLASS__ . "\n";
+ }
+}
+
+Foo::Bar->printClass();
+
+?>
+--EXPECT--
+Foo
diff --git a/Zend/tests/enum/__function__.phpt b/Zend/tests/enum/__function__.phpt
new file mode 100644
index 0000000000..d460593873
--- /dev/null
+++ b/Zend/tests/enum/__function__.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Enum __FUNCTION__
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+
+ public function printFunction()
+ {
+ echo __FUNCTION__ . "\n";
+ }
+}
+
+Foo::Bar->printFunction();
+
+?>
+--EXPECT--
+printFunction
diff --git a/Zend/tests/enum/__get.phpt b/Zend/tests/enum/__get.phpt
new file mode 100644
index 0000000000..885cc2f2c8
--- /dev/null
+++ b/Zend/tests/enum/__get.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Enum __get
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+
+ public function __get(string $name)
+ {
+ return '__get';
+ }
+}
+
+?>
+--EXPECTF--
+Fatal error: Enum may not include __get in %s on line %d
diff --git a/Zend/tests/enum/__invoke.phpt b/Zend/tests/enum/__invoke.phpt
new file mode 100644
index 0000000000..63a35b56fd
--- /dev/null
+++ b/Zend/tests/enum/__invoke.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Enum __invoke
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+
+ public function __invoke(...$args)
+ {
+ return $args;
+ }
+}
+
+var_dump((Foo::Bar)('baz', 'qux'));
+
+?>
+--EXPECT--
+array(2) {
+ [0]=>
+ string(3) "baz"
+ [1]=>
+ string(3) "qux"
+}
diff --git a/Zend/tests/enum/__isset.phpt b/Zend/tests/enum/__isset.phpt
new file mode 100644
index 0000000000..76409f075b
--- /dev/null
+++ b/Zend/tests/enum/__isset.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Enum __isset
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+
+ public function __isset($property) {
+ return true;
+ }
+}
+
+?>
+--EXPECTF--
+Fatal error: Enum may not include __isset in %s on line %d
diff --git a/Zend/tests/enum/__method__.phpt b/Zend/tests/enum/__method__.phpt
new file mode 100644
index 0000000000..5d4195566e
--- /dev/null
+++ b/Zend/tests/enum/__method__.phpt
@@ -0,0 +1,19 @@
+--TEST--
+Enum __METHOD__
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+
+ public function printMethod()
+ {
+ echo __METHOD__ . "\n";
+ }
+}
+
+Foo::Bar->printMethod();
+
+?>
+--EXPECT--
+Foo::printMethod
diff --git a/Zend/tests/enum/ast-dumper.phpt b/Zend/tests/enum/ast-dumper.phpt
new file mode 100644
index 0000000000..ed38e44e7c
--- /dev/null
+++ b/Zend/tests/enum/ast-dumper.phpt
@@ -0,0 +1,48 @@
+--TEST--
+Enum AST dumper
+--FILE--
+<?php
+
+try {
+ assert((function () {
+ enum Foo {
+ case Bar;
+ }
+
+ #[EnumAttr]
+ enum IntFoo: int {
+ #[CaseAttr]
+ case Bar = 1 << 0;
+ case Baz = 1 << 1;
+
+ public function self() {
+ return $this;
+ }
+ }
+
+ return false;
+ })());
+} catch (Error $e) {
+ echo $e->getMessage();
+}
+
+?>
+--EXPECT--
+assert(function () {
+ enum Foo {
+ case Bar;
+ }
+
+ #[EnumAttr]
+ enum IntFoo: int {
+ #[CaseAttr]
+ case Bar = 1 << 0;
+ case Baz = 1 << 1;
+ public function self() {
+ return $this;
+ }
+
+ }
+
+ return false;
+}())
diff --git a/Zend/tests/enum/backed-cases-int.phpt b/Zend/tests/enum/backed-cases-int.phpt
new file mode 100644
index 0000000000..85d2527c39
--- /dev/null
+++ b/Zend/tests/enum/backed-cases-int.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Int backed enums with can list cases
+--FILE--
+<?php
+
+enum Suit: int {
+ case Hearts = 2;
+ case Diamonds = 1;
+ case Clubs = 4;
+ case Spades = 3;
+}
+
+var_dump(Suit::cases());
+
+?>
+--EXPECT--
+array(4) {
+ [0]=>
+ enum(Suit::Hearts)
+ [1]=>
+ enum(Suit::Diamonds)
+ [2]=>
+ enum(Suit::Clubs)
+ [3]=>
+ enum(Suit::Spades)
+}
diff --git a/Zend/tests/enum/backed-cases-string.phpt b/Zend/tests/enum/backed-cases-string.phpt
new file mode 100644
index 0000000000..6d7deef77e
--- /dev/null
+++ b/Zend/tests/enum/backed-cases-string.phpt
@@ -0,0 +1,26 @@
+--TEST--
+String backed enums can list cases
+--FILE--
+<?php
+
+enum Suit: string {
+ case Hearts = 'H';
+ case Diamonds = 'D';
+ case Clubs = 'C';
+ case Spades = 'S';
+}
+
+var_dump(Suit::cases());
+
+?>
+--EXPECT--
+array(4) {
+ [0]=>
+ enum(Suit::Hearts)
+ [1]=>
+ enum(Suit::Diamonds)
+ [2]=>
+ enum(Suit::Clubs)
+ [3]=>
+ enum(Suit::Spades)
+}
diff --git a/Zend/tests/enum/backed-duplicate-int.phpt b/Zend/tests/enum/backed-duplicate-int.phpt
new file mode 100644
index 0000000000..4da70bc3dd
--- /dev/null
+++ b/Zend/tests/enum/backed-duplicate-int.phpt
@@ -0,0 +1,13 @@
+--TEST--
+Backed enums reject duplicate int values
+--FILE--
+<?php
+
+enum Foo: int {
+ case Bar = 0;
+ case Baz = 0;
+}
+
+?>
+--EXPECTF--
+Fatal error: Duplicate value in enum Foo for cases Bar and Baz in %s on line %s
diff --git a/Zend/tests/enum/backed-duplicate-string.phpt b/Zend/tests/enum/backed-duplicate-string.phpt
new file mode 100644
index 0000000000..7d42c2ac30
--- /dev/null
+++ b/Zend/tests/enum/backed-duplicate-string.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Backed enums reject duplicate string values
+--FILE--
+<?php
+
+enum Suit: string {
+ case Hearts = 'H';
+ case Diamonds = 'D';
+ case Clubs = 'C';
+ case Spades = 'H';
+}
+
+?>
+--EXPECTF--
+Fatal error: Duplicate value in enum Suit for cases Hearts and Spades in %s on line %s
diff --git a/Zend/tests/enum/backed-from-invalid-int.phpt b/Zend/tests/enum/backed-from-invalid-int.phpt
new file mode 100644
index 0000000000..d976f3d0fb
--- /dev/null
+++ b/Zend/tests/enum/backed-from-invalid-int.phpt
@@ -0,0 +1,19 @@
+--TEST--
+BackedEnum::from() reject invalid int
+--FILE--
+<?php
+
+enum Foo: int {
+ case Bar = 0;
+ case Baz = 1;
+}
+
+try {
+ var_dump(Foo::from(2));
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+2 is not a valid backing value for enum "Foo"
diff --git a/Zend/tests/enum/backed-from-invalid-string.phpt b/Zend/tests/enum/backed-from-invalid-string.phpt
new file mode 100644
index 0000000000..db8b791d8b
--- /dev/null
+++ b/Zend/tests/enum/backed-from-invalid-string.phpt
@@ -0,0 +1,21 @@
+--TEST--
+BackedEnum::from() reject invalid string
+--FILE--
+<?php
+
+enum Suit: string {
+ case Hearts = 'H';
+ case Diamonds = 'D';
+ case Clubs = 'C';
+ case Spades = 'S';
+}
+
+try {
+ var_dump(Suit::from('A'));
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+"A" is not a valid backing value for enum "Suit"
diff --git a/Zend/tests/enum/backed-from-invalid-type.phpt b/Zend/tests/enum/backed-from-invalid-type.phpt
new file mode 100644
index 0000000000..3f35bef64f
--- /dev/null
+++ b/Zend/tests/enum/backed-from-invalid-type.phpt
@@ -0,0 +1,34 @@
+--TEST--
+BackedEnum::from() reject invalid type
+--FILE--
+<?php
+
+enum Suit: string {
+ case Hearts = 'H';
+ case Diamonds = 'D';
+ case Clubs = 'C';
+ case Spades = 'S';
+}
+
+try {
+ var_dump(Suit::from(42));
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+enum Foo: int {
+ case Bar = 0;
+ case Baz = 1;
+}
+
+try {
+ var_dump(Foo::from('H'));
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+
+?>
+--EXPECT--
+"42" is not a valid backing value for enum "Suit"
+Foo::from(): Argument #1 ($value) must be of type int, string given
diff --git a/Zend/tests/enum/backed-from-unknown-hash.phpt b/Zend/tests/enum/backed-from-unknown-hash.phpt
new file mode 100644
index 0000000000..eb5938d0d6
--- /dev/null
+++ b/Zend/tests/enum/backed-from-unknown-hash.phpt
@@ -0,0 +1,17 @@
+--TEST--
+BackedEnum::from() unknown hash
+--FILE--
+<?php
+
+enum Foo: string {
+ case Bar = 'B';
+}
+
+$s = 'A';
+$s++;
+
+var_dump(Foo::from($s));
+
+?>
+--EXPECT--
+enum(Foo::Bar)
diff --git a/Zend/tests/enum/backed-from.phpt b/Zend/tests/enum/backed-from.phpt
new file mode 100644
index 0000000000..2c77c3e388
--- /dev/null
+++ b/Zend/tests/enum/backed-from.phpt
@@ -0,0 +1,36 @@
+--TEST--
+BackedEnum::from()
+--FILE--
+<?php
+
+enum Suit: string {
+ case Hearts = 'H';
+ case Diamonds = 'D';
+ case Clubs = 'C';
+ case Spades = 'S';
+}
+
+var_dump(Suit::from('H'));
+var_dump(Suit::from('D'));
+var_dump(Suit::from('C'));
+var_dump(Suit::from('S'));
+
+enum Foo: int {
+ case Bar = 1;
+ case Baz = 2;
+ case Beep = 3;
+}
+
+var_dump(Foo::from(1));
+var_dump(Foo::from(2));
+var_dump(Foo::from(3));
+
+?>
+--EXPECT--
+enum(Suit::Hearts)
+enum(Suit::Diamonds)
+enum(Suit::Clubs)
+enum(Suit::Spades)
+enum(Foo::Bar)
+enum(Foo::Baz)
+enum(Foo::Beep)
diff --git a/Zend/tests/enum/backed-int-case-without-value.phpt b/Zend/tests/enum/backed-int-case-without-value.phpt
new file mode 100644
index 0000000000..4b84c069b4
--- /dev/null
+++ b/Zend/tests/enum/backed-int-case-without-value.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Int backed enums with case without value
+--FILE--
+<?php
+
+enum Foo: int {
+ case Bar;
+}
+
+var_dump(Foo::Bar->value);
+
+?>
+--EXPECTF--
+Fatal error: Case Bar of backed enum Foo must have a value in %s on line %d
diff --git a/Zend/tests/enum/backed-int-const-expr.phpt b/Zend/tests/enum/backed-int-const-expr.phpt
new file mode 100644
index 0000000000..afb6491c51
--- /dev/null
+++ b/Zend/tests/enum/backed-int-const-expr.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Int enum const expr
+--FILE--
+<?php
+
+enum Foo: int {
+ case Bar = 1 << 0;
+ case Baz = 1 << 1;
+ case Qux = 1 << 2;
+}
+
+var_dump(Foo::Bar->value);
+var_dump(Foo::Baz->value);
+var_dump(Foo::Qux->value);
+
+?>
+--EXPECT--
+int(1)
+int(2)
+int(4)
diff --git a/Zend/tests/enum/backed-int-const-invalid-expr.phpt b/Zend/tests/enum/backed-int-const-invalid-expr.phpt
new file mode 100644
index 0000000000..097bb27883
--- /dev/null
+++ b/Zend/tests/enum/backed-int-const-invalid-expr.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Int enum invalid const expr
+--FILE--
+<?php
+
+enum Foo: int {
+ case Bar = 1 + $x;
+}
+
+var_dump(Foo::Bar->value);
+
+?>
+--EXPECTF--
+Fatal error: Enum case value must be constant in %s on line %d
diff --git a/Zend/tests/enum/backed-int.phpt b/Zend/tests/enum/backed-int.phpt
new file mode 100644
index 0000000000..113629f692
--- /dev/null
+++ b/Zend/tests/enum/backed-int.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Int enum value
+--FILE--
+<?php
+
+enum Foo: int {
+ case Bar = 0;
+ case Baz = 1;
+}
+
+var_dump(Foo::Bar->value);
+var_dump(Foo::Baz->value);
+
+?>
+--EXPECT--
+int(0)
+int(1)
diff --git a/Zend/tests/enum/backed-invalid.phpt b/Zend/tests/enum/backed-invalid.phpt
new file mode 100644
index 0000000000..bc6f71f79d
--- /dev/null
+++ b/Zend/tests/enum/backed-invalid.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Invalid enum backing type
+--FILE--
+<?php
+
+enum Foo: Bar {}
+
+?>
+--EXPECTF--
+Fatal error: Enum backing type must be int or string, Bar given in %s on line %d
diff --git a/Zend/tests/enum/backed-mismatch.phpt b/Zend/tests/enum/backed-mismatch.phpt
new file mode 100644
index 0000000000..0b28fb3be5
--- /dev/null
+++ b/Zend/tests/enum/backed-mismatch.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Mismatched enum backing type
+--FILE--
+<?php
+
+enum Foo: int {
+ case Bar = 'bar';
+}
+
+?>
+--EXPECTF--
+Fatal error: Enum case type string does not match enum backing type int in %s on line %d
diff --git a/Zend/tests/enum/backed-negative-int.phpt b/Zend/tests/enum/backed-negative-int.phpt
new file mode 100644
index 0000000000..4342ecd7b2
--- /dev/null
+++ b/Zend/tests/enum/backed-negative-int.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Backed enum with negative int
+--FILE--
+<?php
+
+enum Foo: int {
+ case Bar = -1;
+ case Baz = -2;
+}
+
+var_dump(Foo::Bar->value);
+var_dump(Foo::Baz->value);
+var_dump(Foo::from(-1));
+var_dump(Foo::from(-2));
+
+?>
+--EXPECT--
+int(-1)
+int(-2)
+enum(Foo::Bar)
+enum(Foo::Baz)
diff --git a/Zend/tests/enum/backed-string-heredoc.phpt b/Zend/tests/enum/backed-string-heredoc.phpt
new file mode 100644
index 0000000000..b2e9b8f3e9
--- /dev/null
+++ b/Zend/tests/enum/backed-string-heredoc.phpt
@@ -0,0 +1,30 @@
+--TEST--
+String enum value with heredoc
+--FILE--
+<?php
+
+enum Foo: string {
+ case Bar = <<<BAR
+ Bar
+ bar
+ bar
+ BAR;
+
+ case Baz = <<<'BAZ'
+ Baz
+ baz
+ baz
+ BAZ;
+}
+
+echo Foo::Bar->value . "\n";
+echo Foo::Baz->value . "\n";
+
+?>
+--EXPECT--
+Bar
+bar
+bar
+Baz
+baz
+baz
diff --git a/Zend/tests/enum/backed-string.phpt b/Zend/tests/enum/backed-string.phpt
new file mode 100644
index 0000000000..afa7db827e
--- /dev/null
+++ b/Zend/tests/enum/backed-string.phpt
@@ -0,0 +1,17 @@
+--TEST--
+String enum value
+--FILE--
+<?php
+
+enum Foo: string {
+ case Bar = 'bar';
+ case Baz = 'baz';
+}
+
+echo Foo::Bar->value . "\n";
+echo Foo::Baz->value . "\n";
+
+?>
+--EXPECT--
+bar
+baz
diff --git a/Zend/tests/enum/backed-tryFrom-casing.phpt b/Zend/tests/enum/backed-tryFrom-casing.phpt
new file mode 100644
index 0000000000..213faa3a62
--- /dev/null
+++ b/Zend/tests/enum/backed-tryFrom-casing.phpt
@@ -0,0 +1,15 @@
+--TEST--
+BackedEnum::tryFrom() casing in reflection
+--FILE--
+<?php
+
+enum Foo: string {}
+
+$reflectionEnum = new ReflectionEnum(Foo::class);
+$reflectionMethod = $reflectionEnum->getMethod('tryFrom');
+
+echo $reflectionMethod->getName() . "\n";
+
+?>
+--EXPECT--
+tryFrom
diff --git a/Zend/tests/enum/backed-tryFrom-unknown-hash.phpt b/Zend/tests/enum/backed-tryFrom-unknown-hash.phpt
new file mode 100644
index 0000000000..80ffe1dc39
--- /dev/null
+++ b/Zend/tests/enum/backed-tryFrom-unknown-hash.phpt
@@ -0,0 +1,17 @@
+--TEST--
+BackedEnum::tryFrom() unknown hash
+--FILE--
+<?php
+
+enum Foo: string {
+ case Bar = 'B';
+}
+
+$s = 'A';
+$s++;
+
+var_dump(Foo::tryFrom($s));
+
+?>
+--EXPECT--
+enum(Foo::Bar)
diff --git a/Zend/tests/enum/backed-tryFrom.phpt b/Zend/tests/enum/backed-tryFrom.phpt
new file mode 100644
index 0000000000..81c36fcc2b
--- /dev/null
+++ b/Zend/tests/enum/backed-tryFrom.phpt
@@ -0,0 +1,40 @@
+--TEST--
+BackedEnum::tryFrom()
+--FILE--
+<?php
+
+enum Suit: string {
+ case Hearts = 'H';
+ case Diamonds = 'D';
+ case Clubs = 'C';
+ case Spades = 'S';
+}
+
+var_dump(Suit::tryFrom('H'));
+var_dump(Suit::tryFrom('D'));
+var_dump(Suit::tryFrom('C'));
+var_dump(Suit::tryFrom('S'));
+var_dump(Suit::tryFrom('X'));
+
+enum Foo: int {
+ case Bar = 1;
+ case Baz = 2;
+ case Beep = 3;
+}
+
+var_dump(Foo::tryFrom(1));
+var_dump(Foo::tryFrom(2));
+var_dump(Foo::tryFrom(3));
+var_dump(Foo::tryFrom(4));
+
+?>
+--EXPECT--
+enum(Suit::Hearts)
+enum(Suit::Diamonds)
+enum(Suit::Clubs)
+enum(Suit::Spades)
+NULL
+enum(Foo::Bar)
+enum(Foo::Baz)
+enum(Foo::Beep)
+NULL
diff --git a/Zend/tests/enum/backed-type-no-union.phpt b/Zend/tests/enum/backed-type-no-union.phpt
new file mode 100644
index 0000000000..8330f8de3d
--- /dev/null
+++ b/Zend/tests/enum/backed-type-no-union.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Backed enums type can't be union
+--FILE--
+<?php
+
+enum Foo: int|string {}
+
+?>
+--EXPECTF--
+Fatal error: Enum backing type must be int or string, string|int given in %s on line %d
diff --git a/Zend/tests/enum/basic-methods.phpt b/Zend/tests/enum/basic-methods.phpt
new file mode 100644
index 0000000000..ee1c2c24d7
--- /dev/null
+++ b/Zend/tests/enum/basic-methods.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Enum methods
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+ case Baz;
+
+ public function dump() {
+ var_dump($this);
+ }
+}
+
+Foo::Bar->dump();
+Foo::Baz->dump();
+
+?>
+--EXPECT--
+enum(Foo::Bar)
+enum(Foo::Baz)
diff --git a/Zend/tests/enum/case-attributes.phpt b/Zend/tests/enum/case-attributes.phpt
new file mode 100644
index 0000000000..a5ec8600cd
--- /dev/null
+++ b/Zend/tests/enum/case-attributes.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Enum case attributes
+--FILE--
+<?php
+
+#[Attribute(Attribute::TARGET_CLASS_CONSTANT)]
+class EnumCaseAttribute {
+ public function __construct(
+ public string $value,
+ ) {}
+}
+
+enum Foo {
+ #[EnumCaseAttribute('Bar')]
+ case Bar;
+}
+
+var_dump((new \ReflectionClassConstant(Foo::class, 'Bar'))->getAttributes(EnumCaseAttribute::class)[0]->newInstance());
+
+?>
+--EXPECT--
+object(EnumCaseAttribute)#1 (1) {
+ ["value"]=>
+ string(3) "Bar"
+}
diff --git a/Zend/tests/enum/case-in-class.phpt b/Zend/tests/enum/case-in-class.phpt
new file mode 100644
index 0000000000..61713e6f89
--- /dev/null
+++ b/Zend/tests/enum/case-in-class.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Enum case in class
+--FILE--
+<?php
+
+class Foo {
+ case Bar;
+}
+
+?>
+--EXPECTF--
+Fatal error: Case can only be used in enums in %s on line %d
diff --git a/Zend/tests/enum/cases-refcount.phpt b/Zend/tests/enum/cases-refcount.phpt
new file mode 100644
index 0000000000..040589ca23
--- /dev/null
+++ b/Zend/tests/enum/cases-refcount.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Enum cases increases refcount
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+function callCases() {
+ Foo::cases();
+}
+
+callCases();
+debug_zval_dump(Foo::Bar);
+
+?>
+--EXPECT--
+object(Foo)#1 (1) refcount(2){
+ ["name"]=>
+ string(3) "Bar" interned
+}
diff --git a/Zend/tests/enum/comparison.phpt b/Zend/tests/enum/comparison.phpt
new file mode 100644
index 0000000000..5df2f282ec
--- /dev/null
+++ b/Zend/tests/enum/comparison.phpt
@@ -0,0 +1,57 @@
+--TEST--
+Enum comparison
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+ case Baz;
+}
+
+$bar = Foo::Bar;
+$baz = Foo::Baz;
+
+var_dump($bar === $bar);
+var_dump($bar == $bar);
+
+var_dump($bar === $baz);
+var_dump($bar == $baz);
+
+var_dump($baz === $bar);
+var_dump($baz == $bar);
+
+var_dump($bar > $bar);
+var_dump($bar < $bar);
+var_dump($bar >= $bar);
+var_dump($bar <= $bar);
+
+var_dump($bar > $baz);
+var_dump($bar < $baz);
+var_dump($bar >= $baz);
+var_dump($bar <= $baz);
+
+var_dump($bar > true);
+var_dump($bar < true);
+var_dump($bar >= true);
+var_dump($bar <= true);
+
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(true)
+bool(true)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
+bool(false)
diff --git a/Zend/tests/enum/constant-aliases.phpt b/Zend/tests/enum/constant-aliases.phpt
new file mode 100644
index 0000000000..1652eb3c48
--- /dev/null
+++ b/Zend/tests/enum/constant-aliases.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Enum constants can alias cases
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+ const Baz = self::Bar;
+}
+
+function test(Foo $var) {
+ echo "works\n";
+}
+
+test(Foo::Bar);
+test(Foo::Baz);
+
+?>
+--EXPECT--
+works
+works
diff --git a/Zend/tests/enum/constants.phpt b/Zend/tests/enum/constants.phpt
new file mode 100644
index 0000000000..cc983e5184
--- /dev/null
+++ b/Zend/tests/enum/constants.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Enum allows constants
+--FILE--
+<?php
+
+enum Foo {
+ const BAR = 'Bar';
+}
+
+echo Foo::BAR . "\n";
+
+?>
+--EXPECT--
+Bar
diff --git a/Zend/tests/enum/default-parameter.phpt b/Zend/tests/enum/default-parameter.phpt
new file mode 100644
index 0000000000..02aa05b5a8
--- /dev/null
+++ b/Zend/tests/enum/default-parameter.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Enum in default parameter
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+function baz(Foo $foo = Foo::Bar) {
+ var_dump($foo);
+}
+
+baz();
+
+?>
+--EXPECT--
+enum(Foo::Bar)
diff --git a/Zend/tests/enum/enum-as-constant.phpt b/Zend/tests/enum/enum-as-constant.phpt
new file mode 100644
index 0000000000..26a589ae79
--- /dev/null
+++ b/Zend/tests/enum/enum-as-constant.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Enum cases can be referenced by constants
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+const Beep = Foo::Bar;
+
+class Test {
+ const Beep = Foo::Bar;
+}
+
+var_dump(Beep);
+var_dump(Test::Beep);
+
+?>
+--EXPECT--
+enum(Foo::Bar)
+enum(Foo::Bar)
diff --git a/Zend/tests/enum/enum-as-params.phpt b/Zend/tests/enum/enum-as-params.phpt
new file mode 100644
index 0000000000..90a1f0b5d0
--- /dev/null
+++ b/Zend/tests/enum/enum-as-params.phpt
@@ -0,0 +1,35 @@
+--TEST--
+Enum types as parameters
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+enum Baz {
+ case Qux;
+}
+
+function takesFoo(Foo $foo) {}
+function takesBaz(Baz $baz) {}
+
+takesFoo(Foo::Bar);
+takesBaz(Baz::Qux);
+
+try {
+ takesBaz(Foo::Bar);
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+try {
+ takesFoo(Baz::Qux);
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECTF--
+takesBaz(): Argument #1 ($baz) must be of type Baz, Foo given, called in %s on line %d
+takesFoo(): Argument #1 ($foo) must be of type Foo, Baz given, called in %s on line %d
diff --git a/Zend/tests/enum/enum-attributes.phpt b/Zend/tests/enum/enum-attributes.phpt
new file mode 100644
index 0000000000..e0e57c744c
--- /dev/null
+++ b/Zend/tests/enum/enum-attributes.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Enum attributes
+--FILE--
+<?php
+
+#[Attribute]
+class EnumAttribute {
+ public function __construct(
+ public string $value,
+ ) {}
+}
+
+#[EnumAttribute('Foo')]
+enum Foo {}
+
+var_dump((new \ReflectionClass(Foo::class))->getAttributes(EnumAttribute::class)[0]->newInstance());
+
+?>
+--EXPECT--
+object(EnumAttribute)#1 (1) {
+ ["value"]=>
+ string(3) "Foo"
+}
diff --git a/Zend/tests/enum/enum-in-constant.phpt b/Zend/tests/enum/enum-in-constant.phpt
new file mode 100644
index 0000000000..487b4a6aad
--- /dev/null
+++ b/Zend/tests/enum/enum-in-constant.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Enum in constant
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+class Baz {
+ const BAR = Foo::Bar;
+}
+
+var_dump(Foo::Bar);
+var_dump(Baz::BAR);
+var_dump(Foo::Bar === Baz::BAR);
+
+?>
+--EXPECT--
+enum(Foo::Bar)
+enum(Foo::Bar)
+bool(true)
diff --git a/Zend/tests/enum/enum-in-static-var.phpt b/Zend/tests/enum/enum-in-static-var.phpt
new file mode 100644
index 0000000000..9f5ebc6264
--- /dev/null
+++ b/Zend/tests/enum/enum-in-static-var.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Enum in static var
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+function example() {
+ static $bar = Foo::Bar;
+ return $bar;
+}
+
+var_dump(example());
+var_dump(example());
+
+?>
+--EXPECT--
+enum(Foo::Bar)
+enum(Foo::Bar)
diff --git a/Zend/tests/enum/enum-reserved-non-modifiers.phpt b/Zend/tests/enum/enum-reserved-non-modifiers.phpt
new file mode 100644
index 0000000000..b07114856f
--- /dev/null
+++ b/Zend/tests/enum/enum-reserved-non-modifiers.phpt
@@ -0,0 +1,32 @@
+--TEST--
+enum keyword is reserved_non_modifiers
+--FILE--
+<?php
+
+namespace enum {
+ class Foo {
+ public static function bar() {
+ return 'enum\Foo::bar()';
+ }
+ }
+}
+
+namespace {
+ class Foo {
+ const enum = 'enum const';
+
+ public static function enum() {
+ return 'enum static method';
+ }
+ }
+
+ echo \enum\Foo::bar() . "\n";
+ echo Foo::enum . "\n";
+ echo Foo::enum() . "\n";
+}
+
+?>
+--EXPECT--
+enum\Foo::bar()
+enum const
+enum static method
diff --git a/Zend/tests/enum/enum_exists.phpt b/Zend/tests/enum/enum_exists.phpt
new file mode 100644
index 0000000000..d1e86447c4
--- /dev/null
+++ b/Zend/tests/enum/enum_exists.phpt
@@ -0,0 +1,41 @@
+--TEST--
+enum_exists
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+class Baz {}
+
+spl_autoload_register(function ($className) {
+ echo "Triggered autoloader with class $className\n";
+
+ if ($className === 'Quux') {
+ enum Quux {}
+ }
+});
+
+var_dump(enum_exists(Foo::class));
+var_dump(enum_exists(Foo::Bar::class));
+var_dump(enum_exists(Baz::class));
+var_dump(enum_exists(Qux::class));
+var_dump(enum_exists(Quux::class, false));
+var_dump(enum_exists(Quux::class, true));
+var_dump(enum_exists(Quuz::class, false));
+var_dump(enum_exists(Quuz::class, true));
+
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(false)
+Triggered autoloader with class Qux
+bool(false)
+bool(false)
+Triggered autoloader with class Quux
+bool(true)
+bool(false)
+Triggered autoloader with class Quuz
+bool(false)
diff --git a/Zend/tests/enum/final.phpt b/Zend/tests/enum/final.phpt
new file mode 100644
index 0000000000..ca13b7da8c
--- /dev/null
+++ b/Zend/tests/enum/final.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Enum is final
+--FILE--
+<?php
+
+enum Foo {}
+
+class Bar extends Foo {}
+
+?>
+--EXPECTF--
+Fatal error: Class Bar may not inherit from final class (Foo) in %s on line %d
diff --git a/Zend/tests/enum/implements.phpt b/Zend/tests/enum/implements.phpt
new file mode 100644
index 0000000000..fc90ab2aef
--- /dev/null
+++ b/Zend/tests/enum/implements.phpt
@@ -0,0 +1,34 @@
+--TEST--
+Enum implements
+--FILE--
+<?php
+
+interface Colorful {
+ public function color(): string;
+}
+
+enum Suit implements Colorful {
+ case Hearts;
+ case Diamonds;
+ case Clubs;
+ case Spades;
+
+ public function color(): string {
+ return match ($this) {
+ self::Hearts, self::Diamonds => 'Red',
+ self::Clubs, self::Spades => 'Black',
+ };
+ }
+}
+
+echo Suit::Hearts->color() . "\n";
+echo Suit::Diamonds->color() . "\n";
+echo Suit::Clubs->color() . "\n";
+echo Suit::Spades->color() . "\n";
+
+?>
+--EXPECT--
+Red
+Red
+Black
+Black
diff --git a/Zend/tests/enum/instanceof-backed-enum.phpt b/Zend/tests/enum/instanceof-backed-enum.phpt
new file mode 100644
index 0000000000..4716835d11
--- /dev/null
+++ b/Zend/tests/enum/instanceof-backed-enum.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Auto implement BackedEnum interface
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+enum Baz: int {
+ case Qux = 0;
+}
+
+var_dump(Foo::Bar instanceof BackedEnum);
+var_dump(Baz::Qux instanceof BackedEnum);
+
+?>
+--EXPECT--
+bool(false)
+bool(true)
diff --git a/Zend/tests/enum/instanceof-unitenum.phpt b/Zend/tests/enum/instanceof-unitenum.phpt
new file mode 100644
index 0000000000..5523796325
--- /dev/null
+++ b/Zend/tests/enum/instanceof-unitenum.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Auto implement UnitEnum interface
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+class Baz {}
+
+var_dump(Foo::Bar instanceof UnitEnum);
+var_dump((new Baz()) instanceof UnitEnum);
+
+?>
+--EXPECT--
+bool(true)
+bool(false)
diff --git a/Zend/tests/enum/instanceof.phpt b/Zend/tests/enum/instanceof.phpt
new file mode 100644
index 0000000000..0e29b9d282
--- /dev/null
+++ b/Zend/tests/enum/instanceof.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Enum instanceof
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+enum Baz {
+ case Qux;
+}
+
+var_dump(Foo::Bar instanceof Foo);
+var_dump(Baz::Qux instanceof Baz);
+
+var_dump(Foo::Bar instanceof Baz);
+var_dump(Baz::Qux instanceof Foo);
+
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(false)
+bool(false)
diff --git a/Zend/tests/enum/json_encode.phpt b/Zend/tests/enum/json_encode.phpt
new file mode 100644
index 0000000000..016ca6107f
--- /dev/null
+++ b/Zend/tests/enum/json_encode.phpt
@@ -0,0 +1,59 @@
+--TEST--
+Enum in json_encode
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+enum IntFoo: int {
+ case Bar = 0;
+}
+
+enum StringFoo: string {
+ case Bar = 'Bar';
+}
+
+enum CustomFoo implements JsonSerializable {
+ case Bar;
+
+ public function jsonSerialize() {
+ return 'Custom ' . $this->name;
+ }
+}
+
+function test($value) {
+ var_dump(json_encode($value));
+ echo json_last_error_msg() . "\n";
+
+ try {
+ var_dump(json_encode($value, JSON_THROW_ON_ERROR));
+ echo json_last_error_msg() . "\n";
+ } catch (Exception $e) {
+ echo get_class($e) . ': ' . $e->getMessage() . "\n";
+ }
+}
+
+test(Foo::Bar);
+test(IntFoo::Bar);
+test(StringFoo::Bar);
+test(CustomFoo::Bar);
+
+?>
+--EXPECT--
+bool(false)
+Non-backed enums have no default serialization
+JsonException: Non-backed enums have no default serialization
+string(1) "0"
+No error
+string(1) "0"
+No error
+string(5) ""Bar""
+No error
+string(5) ""Bar""
+No error
+string(12) ""Custom Bar""
+No error
+string(12) ""Custom Bar""
+No error
diff --git a/Zend/tests/enum/keyword-no-bc-break.phpt b/Zend/tests/enum/keyword-no-bc-break.phpt
new file mode 100644
index 0000000000..92b8226afa
--- /dev/null
+++ b/Zend/tests/enum/keyword-no-bc-break.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Enum keyword can still be used in classes, namespaces, functions and constants
+--FILE--
+<?php
+
+namespace enum {
+ class Foo {}
+}
+
+namespace foo {
+ class Bar {}
+ class enum extends Bar {}
+}
+
+namespace bar {
+ interface Baz {}
+ class enum implements Baz {}
+}
+
+namespace {
+ class enum {}
+
+ function enum() {
+ return 'enum function';
+ }
+
+ const enum = 'enum constant';
+
+ var_dump(new enum\Foo());
+ var_dump(new enum());
+ var_dump(enum());
+ var_dump(enum);
+}
+
+?>
+--EXPECT--
+object(enum\Foo)#1 (0) {
+}
+object(enum)#1 (0) {
+}
+string(13) "enum function"
+string(13) "enum constant"
diff --git a/Zend/tests/enum/keyword-whitespace.phpt b/Zend/tests/enum/keyword-whitespace.phpt
new file mode 100644
index 0000000000..bdcbd94079
--- /dev/null
+++ b/Zend/tests/enum/keyword-whitespace.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Enum keyword can be followed by arbitrary whitespaces
+--FILE--
+<?php
+
+enum A {}
+enum B {}
+enum C {}
+enum E {}
+enum
+F {}
+enum
+ G {}
+
+?>
+--EXPECT--
diff --git a/Zend/tests/enum/name-property.phpt b/Zend/tests/enum/name-property.phpt
new file mode 100644
index 0000000000..2197f235bb
--- /dev/null
+++ b/Zend/tests/enum/name-property.phpt
@@ -0,0 +1,50 @@
+--TEST--
+Enum name property
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+ case Baz;
+}
+
+enum IntFoo: int {
+ case Bar = 0;
+ case Baz = 1;
+}
+
+var_dump((new ReflectionClass(Foo::class))->getProperties());
+var_dump(Foo::Bar->name);
+
+var_dump((new ReflectionClass(IntFoo::class))->getProperties());
+var_dump(IntFoo::Bar->name);
+
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ object(ReflectionProperty)#2 (2) {
+ ["name"]=>
+ string(4) "name"
+ ["class"]=>
+ string(3) "Foo"
+ }
+}
+string(3) "Bar"
+array(2) {
+ [0]=>
+ object(ReflectionProperty)#3 (2) {
+ ["name"]=>
+ string(4) "name"
+ ["class"]=>
+ string(6) "IntFoo"
+ }
+ [1]=>
+ object(ReflectionProperty)#4 (2) {
+ ["name"]=>
+ string(5) "value"
+ ["class"]=>
+ string(6) "IntFoo"
+ }
+}
+string(3) "Bar"
diff --git a/Zend/tests/enum/namespaces.phpt b/Zend/tests/enum/namespaces.phpt
new file mode 100644
index 0000000000..261a885b55
--- /dev/null
+++ b/Zend/tests/enum/namespaces.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Enum namespace
+--FILE--
+<?php
+
+namespace Foo {
+ enum Bar {
+ case Baz;
+
+ public function dump() {
+ var_dump(Bar::Baz);
+ }
+ }
+
+ function dumpBar() {
+ Bar::Baz->dump();
+ }
+}
+
+namespace {
+ use Foo\Bar;
+
+ \Foo\dumpBar();
+ \Foo\Bar::Baz->dump();
+ Bar::Baz->dump();
+}
+
+?>
+--EXPECT--
+enum(Foo\Bar::Baz)
+enum(Foo\Bar::Baz)
+enum(Foo\Bar::Baz)
diff --git a/Zend/tests/enum/no-cases.phpt b/Zend/tests/enum/no-cases.phpt
new file mode 100644
index 0000000000..cf518aefb9
--- /dev/null
+++ b/Zend/tests/enum/no-cases.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Enum no manual cases method
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+
+ public static function cases(): array {
+ return [];
+ }
+}
+
+?>
+--EXPECTF--
+Fatal error: Cannot redeclare Foo::cases() in %s on line %d
diff --git a/Zend/tests/enum/no-class-implements-backed-enum.phpt b/Zend/tests/enum/no-class-implements-backed-enum.phpt
new file mode 100644
index 0000000000..f6f37818da
--- /dev/null
+++ b/Zend/tests/enum/no-class-implements-backed-enum.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Class cannot implement BackedEnum
+--FILE--
+<?php
+
+class Foo implements BackedEnum {}
+
+?>
+--EXPECTF--
+Fatal error: Non-enum class Foo cannot implement interface BackedEnum in %s on line %d
diff --git a/Zend/tests/enum/no-class-implements-unit-enum.phpt b/Zend/tests/enum/no-class-implements-unit-enum.phpt
new file mode 100644
index 0000000000..02ddd4567f
--- /dev/null
+++ b/Zend/tests/enum/no-class-implements-unit-enum.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Class cannot implement UnitEnum
+--FILE--
+<?php
+
+class Foo implements UnitEnum {}
+
+?>
+--EXPECTF--
+Fatal error: Non-enum class Foo cannot implement interface UnitEnum in %s on line %d
diff --git a/Zend/tests/enum/no-clone.phpt b/Zend/tests/enum/no-clone.phpt
new file mode 100644
index 0000000000..2fadbb26b7
--- /dev/null
+++ b/Zend/tests/enum/no-clone.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Enum disallows cloning
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+try {
+ var_dump(clone Foo::Bar);
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+Trying to clone an uncloneable object of class Foo
diff --git a/Zend/tests/enum/no-constructors.phpt b/Zend/tests/enum/no-constructors.phpt
new file mode 100644
index 0000000000..70504e14eb
--- /dev/null
+++ b/Zend/tests/enum/no-constructors.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Enum disallows constructor
+--FILE--
+<?php
+
+enum Foo {
+ public function __construct() {}
+}
+
+?>
+--EXPECTF--
+Fatal error: Enum may not include __construct in %s on line %d
diff --git a/Zend/tests/enum/no-destruct.phpt b/Zend/tests/enum/no-destruct.phpt
new file mode 100644
index 0000000000..730c29f847
--- /dev/null
+++ b/Zend/tests/enum/no-destruct.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Enum disallows destructor
+--FILE--
+<?php
+
+enum Foo {
+ public function __destruct() {}
+}
+
+?>
+--EXPECTF--
+Fatal error: Enum may not include __destruct in %s on line %d
diff --git a/Zend/tests/enum/no-dynamic-properties.phpt b/Zend/tests/enum/no-dynamic-properties.phpt
new file mode 100644
index 0000000000..21766ce1b4
--- /dev/null
+++ b/Zend/tests/enum/no-dynamic-properties.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Enum case disallows dynamic properties
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+$bar = Foo::Bar;
+
+try {
+ $bar->baz = 'Baz';
+} catch (\Error $e) {
+ echo $e->getMessage();
+}
+
+?>
+--EXPECT--
+Enum properties are immutable
diff --git a/Zend/tests/enum/no-enum-implements-backed-enum.phpt b/Zend/tests/enum/no-enum-implements-backed-enum.phpt
new file mode 100644
index 0000000000..69922c13a8
--- /dev/null
+++ b/Zend/tests/enum/no-enum-implements-backed-enum.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Enum cannot manually implement BackedEnum
+--FILE--
+<?php
+
+enum Foo: int implements BackedEnum {}
+
+?>
+--EXPECTF--
+Fatal error: Class Foo cannot implement previously implemented interface BackedEnum in %s on line %d
diff --git a/Zend/tests/enum/no-enum-implements-unit-enum.phpt b/Zend/tests/enum/no-enum-implements-unit-enum.phpt
new file mode 100644
index 0000000000..458efbb67c
--- /dev/null
+++ b/Zend/tests/enum/no-enum-implements-unit-enum.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Enum cannot manually implement UnitEnum
+--FILE--
+<?php
+
+enum Foo implements UnitEnum {}
+
+?>
+--EXPECTF--
+Fatal error: Class Foo cannot implement previously implemented interface UnitEnum in %s on line %d
diff --git a/Zend/tests/enum/no-from.phpt b/Zend/tests/enum/no-from.phpt
new file mode 100644
index 0000000000..1cf717de88
--- /dev/null
+++ b/Zend/tests/enum/no-from.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Enum no manual from method
+--FILE--
+<?php
+
+enum Foo: int {
+ case Bar = 0;
+
+ public static function from(string|int $value): self {
+ return $this;
+ }
+}
+
+?>
+--EXPECTF--
+Fatal error: Cannot redeclare Foo::from() in %s on line %d
diff --git a/Zend/tests/enum/no-implement-serializable-indirect.phpt b/Zend/tests/enum/no-implement-serializable-indirect.phpt
new file mode 100644
index 0000000000..5e7bdc9338
--- /dev/null
+++ b/Zend/tests/enum/no-implement-serializable-indirect.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Enum must not implement Serializable indirectly
+--FILE--
+<?php
+
+interface MySerializable extends Serializable {}
+
+enum Foo implements MySerializable {
+ case Bar;
+
+ public function serialize() {
+ return serialize('Hello');
+ }
+
+ public function unserialize($data) {
+ return unserialize($data);
+ }
+}
+
+var_dump(unserialize(serialize(Foo::Bar)));
+
+?>
+--EXPECTF--
+Fatal error: Enums may not implement the Serializable interface in %s on line %d
diff --git a/Zend/tests/enum/no-implement-serializable.phpt b/Zend/tests/enum/no-implement-serializable.phpt
new file mode 100644
index 0000000000..62485f2934
--- /dev/null
+++ b/Zend/tests/enum/no-implement-serializable.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Enum must not implement Serializable
+--FILE--
+<?php
+
+enum Foo implements Serializable {
+ case Bar;
+
+ public function serialize() {
+ return serialize('Hello');
+ }
+
+ public function unserialize($data) {
+ return unserialize($data);
+ }
+}
+
+var_dump(unserialize(serialize(Foo::Bar)));
+
+?>
+--EXPECTF--
+Fatal error: Enums may not implement the Serializable interface in %s on line %d
diff --git a/Zend/tests/enum/no-name-property.phpt b/Zend/tests/enum/no-name-property.phpt
new file mode 100644
index 0000000000..a565c08e83
--- /dev/null
+++ b/Zend/tests/enum/no-name-property.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Enum disallows name property
+--FILE--
+<?php
+
+enum Foo {
+ public string $name;
+}
+
+?>
+--EXPECTF--
+Fatal error: Enums may not include properties in %s on line %d
diff --git a/Zend/tests/enum/no-new-through-reflection.phpt b/Zend/tests/enum/no-new-through-reflection.phpt
new file mode 100644
index 0000000000..9a92559cd3
--- /dev/null
+++ b/Zend/tests/enum/no-new-through-reflection.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Enum no new through reflection
+--FILE--
+<?php
+
+enum Foo {}
+
+try {
+ (new \ReflectionClass(Foo::class))->newInstanceWithoutConstructor();
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+Cannot instantiate enum Foo
diff --git a/Zend/tests/enum/no-new.phpt b/Zend/tests/enum/no-new.phpt
new file mode 100644
index 0000000000..698e954882
--- /dev/null
+++ b/Zend/tests/enum/no-new.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Enum no new
+--FILE--
+<?php
+
+enum Foo {}
+
+try {
+ new Foo();
+} catch (\Error $e) {
+ echo $e->getMessage();
+}
+
+?>
+--EXPECT--
+Cannot instantiate enum Foo
diff --git a/Zend/tests/enum/no-non-backed-enum-implements-backed-enum.phpt b/Zend/tests/enum/no-non-backed-enum-implements-backed-enum.phpt
new file mode 100644
index 0000000000..f560490de4
--- /dev/null
+++ b/Zend/tests/enum/no-non-backed-enum-implements-backed-enum.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Non-backed enum cannot implement BackedEnum
+--FILE--
+<?php
+
+enum Foo implements BackedEnum {}
+
+?>
+--EXPECTF--
+Fatal error: Non-backed enum Foo cannot implement interface BackedEnum in %s on line %d
diff --git a/Zend/tests/enum/no-pass-properties-by-ref.phpt b/Zend/tests/enum/no-pass-properties-by-ref.phpt
new file mode 100644
index 0000000000..8f429dfa14
--- /dev/null
+++ b/Zend/tests/enum/no-pass-properties-by-ref.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Enum properties cannot be passed by-ref
+--FILE--
+<?php
+
+enum Foo: int {
+ case Bar = 0;
+}
+
+function setBarValueByRef(&$bar, $value) {
+ $bar = $value;
+}
+
+try {
+ $bar = Foo::Bar;
+ $value = setBarValueByRef($bar->value, 1);
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+var_dump(Foo::Bar->value);
+
+?>
+--EXPECT--
+Cannot acquire reference to property Foo::$value
+int(0)
diff --git a/Zend/tests/enum/no-properties.phpt b/Zend/tests/enum/no-properties.phpt
new file mode 100644
index 0000000000..e846845d22
--- /dev/null
+++ b/Zend/tests/enum/no-properties.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Enum disallows properties
+--FILE--
+<?php
+
+enum Foo {
+ public $bar;
+}
+
+?>
+--EXPECTF--
+Fatal error: Enums may not include properties in %s on line %d
diff --git a/Zend/tests/enum/no-return-properties-by-ref.phpt b/Zend/tests/enum/no-return-properties-by-ref.phpt
new file mode 100644
index 0000000000..988d480de1
--- /dev/null
+++ b/Zend/tests/enum/no-return-properties-by-ref.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Enum properties cannot be returned by-ref
+--FILE--
+<?php
+
+enum Foo: int {
+ case Bar = 0;
+}
+
+function &getBarValueByRef() {
+ $bar = Foo::Bar;
+ return $bar->value;
+}
+
+try {
+ $value = &getBarValueByRef();
+ $value = 1;
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+var_dump(Foo::Bar->value);
+
+?>
+--EXPECT--
+Cannot acquire reference to property Foo::$value
+int(0)
diff --git a/Zend/tests/enum/no-static-properties.phpt b/Zend/tests/enum/no-static-properties.phpt
new file mode 100644
index 0000000000..4b823e98e9
--- /dev/null
+++ b/Zend/tests/enum/no-static-properties.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Enum disallows static properties
+--FILE--
+<?php
+
+enum Foo {
+ public static $bar;
+}
+
+?>
+--EXPECTF--
+Fatal error: Enums may not include properties in %s on line %d
diff --git a/Zend/tests/enum/no-unsed-value.phpt b/Zend/tests/enum/no-unsed-value.phpt
new file mode 100644
index 0000000000..a1cbdd43ca
--- /dev/null
+++ b/Zend/tests/enum/no-unsed-value.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Enum prevent unsetting value
+--FILE--
+<?php
+
+enum Foo: int {
+ case Bar = 0;
+}
+
+unset(Foo::Bar->value);
+
+?>
+--EXPECTF--
+Fatal error: Cannot use temporary expression in write context in %s on line %d
diff --git a/Zend/tests/enum/no-unset-propertes.phpt b/Zend/tests/enum/no-unset-propertes.phpt
new file mode 100644
index 0000000000..338b22a026
--- /dev/null
+++ b/Zend/tests/enum/no-unset-propertes.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Enum properties cannot be unset
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+enum IntFoo: int {
+ case Bar = 0;
+}
+
+$foo = Foo::Bar;
+try {
+ unset($foo->name);
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+$intFoo = IntFoo::Bar;
+try {
+ unset($intFoo->name);
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+try {
+ unset($intFoo->value);
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+Enum properties are immutable
+Enum properties are immutable
+Enum properties are immutable
diff --git a/Zend/tests/enum/no-value-property.phpt b/Zend/tests/enum/no-value-property.phpt
new file mode 100644
index 0000000000..d8b12f2ca8
--- /dev/null
+++ b/Zend/tests/enum/no-value-property.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Enum disallows value property
+--FILE--
+<?php
+
+enum Foo: int {
+ public int $value;
+}
+
+?>
+--EXPECTF--
+Fatal error: Enums may not include properties in %s on line %d
diff --git a/Zend/tests/enum/no-write-properties-through-foreach-reference.phpt b/Zend/tests/enum/no-write-properties-through-foreach-reference.phpt
new file mode 100644
index 0000000000..d1c211802f
--- /dev/null
+++ b/Zend/tests/enum/no-write-properties-through-foreach-reference.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Enum properties cannot be written to through reference in foreach
+--FILE--
+<?php
+
+enum Foo: int {
+ case Bar = 0;
+}
+
+try {
+ $bar = Foo::Bar;
+ foreach ([1] as &$bar->value) {}
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+var_dump(Foo::Bar->value);
+
+?>
+--EXPECT--
+Cannot acquire reference to property Foo::$value
+int(0)
diff --git a/Zend/tests/enum/no-write-properties-through-references.phpt b/Zend/tests/enum/no-write-properties-through-references.phpt
new file mode 100644
index 0000000000..81543af78d
--- /dev/null
+++ b/Zend/tests/enum/no-write-properties-through-references.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Enum properties cannot be written to through references
+--FILE--
+<?php
+
+enum Foo: int {
+ case Bar = 0;
+}
+
+try {
+ $bar = Foo::Bar;
+ $value = &$bar->value;
+ $value = 1;
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+var_dump(Foo::Bar->value);
+
+?>
+--EXPECT--
+Cannot acquire reference to property Foo::$value
+int(0)
diff --git a/Zend/tests/enum/no-write-properties.phpt b/Zend/tests/enum/no-write-properties.phpt
new file mode 100644
index 0000000000..13cbe69e74
--- /dev/null
+++ b/Zend/tests/enum/no-write-properties.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Enum properties cannot be written to
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+enum IntFoo: int {
+ case Bar = 0;
+}
+
+$bar = Foo::Bar;
+try {
+ $bar->name = 'Baz';
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+$intBar = Foo::Bar;
+try {
+ $intBar->name = 'Baz';
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+try {
+ $intBar->value = 1;
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+Enum properties are immutable
+Enum properties are immutable
+Enum properties are immutable
diff --git a/Zend/tests/enum/non-backed-enum-with-expr-value.phpt b/Zend/tests/enum/non-backed-enum-with-expr-value.phpt
new file mode 100644
index 0000000000..95bd85f396
--- /dev/null
+++ b/Zend/tests/enum/non-backed-enum-with-expr-value.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Non-backed enum errors when case has int expression value
+--FILE--
+<?php
+
+enum Foo {
+ case Bar = 1 + 1;
+}
+
+?>
+--EXPECTF--
+Fatal error: Case Bar of non-backed enum Foo must not have a value, try adding ": int" to the enum declaration in %s on line %d
diff --git a/Zend/tests/enum/non-backed-enum-with-int-value.phpt b/Zend/tests/enum/non-backed-enum-with-int-value.phpt
new file mode 100644
index 0000000000..4932e63d18
--- /dev/null
+++ b/Zend/tests/enum/non-backed-enum-with-int-value.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Non-backed enum errors when case has int value
+--FILE--
+<?php
+
+enum Foo {
+ case Bar = 1;
+}
+
+?>
+--EXPECTF--
+Fatal error: Case Bar of non-backed enum Foo must not have a value, try adding ": int" to the enum declaration in %s on line %d
diff --git a/Zend/tests/enum/non-backed-enum-with-invalid-value.phpt b/Zend/tests/enum/non-backed-enum-with-invalid-value.phpt
new file mode 100644
index 0000000000..24b8277805
--- /dev/null
+++ b/Zend/tests/enum/non-backed-enum-with-invalid-value.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Non-backed enum errors when case has invalid value
+--FILE--
+<?php
+
+enum Foo {
+ case Bar = 3.141;
+}
+
+?>
+--EXPECTF--
+Fatal error: Case Bar of non-backed enum Foo must not have a value in %s on line %d
diff --git a/Zend/tests/enum/non-backed-enum-with-string-value.phpt b/Zend/tests/enum/non-backed-enum-with-string-value.phpt
new file mode 100644
index 0000000000..f1a06e1a17
--- /dev/null
+++ b/Zend/tests/enum/non-backed-enum-with-string-value.phpt
@@ -0,0 +1,12 @@
+--TEST--
+Non-backed enum errors when case has string value
+--FILE--
+<?php
+
+enum Foo {
+ case Bar = 'Bar';
+}
+
+?>
+--EXPECTF--
+Fatal error: Case Bar of non-backed enum Foo must not have a value, try adding ": string" to the enum declaration in %s on line %d
diff --git a/Zend/tests/enum/offsetGet-in-const-expr.phpt b/Zend/tests/enum/offsetGet-in-const-expr.phpt
new file mode 100644
index 0000000000..e288b9a694
--- /dev/null
+++ b/Zend/tests/enum/offsetGet-in-const-expr.phpt
@@ -0,0 +1,29 @@
+--TEST--
+Enum offsetGet in constant expression
+--FILE--
+<?php
+
+enum Foo implements ArrayAccess {
+ case Bar;
+
+ public function offsetGet($key) {
+ return 42;
+ }
+
+ public function offsetExists($key) {}
+ public function offsetSet($key, $value) {}
+ public function offsetUnset($key) {}
+}
+
+class X {
+ const FOO_BAR = Foo::Bar[0];
+}
+
+var_dump(X::FOO_BAR);
+
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Cannot use [] on objects in constant expression in %s:%d
+Stack trace:
+#0 {main}
+ thrown in %s on line %d
diff --git a/Zend/tests/enum/print_r.phpt b/Zend/tests/enum/print_r.phpt
new file mode 100644
index 0000000000..9cb08fec73
--- /dev/null
+++ b/Zend/tests/enum/print_r.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Enum print_r
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+enum IntFoo: int {
+ case Bar = 42;
+}
+
+enum StringFoo: string {
+ case Bar = 'Bar';
+}
+
+print_r(Foo::Bar);
+print_r(IntFoo::Bar);
+print_r(StringFoo::Bar);
+
+?>
+--EXPECT--
+Foo Enum
+(
+ [name] => Bar
+)
+IntFoo Enum:int
+(
+ [name] => Bar
+ [value] => 42
+)
+StringFoo Enum:string
+(
+ [name] => Bar
+ [value] => Bar
+)
diff --git a/Zend/tests/enum/reflectionclass.phpt b/Zend/tests/enum/reflectionclass.phpt
new file mode 100644
index 0000000000..59b99b3e6d
--- /dev/null
+++ b/Zend/tests/enum/reflectionclass.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Enum reflection getConstants()
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+ case Baz;
+}
+
+var_dump((new \ReflectionClass(Foo::class))->getConstants());
+
+?>
+--EXPECT--
+array(2) {
+ ["Bar"]=>
+ enum(Foo::Bar)
+ ["Baz"]=>
+ enum(Foo::Baz)
+}
diff --git a/Zend/tests/enum/serialization-round-trip.phpt b/Zend/tests/enum/serialization-round-trip.phpt
new file mode 100644
index 0000000000..d69cbea71f
--- /dev/null
+++ b/Zend/tests/enum/serialization-round-trip.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Enum unserialize same instance
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+var_dump(Foo::Bar === unserialize(serialize(Foo::Bar)));
+
+?>
+--EXPECT--
+bool(true)
diff --git a/Zend/tests/enum/serialize.phpt b/Zend/tests/enum/serialize.phpt
new file mode 100644
index 0000000000..56545ebfae
--- /dev/null
+++ b/Zend/tests/enum/serialize.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Enum serialize
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+echo serialize(Foo::Bar);
+
+?>
+--EXPECT--
+E:7:"Foo:Bar";
diff --git a/Zend/tests/enum/spl-object-storage.phpt b/Zend/tests/enum/spl-object-storage.phpt
new file mode 100644
index 0000000000..7c72299c34
--- /dev/null
+++ b/Zend/tests/enum/spl-object-storage.phpt
@@ -0,0 +1,37 @@
+--TEST--
+Enum in SplObjectStorage
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+ case Baz;
+ case Qux;
+}
+
+$storage = new SplObjectStorage();
+$storage[Foo::Bar] = 'Bar';
+$storage[Foo::Baz] = 'Baz';
+
+var_dump($storage[Foo::Bar]);
+var_dump($storage[Foo::Baz]);
+
+var_dump($storage->contains(Foo::Bar));
+var_dump($storage->contains(Foo::Qux));
+
+$serialized = serialize($storage);
+var_dump($serialized);
+
+$unserialized = unserialize($serialized);
+var_dump($unserialized[Foo::Bar]);
+var_dump($unserialized[Foo::Baz]);
+
+?>
+--EXPECT--
+string(3) "Bar"
+string(3) "Baz"
+bool(true)
+bool(false)
+string(112) "O:16:"SplObjectStorage":2:{i:0;a:4:{i:0;E:7:"Foo:Bar";i:1;s:3:"Bar";i:2;E:7:"Foo:Baz";i:3;s:3:"Baz";}i:1;a:0:{}}"
+string(3) "Bar"
+string(3) "Baz"
diff --git a/Zend/tests/enum/static-methods.phpt b/Zend/tests/enum/static-methods.phpt
new file mode 100644
index 0000000000..361ee77664
--- /dev/null
+++ b/Zend/tests/enum/static-methods.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Enum supports static methods
+--FILE--
+<?php
+
+enum Size {
+ case Small;
+ case Medium;
+ case Large;
+
+ public static function fromLength(int $cm) {
+ return match(true) {
+ $cm < 50 => static::Small,
+ $cm < 100 => static::Medium,
+ default => static::Large,
+ };
+ }
+}
+
+var_dump(Size::fromLength(23));
+var_dump(Size::fromLength(63));
+var_dump(Size::fromLength(123));
+
+?>
+--EXPECT--
+enum(Size::Small)
+enum(Size::Medium)
+enum(Size::Large)
diff --git a/Zend/tests/enum/traits-no-__construct.phpt b/Zend/tests/enum/traits-no-__construct.phpt
new file mode 100644
index 0000000000..0529a2a989
--- /dev/null
+++ b/Zend/tests/enum/traits-no-__construct.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Enum traits no __construct
+--FILE--
+<?php
+
+trait Foo {
+ public function __construct() {
+ echo "Evil code\n";
+ }
+}
+
+enum Bar {
+ use Foo;
+ case Baz;
+}
+
+var_dump(Bar::Baz);
+
+?>
+--EXPECTF--
+Fatal error: Enum may not include __construct in %s on line %d
diff --git a/Zend/tests/enum/traits-no-cases-method.phpt b/Zend/tests/enum/traits-no-cases-method.phpt
new file mode 100644
index 0000000000..9acfb52d0e
--- /dev/null
+++ b/Zend/tests/enum/traits-no-cases-method.phpt
@@ -0,0 +1,34 @@
+--TEST--
+Using cases method from traits in enums has no effect
+--FILE--
+<?php
+
+trait Rectangle {
+ public static function cases(): array {
+ return [];
+ }
+}
+
+enum Suit {
+ use Rectangle;
+
+ case Hearts;
+ case Diamonds;
+ case Clubs;
+ case Spades;
+}
+
+var_dump(Suit::cases());
+
+?>
+--EXPECT--
+array(4) {
+ [0]=>
+ enum(Suit::Hearts)
+ [1]=>
+ enum(Suit::Diamonds)
+ [2]=>
+ enum(Suit::Clubs)
+ [3]=>
+ enum(Suit::Spades)
+}
diff --git a/Zend/tests/enum/traits-no-forbidden-methods.phpt b/Zend/tests/enum/traits-no-forbidden-methods.phpt
new file mode 100644
index 0000000000..1f310f48cd
--- /dev/null
+++ b/Zend/tests/enum/traits-no-forbidden-methods.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Enum cannot have forbidden methods, even via traits
+--FILE--
+<?php
+
+trait Rectangle {
+ public function __construct() {}
+}
+
+enum Suit {
+ use Rectangle;
+
+ case Hearts;
+ case Diamonds;
+ case Clubs;
+ case Spades;
+}
+
+?>
+--EXPECTF--
+Fatal error: Enum may not include __construct in %s on line %d
diff --git a/Zend/tests/enum/traits-no-properties.phpt b/Zend/tests/enum/traits-no-properties.phpt
new file mode 100644
index 0000000000..5a6faf9dea
--- /dev/null
+++ b/Zend/tests/enum/traits-no-properties.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Enum cannot have properties, even via traits
+--FILE--
+<?php
+
+trait Rectangle {
+ protected string $shape = "Rectangle";
+
+ public function shape(): string {
+ return $this->shape;
+ }
+}
+
+enum Suit {
+ use Rectangle;
+
+ case Hearts;
+ case Diamonds;
+ case Clubs;
+ case Spades;
+}
+
+?>
+--EXPECTF--
+Fatal error: Enum "Suit" may not include properties in %s on line %d
diff --git a/Zend/tests/enum/traits.phpt b/Zend/tests/enum/traits.phpt
new file mode 100644
index 0000000000..3de8e1559f
--- /dev/null
+++ b/Zend/tests/enum/traits.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Enum can use traits
+--FILE--
+<?php
+
+trait Rectangle {
+ public function shape(): string {
+ return "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
diff --git a/Zend/tests/enum/unit-cases.phpt b/Zend/tests/enum/unit-cases.phpt
new file mode 100644
index 0000000000..83f2232159
--- /dev/null
+++ b/Zend/tests/enum/unit-cases.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Unit enums can list cases
+--FILE--
+<?php
+
+enum Suit {
+ case Hearts;
+ case Diamonds;
+ case Clubs;
+ case Spades;
+ /** @deprecated Typo, use Suit::Hearts */
+ const Hearst = self::Hearts;
+}
+
+var_dump(Suit::cases());
+
+?>
+--EXPECT--
+array(4) {
+ [0]=>
+ enum(Suit::Hearts)
+ [1]=>
+ enum(Suit::Diamonds)
+ [2]=>
+ enum(Suit::Clubs)
+ [3]=>
+ enum(Suit::Spades)
+}
diff --git a/Zend/tests/enum/unserialize-const.phpt b/Zend/tests/enum/unserialize-const.phpt
new file mode 100644
index 0000000000..0731a68dcc
--- /dev/null
+++ b/Zend/tests/enum/unserialize-const.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Enum unserialize const
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+ const Baz = Foo::Bar;
+}
+
+var_dump(unserialize('E:7:"Foo:Baz";'));
+
+?>
+--EXPECTF--
+Warning: unserialize(): Foo::Baz is not an enum case in %s on line %d
+
+Notice: unserialize(): Error at offset 14 of 14 bytes in %s on line %d
+bool(false)
diff --git a/Zend/tests/enum/unserialize-missing-colon.phpt b/Zend/tests/enum/unserialize-missing-colon.phpt
new file mode 100644
index 0000000000..86cce32df4
--- /dev/null
+++ b/Zend/tests/enum/unserialize-missing-colon.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Enum unserialize with missing colon
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+var_dump(unserialize('E:6:"FooBar";'));
+
+?>
+--EXPECTF--
+Warning: unserialize(): Invalid enum name 'FooBar' (missing colon) in %s on line %d
+
+Notice: unserialize(): Error at offset 0 of 13 bytes in %s on line %d
+bool(false)
diff --git a/Zend/tests/enum/unserialize-non-enum.phpt b/Zend/tests/enum/unserialize-non-enum.phpt
new file mode 100644
index 0000000000..82f4ec93db
--- /dev/null
+++ b/Zend/tests/enum/unserialize-non-enum.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Enum unserialize non-enum
+--FILE--
+<?php
+
+class Foo {}
+
+var_dump(unserialize('E:7:"Foo:Bar";'));
+
+?>
+--EXPECTF--
+Warning: unserialize(): Class 'Foo' is not an enum in %s on line %d
+
+Notice: unserialize(): Error at offset 0 of 14 bytes in %s on line %d
+bool(false)
diff --git a/Zend/tests/enum/unserialize-non-existent-case.phpt b/Zend/tests/enum/unserialize-non-existent-case.phpt
new file mode 100644
index 0000000000..2364db5b15
--- /dev/null
+++ b/Zend/tests/enum/unserialize-non-existent-case.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Enum unserialize non-existent case
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+var_dump(unserialize('E:7:"Foo:Baz";'));
+
+?>
+--EXPECTF--
+Warning: unserialize(): Undefined constant Foo::Baz in %s on line %d
+
+Notice: unserialize(): Error at offset 14 of 14 bytes in %s on line %d
+bool(false)
diff --git a/Zend/tests/enum/unserialize-refcount.phpt b/Zend/tests/enum/unserialize-refcount.phpt
new file mode 100644
index 0000000000..447b3af182
--- /dev/null
+++ b/Zend/tests/enum/unserialize-refcount.phpt
@@ -0,0 +1,45 @@
+--TEST--
+Enum unserialize refcount
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+debug_zval_dump(Foo::Bar);
+
+$foo = Foo::Bar;
+debug_zval_dump($foo);
+
+$bar = unserialize('E:7:"Foo:Bar";');
+debug_zval_dump($foo);
+
+unset($bar);
+debug_zval_dump($foo);
+
+unset($foo);
+debug_zval_dump(Foo::Bar);
+
+?>
+--EXPECT--
+object(Foo)#1 (1) refcount(2){
+ ["name"]=>
+ string(3) "Bar" interned
+}
+object(Foo)#1 (1) refcount(3){
+ ["name"]=>
+ string(3) "Bar" interned
+}
+object(Foo)#1 (1) refcount(4){
+ ["name"]=>
+ string(3) "Bar" interned
+}
+object(Foo)#1 (1) refcount(3){
+ ["name"]=>
+ string(3) "Bar" interned
+}
+object(Foo)#1 (1) refcount(2){
+ ["name"]=>
+ string(3) "Bar" interned
+}
diff --git a/Zend/tests/enum/unserialize.phpt b/Zend/tests/enum/unserialize.phpt
new file mode 100644
index 0000000000..c5a10a8fe5
--- /dev/null
+++ b/Zend/tests/enum/unserialize.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Enum unserialize
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+ case Quux;
+}
+
+$bar = unserialize('E:7:"Foo:Bar";');
+var_dump($bar);
+var_dump($bar === Foo::Bar);
+
+$quux = unserialize('E:8:"Foo:Quux";');
+var_dump($quux);
+var_dump($quux === Foo::Quux);
+
+?>
+--EXPECT--
+enum(Foo::Bar)
+bool(true)
+enum(Foo::Quux)
+bool(true)
diff --git a/Zend/tests/enum/value-property-type.phpt b/Zend/tests/enum/value-property-type.phpt
new file mode 100644
index 0000000000..ed009dd981
--- /dev/null
+++ b/Zend/tests/enum/value-property-type.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Enum value property has automatic type
+--FILE--
+<?php
+
+enum IntEnum: int {
+ case Foo = 0;
+}
+
+enum StringEnum: string {
+ case Foo = 'Foo';
+}
+
+echo (new ReflectionProperty(IntEnum::class, 'value'))->getType() . "\n";
+echo (new ReflectionProperty(StringEnum::class, 'value'))->getType() . "\n";
+
+?>
+--EXPECT--
+int
+string
diff --git a/Zend/tests/enum/var_dump-nested.phpt b/Zend/tests/enum/var_dump-nested.phpt
new file mode 100644
index 0000000000..73a4e51c38
--- /dev/null
+++ b/Zend/tests/enum/var_dump-nested.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Enum var_dump nested
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+var_dump([[Foo::Bar]]);
+
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ array(1) {
+ [0]=>
+ enum(Foo::Bar)
+ }
+}
diff --git a/Zend/tests/enum/var_dump-reference.phpt b/Zend/tests/enum/var_dump-reference.phpt
new file mode 100644
index 0000000000..fe04e7865c
--- /dev/null
+++ b/Zend/tests/enum/var_dump-reference.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Enum var_dump reference
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+ case Baz;
+}
+
+$arr = [Foo::Bar];
+$arr[1] = &$arr[0];
+var_dump($arr);
+
+?>
+--EXPECT--
+array(2) {
+ [0]=>
+ &enum(Foo::Bar)
+ [1]=>
+ &enum(Foo::Bar)
+}
diff --git a/Zend/tests/enum/var_export.phpt b/Zend/tests/enum/var_export.phpt
new file mode 100644
index 0000000000..430444d13d
--- /dev/null
+++ b/Zend/tests/enum/var_export.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Enum var_export
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+var_export(Foo::Bar);
+
+?>
+--EXPECT--
+Foo::Bar