1
1
<?php
2
2
3
+ namespace MabeEnum ;
4
+
5
+ use ReflectionClass ;
6
+ use InvalidArgumentException ;
7
+ use LogicException ;
8
+ use BadMethodCallException ;
9
+
3
10
/**
4
11
* Class to implement enumerations for PHP 5 (without SplEnum)
5
12
*
6
13
* @link http://github.com/marc-mabe/php-enum for the canonical source repository
7
14
* @copyright Copyright (c) 2012 Marc Bennewitz
8
15
* @license http://github.com/marc-mabe/php-enum/blob/master/LICENSE.txt New BSD License
9
16
*/
10
- abstract class MabeEnum_Enum
17
+ abstract class Enum
11
18
{
12
19
/**
13
- * The current selected value
20
+ * The selected value
14
21
* @var null|scalar
15
22
*/
16
23
private $ value ;
@@ -22,46 +29,53 @@ abstract class MabeEnum_Enum
22
29
private $ ordinal ;
23
30
24
31
/**
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, ...]
27
40
*/
28
- private $ constants ;
41
+ private static $ instances = array () ;
29
42
30
43
/**
31
44
* Constructor
32
45
*
33
46
* @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
35
49
*/
36
- public function __construct ($ value )
50
+ final private function __construct ($ value )
37
51
{
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
-
49
52
// find and set the given value
50
53
// 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 );
52
56
if ($ const === false ) {
53
57
throw new InvalidArgumentException ("Unknown value ' {$ value }' " );
54
58
}
55
- $ this ->value = $ this -> constants [$ const ];
59
+ $ this ->value = $ constants [$ const ];
56
60
}
57
61
58
62
/**
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
61
75
*/
62
- final public function getConstants ()
76
+ final private function __clone ()
63
77
{
64
- return $ this -> constants ;
78
+ throw new LogicException ( ' Enums are not cloneable ' ) ;
65
79
}
66
80
67
81
/**
@@ -79,9 +93,13 @@ final public function getValue()
79
93
*/
80
94
final public function getName ()
81
95
{
82
- return array_search ($ this ->value , $ this -> constants , true );
96
+ return array_search ($ this ->value , $ this :: getConstants () , true );
83
97
}
84
98
99
+ /**
100
+ * Get the ordinal number of the selected value
101
+ * @return int
102
+ */
85
103
final public function getOrdinal ()
86
104
{
87
105
if ($ this ->ordinal !== null ) {
@@ -91,7 +109,7 @@ final public function getOrdinal()
91
109
// detect ordinal
92
110
$ ordinal = 0 ;
93
111
$ value = $ this ->value ;
94
- foreach ($ this -> constants as $ constValue ) {
112
+ foreach ($ this :: getConstants () as $ constValue ) {
95
113
if ($ value === $ constValue ) {
96
114
break ;
97
115
}
@@ -103,13 +121,89 @@ final public function getOrdinal()
103
121
}
104
122
105
123
/**
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
109
130
*/
110
- final public function __toString ( )
131
+ static public function get ( $ value )
111
132
{
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 ;
113
207
}
114
208
115
209
/**
@@ -118,19 +212,18 @@ final public function __toString()
118
212
* This will be called automatically on calling a method
119
213
* with the same name of a defined constant.
120
214
*
121
- * NOTE: THIS WORKS FOR PHP >= 5.3 ONLY
122
- *
123
215
* @param string $const The name of the constant to instantiate the enum with
124
216
* @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
126
219
*/
127
220
final public static function __callStatic ($ const , array $ args )
128
221
{
129
- $ class = get_called_class ();
130
- $ classConst = $ class . ':: ' . $ const ;
222
+ $ classConst = 'static:: ' . $ const ;
131
223
if (!defined ($ classConst )) {
132
- throw new BadMethodCallException ($ classConst . ' not defined ' );
224
+ $ class = get_called_class ();
225
+ throw new BadMethodCallException ($ class . ':: ' . $ const . ' not defined ' );
133
226
}
134
- return new $ class (constant ($ classConst ));
227
+ return static :: get (constant ($ classConst ));
135
228
}
136
229
}
0 commit comments