Commit 075b6b85f6b for php.net
commit 075b6b85f6b964108c76cea7c64c944bf1585954
Author: Arnaud Le Blanc <365207+arnaud-lb@users.noreply.github.com>
Date: Tue Jan 13 12:32:52 2026 +0100
Set default_object_handlers when registering internal enums
Internal enums can be cloned and compared, unlike user enums, because we didn't set default_object_handlers when registering internal enums.
Fix by setting default_object_handlers when registering internal enums.
Fixes GH-20914
Closes GH-20915
diff --git a/Zend/tests/enum/comparison-internal.phpt b/Zend/tests/enum/comparison-internal.phpt
new file mode 100644
index 00000000000..3d0a2f1e172
--- /dev/null
+++ b/Zend/tests/enum/comparison-internal.phpt
@@ -0,0 +1,54 @@
+--TEST--
+Enum comparison (internal enum)
+--EXTENSIONS--
+zend_test
+--FILE--
+<?php
+
+$foo = ZendTestUnitEnum::Foo;
+$bar = ZendTestUnitEnum::Bar;
+
+var_dump($foo === $foo);
+var_dump($foo == $foo);
+
+var_dump($foo === $bar);
+var_dump($foo == $bar);
+
+var_dump($bar === $foo);
+var_dump($bar == $foo);
+
+var_dump($foo > $foo);
+var_dump($foo < $foo);
+var_dump($foo >= $foo);
+var_dump($foo <= $foo);
+
+var_dump($foo > $bar);
+var_dump($foo < $bar);
+var_dump($foo >= $bar);
+var_dump($foo <= $bar);
+
+var_dump($foo > true);
+var_dump($foo < true);
+var_dump($foo >= true);
+var_dump($foo <= 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/implements-internal.phpt b/Zend/tests/enum/implements-internal.phpt
new file mode 100644
index 00000000000..518602af97e
--- /dev/null
+++ b/Zend/tests/enum/implements-internal.phpt
@@ -0,0 +1,14 @@
+--TEST--
+Enum implements (internal enum)
+--EXTENSIONS--
+zend_test
+--FILE--
+<?php
+
+var_dump(ZendTestUnitEnum::Foo instanceof _ZendTestInterface);
+var_dump(ZendTestEnumWithInterface::Foo instanceof _ZendTestInterface);
+
+?>
+--EXPECT--
+bool(false)
+bool(true)
diff --git a/Zend/tests/enum/instanceof-backed-enum.phpt b/Zend/tests/enum/instanceof-backed-enum.phpt
index 4716835d116..7087cfec74b 100644
--- a/Zend/tests/enum/instanceof-backed-enum.phpt
+++ b/Zend/tests/enum/instanceof-backed-enum.phpt
@@ -1,5 +1,7 @@
--TEST--
Auto implement BackedEnum interface
+--EXTENSIONS--
+zend_test
--FILE--
<?php
@@ -13,8 +15,12 @@ enum Baz: int {
var_dump(Foo::Bar instanceof BackedEnum);
var_dump(Baz::Qux instanceof BackedEnum);
+var_dump(ZendTestUnitEnum::Foo instanceof BackedEnum);
+var_dump(ZendTestIntEnum::Foo instanceof BackedEnum);
?>
--EXPECT--
bool(false)
bool(true)
+bool(false)
+bool(true)
diff --git a/Zend/tests/enum/instanceof-unitenum.phpt b/Zend/tests/enum/instanceof-unitenum.phpt
index 55237963253..89b04b50466 100644
--- a/Zend/tests/enum/instanceof-unitenum.phpt
+++ b/Zend/tests/enum/instanceof-unitenum.phpt
@@ -1,5 +1,7 @@
--TEST--
Auto implement UnitEnum interface
+--EXTENSIONS--
+zend_test
--FILE--
<?php
@@ -11,8 +13,10 @@ class Baz {}
var_dump(Foo::Bar instanceof UnitEnum);
var_dump((new Baz()) instanceof UnitEnum);
+var_dump(ZendTestUnitEnum::Foo instanceof UnitEnum);
?>
--EXPECT--
bool(true)
bool(false)
+bool(true)
diff --git a/Zend/tests/enum/no-clone-internal.phpt b/Zend/tests/enum/no-clone-internal.phpt
new file mode 100644
index 00000000000..84b7ee2634d
--- /dev/null
+++ b/Zend/tests/enum/no-clone-internal.phpt
@@ -0,0 +1,16 @@
+--TEST--
+Enum disallows cloning (internal enum)
+--EXTENSIONS--
+zend_test
+--FILE--
+<?php
+
+try {
+ var_dump(clone ZendTestIntEnum::Foo);
+} catch (Error $e) {
+ echo $e->getMessage() . "\n";
+}
+
+?>
+--EXPECT--
+Trying to clone an uncloneable object of class ZendTestIntEnum
diff --git a/Zend/tests/enum/no-dynamic-properties-internal.phpt b/Zend/tests/enum/no-dynamic-properties-internal.phpt
new file mode 100644
index 00000000000..8d821a5f629
--- /dev/null
+++ b/Zend/tests/enum/no-dynamic-properties-internal.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Enum case disallows dynamic properties (internal enum)
+--EXTENSIONS--
+zend_test
+--FILE--
+<?php
+
+$bar = ZendTestUnitEnum::Bar;
+
+try {
+ $bar->baz = 'Baz';
+} catch (\Error $e) {
+ echo $e->getMessage();
+}
+
+?>
+--EXPECT--
+Cannot create dynamic property ZendTestUnitEnum::$baz
diff --git a/Zend/zend_enum.c b/Zend/zend_enum.c
index ccafca48fe9..464e7d801a8 100644
--- a/Zend/zend_enum.c
+++ b/Zend/zend_enum.c
@@ -528,6 +528,8 @@ ZEND_API zend_class_entry *zend_register_internal_enum(
zend_class_implements(ce, 1, zend_ce_backed_enum);
}
+ ce->default_object_handlers = &zend_enum_object_handlers;
+
return ce;
}
diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c
index 59b2c79edf6..576bacd5e4a 100644
--- a/ext/zend_test/test.c
+++ b/ext/zend_test/test.c
@@ -74,6 +74,7 @@ static zend_class_entry *zend_test_ns2_ns_foo_class;
static zend_class_entry *zend_test_unit_enum;
static zend_class_entry *zend_test_string_enum;
static zend_class_entry *zend_test_int_enum;
+static zend_class_entry *zend_test_enum_with_interface;
static zend_class_entry *zend_test_magic_call;
static zend_object_handlers zend_test_class_handlers;
@@ -1318,6 +1319,7 @@ PHP_MINIT_FUNCTION(zend_test)
zend_test_unit_enum = register_class_ZendTestUnitEnum();
zend_test_string_enum = register_class_ZendTestStringEnum();
zend_test_int_enum = register_class_ZendTestIntEnum();
+ zend_test_enum_with_interface = register_class_ZendTestEnumWithInterface(zend_test_interface);
zend_test_magic_call = register_class__ZendTestMagicCall();
diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php
index 1be61b7a170..9116245c30f 100644
--- a/ext/zend_test/test.stub.php
+++ b/ext/zend_test/test.stub.php
@@ -201,6 +201,11 @@ enum ZendTestIntEnum: int {
case Baz = -1;
}
+ enum ZendTestEnumWithInterface implements _ZendTestInterface {
+ case Foo;
+ case Bar;
+ }
+
function zend_test_array_return(): array {}
/** @genstubs-expose-comment-block
diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h
index b38a3546518..039757207e6 100644
Binary files a/ext/zend_test/test_arginfo.h and b/ext/zend_test/test_arginfo.h differ