Skip to content

Commit ed9a5ec

Browse files
committed
Merge branch 'master' of github.com:marc-mabe/php-enum into EnumMap
2 parents 4a76a7e + b378c30 commit ed9a5ec

File tree

11 files changed

+254
-123
lines changed

11 files changed

+254
-123
lines changed

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
language: php
22

33
php:
4-
- 5.2
54
- 5.3
65
- 5.4
76
- 5.5

README.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
[![Latest Unstable Version](https://poser.pugx.org/marc-mabe/php-enum/v/unstable.png)](https://packagist.org/packages/marc-mabe/php-enum)
77
[![Dependency Status](https://www.versioneye.com/php/marc-mabe:php-enum/dev-master/badge.png)](https://www.versioneye.com/php/marc-mabe:php-enum/dev-master)
88

9-
This is a native PHP implementation to add enumeration support to PHP 5.x.
9+
This is a native PHP implementation to add enumeration support to PHP >= 5.3
1010
It's an abstract class that needs to be extended to use it.
1111

1212

@@ -32,7 +32,7 @@ It's an abstract class that needs to be extended to use it.
3232

3333
# API
3434

35-
abstract class MabeEnum_Enum
35+
abstract class MabeEnum\Enum
3636
{
3737
/**
3838
* Constructor
@@ -80,7 +80,7 @@ It's an abstract class that needs to be extended to use it.
8080
* @param string $name The name of the constant to instantiate
8181
* @param array $args This should be an empty array (no arguments)
8282
* @throws BadMethodCallException If the called method hasn't the same name as a constant
83-
* @return MabeEnum_Enum The instantiated enum
83+
* @return MabeEnum\Enum The instantiated enum
8484
*/
8585
final public static __callStatic($name, array $args);
8686
}
@@ -129,7 +129,9 @@ It's an abstract class that needs to be extended to use it.
129129

130130
## The way of php-enum:
131131

132-
class UserStatusEnum extends MabeEnum_Enum
132+
use MabeEnum\Enum;
133+
134+
class UserStatusEnum extends Enum
133135
{
134136
const INACTIVE = 0;
135137
const ACTIVE = 1;
@@ -149,23 +151,23 @@ It's an abstract class that needs to be extended to use it.
149151
{
150152
if (!$this->status) {
151153
// init default status
152-
$this->status = new UserStatusEnum(UserStatusEnum::INACTIVE);
154+
$this->status = UserStatusEnum::INACTIVE();
153155
}
154156
return $this->status;
155157
}
156158
}
157159

158160
$user = new User();
159161
echo 'Default user status: ' . $user->getStatus() . '(' . $user->getStatus()->getValue() . ')' . PHP_EOL;
160-
$user->setStatus(new UserStatusEnum(UserStatusEnum::ACTIVE));
162+
$user->setStatus(UserStatusEnum::ACTIVE());
161163
echo 'Changed user status: ' . $user->getStatus() . '(' . $user->getStatus()->getValue() . ')' . PHP_EOL;
162164

163165
**PRINTS**
164166

165167
Default user status: INACTIVE (0)
166168
Changed user status: ACTIVE (1)
167169

168-
* Validation will be already done on basic class ```MabeEnum_Enum```
170+
* Validation will be already done on basic class ```MabeEnum\Enum```
169171
* Using type-hint makes arguments save
170172
* Human readable name of a value is simple accessable
171173

@@ -194,7 +196,9 @@ and extract it.
194196
This example defines the constant ```ONE``` with value ```1``` as default
195197
value.
196198

197-
class MyEnumWithDefaultValue extends MabeEnum_Enum
199+
use MabeEnum\Enum;
200+
201+
class MyEnumWithDefaultValue extends Enum
198202
{
199203
const ONE = 1;
200204
const TWO = 2;
@@ -209,7 +213,9 @@ value.
209213

210214
It's also possible to extend other enumerations.
211215

212-
class MyEnum extends MabeEnum_Enum
216+
use MabeEnum\Enum;
217+
218+
class MyEnum extends Enum
213219
{
214220
const ONE = 1;
215221
const TWO = 2;
@@ -222,10 +228,12 @@ It's also possible to extend other enumerations.
222228

223229
## Simplified instantiation
224230

225-
With PHP >= 5.3 it possible to call one of the defined constants like a method
231+
It's possible to call one of the defined constants like a method
226232
and you will get the instantiated enum as a result.
227233

228-
class MyEnum extends MabeEnum_Enum
234+
use MabeEnum\Enum;
235+
236+
class MyEnum extends Enum
229237
{
230238
const ONE = 1;
231239
const TWO = 2;

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@
1717
}],
1818
"license": "BSD-3-Clause",
1919
"require": {
20-
"php": ">=5.0",
20+
"php": ">=5.3",
2121
"ext-reflection": "*"
2222
},
2323
"require-dev": {
2424
"phpunit/phpunit": ">=3.6"
2525
},
2626
"autoload": {
2727
"psr-0": {
28-
"MabeEnum_": "src/"
28+
"MabeEnum\\": "src/"
2929
}
3030
}
3131
}

src/MabeEnum/Enum.php

Lines changed: 131 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
<?php
22

3+
namespace MabeEnum;
4+
5+
use ReflectionClass;
6+
use InvalidArgumentException;
7+
use LogicException;
8+
use BadMethodCallException;
9+
310
/**
411
* Class to implement enumerations for PHP 5 (without SplEnum)
512
*
613
* @link http://github.com/marc-mabe/php-enum for the canonical source repository
714
* @copyright Copyright (c) 2012 Marc Bennewitz
815
* @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License
916
*/
10-
abstract class MabeEnum_Enum
17+
abstract class Enum
1118
{
1219
/**
13-
* The current selected value
20+
* The selected value
1421
* @var null|scalar
1522
*/
1623
private $value;
@@ -22,46 +29,53 @@ abstract class MabeEnum_Enum
2229
private $ordinal;
2330

2431
/**
25-
* An array of available constants
26-
* @var null|array
32+
* An array of available constants by class
33+
* @var array ["$class" => ["$const" => $value, ...], ...]
34+
*/
35+
private static $constants = array();
36+
37+
/**
38+
* Already instantiated enums
39+
* @param array ["$class.$value" => MabeEnum\Enum, ...]
2740
*/
28-
private $constants;
41+
private static $instances = array();
2942

3043
/**
3144
* Constructor
3245
*
3346
* @param scalar $value The value to select
34-
* @throws InvalidArgumentException
47+
* @throws InvalidArgumentException On an unknwon or invalid value
48+
* @throws LogicException On ambiguous constant values
3549
*/
36-
public function __construct($value)
50+
final private function __construct($value)
3751
{
38-
$reflectionClass = new ReflectionClass($this);
39-
$constants = $reflectionClass->getConstants();
40-
41-
// This is required to make sure that constants of base classes will be the first
42-
while ( ($reflectionClass = $reflectionClass->getParentClass()) ) {
43-
$constants = $reflectionClass->getConstants() + $constants;
44-
}
45-
$this->constants = $constants;
46-
47-
// TODO: Check that constant values are equal (non strict comparison)
48-
4952
// find and set the given value
5053
// set the defined value because of non strict comparison
51-
$const = array_search($value, $this->constants);
54+
$constants = static::getConstants();
55+
$const = array_search($value, $constants);
5256
if ($const === false) {
5357
throw new InvalidArgumentException("Unknown value '{$value}'");
5458
}
55-
$this->value = $this->constants[$const];
59+
$this->value = $constants[$const];
5660
}
5761

5862
/**
59-
* Get all available constants
60-
* @return array
63+
* Get the selected value
64+
* @return string
65+
* @see getValue()
66+
*/
67+
final public function __toString()
68+
{
69+
return (string) $this->value;
70+
}
71+
72+
/**
73+
* @throws LogicException Enums are not cloneable
74+
* because instances are implemented as singletons
6175
*/
62-
final public function getConstants()
76+
final private function __clone()
6377
{
64-
return $this->constants;
78+
throw new LogicException('Enums are not cloneable');
6579
}
6680

6781
/**
@@ -79,9 +93,13 @@ final public function getValue()
7993
*/
8094
final public function getName()
8195
{
82-
return array_search($this->value, $this->constants, true);
96+
return array_search($this->value, $this::getConstants(), true);
8397
}
8498

99+
/**
100+
* Get the ordinal number of the selected value
101+
* @return int
102+
*/
85103
final public function getOrdinal()
86104
{
87105
if ($this->ordinal !== null) {
@@ -91,7 +109,7 @@ final public function getOrdinal()
91109
// detect ordinal
92110
$ordinal = 0;
93111
$value = $this->value;
94-
foreach ($this->constants as $constValue) {
112+
foreach ($this::getConstants() as $constValue) {
95113
if ($value === $constValue) {
96114
break;
97115
}
@@ -103,13 +121,89 @@ final public function getOrdinal()
103121
}
104122

105123
/**
106-
* Get the current selected constant name
107-
* @return string
108-
* @see getName()
124+
* Get an enum of the given value
125+
*
126+
* @param scalar $value
127+
* @return Enum
128+
* @throws InvalidArgumentException On an unknwon or invalid value
129+
* @throws LogicException On ambiguous constant values
109130
*/
110-
final public function __toString()
131+
static public function get($value)
111132
{
112-
return (string) $this->value;
133+
$class = get_called_class();
134+
$id = $class . '.' . $value;
135+
if (isset(self::$instances[$id])) {
136+
return self::$instances[$id];
137+
}
138+
139+
$instance = new $class($value);
140+
self::$instances[$id] = $instance;
141+
return $instance;
142+
}
143+
144+
/**
145+
* Clears all instantiated enums
146+
*
147+
* NOTE: This can break singleton behavior ... use it with caution!
148+
*
149+
* @param null|string $class
150+
* @return void
151+
*/
152+
final static function clear()
153+
{
154+
$class = get_called_class();
155+
156+
// clear instantiated enums
157+
$prefix = $class . '.';
158+
$prefixLength = strlen($prefix);
159+
foreach (self::$instances as $id => $enum) {
160+
if (strncasecmp($prefix, $id, $prefixLength) === 0) {
161+
unset(self::$instances[$id]);
162+
}
163+
}
164+
165+
// clear constants buffer
166+
foreach (self::$constants as $constantsClass => & $constants) {
167+
if (strcasecmp($class, $constantsClass) === 0) {
168+
unset(self::$constants[$constantsClass]);
169+
break;
170+
}
171+
}
172+
}
173+
174+
/**
175+
* Get all available constants
176+
* @return array
177+
* @throws LogicException On ambiguous constant values
178+
*/
179+
final static public function getConstants()
180+
{
181+
$class = get_called_class();
182+
if (isset(self::$constants[$class])) {
183+
return self::$constants[$class];
184+
}
185+
186+
$reflection = new ReflectionClass($class);
187+
$constants = $reflection->getConstants();
188+
189+
// Constant values needs to be unique
190+
if (max(array_count_values($constants)) > 1) {
191+
$ambiguous = array_map(function ($v) use ($constants) {
192+
return implode('/', array_keys($constants, $v)) . '=' . $v;
193+
}, array_unique(array_diff_assoc($constants, array_unique($constants))));
194+
throw new LogicException(sprintf(
195+
'All possible values needs to be unique. The following are ambiguous: %s',
196+
implode(', ', $ambiguous)
197+
));
198+
}
199+
200+
// This is required to make sure that constants of base classes will be the first
201+
while ( ($reflection = $reflection->getParentClass()) ) {
202+
$constants = $reflection->getConstants() + $constants;
203+
}
204+
205+
self::$constants[$class] = $constants;
206+
return $constants;
113207
}
114208

115209
/**
@@ -118,19 +212,18 @@ final public function __toString()
118212
* This will be called automatically on calling a method
119213
* with the same name of a defined constant.
120214
*
121-
* NOTE: THIS WORKS FOR PHP >= 5.3 ONLY
122-
*
123215
* @param string $const The name of the constant to instantiate the enum with
124216
* @param array $args There should be no arguments
125-
* @throws BadMethodCallException
217+
* @throws BadMethodCallException On an unknown constant name (method name)
218+
* @throws LogicException On ambiguous constant values
126219
*/
127220
final public static function __callStatic($const, array $args)
128221
{
129-
$class = get_called_class();
130-
$classConst = $class . '::' . $const;
222+
$classConst = 'static::' . $const;
131223
if (!defined($classConst)) {
132-
throw new BadMethodCallException($classConst . ' not defined');
224+
$class = get_called_class();
225+
throw new BadMethodCallException($class . '::' . $const . ' not defined');
133226
}
134-
return new $class(constant($classConst));
227+
return static::get(constant($classConst));
135228
}
136229
}

tests/Bootstrap.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111

1212
function autoload($className)
1313
{
14-
$test = dirname(__FILE__) . '/../src/' . str_replace('_', '/', $className) . '.php';
14+
$test = dirname(__FILE__) . '/../src/' . str_replace('\\', '/', $className) . '.php';
1515
if (file_exists($test)) {
1616
require $test;
1717
}
18-
$test = dirname(__FILE__) . '/' . str_replace('_', '/', $className) . '.php';
18+
$test = dirname(__FILE__) . '/' . str_replace('\\', '/', $className) . '.php';
1919
if (file_exists($test)) {
2020
require $test;
2121
}

0 commit comments

Comments
 (0)