diff --git a/example/php7.4/Genre.php b/example/php7.4/Genre.php index 45a7e78..ff920e7 100644 --- a/example/php7.4/Genre.php +++ b/example/php7.4/Genre.php @@ -14,12 +14,29 @@ * * --- * + * Readonly properties: * @property-read string $name * @property-read string $value + * + * Cases: + * @method static Genre ROMANCE + * @method static Genre COMEDY + * @method static Genre DRAMA + * @method static Genre NON_FICTION + * @method static Genre SCIENTIFIC_WORK */ final class Genre implements \JsonSerializable { - private static ?array $map; + private static array $instances = []; + + private static array $cases = [ + 'ROMANCE' => 'romance', + 'COMEDY' => 'comedy', + 'DRAMA' => 'drama', + 'NON_FICTION' => 'non-fiction', + 'SCIENTIFIC_WORK' => 'scientific-work', + ]; + private string $name; private string $value; @@ -34,13 +51,7 @@ private function __construct(string $name, string $value) */ public static function cases(): array { - return self::$map = self::$map ?? [ - new self('ROMANCE', 'romance'), - new self('COMEDY', 'comedy'), - new self('DRAMA', 'drama'), - new self('NON_FICTION', 'non-fiction'), - new self('SCIENTIFIC_WORK', 'scientific-work'), - ]; + return [self::ROMANCE(), self::COMEDY(), self::DRAMA(), self::NON_FICTION(), self::SCIENTIFIC_WORK()]; } public function __get($propertyName) @@ -56,10 +67,22 @@ public function __get($propertyName) } } + public static function __callStatic($name, $args) + { + $instance = self::$instances[$name] ?? null; + if ($instance === null) { + if (!array_key_exists($name, self::$cases)) { + throw new \ValueError("unknown case '$name'"); + } + self::$instances[$name] = $instance = new self($name, self::$cases[$name]); + } + return $instance; + } + public static function tryFrom(string $value): ?self { - $cases = self::cases(); - return $cases[$value] ?? null; + $case = array_search($value, self::$cases, true); + return $case ? self::$case() : null; } public static function from(string $value): self @@ -74,31 +97,6 @@ public static function from(string $value): self return $case; } - public static function ROMANCE(): self - { - return self::from('romance'); - } - - public static function COMEDY(): self - { - return self::from('comedy'); - } - - public static function DRAMA(): self - { - return self::from('drama'); - } - - public static function NON_FICTION(): self - { - return self::from('non-fiction'); - } - - public static function SCIENTIFIC_WORK(): self - { - return self::from('scientific-work'); - } - public function jsonSerialize(): string { return $this->value; diff --git a/src/Generator/Builder/EnumLegacyBuilder.php b/src/Generator/Builder/EnumLegacyBuilder.php index 79448c1..a39934a 100644 --- a/src/Generator/Builder/EnumLegacyBuilder.php +++ b/src/Generator/Builder/EnumLegacyBuilder.php @@ -16,14 +16,24 @@ public function build(Enum $enum, PhpNamespace $ns): EnumType|ClassType $class = $ns->addClass($enum->getShortName())->setFinal(); $class + ->addComment('Readonly properties:') ->addComment("@property-read string \$name") - ->addComment("@property-read {$enum->getBackedType()} \$value"); + ->addComment("@property-read {$enum->getBackedType()} \$value") + ->addComment("") + ->addComment("Cases:"); - $class->addProperty('map') + + $class->addProperty('instances') ->setStatic() ->setPrivate() - ->setNullable() - ->setType('array'); + ->setType('array') + ->setValue([]); + + $class->addProperty('cases') + ->setStatic() + ->setPrivate() + ->setType('array') + ->setValue($enum->getCases()); $class->addProperty('name') ->setPrivate() @@ -63,7 +73,20 @@ public function build(Enum $enum, PhpNamespace $ns): EnumType|ClassType ->addBody(' return null;') ->addBody('}'); - // ::tryFrom(value) + $class + ->addMethod('__callStatic') + ->setParameters([ new Parameter('name'), new Parameter('args') ]) + ->setStatic() + ->setPublic() + ->addBody('$instance = self::$instances[$name] ?? null;') + ->addBody('if ($instance === null) {') + ->addBody(' if (!array_key_exists($name, self::$cases)) {') + ->addBody(' throw new \ValueError("unknown case \'$name\'");') + ->addBody(' }') + ->addBody(' self::$instances[$name] = $instance = new self($name, self::$cases[$name]);') + ->addBody('}') + ->addBody('return $instance;'); + $class->addMethod('tryFrom') ->setStatic() ->setPublic() @@ -72,10 +95,9 @@ public function build(Enum $enum, PhpNamespace $ns): EnumType|ClassType ->setParameters([ (new Parameter('value'))->setType($enum->getBackedType()) ]) - ->addBody('$cases = self::cases();') - ->addBody('return $cases[$value] ?? null;'); + ->addBody('$case = array_search($value, self::$cases, true);') + ->addBody('return $case ? self::$case() : null;'); - // ::from($value) $class->addMethod('from') ->setStatic() ->setPublic() @@ -94,16 +116,11 @@ public function build(Enum $enum, PhpNamespace $ns): EnumType|ClassType $casesMap = []; foreach ($enum->getCases() as $case => $value) { - $class->addMethod($case) - ->setStatic() - ->setPublic() - ->setReturnType('self') - ->addBody('return self::from(?);', [$value]); - - $casesMap[] = new Literal('new self(?, ?)', [$case, $value]); + $class->addComment("@method static {$enum->getShortName()} $case"); + $casesMap[] = new Literal("self::$case()"); } - $casesMethod->addBody('return self::$map = self::$map \?\? ?;', [$casesMap]); + $casesMethod->addBody('return ?;', [$casesMap]); $class->addImplement('\\JsonSerializable'); $class->addMethod('jsonSerialize')