Skip to content

Commit f687915

Browse files
author
Marc Bennewitz
committed
Merge pull request #14 from marc-mabe/singleton
Singleton
2 parents 8eca68b + da11a15 commit f687915

File tree

5 files changed

+166
-108
lines changed

5 files changed

+166
-108
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

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
}],
1818
"license": "BSD-3-Clause",
1919
"require": {
20-
"php": ">=5.0",
20+
"php": ">=5.3",
2121
"ext-reflection": "*"
2222
},
2323
"require-dev": {

src/MabeEnum/Enum.php

Lines changed: 123 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
abstract class MabeEnum_Enum
1111
{
1212
/**
13-
* The current selected value
13+
* The selected value
1414
* @var null|scalar
1515
*/
1616
private $value;
@@ -22,59 +22,53 @@ abstract class MabeEnum_Enum
2222
private $ordinal;
2323

2424
/**
25-
* An array of available constants
26-
* @var null|array
25+
* An array of available constants by class
26+
* @var array ["$class" => ["$const" => $value, ...], ...]
2727
*/
28-
private $constants;
28+
private static $constants = array();
29+
30+
/**
31+
* Already instantiated enums
32+
* @param array ["$class.$value" => MabeEnum_Enum, ...]
33+
*/
34+
private static $instances = array();
2935

3036
/**
3137
* Constructor
3238
*
3339
* @param scalar $value The value to select
34-
* @throws InvalidArgumentException
40+
* @throws InvalidArgumentException On an unknwon or invalid value
41+
* @throws LogicException On ambiguous constant values
3542
*/
36-
public function __construct($value)
43+
final private function __construct($value)
3744
{
38-
$reflectionClass = new ReflectionClass($this);
39-
$constants = $reflectionClass->getConstants();
40-
41-
// Constant values needs to be unique
42-
if (count($constants) > count(array_unique($constants))) {
43-
$ambiguous = array();
44-
foreach (array_count_values($constants) as $constValue => $countValue) {
45-
if ($countValue < 2) {
46-
continue;
47-
}
48-
$ambiguous[] = $constValue;
49-
}
50-
throw new LogicException(sprintf(
51-
'All possible values needs to be unique. The following are ambiguous: %s',
52-
"'" . implode("', '", $ambiguous) . "'"
53-
));
54-
}
55-
56-
// This is required to make sure that constants of base classes will be the first
57-
while ( ($reflectionClass = $reflectionClass->getParentClass()) ) {
58-
$constants = $reflectionClass->getConstants() + $constants;
59-
}
60-
$this->constants = $constants;
61-
6245
// find and set the given value
6346
// set the defined value because of non strict comparison
64-
$const = array_search($value, $this->constants);
47+
$constants = static::getConstants();
48+
$const = array_search($value, $constants);
6549
if ($const === false) {
6650
throw new InvalidArgumentException("Unknown value '{$value}'");
6751
}
68-
$this->value = $this->constants[$const];
52+
$this->value = $constants[$const];
6953
}
7054

7155
/**
72-
* Get all available constants
73-
* @return array
56+
* Get the selected value
57+
* @return string
58+
* @see getValue()
7459
*/
75-
final public function getConstants()
60+
final public function __toString()
7661
{
77-
return $this->constants;
62+
return (string) $this->value;
63+
}
64+
65+
/**
66+
* @throws LogicException Enums are not cloneable
67+
* because instances are implemented as singletons
68+
*/
69+
final private function __clone()
70+
{
71+
throw new LogicException('Enums are not cloneable');
7872
}
7973

8074
/**
@@ -92,9 +86,13 @@ final public function getValue()
9286
*/
9387
final public function getName()
9488
{
95-
return array_search($this->value, $this->constants, true);
89+
return array_search($this->value, $this::getConstants(), true);
9690
}
9791

92+
/**
93+
* Get the ordinal number of the selected value
94+
* @return int
95+
*/
9896
final public function getOrdinal()
9997
{
10098
if ($this->ordinal !== null) {
@@ -104,7 +102,7 @@ final public function getOrdinal()
104102
// detect ordinal
105103
$ordinal = 0;
106104
$value = $this->value;
107-
foreach ($this->constants as $constValue) {
105+
foreach ($this::getConstants() as $constValue) {
108106
if ($value === $constValue) {
109107
break;
110108
}
@@ -116,13 +114,89 @@ final public function getOrdinal()
116114
}
117115

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

128202
/**
@@ -131,19 +205,18 @@ final public function __toString()
131205
* This will be called automatically on calling a method
132206
* with the same name of a defined constant.
133207
*
134-
* NOTE: THIS WORKS FOR PHP >= 5.3 ONLY
135-
*
136208
* @param string $const The name of the constant to instantiate the enum with
137209
* @param array $args There should be no arguments
138-
* @throws BadMethodCallException
210+
* @throws BadMethodCallException On an unknown constant name (method name)
211+
* @throws LogicException On ambiguous constant values
139212
*/
140213
final public static function __callStatic($const, array $args)
141214
{
142-
$class = get_called_class();
143-
$classConst = $class . '::' . $const;
215+
$classConst = 'static::' . $const;
144216
if (!defined($classConst)) {
145-
throw new BadMethodCallException($classConst . ' not defined');
217+
$class = get_called_class();
218+
throw new BadMethodCallException($class . '::' . $const . ' not defined');
146219
}
147-
return new $class(constant($classConst));
220+
return static::get(constant($classConst));
148221
}
149222
}

0 commit comments

Comments
 (0)