diff --git a/src/Enum.php b/src/Enum.php index 3790f6e..b665b0c 100644 --- a/src/Enum.php +++ b/src/Enum.php @@ -28,6 +28,13 @@ abstract class Enum implements \JsonSerializable */ protected $value; + /** + * Enum key, the constant name + * + * @var string + */ + private $key; + /** * Store existing constants in a static cache per object. * @@ -61,12 +68,19 @@ public function __construct($value) $value = $value->getValue(); } - static::assertValidValue($value); + $this->key = static::assertValidValueReturningKey($value); /** @psalm-var T */ $this->value = $value; } + public function __wakeup() + { + if ($this->key === null) { + $this->key = static::search($this->value); + } + } + /** * @param mixed $value * @return static @@ -74,9 +88,9 @@ public function __construct($value) */ public static function from($value): self { - static::assertValidValue($value); + $key = static::assertValidValueReturningKey($value); - return new static($value); + return self::__callStatic($key, []); } /** @@ -93,11 +107,11 @@ public function getValue() * Returns the enum key (i.e. the constant name). * * @psalm-pure - * @return mixed + * @return string */ public function getKey() { - return static::search($this->value); + return $this->key; } /** @@ -201,9 +215,22 @@ public static function isValid($value) */ public static function assertValidValue($value): void { - if (!static::isValid($value)) { + self::assertValidValueReturningKey($value); + } + + /** + * Asserts valid enum value + * + * @psalm-pure + * @psalm-assert T $value + */ + private static function assertValidValueReturningKey($value): string + { + if (false === ($key = static::search($value))) { throw new \UnexpectedValueException("Value '$value' is not part of the enum " . static::class); } + + return $key; } /** @@ -224,11 +251,11 @@ public static function isValidKey($key) /** * Return key for value * - * @param $value + * @param mixed $value * * @psalm-param mixed $value * @psalm-pure - * @return mixed + * @return string|false */ public static function search($value) { diff --git a/tests/EnumTest.php b/tests/EnumTest.php index 674b977..154b8ba 100755 --- a/tests/EnumTest.php +++ b/tests/EnumTest.php @@ -60,6 +60,14 @@ public function testFailToCreateEnumWithInvalidValueThroughNamedConstructor($val EnumFixture::from($value); } + public function testFailToCreateEnumWithEnumItselfThroughNamedConstructor(): void + { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessage("Value 'foo' is not part of the enum " . EnumFixture::class); + + EnumFixture::from(EnumFixture::FOO()); + } + /** * Contains values not existing in EnumFixture * @return array @@ -316,12 +324,13 @@ public function testSerialize() { // split string for Pretty CI: "Line exceeds 120 characters" $bin = '4f3a33303a224d79434c6162735c54657374735c456e756d5c456e756d4669787'. - '4757265223a313a7b733a383a22002a0076616c7565223b733a333a22666f6f223b7d'; + '4757265223a323a7b733a383a22002a0076616c7565223b733a333a22666f6f223b73'. + '3a32323a22004d79434c6162735c456e756d5c456e756d006b6579223b733a333a22464f4f223b7d'; $this->assertEquals($bin, bin2hex(serialize(EnumFixture::FOO()))); } - public function testUnserialize() + public function testUnserializeVersionWithoutKey() { // split string for Pretty CI: "Line exceeds 120 characters" $bin = '4f3a33303a224d79434c6162735c54657374735c456e756d5c456e756d4669787'. @@ -332,6 +341,22 @@ public function testUnserialize() $this->assertEquals(EnumFixture::FOO, $value->getValue()); $this->assertTrue(EnumFixture::FOO()->equals($value)); + $this->assertTrue(EnumFixture::FOO() == $value); + } + + public function testUnserialize() + { + // split string for Pretty CI: "Line exceeds 120 characters" + $bin = '4f3a33303a224d79434c6162735c54657374735c456e756d5c456e756d4669787'. + '4757265223a323a7b733a383a22002a0076616c7565223b733a333a22666f6f223b73'. + '3a32323a22004d79434c6162735c456e756d5c456e756d006b6579223b733a333a22464f4f223b7d'; + + /* @var $value EnumFixture */ + $value = unserialize(pack('H*', $bin)); + + $this->assertEquals(EnumFixture::FOO, $value->getValue()); + $this->assertTrue(EnumFixture::FOO()->equals($value)); + $this->assertTrue(EnumFixture::FOO() == $value); } /**