10
10
abstract class MabeEnum_Enum
11
11
{
12
12
/**
13
- * The current selected value
13
+ * The selected value
14
14
* @var null|scalar
15
15
*/
16
16
private $ value ;
@@ -22,59 +22,53 @@ abstract class MabeEnum_Enum
22
22
private $ ordinal ;
23
23
24
24
/**
25
- * An array of available constants
26
- * @var null| array
25
+ * An array of available constants by class
26
+ * @var array ["$class" => ["$const" => $value, ...], ...]
27
27
*/
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 ();
29
35
30
36
/**
31
37
* Constructor
32
38
*
33
39
* @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
35
42
*/
36
- public function __construct ($ value )
43
+ final private function __construct ($ value )
37
44
{
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
-
62
45
// find and set the given value
63
46
// 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 );
65
49
if ($ const === false ) {
66
50
throw new InvalidArgumentException ("Unknown value ' {$ value }' " );
67
51
}
68
- $ this ->value = $ this -> constants [$ const ];
52
+ $ this ->value = $ constants [$ const ];
69
53
}
70
54
71
55
/**
72
- * Get all available constants
73
- * @return array
56
+ * Get the selected value
57
+ * @return string
58
+ * @see getValue()
74
59
*/
75
- final public function getConstants ()
60
+ final public function __toString ()
76
61
{
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 ' );
78
72
}
79
73
80
74
/**
@@ -92,9 +86,13 @@ final public function getValue()
92
86
*/
93
87
final public function getName ()
94
88
{
95
- return array_search ($ this ->value , $ this -> constants , true );
89
+ return array_search ($ this ->value , $ this :: getConstants () , true );
96
90
}
97
91
92
+ /**
93
+ * Get the ordinal number of the selected value
94
+ * @return int
95
+ */
98
96
final public function getOrdinal ()
99
97
{
100
98
if ($ this ->ordinal !== null ) {
@@ -104,7 +102,7 @@ final public function getOrdinal()
104
102
// detect ordinal
105
103
$ ordinal = 0 ;
106
104
$ value = $ this ->value ;
107
- foreach ($ this -> constants as $ constValue ) {
105
+ foreach ($ this :: getConstants () as $ constValue ) {
108
106
if ($ value === $ constValue ) {
109
107
break ;
110
108
}
@@ -116,13 +114,89 @@ final public function getOrdinal()
116
114
}
117
115
118
116
/**
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
122
123
*/
123
- final public function __toString ( )
124
+ static public function get ( $ value )
124
125
{
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 ;
126
200
}
127
201
128
202
/**
@@ -131,19 +205,18 @@ final public function __toString()
131
205
* This will be called automatically on calling a method
132
206
* with the same name of a defined constant.
133
207
*
134
- * NOTE: THIS WORKS FOR PHP >= 5.3 ONLY
135
- *
136
208
* @param string $const The name of the constant to instantiate the enum with
137
209
* @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
139
212
*/
140
213
final public static function __callStatic ($ const , array $ args )
141
214
{
142
- $ class = get_called_class ();
143
- $ classConst = $ class . ':: ' . $ const ;
215
+ $ classConst = 'static:: ' . $ const ;
144
216
if (!defined ($ classConst )) {
145
- throw new BadMethodCallException ($ classConst . ' not defined ' );
217
+ $ class = get_called_class ();
218
+ throw new BadMethodCallException ($ class . ':: ' . $ const . ' not defined ' );
146
219
}
147
- return new $ class (constant ($ classConst ));
220
+ return static :: get (constant ($ classConst ));
148
221
}
149
222
}
0 commit comments