Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/main/php/lang.base.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -438,6 +449,7 @@ function __construct($str) {
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();

Expand Down
5 changes: 3 additions & 2 deletions src/main/php/lang/AbstractClassLoader.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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);
Expand Down
35 changes: 15 additions & 20 deletions src/main/php/lang/Enum.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 lang.XPClass<? extends lang.Enum>');
}

if ($class->isSubclassOf(self::class)) {
try {
Expand All @@ -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');
}

/**
Expand All @@ -76,22 +75,18 @@ 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<? extends lang.Enum>');
}

$r= [];
if ($class->isSubclassOf(self::class)) {
$r= [];
foreach ($class->reflect()->getStaticProperties() as $prop) {
$class->isInstance($prop) && $r[]= $prop;
}
} else {
$t= ClassLoader::defineClass($class->getName().'Enum', self::class, []);
foreach ($class->reflect()->getMethod('getValues')->invoke(null) as $name => $ordinal) {
$r[]= $t->newInstance($ordinal, $name);
}
return $r;
} else if ($class->isSubclassOf(\UnitEnum::class)) {
return $class->getMethod('cases')->invoke(null);
}
return $r;

throw new IllegalArgumentException('Argument class must be enum');
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/main/php/lang/GenericTypes.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions src/main/php/lang/XPClass.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -800,7 +801,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);
Expand Down
5 changes: 2 additions & 3 deletions src/main/php/lang/reflect/ClassParser.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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= '';
Expand Down
Loading