Skip to content

Improve getKey method performance #136

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 15, 2021
43 changes: 35 additions & 8 deletions src/Enum.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -61,22 +68,29 @@ 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
* @psalm-return static<T>
*/
public static function from($value): self
{
static::assertValidValue($value);
$key = static::assertValidValueReturningKey($value);

return new static($value);
return self::__callStatic($key, []);
}

/**
Expand All @@ -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;
}

/**
Expand Down Expand Up @@ -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;
}

/**
Expand All @@ -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)
{
Expand Down
29 changes: 27 additions & 2 deletions tests/EnumTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Copy link
Contributor Author

@drealecs drealecs Feb 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added one more test here to clearly mention from() should not work with the enum object itself, even if the exception text is unclear that 'foo' there is the actual object casted to string.

}

/**
* Contains values not existing in EnumFixture
* @return array
Expand Down Expand Up @@ -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'.
Expand All @@ -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);
}

/**
Expand Down