Skip to content

Commit b40ae5e

Browse files
authored
Merge pull request #89 from marc-mabe/optimize_byOrdinal
Optimized Enum::byOrdinal() and Enum::getName[s]()
2 parents 65aaeac + 77e489e commit b40ae5e

File tree

2 files changed

+81
-12
lines changed

2 files changed

+81
-12
lines changed

src/Enum.php

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,19 @@ abstract class Enum
3030
private $ordinal;
3131

3232
/**
33-
* An array of available constants by class
33+
* A map of enumerator names and values by enumeration class
3434
*
3535
* @var array ["$class" => ["$name" => $value, ...], ...]
3636
*/
3737
private static $constants = array();
3838

39+
/**
40+
* A List of of available enumerator names by enumeration class
41+
*
42+
* @var array ["$class" => ["$name0", ...], ...]
43+
*/
44+
private static $names = array();
45+
3946
/**
4047
* Already instantiated enumerators
4148
*
@@ -110,6 +117,9 @@ final public function getValue()
110117
*/
111118
final public function getName()
112119
{
120+
if ($this->ordinal !== null) {
121+
return self::$names[static::class][$this->ordinal];
122+
}
113123
return array_search($this->value, self::detectConstants(static::class), true);
114124
}
115125

@@ -235,21 +245,25 @@ final public static function byOrdinal($ordinal)
235245
{
236246
$ordinal = (int) $ordinal;
237247
$class = static::class;
238-
$constants = self::detectConstants($class);
239-
$item = array_slice($constants, $ordinal, 1, true);
240-
if (empty($item)) {
248+
249+
if (!isset(self::$names[$class])) {
250+
self::detectConstants($class);
251+
}
252+
253+
if (!isset(self::$names[$class][$ordinal])) {
241254
throw new InvalidArgumentException(sprintf(
242255
'Invalid ordinal number, must between 0 and %s',
243-
count($constants) - 1
256+
count(self::$names[$class]) - 1
244257
));
245258
}
246259

247-
$name = key($item);
260+
$name = self::$names[$class][$ordinal];
248261
if (isset(self::$instances[$class][$name])) {
249262
return self::$instances[$class][$name];
250263
}
251264

252-
return self::$instances[$class][$name] = new $class(current($item), $ordinal);
265+
$const = $class . '::' . $name;
266+
return self::$instances[$class][$name] = new $class(constant($const));
253267
}
254268

255269
/**
@@ -259,7 +273,10 @@ final public static function byOrdinal($ordinal)
259273
*/
260274
final public static function getEnumerators()
261275
{
262-
return array_map([static::class, 'byName'], array_keys(self::detectConstants(static::class)));
276+
if (!isset(self::$names[static::class])) {
277+
self::detectConstants(static::class);
278+
}
279+
return array_map([static::class, 'byName'], self::$names[static::class]);
263280
}
264281

265282
/**
@@ -279,7 +296,10 @@ final public static function getValues()
279296
*/
280297
final public static function getNames()
281298
{
282-
return array_keys(self::detectConstants(static::class));
299+
if (!isset(self::$names[static::class])) {
300+
self::detectConstants(static::class);
301+
}
302+
return self::$names[static::class];
283303
}
284304
/*
285305
* Get a list of enumerator ordinal numbers
@@ -320,7 +340,7 @@ final public static function has($value)
320340
}
321341

322342
/**
323-
* Detect all available constants by the given class
343+
* Detect all public available constants of given enumeration class
324344
*
325345
* @param string $class
326346
* @return array
@@ -368,6 +388,7 @@ private static function detectConstants($class)
368388
}
369389

370390
self::$constants[$class] = $constants;
391+
self::$names[$class] = array_keys($constants);
371392
}
372393

373394
return self::$constants[$class];

tests/MabeEnumTest/EnumTest.php

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use InvalidArgumentException;
66
use LogicException;
7+
use MabeEnum\Enum;
78
use MabeEnumTest\TestAsset\EnumBasic;
89
use MabeEnumTest\TestAsset\EnumInheritance;
910
use MabeEnumTest\TestAsset\EnumAmbiguous;
@@ -23,6 +24,23 @@
2324
*/
2425
class EnumTest extends TestCase
2526
{
27+
public function setUp()
28+
{
29+
$enumRefl = new ReflectionClass(Enum::class);
30+
31+
$constantsProp = $enumRefl->getProperty('constants');
32+
$namesProp = $enumRefl->getProperty('names');
33+
$instancesProp = $enumRefl->getProperty('instances');
34+
35+
$constantsProp->setAccessible(true);
36+
$namesProp->setAccessible(true);
37+
$instancesProp->setAccessible(true);
38+
39+
$constantsProp->setValue(null, []);
40+
$namesProp->setValue(null, []);
41+
$instancesProp->setValue(null, []);
42+
}
43+
2644
public function testGetNameReturnsConstantNameOfCurrentValue()
2745
{
2846
$enum = EnumBasic::get(EnumBasic::ONE);
@@ -106,7 +124,7 @@ public function testGetByExtendedInstanceOfKnownValue()
106124
EnumBasic::get($enum);
107125
}
108126

109-
public function testGetEnumerators()
127+
public function testGetEnumeratorsConstansAlreadyDetected()
110128
{
111129
$constants = EnumInheritance::getConstants();
112130
$enumerators = EnumInheritance::getEnumerators();
@@ -123,6 +141,23 @@ public function testGetEnumerators()
123141
}
124142
}
125143

144+
public function testGetEnumeratorsConstansNotDetected()
145+
{
146+
$enumerators = EnumInheritance::getEnumerators();
147+
$constants = EnumInheritance::getConstants();
148+
$count = count($enumerators);
149+
150+
$this->assertSame(count($constants), $count);
151+
for ($i = 0; $i < $count; ++$i) {
152+
$this->assertArrayHasKey($i, $enumerators);
153+
$this->assertInstanceOf(EnumInheritance::class, $enumerators[$i]);
154+
155+
$enumerator = $enumerators[$i];
156+
$this->assertArrayHasKey($enumerator->getName(), $constants);
157+
$this->assertSame($constants[$enumerator->getName()], $enumerator->getValue());
158+
}
159+
}
160+
126161
public function testGetValues()
127162
{
128163
$expectedValues = array_values(EnumInheritance::getConstants());
@@ -136,7 +171,7 @@ public function testGetValues()
136171
}
137172
}
138173

139-
public function testGetNames()
174+
public function testGetNamesConstantsAlreadyDetected()
140175
{
141176
$expectedNames = array_keys(EnumInheritance::getConstants());
142177
$names = EnumInheritance::getNames();
@@ -149,6 +184,19 @@ public function testGetNames()
149184
}
150185
}
151186

187+
public function testGetNamesConstantsNotDetected()
188+
{
189+
$names = EnumInheritance::getNames();
190+
$expectedNames = array_keys(EnumInheritance::getConstants());
191+
$count = count($names);
192+
193+
$this->assertSame(count($expectedNames), $count);
194+
for ($i = 0; $i < $count; ++$i) {
195+
$this->assertArrayHasKey($i, $names);
196+
$this->assertSame($expectedNames[$i], $names[$i]);
197+
}
198+
}
199+
152200
public function testGetOrdinals()
153201
{
154202
$constants = EnumInheritance::getConstants();

0 commit comments

Comments
 (0)