From 6b161827f17cbe8d7e42e65a8a3a8774b2d58ede Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Mon, 8 Mar 2021 22:14:51 +0100 Subject: [PATCH 01/15] Add enum_exists() as well as UnitEnum and BackedEnum interfaces Step 1 in forwards compatibility with PHP 8.1 enumerations See https://wiki.php.net/rfc/enumerations --- src/main/php/lang.base.php | 11 ++++++++--- src/main/php/lang/AbstractClassLoader.class.php | 5 +++-- src/main/php/lang/GenericTypes.class.php | 2 +- src/main/php/lang/XPClass.class.php | 2 +- src/main/php/lang/reflect/ClassParser.class.php | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/php/lang.base.php b/src/main/php/lang.base.php index 698cebf66c..1673190866 100755 --- a/src/main/php/lang.base.php +++ b/src/main/php/lang.base.php @@ -412,9 +412,14 @@ function __construct($str) { } // }}} -// {{{ interface IDisposable -if (!interface_exists(\IDisposable::class, false)) { - eval('interface IDisposable { public function __dispose(); }'); +// {{{ PHP 8.1 enums +if (!function_exists('enum_exists')) { + interface UnitEnum { } + interface BackedEnum extends UnitEnum { } + + function enum_exists($name, $load) { + return class_exists($name, $load) && $name instanceof \UnitEnum; + } } // }}} diff --git a/src/main/php/lang/AbstractClassLoader.class.php b/src/main/php/lang/AbstractClassLoader.class.php index b42c92d274..4c057029f9 100755 --- a/src/main/php/lang/AbstractClassLoader.class.php +++ b/src/main/php/lang/AbstractClassLoader.class.php @@ -85,7 +85,7 @@ public function loadClass0($class) { // If class was declared, but loading threw an exception it means // a "soft" dependency, one that is only required at runtime, was // not loaded, the class itself has been declared. - if (class_exists($name, false) || interface_exists($name, false) || trait_exists($name, false)) { + if (class_exists($name, false) || interface_exists($name, false) || trait_exists($name, false) || enum_exists($name, false)) { throw new ClassDependencyException($class, [$this], $e); } @@ -101,7 +101,8 @@ public function loadClass0($class) { $e= new ClassNotFoundException($class, [$this]); \xp::gc(__FILE__); throw $e; - } else if (!class_exists($name, false) && !interface_exists($name, false) && !trait_exists($name, false)) { + } else if (!class_exists($name, false) && !interface_exists($name, false) && !trait_exists($name, false) && !enum_exists($name, false)) { + \xp::gc(__FILE__); $bytes= $this->loadClassBytes($class); if (preg_match('/(class|interface|trait)\s+([^ ]+)/', $bytes, $decl)) { preg_match('/namespace\s+([^;]+);/', $bytes, $ns); diff --git a/src/main/php/lang/GenericTypes.class.php b/src/main/php/lang/GenericTypes.class.php index 830b47e3ac..19588127aa 100755 --- a/src/main/php/lang/GenericTypes.class.php +++ b/src/main/php/lang/GenericTypes.class.php @@ -57,7 +57,7 @@ public function newType0($base, $arguments) { $qname= $base->name.'<'.substr($qc, 1).'>'; // Create class if it doesn't exist yet - if (!class_exists($name, false) && !interface_exists($name, false)) { + if (!class_exists($name, false) && !interface_exists($name, false) && !trait_exists($name, false) && !enum_exists($name, false)) { $meta= \xp::$meta[$base->name]; // Parse placeholders into a lookup map diff --git a/src/main/php/lang/XPClass.class.php b/src/main/php/lang/XPClass.class.php index 04248460db..6b40c5434b 100755 --- a/src/main/php/lang/XPClass.class.php +++ b/src/main/php/lang/XPClass.class.php @@ -800,7 +800,7 @@ public static function forName($name, IClassLoader $classloader= null): self { $name= strtr($resolved, '\\', '.'); } - if (class_exists($resolved, false) || interface_exists($resolved, false) || trait_exists($resolved, false)) { + if (class_exists($resolved, false) || interface_exists($resolved, false) || trait_exists($resolved, false) || enum_exists($resolved, false)) { return new self($resolved); } else if (null === $classloader) { return ClassLoader::getDefault()->loadClass($name); diff --git a/src/main/php/lang/reflect/ClassParser.class.php b/src/main/php/lang/reflect/ClassParser.class.php index 6058491dbe..ceeb697d0c 100755 --- a/src/main/php/lang/reflect/ClassParser.class.php +++ b/src/main/php/lang/reflect/ClassParser.class.php @@ -36,7 +36,7 @@ protected function resolve($type, $context, $imports) { return XPClass::forName($type); } else if (isset($imports[$type])) { return XPClass::forName($imports[$type]); - } else if (class_exists($type, false) || interface_exists($type, false)) { + } else if (class_exists($type, false) || interface_exists($type, false) || trait_exists($type, false) || enum_exists($type, false)) { return new XPClass($type); } else if (false !== ($p= strrpos($context, '.'))) { return XPClass::forName(substr($context, 0, $p + 1).$type); From 131d90d9c9f598e1e020175a0d8588b50574adda Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Mon, 8 Mar 2021 22:20:00 +0100 Subject: [PATCH 02/15] Add back IDisposable --- src/main/php/lang.base.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/php/lang.base.php b/src/main/php/lang.base.php index 1673190866..0ec75ab120 100755 --- a/src/main/php/lang.base.php +++ b/src/main/php/lang.base.php @@ -412,6 +412,12 @@ function __construct($str) { } // }}} +// {{{ interface IDisposable +if (!interface_exists(\IDisposable::class, false)) { + eval('interface IDisposable { public function __dispose(); }'); +} +// }}} + // {{{ PHP 8.1 enums if (!function_exists('enum_exists')) { interface UnitEnum { } From c6a4a2633b9a09a69d262639ca02960af288f65b Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Mon, 8 Mar 2021 23:25:09 +0100 Subject: [PATCH 03/15] Rename lang.Enum -> lang.XPEnum See https://github.com/xp-framework/core/pull/261#issuecomment-793116865 --- src/main/php/lang.base.php | 22 ++++++------- src/main/php/lang/CommandLine.class.php | 2 +- src/main/php/lang/XPClass.class.php | 2 +- .../lang/{Enum.class.php => XPEnum.class.php} | 4 +-- src/main/php/util/Currency.class.php | 4 +-- .../xp_framework/unittest/core/Coin.class.php | 2 +- .../unittest/core/EnumTest.class.php | 32 +++++++++---------- .../unittest/core/Operation.class.php | 2 +- .../unittest/core/Profiling.class.php | 2 +- 9 files changed, 36 insertions(+), 36 deletions(-) rename src/main/php/lang/{Enum.class.php => XPEnum.class.php} (98%) diff --git a/src/main/php/lang.base.php b/src/main/php/lang.base.php index 0ec75ab120..8e7fe7b0f0 100755 --- a/src/main/php/lang.base.php +++ b/src/main/php/lang.base.php @@ -418,17 +418,6 @@ function __construct($str) { } // }}} -// {{{ PHP 8.1 enums -if (!function_exists('enum_exists')) { - interface UnitEnum { } - interface BackedEnum extends UnitEnum { } - - function enum_exists($name, $load) { - return class_exists($name, $load) && $name instanceof \UnitEnum; - } -} -// }}} - // {{{ main error_reporting(E_ALL); set_error_handler('__error'); @@ -478,4 +467,15 @@ class_alias('import', $class); return true; } }); + +if (!function_exists('enum_exists')) { + interface UnitEnum { } + interface BackedEnum extends UnitEnum { } + + function enum_exists($name, $load) { + return class_exists($name, $load) && $name instanceof \UnitEnum; + } + + class_alias(\lang\XPEnum::class, \lang\Enum::class); +} // }}} \ No newline at end of file diff --git a/src/main/php/lang/CommandLine.class.php b/src/main/php/lang/CommandLine.class.php index 3b554edd94..7b6ca6b42a 100755 --- a/src/main/php/lang/CommandLine.class.php +++ b/src/main/php/lang/CommandLine.class.php @@ -23,7 +23,7 @@ * @see xp://lang.Process * @test xp://net.xp_framework.unittest.core.CommandLineTest */ -abstract class CommandLine extends Enum { +abstract class CommandLine extends XPEnum { public static $WINDOWS, $UNIX; static function __static() { diff --git a/src/main/php/lang/XPClass.class.php b/src/main/php/lang/XPClass.class.php index 6b40c5434b..c0dcf25a2f 100755 --- a/src/main/php/lang/XPClass.class.php +++ b/src/main/php/lang/XPClass.class.php @@ -471,7 +471,7 @@ public function isTrait(): bool { * @return bool */ public function isEnum(): bool { - return class_exists(Enum::class, false) && $this->reflect()->isSubclassOf(Enum::class); + return class_exists(XPEnum::class, false) && $this->reflect()->isSubclassOf(XPEnum::class); } /** diff --git a/src/main/php/lang/Enum.class.php b/src/main/php/lang/XPEnum.class.php similarity index 98% rename from src/main/php/lang/Enum.class.php rename to src/main/php/lang/XPEnum.class.php index 55e4b21abc..d74efdeead 100755 --- a/src/main/php/lang/Enum.class.php +++ b/src/main/php/lang/XPEnum.class.php @@ -7,7 +7,7 @@ * @see http://news.xp-framework.net/article/207/2007/07/29/ * @test xp://net.xp_framework.unittest.core.EnumTest */ -abstract class Enum implements Value { +abstract class XPEnum implements Value { public $name= ''; protected $ordinal= 0; @@ -48,7 +48,7 @@ public function __construct(int $ordinal= 0, string $name= '') { public static function valueOf($type, string $name): self { $class= $type instanceof XPClass ? $type : XPClass::forName($type); if (!$class->isEnum()) { - throw new IllegalArgumentException('Argument class must be lang.XPClass'); + throw new IllegalArgumentException('Argument class must be lang.XPClass'); } if ($class->isSubclassOf(self::class)) { diff --git a/src/main/php/util/Currency.class.php b/src/main/php/util/Currency.class.php index 6a50f1a8ca..91eea31bff 100755 --- a/src/main/php/util/Currency.class.php +++ b/src/main/php/util/Currency.class.php @@ -1,6 +1,6 @@ assertAbstract(XPClass::forName(Enum::class)->getModifiers()); + $this->assertAbstract(XPClass::forName(XPEnum::class)->getModifiers()); } #[Test] @@ -146,7 +146,7 @@ public function enumMembersAreNotCloneable() { public function valueOf() { $this->assertEquals( Coin::$penny, - Enum::valueOf(XPClass::forName(Coin::class), 'penny') + XPEnum::valueOf(XPClass::forName(Coin::class), 'penny') ); } @@ -154,25 +154,25 @@ public function valueOf() { public function valueOf_string() { $this->assertEquals( Coin::$penny, - Enum::valueOf(Coin::class, 'penny') + XPEnum::valueOf(Coin::class, 'penny') ); } #[Test, Expect(IllegalArgumentException::class)] public function valueOfNonExistant() { - Enum::valueOf(XPClass::forName(Coin::class), '@@DOES_NOT_EXIST@@'); + XPEnum::valueOf(XPClass::forName(Coin::class), '@@DOES_NOT_EXIST@@'); } #[Test, Expect(IllegalArgumentException::class)] public function valueOfNonEnum() { - Enum::valueOf(self::class, 'irrelevant'); + XPEnum::valueOf(self::class, 'irrelevant'); } #[Test] public function valueOfAbstractEnum() { $this->assertEquals( Operation::$plus, - Enum::valueOf(XPClass::forName(Operation::class), 'plus') + XPEnum::valueOf(XPClass::forName(Operation::class), 'plus') ); } @@ -180,7 +180,7 @@ public function valueOfAbstractEnum() { public function valuesOf() { $this->assertEquals( [Coin::$penny, Coin::$nickel, Coin::$dime, Coin::$quarter], - Enum::valuesOf(XPClass::forName(Coin::class)) + XPEnum::valuesOf(XPClass::forName(Coin::class)) ); } @@ -188,7 +188,7 @@ public function valuesOf() { public function valuesOf_string() { $this->assertEquals( [Coin::$penny, Coin::$nickel, Coin::$dime, Coin::$quarter], - Enum::valuesOf(Coin::class) + XPEnum::valuesOf(Coin::class) ); } @@ -196,13 +196,13 @@ public function valuesOf_string() { public function valuesOfAbstractEnum() { $this->assertEquals( [Operation::$plus, Operation::$minus, Operation::$times, Operation::$divided_by], - Enum::valuesOf(XPClass::forName(Operation::class)) + XPEnum::valuesOf(XPClass::forName(Operation::class)) ); } #[Test, Expect(IllegalArgumentException::class)] public function valuesOfNonEnum() { - Enum::valuesOf(self::class); + XPEnum::valuesOf(self::class); } #[Test] @@ -229,7 +229,7 @@ public function dividedByOperation() { public function staticMemberNotInEnumValuesOf() { $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], - Enum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) + XPEnum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) ); } @@ -243,7 +243,7 @@ public function staticMemberNotInValues() { #[Test, Expect(IllegalArgumentException::class)] public function staticMemberNotWithEnumValueOf() { - Enum::valueOf(XPClass::forName('net.xp_framework.unittest.core.Profiling'), 'fixture'); + XPEnum::valueOf(XPClass::forName('net.xp_framework.unittest.core.Profiling'), 'fixture'); } #[Test] @@ -251,7 +251,7 @@ public function staticEnumMemberNotInEnumValuesOf() { Profiling::$fixture= Coin::$penny; $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], - Enum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) + XPEnum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) ); Profiling::$fixture= null; } @@ -271,7 +271,7 @@ public function staticObjectMemberNotInEnumValuesOf() { Profiling::$fixture= $this; $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], - Enum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) + XPEnum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) ); Profiling::$fixture= null; } @@ -291,7 +291,7 @@ public function staticPrimitiveMemberNotInEnumValuesOf() { Profiling::$fixture= [$this, $this->name]; $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], - Enum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) + XPEnum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) ); Profiling::$fixture= null; } diff --git a/src/test/php/net/xp_framework/unittest/core/Operation.class.php b/src/test/php/net/xp_framework/unittest/core/Operation.class.php index ec1e88aa4f..0d94e6af8c 100755 --- a/src/test/php/net/xp_framework/unittest/core/Operation.class.php +++ b/src/test/php/net/xp_framework/unittest/core/Operation.class.php @@ -3,7 +3,7 @@ /** * Operation enumeration */ -abstract class Operation extends \lang\Enum { +abstract class Operation extends \lang\XPEnum { public static $plus, $minus, diff --git a/src/test/php/net/xp_framework/unittest/core/Profiling.class.php b/src/test/php/net/xp_framework/unittest/core/Profiling.class.php index 2fb06fec25..568e9d772b 100755 --- a/src/test/php/net/xp_framework/unittest/core/Profiling.class.php +++ b/src/test/php/net/xp_framework/unittest/core/Profiling.class.php @@ -3,7 +3,7 @@ /** * Profiling enumeration */ -class Profiling extends \lang\Enum { +class Profiling extends \lang\XPEnum { public static $INSTANCE; public static $EXTENSION; From 32cb6aaf20f9e840c9e8e9915338039aa2279c29 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Tue, 9 Mar 2021 21:36:00 +0100 Subject: [PATCH 04/15] Adjust to renaming of Enum -> XPEnum Should also be done in unittest library one XP core is released with this change! --- test.xar | Bin 164009 -> 163695 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test.xar b/test.xar index e285d9ef26a7c3f706b062fba40eeb067159fe70..5de1125e84980b5d7c26b506d071b9b0d2ba7cc8 100755 GIT binary patch delta 521 zcmXw$O-K{~6omJ^b~SY~&8#df`>`LZwV$oDe_JbA5Q?G%sSY7w6yZUlTV~KLvN?2! zP6ZVNK~Ww84^jk49Xcf*f;tp**h9T1czDxp&|Ky+1K%)Dhbs$Fk;>&dwR^O-Fxs5H ztA8W`Q_4ly9Lfb)l9Gg_C~dGZN(Zc*auYU}@*Gw{IR&ev48f`>)vz?R&;y?r(g&-i z+=tDlPM?I=gwFKAYeSyF7Ep4qI_ks~_`=Z1L3n*gJ!}#6=T-RPkh8EQEb%l@F2j~m z9>A7S-`#;Xh8%}AQ9nF{FAtr50$&kw3)W1@z*bW7uvOIGui&dguEW+)val9PJ8UiG z8f+bPb_%{eWCXT>`td8gHKYo*k^1X9d{f9EY%}#s8lDL$hqY0fl?Wv|?K^ zoPlo-c?8Q+F2QzCZo_s`-oSQIYGCcuPrdMtBI*Eqca-q`rYBo9@n0Bz g(SY&O)g%4+vHrZ=_^iFh?)_-go}!Osn{?>>U#ZRng8%>k delta 596 zcmXw%O-R#m9LD$kuGKa*vrJv z5!J0o^AHlWTTl^n2!d|C93naeAs)UzNaFWxJM^5N2RCWTSH6B22aAjwH+`OTQpBN^_9S%C0QT7R$aIOs%C0 zkgdv%voLj*9zyE1%+sK>1PLg8g=|xH7hr;x?m)IHdmqE>upC%~*=gw=WS3F~5>mPi z2`jh1gxPKB6=aW64zgG2I%JaL;h@}K%zjEX!OpB%GkORtf zRWPlVh9C!(<{@p$p<0+jwrtT}D!0KLw)6vXMClpisL}^WhteO&F{Kg6aplU(Fr6jT zYcMBl8851|3W+KGg>)(N0!+810;EUjDWq4qy9m=~=>ep_w0{dTV7dN1Onl7~_PAV+ zAcI&B From cec61c95cc0fb8660f0dc9c06fa083598a530d1f Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Tue, 9 Mar 2021 21:37:05 +0100 Subject: [PATCH 05/15] Fix missing Enum -> XPEnum renaming --- src/main/php/util/TimeInterval.class.php | 4 +++- src/test/php/net/xp_framework/unittest/core/Weekday.class.php | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/php/util/TimeInterval.class.php b/src/main/php/util/TimeInterval.class.php index 8ef96d968f..e75325f973 100755 --- a/src/main/php/util/TimeInterval.class.php +++ b/src/main/php/util/TimeInterval.class.php @@ -1,12 +1,14 @@ Date: Wed, 10 Mar 2021 12:42:51 +0100 Subject: [PATCH 06/15] Revert "Fix missing Enum -> XPEnum renaming" This reverts commit cec61c95cc0fb8660f0dc9c06fa083598a530d1f. --- src/main/php/util/TimeInterval.class.php | 4 +--- src/test/php/net/xp_framework/unittest/core/Weekday.class.php | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/php/util/TimeInterval.class.php b/src/main/php/util/TimeInterval.class.php index e75325f973..8ef96d968f 100755 --- a/src/main/php/util/TimeInterval.class.php +++ b/src/main/php/util/TimeInterval.class.php @@ -1,14 +1,12 @@ Date: Wed, 10 Mar 2021 12:43:04 +0100 Subject: [PATCH 07/15] Revert "Adjust to renaming of Enum -> XPEnum" This reverts commit 32cb6aaf20f9e840c9e8e9915338039aa2279c29. --- test.xar | Bin 163695 -> 164009 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test.xar b/test.xar index 5de1125e84980b5d7c26b506d071b9b0d2ba7cc8..e285d9ef26a7c3f706b062fba40eeb067159fe70 100755 GIT binary patch delta 596 zcmXw%O-R#m9LD$kuGKa*vrJv z5!J0o^AHlWTTl^n2!d|C93naeAs)UzNaFWxJM^5N2RCWTSH6B22aAjwH+`OTQpBN^_9S%C0QT7R$aIOs%C0 zkgdv%voLj*9zyE1%+sK>1PLg8g=|xH7hr;x?m)IHdmqE>upC%~*=gw=WS3F~5>mPi z2`jh1gxPKB6=aW64zgG2I%JaL;h@}K%zjEX!OpB%GkORtf zRWPlVh9C!(<{@p$p<0+jwrtT}D!0KLw)6vXMClpisL}^WhteO&F{Kg6aplU(Fr6jT zYcMBl8851|3W+KGg>)(N0!+810;EUjDWq4qy9m=~=>ep_w0{dTV7dN1Onl7~_PAV+ zAcI&B delta 521 zcmXw$O-K{~6omJ^b~SY~&8#df`>`LZwV$oDe_JbA5Q?G%sSY7w6yZUlTV~KLvN?2! zP6ZVNK~Ww84^jk49Xcf*f;tp**h9T1czDxp&|Ky+1K%)Dhbs$Fk;>&dwR^O-Fxs5H ztA8W`Q_4ly9Lfb)l9Gg_C~dGZN(Zc*auYU}@*Gw{IR&ev48f`>)vz?R&;y?r(g&-i z+=tDlPM?I=gwFKAYeSyF7Ep4qI_ks~_`=Z1L3n*gJ!}#6=T-RPkh8EQEb%l@F2j~m z9>A7S-`#;Xh8%}AQ9nF{FAtr50$&kw3)W1@z*bW7uvOIGui&dguEW+)val9PJ8UiG z8f+bPb_%{eWCXT>`td8gHKYo*k^1X9d{f9EY%}#s8lDL$hqY0fl?Wv|?K^ zoPlo-c?8Q+F2QzCZo_s`-oSQIYGCcuPrdMtBI*Eqca-q`rYBo9@n0Bz g(SY&O)g%4+vHrZ=_^iFh?)_-go}!Osn{?>>U#ZRng8%>k From 5fb21e0cb41ca522f76eef6717f77f21f240b60e Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Wed, 10 Mar 2021 12:43:08 +0100 Subject: [PATCH 08/15] Revert "Rename lang.Enum -> lang.XPEnum" This reverts commit c6a4a2633b9a09a69d262639ca02960af288f65b. --- src/main/php/lang.base.php | 22 ++++++------- src/main/php/lang/CommandLine.class.php | 2 +- .../lang/{XPEnum.class.php => Enum.class.php} | 4 +-- src/main/php/lang/XPClass.class.php | 2 +- src/main/php/util/Currency.class.php | 4 +-- .../xp_framework/unittest/core/Coin.class.php | 2 +- .../unittest/core/EnumTest.class.php | 32 +++++++++---------- .../unittest/core/Operation.class.php | 2 +- .../unittest/core/Profiling.class.php | 2 +- 9 files changed, 36 insertions(+), 36 deletions(-) rename src/main/php/lang/{XPEnum.class.php => Enum.class.php} (98%) diff --git a/src/main/php/lang.base.php b/src/main/php/lang.base.php index 8e7fe7b0f0..0ec75ab120 100755 --- a/src/main/php/lang.base.php +++ b/src/main/php/lang.base.php @@ -418,6 +418,17 @@ function __construct($str) { } // }}} +// {{{ PHP 8.1 enums +if (!function_exists('enum_exists')) { + interface UnitEnum { } + interface BackedEnum extends UnitEnum { } + + function enum_exists($name, $load) { + return class_exists($name, $load) && $name instanceof \UnitEnum; + } +} +// }}} + // {{{ main error_reporting(E_ALL); set_error_handler('__error'); @@ -467,15 +478,4 @@ class_alias('import', $class); return true; } }); - -if (!function_exists('enum_exists')) { - interface UnitEnum { } - interface BackedEnum extends UnitEnum { } - - function enum_exists($name, $load) { - return class_exists($name, $load) && $name instanceof \UnitEnum; - } - - class_alias(\lang\XPEnum::class, \lang\Enum::class); -} // }}} \ No newline at end of file diff --git a/src/main/php/lang/CommandLine.class.php b/src/main/php/lang/CommandLine.class.php index 7b6ca6b42a..3b554edd94 100755 --- a/src/main/php/lang/CommandLine.class.php +++ b/src/main/php/lang/CommandLine.class.php @@ -23,7 +23,7 @@ * @see xp://lang.Process * @test xp://net.xp_framework.unittest.core.CommandLineTest */ -abstract class CommandLine extends XPEnum { +abstract class CommandLine extends Enum { public static $WINDOWS, $UNIX; static function __static() { diff --git a/src/main/php/lang/XPEnum.class.php b/src/main/php/lang/Enum.class.php similarity index 98% rename from src/main/php/lang/XPEnum.class.php rename to src/main/php/lang/Enum.class.php index d74efdeead..55e4b21abc 100755 --- a/src/main/php/lang/XPEnum.class.php +++ b/src/main/php/lang/Enum.class.php @@ -7,7 +7,7 @@ * @see http://news.xp-framework.net/article/207/2007/07/29/ * @test xp://net.xp_framework.unittest.core.EnumTest */ -abstract class XPEnum implements Value { +abstract class Enum implements Value { public $name= ''; protected $ordinal= 0; @@ -48,7 +48,7 @@ public function __construct(int $ordinal= 0, string $name= '') { public static function valueOf($type, string $name): self { $class= $type instanceof XPClass ? $type : XPClass::forName($type); if (!$class->isEnum()) { - throw new IllegalArgumentException('Argument class must be lang.XPClass'); + throw new IllegalArgumentException('Argument class must be lang.XPClass'); } if ($class->isSubclassOf(self::class)) { diff --git a/src/main/php/lang/XPClass.class.php b/src/main/php/lang/XPClass.class.php index c0dcf25a2f..6b40c5434b 100755 --- a/src/main/php/lang/XPClass.class.php +++ b/src/main/php/lang/XPClass.class.php @@ -471,7 +471,7 @@ public function isTrait(): bool { * @return bool */ public function isEnum(): bool { - return class_exists(XPEnum::class, false) && $this->reflect()->isSubclassOf(XPEnum::class); + return class_exists(Enum::class, false) && $this->reflect()->isSubclassOf(Enum::class); } /** diff --git a/src/main/php/util/Currency.class.php b/src/main/php/util/Currency.class.php index 91eea31bff..6a50f1a8ca 100755 --- a/src/main/php/util/Currency.class.php +++ b/src/main/php/util/Currency.class.php @@ -1,6 +1,6 @@ assertAbstract(XPClass::forName(XPEnum::class)->getModifiers()); + $this->assertAbstract(XPClass::forName(Enum::class)->getModifiers()); } #[Test] @@ -146,7 +146,7 @@ public function enumMembersAreNotCloneable() { public function valueOf() { $this->assertEquals( Coin::$penny, - XPEnum::valueOf(XPClass::forName(Coin::class), 'penny') + Enum::valueOf(XPClass::forName(Coin::class), 'penny') ); } @@ -154,25 +154,25 @@ public function valueOf() { public function valueOf_string() { $this->assertEquals( Coin::$penny, - XPEnum::valueOf(Coin::class, 'penny') + Enum::valueOf(Coin::class, 'penny') ); } #[Test, Expect(IllegalArgumentException::class)] public function valueOfNonExistant() { - XPEnum::valueOf(XPClass::forName(Coin::class), '@@DOES_NOT_EXIST@@'); + Enum::valueOf(XPClass::forName(Coin::class), '@@DOES_NOT_EXIST@@'); } #[Test, Expect(IllegalArgumentException::class)] public function valueOfNonEnum() { - XPEnum::valueOf(self::class, 'irrelevant'); + Enum::valueOf(self::class, 'irrelevant'); } #[Test] public function valueOfAbstractEnum() { $this->assertEquals( Operation::$plus, - XPEnum::valueOf(XPClass::forName(Operation::class), 'plus') + Enum::valueOf(XPClass::forName(Operation::class), 'plus') ); } @@ -180,7 +180,7 @@ public function valueOfAbstractEnum() { public function valuesOf() { $this->assertEquals( [Coin::$penny, Coin::$nickel, Coin::$dime, Coin::$quarter], - XPEnum::valuesOf(XPClass::forName(Coin::class)) + Enum::valuesOf(XPClass::forName(Coin::class)) ); } @@ -188,7 +188,7 @@ public function valuesOf() { public function valuesOf_string() { $this->assertEquals( [Coin::$penny, Coin::$nickel, Coin::$dime, Coin::$quarter], - XPEnum::valuesOf(Coin::class) + Enum::valuesOf(Coin::class) ); } @@ -196,13 +196,13 @@ public function valuesOf_string() { public function valuesOfAbstractEnum() { $this->assertEquals( [Operation::$plus, Operation::$minus, Operation::$times, Operation::$divided_by], - XPEnum::valuesOf(XPClass::forName(Operation::class)) + Enum::valuesOf(XPClass::forName(Operation::class)) ); } #[Test, Expect(IllegalArgumentException::class)] public function valuesOfNonEnum() { - XPEnum::valuesOf(self::class); + Enum::valuesOf(self::class); } #[Test] @@ -229,7 +229,7 @@ public function dividedByOperation() { public function staticMemberNotInEnumValuesOf() { $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], - XPEnum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) + Enum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) ); } @@ -243,7 +243,7 @@ public function staticMemberNotInValues() { #[Test, Expect(IllegalArgumentException::class)] public function staticMemberNotWithEnumValueOf() { - XPEnum::valueOf(XPClass::forName('net.xp_framework.unittest.core.Profiling'), 'fixture'); + Enum::valueOf(XPClass::forName('net.xp_framework.unittest.core.Profiling'), 'fixture'); } #[Test] @@ -251,7 +251,7 @@ public function staticEnumMemberNotInEnumValuesOf() { Profiling::$fixture= Coin::$penny; $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], - XPEnum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) + Enum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) ); Profiling::$fixture= null; } @@ -271,7 +271,7 @@ public function staticObjectMemberNotInEnumValuesOf() { Profiling::$fixture= $this; $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], - XPEnum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) + Enum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) ); Profiling::$fixture= null; } @@ -291,7 +291,7 @@ public function staticPrimitiveMemberNotInEnumValuesOf() { Profiling::$fixture= [$this, $this->name]; $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], - XPEnum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) + Enum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) ); Profiling::$fixture= null; } diff --git a/src/test/php/net/xp_framework/unittest/core/Operation.class.php b/src/test/php/net/xp_framework/unittest/core/Operation.class.php index 0d94e6af8c..ec1e88aa4f 100755 --- a/src/test/php/net/xp_framework/unittest/core/Operation.class.php +++ b/src/test/php/net/xp_framework/unittest/core/Operation.class.php @@ -3,7 +3,7 @@ /** * Operation enumeration */ -abstract class Operation extends \lang\XPEnum { +abstract class Operation extends \lang\Enum { public static $plus, $minus, diff --git a/src/test/php/net/xp_framework/unittest/core/Profiling.class.php b/src/test/php/net/xp_framework/unittest/core/Profiling.class.php index 568e9d772b..2fb06fec25 100755 --- a/src/test/php/net/xp_framework/unittest/core/Profiling.class.php +++ b/src/test/php/net/xp_framework/unittest/core/Profiling.class.php @@ -3,7 +3,7 @@ /** * Profiling enumeration */ -class Profiling extends \lang\XPEnum { +class Profiling extends \lang\Enum { public static $INSTANCE; public static $EXTENSION; From 2cd3fc844c4cec053e599a6940c752381e1a0701 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Wed, 10 Mar 2021 12:52:18 +0100 Subject: [PATCH 09/15] Recognize PHP 8.1 enums in XPClass::isEnum() --- src/main/php/lang/XPClass.class.php | 3 ++- .../net/xp_framework/unittest/core/EnumTest.class.php | 11 ++++++++--- .../xp_framework/unittest/core/SortOrder.class.php | 6 ++++++ 3 files changed, 16 insertions(+), 4 deletions(-) create mode 100755 src/test/php/net/xp_framework/unittest/core/SortOrder.class.php diff --git a/src/main/php/lang/XPClass.class.php b/src/main/php/lang/XPClass.class.php index 6b40c5434b..04e2af62e1 100755 --- a/src/main/php/lang/XPClass.class.php +++ b/src/main/php/lang/XPClass.class.php @@ -471,7 +471,8 @@ public function isTrait(): bool { * @return bool */ public function isEnum(): bool { - return class_exists(Enum::class, false) && $this->reflect()->isSubclassOf(Enum::class); + $r= $this->reflect(); + return $r->isSubclassOf(Enum::class) || $r->isSubclassOf(\UnitEnum::class); } /** diff --git a/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php b/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php index 00a4fe1605..3a80bb975f 100755 --- a/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php +++ b/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php @@ -1,9 +1,9 @@ assertTrue(XPClass::forName(Operation::class)->isEnum()); } + #[Test, Action(eval: 'new VerifyThat(fn() => class_exists("ReflectionEnum", false))')] + public function sortorder_is_enum() { + $this->assertTrue(XPClass::forName(SortOrder::class)->isEnum()); + } + #[Test] public function thisIsNotAnEnum() { $this->assertFalse(typeof($this)->isEnum()); diff --git a/src/test/php/net/xp_framework/unittest/core/SortOrder.class.php b/src/test/php/net/xp_framework/unittest/core/SortOrder.class.php new file mode 100755 index 0000000000..981741f75d --- /dev/null +++ b/src/test/php/net/xp_framework/unittest/core/SortOrder.class.php @@ -0,0 +1,6 @@ + Date: Wed, 10 Mar 2021 12:59:52 +0100 Subject: [PATCH 10/15] QA: Use "_" notation for test names --- .../unittest/core/EnumTest.class.php | 81 +++++++++---------- 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php b/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php index 3a80bb975f..5a6b7729b4 100755 --- a/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php +++ b/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php @@ -46,58 +46,58 @@ protected function assertNotAbstract($modifiers) { } #[Test] - public function coinIsAnEnums() { + public function coin_is_an_enum() { $this->assertTrue(XPClass::forName(Coin::class)->isEnum()); } #[Test] - public function operationIsAnEnums() { + public function operation_is_an_enum() { $this->assertTrue(XPClass::forName(Operation::class)->isEnum()); } #[Test, Action(eval: 'new VerifyThat(fn() => class_exists("ReflectionEnum", false))')] - public function sortorder_is_enum() { + public function sortorder_is_an_enum() { $this->assertTrue(XPClass::forName(SortOrder::class)->isEnum()); } #[Test] - public function thisIsNotAnEnum() { + public function this_is_not_an_enum() { $this->assertFalse(typeof($this)->isEnum()); } #[Test] - public function enumBaseClassIsAbstract() { + public function enum_base_class_is_abstract() { $this->assertAbstract(XPClass::forName(Enum::class)->getModifiers()); } #[Test] - public function operationEnumIsAbstract() { + public function operation_enum_is_abstract() { $this->assertAbstract(XPClass::forName(Operation::class)->getModifiers()); } #[Test] - public function coinEnumIsNotAbstract() { + public function coin_enum_is_not_abstract() { $this->assertNotAbstract(XPClass::forName(Coin::class)->getModifiers()); } #[Test] - public function coinMemberAreSameClass() { + public function coin_member_is_instance_of_coin() { $this->assertInstanceOf(Coin::class, Coin::$penny); } #[Test] - public function operationMembersAreSubclasses() { + public function operation_member_is_instance_of_operation() { $this->assertInstanceOf(Operation::class, Operation::$plus); } #[Test] - public function enumMembersAreNotAbstract() { + public function enum_members_are_not_abstract() { $this->assertNotAbstract(typeof(Coin::$penny)->getModifiers()); $this->assertNotAbstract(typeof(Operation::$plus)->getModifiers()); } #[Test] - public function coinValues() { + public function coin_values() { $this->assertEquals( [Coin::$penny, Coin::$nickel, Coin::$dime, Coin::$quarter], Coin::values() @@ -105,7 +105,7 @@ public function coinValues() { } #[Test] - public function operationValues() { + public function operation_values() { $this->assertEquals( [Operation::$plus, Operation::$minus, Operation::$times, Operation::$divided_by], Operation::values() @@ -113,37 +113,32 @@ public function operationValues() { } #[Test] - public function pennyCoinClass() { - $this->assertInstanceOf(Coin::class, Coin::$penny); - } - - #[Test] - public function nickelCoinName() { + public function nickel_coin_name() { $this->assertEquals('nickel', Coin::$nickel->name()); } #[Test] - public function nickelCoinValue() { + public function nickel_coin_value() { $this->assertEquals(2, Coin::$nickel->value()); } #[Test] - public function stringRepresentation() { + public function string_representation() { $this->assertEquals('dime', Coin::$dime->toString()); } #[Test] - public function sameCoinsAreEqual() { + public function same_coins_are_equal() { $this->assertEquals(Coin::$quarter, Coin::$quarter); } #[Test] - public function differentCoinsAreNotEqual() { + public function different_coins_are_not_equal() { $this->assertNotEquals(Coin::$penny, Coin::$quarter); } #[Test, Expect(CloneNotSupportedException::class)] - public function enumMembersAreNotCloneable() { + public function enum_members_cannot_be_cloned() { clone Coin::$penny; } @@ -164,17 +159,17 @@ public function valueOf_string() { } #[Test, Expect(IllegalArgumentException::class)] - public function valueOfNonExistant() { + public function valueOf_nonexistant() { Enum::valueOf(XPClass::forName(Coin::class), '@@DOES_NOT_EXIST@@'); } #[Test, Expect(IllegalArgumentException::class)] - public function valueOfNonEnum() { + public function valueOf_non_enum() { Enum::valueOf(self::class, 'irrelevant'); } #[Test] - public function valueOfAbstractEnum() { + public function valueOf_abstract_enum() { $this->assertEquals( Operation::$plus, Enum::valueOf(XPClass::forName(Operation::class), 'plus') @@ -198,7 +193,7 @@ public function valuesOf_string() { } #[Test] - public function valuesOfAbstractEnum() { + public function valuesOf_abstract_enum() { $this->assertEquals( [Operation::$plus, Operation::$minus, Operation::$times, Operation::$divided_by], Enum::valuesOf(XPClass::forName(Operation::class)) @@ -206,32 +201,32 @@ public function valuesOfAbstractEnum() { } #[Test, Expect(IllegalArgumentException::class)] - public function valuesOfNonEnum() { + public function valuesOf_non_enum() { Enum::valuesOf(self::class); } #[Test] - public function plusOperation() { + public function plus_operation() { $this->assertEquals(2, Operation::$plus->evaluate(1, 1)); } #[Test] - public function minusOperation() { + public function minus_operation() { $this->assertEquals(0, Operation::$minus->evaluate(1, 1)); } #[Test] - public function timesOperation() { + public function times_operation() { $this->assertEquals(21, Operation::$times->evaluate(7, 3)); } #[Test] - public function dividedByOperation() { + public function dividedBy_operation() { $this->assertEquals(5, Operation::$divided_by->evaluate(10, 2)); } #[Test] - public function staticMemberNotInEnumValuesOf() { + public function static_member_not_in_enum_valuesOf() { $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], Enum::valuesOf(XPClass::forName('net.xp_framework.unittest.core.Profiling')) @@ -239,7 +234,7 @@ public function staticMemberNotInEnumValuesOf() { } #[Test] - public function staticMemberNotInValues() { + public function static_member_not_in_values() { $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], Profiling::values() @@ -247,12 +242,12 @@ public function staticMemberNotInValues() { } #[Test, Expect(IllegalArgumentException::class)] - public function staticMemberNotWithEnumValueOf() { + public function static_member_not_acceptable_in_valueOf() { Enum::valueOf(XPClass::forName('net.xp_framework.unittest.core.Profiling'), 'fixture'); } #[Test] - public function staticEnumMemberNotInEnumValuesOf() { + public function static_member_with_enum_type_not_in_enum_valuesOf() { Profiling::$fixture= Coin::$penny; $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], @@ -262,7 +257,7 @@ public function staticEnumMemberNotInEnumValuesOf() { } #[Test] - public function staticEnumMemberNotInValues() { + public function static_member_with_enum_type_not_in_enum_values() { Profiling::$fixture= Coin::$penny; $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], @@ -272,7 +267,7 @@ public function staticEnumMemberNotInValues() { } #[Test] - public function staticObjectMemberNotInEnumValuesOf() { + public function static_object_member_not_in_enum_valuesOf() { Profiling::$fixture= $this; $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], @@ -282,7 +277,7 @@ public function staticObjectMemberNotInEnumValuesOf() { } #[Test] - public function staticObjectMemberNotInValues() { + public function static_object_member_not_in_enum_values() { Profiling::$fixture= $this; $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], @@ -292,7 +287,7 @@ public function staticObjectMemberNotInValues() { } #[Test] - public function staticPrimitiveMemberNotInEnumValuesOf() { + public function static_primitive_member_not_in_enum_valuesOf() { Profiling::$fixture= [$this, $this->name]; $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], @@ -302,7 +297,7 @@ public function staticPrimitiveMemberNotInEnumValuesOf() { } #[Test] - public function staticPrimitiveMemberNotInValues() { + public function static_primitive_member_not_in_enum_values() { Profiling::$fixture= [$this, $this->name]; $this->assertEquals( [Profiling::$INSTANCE, Profiling::$EXTENSION], @@ -312,7 +307,7 @@ public function staticPrimitiveMemberNotInValues() { } #[Test] - public function enumValuesMethodProvided() { + public function enum_values_method() { $this->assertEquals( [Weekday::$MON, Weekday::$TUE, Weekday::$WED, Weekday::$THU, Weekday::$FRI, Weekday::$SAT, Weekday::$SUN], Weekday::values() @@ -320,7 +315,7 @@ public function enumValuesMethodProvided() { } #[Test] - public function enumValueInitializedToDeclaration() { + public function enum_value_initialized_to_declaration() { $this->assertEquals(1, Weekday::$MON->ordinal()); } } \ No newline at end of file From 7de743f0da22fc2478304ef8f6882c776c40224d Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Wed, 10 Mar 2021 13:03:59 +0100 Subject: [PATCH 11/15] Change Enum::valuesOf() to return enum cases for PHP 8.1 enums --- src/main/php/lang/Enum.class.php | 17 ++++++----------- .../unittest/core/EnumTest.class.php | 8 ++++++++ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/main/php/lang/Enum.class.php b/src/main/php/lang/Enum.class.php index 55e4b21abc..094ee46178 100755 --- a/src/main/php/lang/Enum.class.php +++ b/src/main/php/lang/Enum.class.php @@ -48,7 +48,7 @@ public function __construct(int $ordinal= 0, string $name= '') { public static function valueOf($type, string $name): self { $class= $type instanceof XPClass ? $type : XPClass::forName($type); if (!$class->isEnum()) { - throw new IllegalArgumentException('Argument class must be lang.XPClass'); + throw new IllegalArgumentException('Argument class must be an enum'); } if ($class->isSubclassOf(self::class)) { @@ -76,22 +76,17 @@ public static function valueOf($type, string $name): self { */ public static function valuesOf($type) { $class= $type instanceof XPClass ? $type : XPClass::forName($type); - if (!$class->isEnum()) { - throw new IllegalArgumentException('Argument class must be lang.XPClass'); - } - - $r= []; if ($class->isSubclassOf(self::class)) { + $r= []; foreach ($class->reflect()->getStaticProperties() as $prop) { $class->isInstance($prop) && $r[]= $prop; } + return $r; + } else if ($class->isSubclassOf(\UnitEnum::class)) { + return $class->getMethod('cases')->invoke(null); } else { - $t= ClassLoader::defineClass($class->getName().'Enum', self::class, []); - foreach ($class->reflect()->getMethod('getValues')->invoke(null) as $name => $ordinal) { - $r[]= $t->newInstance($ordinal, $name); - } + throw new IllegalArgumentException('Argument class must be enum'); } - return $r; } /** diff --git a/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php b/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php index 5a6b7729b4..81208c640c 100755 --- a/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php +++ b/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php @@ -200,6 +200,14 @@ public function valuesOf_abstract_enum() { ); } + #[Test, Action(eval: 'new VerifyThat(fn() => class_exists("ReflectionEnum", false))')] + public function valuesOf_sortorder_() { + $this->assertEquals( + [SortOrder::ASC, SortOrder::DESC], + Enum::valuesOf(XPClass::forName(SortOrder::class)) + ); + } + #[Test, Expect(IllegalArgumentException::class)] public function valuesOf_non_enum() { Enum::valuesOf(self::class); From 5bdff4f45960fda007235a040da2d785bf9864f6 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Wed, 10 Mar 2021 13:11:24 +0100 Subject: [PATCH 12/15] Change Enum::valueOf() to return enum cases for PHP 8.1 enums --- src/main/php/lang/Enum.class.php | 29 +++++++++---------- .../unittest/core/EnumTest.class.php | 10 ++++++- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/main/php/lang/Enum.class.php b/src/main/php/lang/Enum.class.php index 094ee46178..d434165e0d 100755 --- a/src/main/php/lang/Enum.class.php +++ b/src/main/php/lang/Enum.class.php @@ -45,26 +45,22 @@ public function __construct(int $ordinal= 0, string $name= '') { * @return self * @throws lang.IllegalArgumentException in case the enum member does not exist or when the given class is not an enum */ - public static function valueOf($type, string $name): self { + public static function valueOf($type, string $name) { $class= $type instanceof XPClass ? $type : XPClass::forName($type); - if (!$class->isEnum()) { - throw new IllegalArgumentException('Argument class must be an enum'); - } - if ($class->isSubclassOf(self::class)) { - try { + try { + if ($class->isSubclassOf(self::class)) { $prop= $class->reflect()->getStaticPropertyValue($name); if ($class->isInstance($prop)) return $prop; - } catch (\ReflectionException $e) { - throw new IllegalArgumentException($e->getMessage()); - } - } else { - if ($class->reflect()->hasConstant($name)) { - $t= ClassLoader::defineClass($class->getName().'Enum', self::class, []); - return $t->newInstance($class->reflect()->getConstant($name), $name); + throw new IllegalArgumentException('No such member "'.$name.'" in '.$class->getName()); + } else if ($class->isSubclassOf(\UnitEnum::class)) { + return $class->reflect()->getConstant($name); } + } catch (\ReflectionException $e) { + throw new IllegalArgumentException($e->getMessage()); } - throw new IllegalArgumentException('No such member "'.$name.'" in '.$class->getName()); + + throw new IllegalArgumentException('Argument class must be an enum'); } /** @@ -76,6 +72,7 @@ public static function valueOf($type, string $name): self { */ public static function valuesOf($type) { $class= $type instanceof XPClass ? $type : XPClass::forName($type); + if ($class->isSubclassOf(self::class)) { $r= []; foreach ($class->reflect()->getStaticProperties() as $prop) { @@ -84,9 +81,9 @@ public static function valuesOf($type) { return $r; } else if ($class->isSubclassOf(\UnitEnum::class)) { return $class->getMethod('cases')->invoke(null); - } else { - throw new IllegalArgumentException('Argument class must be enum'); } + + throw new IllegalArgumentException('Argument class must be enum'); } /** diff --git a/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php b/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php index 81208c640c..d84a682bf8 100755 --- a/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php +++ b/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php @@ -158,6 +158,14 @@ public function valueOf_string() { ); } + #[Test, Action(eval: 'new VerifyThat(fn() => class_exists("ReflectionEnum", false))')] + public function valueOf_sortorder_enum() { + $this->assertEquals( + SortOrder::ASC, + Enum::valueOf(SortOrder::class, 'ASC') + ); + } + #[Test, Expect(IllegalArgumentException::class)] public function valueOf_nonexistant() { Enum::valueOf(XPClass::forName(Coin::class), '@@DOES_NOT_EXIST@@'); @@ -201,7 +209,7 @@ public function valuesOf_abstract_enum() { } #[Test, Action(eval: 'new VerifyThat(fn() => class_exists("ReflectionEnum", false))')] - public function valuesOf_sortorder_() { + public function valuesOf_sortorder_enum() { $this->assertEquals( [SortOrder::ASC, SortOrder::DESC], Enum::valuesOf(XPClass::forName(SortOrder::class)) From 6e1e615b30852ec36269fb10ad765c74d85088c1 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Wed, 10 Mar 2021 13:11:24 +0100 Subject: [PATCH 13/15] Change Enum::valueOf() to return enum cases for PHP 8.1 enums --- src/main/php/lang/Enum.class.php | 24 +++++++++---------- .../unittest/core/EnumTest.class.php | 15 +++++++++++- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/main/php/lang/Enum.class.php b/src/main/php/lang/Enum.class.php index 094ee46178..4783a4d81d 100755 --- a/src/main/php/lang/Enum.class.php +++ b/src/main/php/lang/Enum.class.php @@ -45,11 +45,8 @@ public function __construct(int $ordinal= 0, string $name= '') { * @return self * @throws lang.IllegalArgumentException in case the enum member does not exist or when the given class is not an enum */ - public static function valueOf($type, string $name): self { + public static function valueOf($type, string $name) { $class= $type instanceof XPClass ? $type : XPClass::forName($type); - if (!$class->isEnum()) { - throw new IllegalArgumentException('Argument class must be an enum'); - } if ($class->isSubclassOf(self::class)) { try { @@ -58,13 +55,15 @@ public static function valueOf($type, string $name): self { } catch (\ReflectionException $e) { throw new IllegalArgumentException($e->getMessage()); } - } else { - if ($class->reflect()->hasConstant($name)) { - $t= ClassLoader::defineClass($class->getName().'Enum', self::class, []); - return $t->newInstance($class->reflect()->getConstant($name), $name); - } + + throw new IllegalArgumentException('Not an enum member "'.$name.'" in '.$class->getName()); + } else if ($class->isSubclassOf(\UnitEnum::class)) { + if ($class->hasConstant($name)) return $class->getConstant($name); + + throw new IllegalArgumentException('Not such case "'.$name.'" in '.$class->getName()); } - throw new IllegalArgumentException('No such member "'.$name.'" in '.$class->getName()); + + throw new IllegalArgumentException('Argument class must be an enum'); } /** @@ -76,6 +75,7 @@ public static function valueOf($type, string $name): self { */ public static function valuesOf($type) { $class= $type instanceof XPClass ? $type : XPClass::forName($type); + if ($class->isSubclassOf(self::class)) { $r= []; foreach ($class->reflect()->getStaticProperties() as $prop) { @@ -84,9 +84,9 @@ public static function valuesOf($type) { return $r; } else if ($class->isSubclassOf(\UnitEnum::class)) { return $class->getMethod('cases')->invoke(null); - } else { - throw new IllegalArgumentException('Argument class must be enum'); } + + throw new IllegalArgumentException('Argument class must be enum'); } /** diff --git a/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php b/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php index 81208c640c..3917fdf760 100755 --- a/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php +++ b/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php @@ -158,6 +158,19 @@ public function valueOf_string() { ); } + #[Test, Action(eval: 'new VerifyThat(fn() => class_exists("ReflectionEnum", false))')] + public function valueOf_sortorder_enum() { + $this->assertEquals( + SortOrder::ASC, + Enum::valueOf(SortOrder::class, 'ASC') + ); + } + + #[Test, Expect(IllegalArgumentException::class), Action(eval: 'new VerifyThat(fn() => class_exists("ReflectionEnum", false))')] + public function valueOf_nonexistant_sortorder_enum() { + Enum::valueOf(SortOrder::class, 'ESC'); + } + #[Test, Expect(IllegalArgumentException::class)] public function valueOf_nonexistant() { Enum::valueOf(XPClass::forName(Coin::class), '@@DOES_NOT_EXIST@@'); @@ -201,7 +214,7 @@ public function valuesOf_abstract_enum() { } #[Test, Action(eval: 'new VerifyThat(fn() => class_exists("ReflectionEnum", false))')] - public function valuesOf_sortorder_() { + public function valuesOf_sortorder_enum() { $this->assertEquals( [SortOrder::ASC, SortOrder::DESC], Enum::valuesOf(XPClass::forName(SortOrder::class)) From 7779d4b1b5d8ad1275d53089b6656618739200ef Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Wed, 10 Mar 2021 13:31:04 +0100 Subject: [PATCH 14/15] Verify class parser handles T_ENUM tokens correctly --- src/main/php/lang.base.php | 1 + src/main/php/lang/reflect/ClassParser.class.php | 3 +-- .../php/net/xp_framework/unittest/core/EnumTest.class.php | 5 +++++ .../php/net/xp_framework/unittest/core/SortOrder.class.php | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/php/lang.base.php b/src/main/php/lang.base.php index 0ec75ab120..9428fd47a3 100755 --- a/src/main/php/lang.base.php +++ b/src/main/php/lang.base.php @@ -449,6 +449,7 @@ function enum_exists($name, $load) { defined('T_ATTRIBUTE') || define('T_ATTRIBUTE', -383); defined('T_NAME_FULLY_QUALIFIED') || define('T_NAME_FULLY_QUALIFIED', -312); defined('T_NAME_QUALIFIED') || define('T_NAME_QUALIFIED', -314); +defined('T_ENUM') || define('T_ENUM', -369); xp::$loader= new xp(); diff --git a/src/main/php/lang/reflect/ClassParser.class.php b/src/main/php/lang/reflect/ClassParser.class.php index ceeb697d0c..7151eccfb8 100755 --- a/src/main/php/lang/reflect/ClassParser.class.php +++ b/src/main/php/lang/reflect/ClassParser.class.php @@ -562,8 +562,7 @@ public function parseDetails($bytes, $context= '') { case T_CLASS: if (isset($details['class'])) break; // Inside class, e.g. $lookup= ['self' => self::class] - case T_INTERFACE: - case T_TRAIT: + case T_INTERFACE: case T_TRAIT: case T_ENUM: if ($parsed) { $annotations= $this->parseAnnotations($parsed, $context, $imports, $tokens[$i][2] ?? -1); $parsed= ''; diff --git a/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php b/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php index 3917fdf760..388fd1a06d 100755 --- a/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php +++ b/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php @@ -339,4 +339,9 @@ public function enum_values_method() { public function enum_value_initialized_to_declaration() { $this->assertEquals(1, Weekday::$MON->ordinal()); } + + #[Test, Action(eval: 'new VerifyThat(fn() => class_exists("ReflectionEnum", false))')] + public function annotations_on_sortorder_enum() { + $this->assertEquals(['usedBy' => self::class], XPClass::forName(SortOrder::class)->getAnnotations()); + } } \ No newline at end of file diff --git a/src/test/php/net/xp_framework/unittest/core/SortOrder.class.php b/src/test/php/net/xp_framework/unittest/core/SortOrder.class.php index 981741f75d..0e16063852 100755 --- a/src/test/php/net/xp_framework/unittest/core/SortOrder.class.php +++ b/src/test/php/net/xp_framework/unittest/core/SortOrder.class.php @@ -1,5 +1,6 @@ Date: Wed, 10 Mar 2021 18:21:30 +0100 Subject: [PATCH 15/15] QA: Update references in class api doc --- .../unittest/core/EnumTest.class.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php b/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php index 388fd1a06d..d1214aa794 100755 --- a/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php +++ b/src/test/php/net/xp_framework/unittest/core/EnumTest.class.php @@ -3,19 +3,20 @@ use lang\reflect\Modifiers; use lang\{CloneNotSupportedException, Enum, Error, IllegalArgumentException, XPClass, ClassLoader}; use unittest\actions\{RuntimeVersion, VerifyThat}; -use unittest\{Action, Expect, Test}; +use unittest\{Action, Expect, Test, TestCase}; /** * TestCase for enumerations * - * @see xp://net.xp_framework.unittest.core.Coin - * @see xp://net.xp_framework.unittest.core.Operation - * @see xp://net.xp_framework.unittest.core.Weekday - * @see xp://lang.Enum - * @see xp://lang.XPClass#isEnum - * @see http://xp-framework.net/rfc/0132 + * @see net.xp_framework.unittest.core.Coin + * @see net.xp_framework.unittest.core.Operation + * @see net.xp_framework.unittest.core.Weekday + * @see net.xp_framework.unittest.core.SortOrder + * @see lang.Enum + * @see lang.XPClass#isEnum + * @see https://github.com/xp-framework/rfc/issues/132 */ -class EnumTest extends \unittest\TestCase { +class EnumTest extends TestCase { /** * Asserts given modifiers contain abstract