Skip to content

Commit 8d8c6ec

Browse files
committed
Merge branch 'master' into improve_getKey_performance
2 parents 86949d6 + 9fcffe3 commit 8d8c6ec

File tree

2 files changed

+72
-10
lines changed

2 files changed

+72
-10
lines changed

src/Enum.php

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ abstract class Enum implements \JsonSerializable
3737
*/
3838
protected static $cache = [];
3939

40+
/**
41+
* Cache of instances of the Enum class
42+
*
43+
* @var array
44+
* @psalm-var array<class-string, array<string, static>>
45+
*/
46+
protected static $instances = [];
47+
4048
/**
4149
* Creates a new value of some type
4250
*
@@ -53,15 +61,24 @@ public function __construct($value)
5361
$value = $value->getValue();
5462
}
5563

56-
if (!$this->isValid($value)) {
57-
/** @psalm-suppress InvalidCast */
58-
throw new \UnexpectedValueException("Value '$value' is not part of the enum " . static::class);
59-
}
64+
static::assertValidValue($value);
6065

6166
/** @psalm-var T */
6267
$this->value = $value;
6368
}
6469

70+
/**
71+
* @param mixed $value
72+
* @return static
73+
* @psalm-return static<T>
74+
*/
75+
public static function from($value): self
76+
{
77+
static::assertValidValue($value);
78+
79+
return new static($value);
80+
}
81+
6582
/**
6683
* @psalm-pure
6784
* @return mixed
@@ -167,13 +184,27 @@ public static function toArray()
167184
* @param $value
168185
* @psalm-param mixed $value
169186
* @psalm-pure
187+
* @psalm-assert-if-true T $value
170188
* @return bool
171189
*/
172190
public static function isValid($value)
173191
{
174192
return \in_array($value, static::toArray(), true);
175193
}
176194

195+
/**
196+
* Asserts valid enum value
197+
*
198+
* @psalm-pure
199+
* @psalm-assert T $value
200+
*/
201+
public static function assertValidValue($value): void
202+
{
203+
if (!static::isValid($value)) {
204+
throw new \UnexpectedValueException("Value '$value' is not part of the enum " . static::class);
205+
}
206+
}
207+
177208
/**
178209
* Check if is valid enum key
179210
*
@@ -210,17 +241,20 @@ public static function search($value)
210241
* @param array $arguments
211242
*
212243
* @return static
213-
* @psalm-pure
214244
* @throws \BadMethodCallException
215245
*/
216246
public static function __callStatic($name, $arguments)
217247
{
218-
$array = static::toArray();
219-
if (isset($array[$name]) || \array_key_exists($name, $array)) {
220-
return new static($array[$name]);
248+
$class = static::class;
249+
if (!isset(self::$instances[$class][$name])) {
250+
$array = static::toArray();
251+
if (!isset($array[$name]) && !\array_key_exists($name, $array)) {
252+
$message = "No static method or enum constant '$name' in class " . static::class;
253+
throw new \BadMethodCallException($message);
254+
}
255+
return self::$instances[$class][$name] = new static($array[$name]);
221256
}
222-
223-
throw new \BadMethodCallException("No static method or enum constant '$name' in class " . static::class);
257+
return clone self::$instances[$class][$name];
224258
}
225259

226260
/**

tests/EnumTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ public function testCreatingEnumWithInvalidValue($value)
4848
new EnumFixture($value);
4949
}
5050

51+
/**
52+
* @dataProvider invalidValueProvider
53+
* @param mixed $value
54+
*/
55+
public function testFailToCreateEnumWithInvalidValueThroughNamedConstructor($value): void
56+
{
57+
$this->expectException(\UnexpectedValueException::class);
58+
$this->expectExceptionMessage('is not part of the enum MyCLabs\Tests\Enum\EnumFixture');
59+
60+
EnumFixture::from($value);
61+
}
62+
5163
/**
5264
* Contains values not existing in EnumFixture
5365
* @return array
@@ -143,6 +155,7 @@ public function testStaticAccess()
143155
$this->assertEquals(new EnumFixture(EnumFixture::FOO), EnumFixture::FOO());
144156
$this->assertEquals(new EnumFixture(EnumFixture::BAR), EnumFixture::BAR());
145157
$this->assertEquals(new EnumFixture(EnumFixture::NUMBER), EnumFixture::NUMBER());
158+
$this->assertNotSame(EnumFixture::NUMBER(), EnumFixture::NUMBER());
146159
}
147160

148161
/**
@@ -331,4 +344,19 @@ public function testEnumValuesInheritance()
331344
$inheritedEnumFixture = InheritedEnumFixture::VALUE();
332345
new EnumFixture($inheritedEnumFixture);
333346
}
347+
348+
/**
349+
* @dataProvider isValidProvider
350+
*/
351+
public function testAssertValidValue($value, $isValid): void
352+
{
353+
if (!$isValid) {
354+
$this->expectException(\UnexpectedValueException::class);
355+
$this->expectExceptionMessage("Value '$value' is not part of the enum " . EnumFixture::class);
356+
}
357+
358+
EnumFixture::assertValidValue($value);
359+
360+
self::assertTrue(EnumFixture::isValid($value));
361+
}
334362
}

0 commit comments

Comments
 (0)