Skip to content

Commit df05fed

Browse files
authored
Merge pull request #94 from marc-mabe/opt_Enum_detectConstnats
Optimize Enum::detectConstants() by using assertion
2 parents 858b9f7 + 15eb4b2 commit df05fed

File tree

8 files changed

+118
-36
lines changed

8 files changed

+118
-36
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ install:
4949

5050
script:
5151
- if [ "$CODE_COVERAGE" == "1" ]; then
52-
php vendor/bin/phpunit --verbose --coverage-text --coverage-clover=coverage.clover;
52+
php -d 'zend.assertions=1' vendor/bin/phpunit --verbose --coverage-text --coverage-clover=coverage.clover;
5353
else
54-
php vendor/bin/phpunit --verbose;
54+
php -d 'zend.assertions=1' vendor/bin/phpunit --verbose;
5555
fi
5656

5757
after_script:

bench/EnumBench.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
namespace MabeEnumBench;
44

5+
use MabeEnum\Enum;
56
use MabeEnumTest\TestAsset\Enum66;
7+
use ReflectionClass;
8+
use ReflectionProperty;
69

710
/**
811
* Benchmark of abstract class Enum tested with enumeration of 66 enumerators.
@@ -17,6 +20,10 @@
1720
*/
1821
class EnumBench
1922
{
23+
/**
24+
* @var ReflectionProperty[]
25+
*/
26+
private $enumPropsRefl;
2027
/**
2128
* @var string[]
2229
*/
@@ -42,12 +49,25 @@ class EnumBench
4249
*/
4350
public function init()
4451
{
52+
$enumRefl = new ReflectionClass(Enum::class);
53+
$this->enumPropsRefl = $enumRefl->getProperties(ReflectionProperty::IS_STATIC);
54+
foreach ($this->enumPropsRefl as $enumPropRefl) {
55+
$enumPropRefl->setAccessible(true);
56+
}
57+
4558
$this->names = Enum66::getNames();
4659
$this->values = Enum66::getValues();
4760
$this->ordinals = Enum66::getOrdinals();
4861
$this->enumerators = Enum66::getEnumerators();
4962
}
5063

64+
private function resetStaticEnumProps()
65+
{
66+
foreach ($this->enumPropsRefl as $enumPropRefl) {
67+
$enumPropRefl->setValue([]);
68+
}
69+
}
70+
5171
public function benchGetName()
5272
{
5373
foreach ($this->enumerators as $enumerator) {
@@ -85,6 +105,7 @@ public function benchIsByValue()
85105

86106
public function benchGetConstants()
87107
{
108+
$this->resetStaticEnumProps();
88109
Enum66::getConstants();
89110
}
90111

bench/bootstrap.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
$zendassertions = ini_get('zend.assertions');
4+
if (\PHP_VERSION_ID >= 70000 && $zendassertions != -1) {
5+
echo 'Please disable zend.assertions in php.ini (zend.assertions = -1)' . PHP_EOL
6+
. "Current ini setting: zend.assertions = {$zendassertions}]" . PHP_EOL;
7+
exit(1);
8+
}
9+
assert_options(ASSERT_ACTIVE, 0);
10+
assert_options(ASSERT_WARNING, 0);
11+
assert_options(ASSERT_BAIL, 0);
12+
assert_options(ASSERT_QUIET_EVAL, 0);
13+
14+
require_once __DIR__ . '/../vendor/autoload.php';

phpbench.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"bootstrap": "vendor/autoload.php",
2+
"bootstrap": "bench/bootstrap.php",
33
"path": "bench/",
44
"retry_threshold": 5
55
}

phpunit.xml.dist

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,8 @@
2121
<directory suffix=".php">./src</directory>
2222
</whitelist>
2323
</filter>
24+
<php>
25+
<ini name="zend.assertions" value="1"/>
26+
<ini name="assert.exception" value="1"/>
27+
</php>
2428
</phpunit>

src/Enum.php

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace MabeEnum;
44

5+
use AssertionError;
56
use ReflectionClass;
67
use InvalidArgumentException;
78
use LogicException;
@@ -337,17 +338,17 @@ final public static function has($value)
337338
*
338339
* @param string $class
339340
* @return array
340-
* @throws LogicException On ambiguous constant values
341+
* @throws AssertionError On ambiguous constant values
341342
*/
342343
private static function detectConstants($class)
343344
{
344345
if (!isset(self::$constants[$class])) {
345346
$reflection = new ReflectionClass($class);
346-
$constants = [];
347+
$publicConstants = [];
347348

348349
do {
349350
$scopeConstants = [];
350-
if (PHP_VERSION_ID >= 70100) {
351+
if (\PHP_VERSION_ID >= 70100) {
351352
// Since PHP-7.1 visibility modifiers are allowed for class constants
352353
// for enumerations we are only interested in public once.
353354
foreach ($reflection->getReflectionConstants() as $reflConstant) {
@@ -360,33 +361,35 @@ private static function detectConstants($class)
360361
$scopeConstants = $reflection->getConstants();
361362
}
362363

363-
$constants = $scopeConstants + $constants;
364+
$publicConstants = $scopeConstants + $publicConstants;
364365
} while (($reflection = $reflection->getParentClass()) && $reflection->name !== __CLASS__);
365366

366-
// Detect ambiguous values and report names
367-
$ambiguous = [];
368-
foreach ($constants as $value) {
369-
$names = \array_keys($constants, $value, true);
370-
if (\count($names) > 1) {
371-
$ambiguous[\var_export($value, true)] = $names;
372-
}
373-
}
374-
if (!empty($ambiguous)) {
375-
throw new LogicException(
376-
'All possible values needs to be unique. The following are ambiguous: '
377-
. \implode(', ', \array_map(function ($names) use ($constants) {
378-
return \implode('/', $names) . '=' . \var_export($constants[$names[0]], true);
379-
}, $ambiguous))
380-
);
381-
}
367+
assert(self::noAmbiguousValues($publicConstants));
382368

383-
self::$constants[$class] = $constants;
384-
self::$names[$class] = \array_keys($constants);
369+
self::$constants[$class] = $publicConstants;
370+
self::$names[$class] = \array_keys($publicConstants);
385371
}
386372

387373
return self::$constants[$class];
388374
}
389375

376+
/**
377+
* Assert that the given enumeration class doesn't define ambiguous enumerator values
378+
* @param array $constants
379+
* @return bool
380+
*/
381+
private static function noAmbiguousValues(array $constants)
382+
{
383+
foreach ($constants as $value) {
384+
$names = \array_keys($constants, $value, true);
385+
if (\count($names) > 1) {
386+
return false;
387+
}
388+
}
389+
390+
return true;
391+
}
392+
390393
/**
391394
* Get an enumerator instance by the given name.
392395
*

tests/MabeEnumTest/EnumTest.php

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace MabeEnumTest;
44

5+
use AssertionError;
56
use InvalidArgumentException;
67
use LogicException;
78
use MabeEnum\Enum;
@@ -41,6 +42,11 @@ public function setUp()
4142
$instancesProp->setValue(null, []);
4243
}
4344

45+
public function tearDown()
46+
{
47+
assert_options(ASSERT_ACTIVE, 1);
48+
}
49+
4450
public function testGetNameReturnsConstantNameOfCurrentValue()
4551
{
4652
$enum = EnumBasic::get(EnumBasic::ONE);
@@ -274,15 +280,31 @@ public function testInstantiateUsingMagicMethod()
274280
$this->assertSame(EnumInheritance::ONE, $enum->getValue());
275281
}
276282

277-
public function testAmbiguousConstantsThrowsLogicException()
283+
public function testEnabledAssertAmbiguousEnumeratorValues()
278284
{
279-
$this->expectException(LogicException::class);
285+
$this->expectException(AssertionError::class);
280286
EnumAmbiguous::get('unknown');
281287
}
282288

283-
public function testExtendedAmbiguousCanstantsThrowsLogicException()
289+
public function testDisabledAssertAmbiguousEnumeratorValues()
284290
{
285-
$this->expectException(LogicException::class);
291+
assert_options(ASSERT_ACTIVE, 0);
292+
$this->expectException(InvalidArgumentException::class);
293+
294+
EnumAmbiguous::get('unknown');
295+
}
296+
297+
public function testExtendedEnabledAssertAmbiguousEnumeratorValues()
298+
{
299+
$this->expectException(AssertionError::class);
300+
EnumExtendedAmbiguous::get('unknown');
301+
}
302+
303+
public function testExtendedDisabledAssertAmbiguousEnumeratorValues()
304+
{
305+
assert_options(ASSERT_ACTIVE, 0);
306+
$this->expectException(InvalidArgumentException::class);
307+
286308
EnumExtendedAmbiguous::get('unknown');
287309
}
288310

@@ -361,10 +383,6 @@ public function testConstVisibilityExtended()
361383

362384
public function testIsSerializableIssue()
363385
{
364-
if (PHP_VERSION_ID < 50400) {
365-
$this->markTestSkipped('This test is for PHP-5.4 and upper only');
366-
}
367-
368386
$enum1 = SerializableEnum::INT();
369387
$enum2 = unserialize(serialize($enum1));
370388

tests/bootstrap.php

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,28 @@
11
<?php
22

3-
ini_set('error_reporting', E_ALL);
3+
// report all errors
4+
error_reporting(E_ALL);
5+
6+
// make sure zend.assertions are available (not disabled on compile time)
7+
$zendassertions = ini_get('zend.assertions');
8+
if (\PHP_VERSION_ID >= 70000 && $zendassertions == -1) {
9+
echo 'Please enable zend.assertions in php.ini (zend.assertions = 1)' . PHP_EOL
10+
. "Current ini setting: zend.assertions = {$zendassertions}]" . PHP_EOL;
11+
exit(1);
12+
}
13+
14+
// activate assertions
15+
assert_options(ASSERT_ACTIVE, 1);
16+
assert_options(ASSERT_WARNING, 0);
17+
assert_options(ASSERT_BAIL, 0);
18+
assert_options(ASSERT_QUIET_EVAL, 0);
19+
if (!class_exists('AssertionError')) {
20+
// AssertionError has been added in PHP-7.0
21+
class AssertionError extends Exception {};
22+
}
23+
assert_options(ASSERT_CALLBACK, function($file, $line, $code) {
24+
throw new AssertionError("assert(): Assertion '{$code}' failed in {$file} on line {$line}");
25+
});
426

527
// installed itself
628
if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
@@ -17,8 +39,8 @@
1739
}
1840

1941
// autload test files
20-
spl_autoload_register(function ($className) {
21-
$file = __DIR__ . '/' . str_replace('\\', '/', $className) . '.php';
42+
spl_autoload_register(function ($class) {
43+
$file = __DIR__ . '/' . str_replace('\\', '/', $class) . '.php';
2244
if (file_exists($file)) {
2345
require $file;
2446
}

0 commit comments

Comments
 (0)