diff --git a/README.md b/README.md index d2b23a5..f9a987e 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,8 @@ composer require k2gl/enum ## Usage ```php -use K2gl\Enum\src\ExtendedBackedEnumInterface;use K2gl\Enum\Types\ExtendedBackedEnum\ExtendedBackedEnum; +use K2gl\Enum\ExtendedBackedEnum; +use K2gl\Enum\ExtendedBackedEnumInterface; enum CardSuit: string implements ExtendedBackedEnumInterface { @@ -54,6 +55,13 @@ $suit->isNot(ResponseCode::HTTP_I_AM_A_TEAPOT); // false CardSuit::names(); // ['HEARTS', 'DIAMONDS', 'CLUBS', 'SPADES'] CardSuit::values(); // ['hearts', 'diamonds', 'clubs', 'spades'] + +// Resolve a case by its name — the counterpart of the native from()/tryFrom(), +// which only resolve by backing value. +CardSuit::fromName('SPADES'); // CardSuit::SPADES +CardSuit::tryFromName('SPADES'); // CardSuit::SPADES +CardSuit::tryFromName('joker'); // null +CardSuit::fromName('joker'); // throws \ValueError ``` ## Pull requests are always welcome diff --git a/phpstan.neon b/phpstan.neon index 3b5158e..9ec7a73 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ parameters: - level: 6 + level: 9 paths: - src - tests/Examples diff --git a/src/ExtendedBackedEnum.php b/src/ExtendedBackedEnum.php index 29b46c0..54eb141 100644 --- a/src/ExtendedBackedEnum.php +++ b/src/ExtendedBackedEnum.php @@ -44,6 +44,31 @@ public static function anyoneExcept(BackedEnum|array $except): static return self::from($values[\array_rand($values)]); } + /** + * Resolve a case by its name, mirroring the native from() which only + * resolves by backing value. + * + * @throws ValueError when no case has the given name + */ + public static function fromName(string $name): static + { + return self::tryFromName($name) + ?? throw new ValueError( + sprintf('"%s" is not a valid name for enum %s', $name, self::class) + ); + } + + public static function tryFromName(string $name): ?static + { + foreach (self::cases() as $case) { + if ($case->name === $name) { + return $case; + } + } + + return null; + } + public function is(mixed $value): bool { if ($value instanceof BackedEnum) { diff --git a/src/ExtendedBackedEnumInterface.php b/src/ExtendedBackedEnumInterface.php index f21c8d9..8ca4658 100644 --- a/src/ExtendedBackedEnumInterface.php +++ b/src/ExtendedBackedEnumInterface.php @@ -5,6 +5,7 @@ namespace K2gl\Enum; use BackedEnum; +use ValueError; interface ExtendedBackedEnumInterface extends BackedEnum { @@ -15,6 +16,13 @@ public static function any(): static; */ public static function anyoneExcept(BackedEnum|array $except): static; + /** + * @throws ValueError when no case has the given name + */ + public static function fromName(string $name): static; + + public static function tryFromName(string $name): ?static; + public function is(mixed $value): bool; public function isNot(mixed $value): bool; diff --git a/tests/ExtendedBackedEnum/FromNameTest.php b/tests/ExtendedBackedEnum/FromNameTest.php new file mode 100644 index 0000000..b402bfc --- /dev/null +++ b/tests/ExtendedBackedEnum/FromNameTest.php @@ -0,0 +1,58 @@ +is($expected); + } + + #[DataProvider('unknownNameDataProvider')] + public function testThrowsOnUnknownName(string $enumClass, string $name): void + { + // assert + $this->expectException(ValueError::class); + + // act + $enumClass::fromName($name); + } + + public static function validNameDataProvider(): array + { + return [ + ['HEARTS', CardSuit::HEARTS], + ['SPADES', CardSuit::SPADES], + ['HTTP_OK', ResponseCode::HTTP_OK], + ['HTTP_I_AM_A_TEAPOT', ResponseCode::HTTP_I_AM_A_TEAPOT], + ]; + } + + public static function unknownNameDataProvider(): array + { + return [ + 'missing name' => [CardSuit::class, 'JOKER'], + 'wrong case' => [CardSuit::class, 'hearts'], + 'backing value' => [CardSuit::class, 'spades'], + 'empty string' => [CardSuit::class, ''], + 'int enum, missing' => [ResponseCode::class, 'HTTP_TEAPOT'], + ]; + } +} diff --git a/tests/ExtendedBackedEnum/TryFromNameTest.php b/tests/ExtendedBackedEnum/TryFromNameTest.php new file mode 100644 index 0000000..f0157da --- /dev/null +++ b/tests/ExtendedBackedEnum/TryFromNameTest.php @@ -0,0 +1,57 @@ +is($expected); + } + + #[DataProvider('unknownNameDataProvider')] + public function testReturnsNullOnUnknownName(string $enumClass, string $name): void + { + // act + $case = $enumClass::tryFromName($name); + + // assert + fact($case)->null(); + } + + public static function validNameDataProvider(): array + { + return [ + ['HEARTS', CardSuit::HEARTS], + ['SPADES', CardSuit::SPADES], + ['HTTP_OK', ResponseCode::HTTP_OK], + ['HTTP_I_AM_A_TEAPOT', ResponseCode::HTTP_I_AM_A_TEAPOT], + ]; + } + + public static function unknownNameDataProvider(): array + { + return [ + 'missing name' => [CardSuit::class, 'JOKER'], + 'wrong case' => [CardSuit::class, 'hearts'], + 'backing value' => [CardSuit::class, 'spades'], + 'empty string' => [CardSuit::class, ''], + 'int enum, missing' => [ResponseCode::class, 'HTTP_TEAPOT'], + ]; + } +}