From 86949d6e7c3f0e4c10a36ac07e147dfe77cf482b Mon Sep 17 00:00:00 2001 From: Simon Podlipsky Date: Wed, 19 Aug 2020 08:59:17 +0200 Subject: [PATCH 1/6] Add getKey() return type as string --- src/Enum.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Enum.php b/src/Enum.php index f5a5940..e239d5b 100644 --- a/src/Enum.php +++ b/src/Enum.php @@ -76,9 +76,8 @@ public function getValue() * Returns the enum key (i.e. the constant name). * * @psalm-pure - * @return mixed */ - public function getKey() + public function getKey(): string { return static::search($this->value); } @@ -193,11 +192,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) { From 0ad4363522ecad3ff2a3f4c46a967c8f3f30be56 Mon Sep 17 00:00:00 2001 From: Alexandru Patranescu Date: Mon, 15 Feb 2021 00:04:55 +0200 Subject: [PATCH 2/6] make assertValidValue private as it's not valuable to extend the API. One can programatically call `isValid()` returning bool or just create a new instance with `from()` throwing exception --- src/Enum.php | 2 +- tests/EnumTest.php | 23 ++++++++--------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/Enum.php b/src/Enum.php index 19318b9..cfb4bd8 100644 --- a/src/Enum.php +++ b/src/Enum.php @@ -198,7 +198,7 @@ public static function isValid($value) * @psalm-pure * @psalm-assert T $value */ - public static function assertValidValue($value): void + private static function assertValidValue($value): void { if (!static::isValid($value)) { throw new \UnexpectedValueException("Value '$value' is not part of the enum " . static::class); diff --git a/tests/EnumTest.php b/tests/EnumTest.php index 674b977..8ad82ea 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 @@ -344,19 +352,4 @@ public function testEnumValuesInheritance() $inheritedEnumFixture = InheritedEnumFixture::VALUE(); new EnumFixture($inheritedEnumFixture); } - - /** - * @dataProvider isValidProvider - */ - public function testAssertValidValue($value, $isValid): void - { - if (!$isValid) { - $this->expectException(\UnexpectedValueException::class); - $this->expectExceptionMessage("Value '$value' is not part of the enum " . EnumFixture::class); - } - - EnumFixture::assertValidValue($value); - - self::assertTrue(EnumFixture::isValid($value)); - } } From edcc10dce18980607f07b46b0539cb306053fb5e Mon Sep 17 00:00:00 2001 From: Alexandru Patranescu Date: Mon, 15 Feb 2021 00:22:01 +0200 Subject: [PATCH 3/6] cache the key value at construct changing from in_array to array_search --- src/Enum.php | 19 ++++++++++++++----- tests/EnumTest.php | 3 ++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/Enum.php b/src/Enum.php index cfb4bd8..ca6aef0 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,7 +68,7 @@ public function __construct($value) $value = $value->getValue(); } - static::assertValidValue($value); + $this->key = static::assertValidValue($value); /** @psalm-var T */ $this->value = $value; @@ -94,9 +101,9 @@ public function getValue() * * @psalm-pure */ - public function getKey(): string + public function getKey() { - return static::search($this->value); + return $this->key ?? ($this->key = static::search($this->value)); } /** @@ -198,11 +205,13 @@ public static function isValid($value) * @psalm-pure * @psalm-assert T $value */ - private static function assertValidValue($value): void + private static function assertValidValue($value): string { - if (!static::isValid($value)) { + if (false === ($key = static::search($value))) { throw new \UnexpectedValueException("Value '$value' is not part of the enum " . static::class); } + + return $key; } /** diff --git a/tests/EnumTest.php b/tests/EnumTest.php index 8ad82ea..bbe848a 100755 --- a/tests/EnumTest.php +++ b/tests/EnumTest.php @@ -324,7 +324,8 @@ public function testSerialize() { // split string for Pretty CI: "Line exceeds 120 characters" $bin = '4f3a33303a224d79434c6162735c54657374735c456e756d5c456e756d4669787'. - '4757265223a313a7b733a383a22002a0076616c7565223b733a333a22666f6f223b7d'; + '4757265223a323a7b733a383a22002a0076616c7565223b733a333a22666f6f223b73'. + '3a32323a22004d79434c6162735c456e756d5c456e756d006b6579223b733a333a22464f4f223b7d'; $this->assertEquals($bin, bin2hex(serialize(EnumFixture::FOO()))); } From 4f2c5723a0b08d73215afa1a28bbb711216fcd00 Mon Sep 17 00:00:00 2001 From: Alexandru Patranescu Date: Mon, 15 Feb 2021 09:57:55 +0200 Subject: [PATCH 4/6] move key property initialization in __wakeup in case an old version is unserialized --- src/Enum.php | 10 +++++++++- tests/EnumTest.php | 18 +++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/Enum.php b/src/Enum.php index ca6aef0..9b845c2 100644 --- a/src/Enum.php +++ b/src/Enum.php @@ -74,6 +74,13 @@ public function __construct($value) $this->value = $value; } + public function __wakeup() + { + if ($this->key === null) { + $this->key = static::search($this->value); + } + } + /** * @param mixed $value * @return static @@ -100,10 +107,11 @@ public function getValue() * Returns the enum key (i.e. the constant name). * * @psalm-pure + * @return string */ public function getKey() { - return $this->key ?? ($this->key = static::search($this->value)); + return $this->key; } /** diff --git a/tests/EnumTest.php b/tests/EnumTest.php index bbe848a..cdf472d 100755 --- a/tests/EnumTest.php +++ b/tests/EnumTest.php @@ -330,7 +330,7 @@ public function testSerialize() $this->assertEquals($bin, bin2hex(serialize(EnumFixture::FOO()))); } - public function testUnserialize() + public function testUnserializeVersionWithoutKey() { // split string for Pretty CI: "Line exceeds 120 characters" $bin = '4f3a33303a224d79434c6162735c54657374735c456e756d5c456e756d4669787'. @@ -341,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); } /** From b072dc299510ab06b3472b541f1b5f342ae7beb4 Mon Sep 17 00:00:00 2001 From: Alexandru Patranescu Date: Mon, 15 Feb 2021 11:17:08 +0200 Subject: [PATCH 5/6] re-add the public static method assertValidValue --- src/Enum.php | 15 +++++++++++++-- tests/EnumTest.php | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/Enum.php b/src/Enum.php index 9b845c2..19cc275 100644 --- a/src/Enum.php +++ b/src/Enum.php @@ -68,7 +68,7 @@ public function __construct($value) $value = $value->getValue(); } - $this->key = static::assertValidValue($value); + $this->key = static::assertValidValueReturningKey($value); /** @psalm-var T */ $this->value = $value; @@ -213,7 +213,18 @@ public static function isValid($value) * @psalm-pure * @psalm-assert T $value */ - private static function assertValidValue($value): string + public static function assertValidValue($value): void + { + 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); diff --git a/tests/EnumTest.php b/tests/EnumTest.php index cdf472d..154b8ba 100755 --- a/tests/EnumTest.php +++ b/tests/EnumTest.php @@ -369,4 +369,19 @@ public function testEnumValuesInheritance() $inheritedEnumFixture = InheritedEnumFixture::VALUE(); new EnumFixture($inheritedEnumFixture); } + + /** + * @dataProvider isValidProvider + */ + public function testAssertValidValue($value, $isValid): void + { + if (!$isValid) { + $this->expectException(\UnexpectedValueException::class); + $this->expectExceptionMessage("Value '$value' is not part of the enum " . EnumFixture::class); + } + + EnumFixture::assertValidValue($value); + + self::assertTrue(EnumFixture::isValid($value)); + } } From be02f8d94563890bd3ec094db5fbb8da24b0f95c Mon Sep 17 00:00:00 2001 From: Alexandru Patranescu Date: Mon, 15 Feb 2021 11:26:04 +0200 Subject: [PATCH 6/6] optimize the from named constructor to have only one array full scan without calling the constructor again --- src/Enum.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Enum.php b/src/Enum.php index 19cc275..b665b0c 100644 --- a/src/Enum.php +++ b/src/Enum.php @@ -88,9 +88,9 @@ public function __wakeup() */ public static function from($value): self { - static::assertValidValue($value); + $key = static::assertValidValueReturningKey($value); - return new static($value); + return self::__callStatic($key, []); } /**