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,34 @@ 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 ];
69
- }
70
-
71
- /**
72
- * Get all available constants
73
- * @return array
74
- */
75
- final public function getConstants ()
76
- {
77
- return $ this ->constants ;
52
+ $ this ->value = $ constants [$ const ];
78
53
}
79
54
80
55
/**
@@ -92,9 +67,13 @@ final public function getValue()
92
67
*/
93
68
final public function getName ()
94
69
{
95
- return array_search ($ this ->value , $ this -> constants , true );
70
+ return array_search ($ this ->value , $ this :: getConstants () , true );
96
71
}
97
72
73
+ /**
74
+ * Get the ordinal number of the selected value
75
+ * @return int
76
+ */
98
77
final public function getOrdinal ()
99
78
{
100
79
if ($ this ->ordinal !== null ) {
@@ -104,7 +83,7 @@ final public function getOrdinal()
104
83
// detect ordinal
105
84
$ ordinal = 0 ;
106
85
$ value = $ this ->value ;
107
- foreach ($ this -> constants as $ constValue ) {
86
+ foreach ($ this :: getConstants () as $ constValue ) {
108
87
if ($ value === $ constValue ) {
109
88
break ;
110
89
}
@@ -125,25 +104,112 @@ final public function __toString()
125
104
return (string ) $ this ->value ;
126
105
}
127
106
107
+ /**
108
+ * Get an enum of the given value
109
+ *
110
+ * @param scalar $value
111
+ * @return MabeEnum_Enum
112
+ * @throws InvalidArgumentException On an unknwon or invalid value
113
+ * @throws LogicException On ambiguous constant values
114
+ */
115
+ static public function get ($ value )
116
+ {
117
+ $ class = get_called_class ();
118
+ $ id = $ class . '. ' . $ value ;
119
+ if (isset (self ::$ instances [$ id ])) {
120
+ return self ::$ instances [$ id ];
121
+ }
122
+
123
+ $ instance = new $ class ($ value );
124
+ self ::$ instances [$ id ] = $ instance ;
125
+ return $ instance ;
126
+ }
127
+
128
+ /**
129
+ * Clears all instantiated enums
130
+ *
131
+ * NOTE: This can break singleton behavior ... use it with caution!
132
+ *
133
+ * @param null|string $class
134
+ * @return void
135
+ */
136
+ final static function clear ($ class = null )
137
+ {
138
+ $ class = get_called_class ();
139
+
140
+ // clear instantiated enums
141
+ foreach (self ::$ enums as $ id => $ enum ) {
142
+ if (strcasecmp ($ class . '. ' , $ id ) === 0 ) {
143
+ unset(self ::$ enums [$ id ]);
144
+ }
145
+ }
146
+
147
+ // clear constants buffer
148
+ foreach (self ::$ constants as $ constantsClass => & $ constants ) {
149
+ if (strcasecmp ($ class , $ constantsClass ) === 0 ) {
150
+ unset(self ::$ constants [$ constantsClass ]);
151
+ break ;
152
+ }
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Get all available constants
158
+ * @return array
159
+ * @throws LogicException On ambiguous constant values
160
+ */
161
+ final static public function getConstants ()
162
+ {
163
+ $ class = get_called_class ();
164
+ if (isset (self ::$ constants [$ class ])) {
165
+ return self ::$ constants [$ class ];
166
+ }
167
+
168
+ $ reflection = new ReflectionClass ($ class );
169
+ $ constants = $ reflection ->getConstants ();
170
+
171
+ // Constant values needs to be unique
172
+ if (count ($ constants ) > count (array_unique ($ constants ))) {
173
+ $ ambiguous = array ();
174
+ foreach (array_count_values ($ constants ) as $ constValue => $ countValue ) {
175
+ if ($ countValue < 2 ) {
176
+ continue ;
177
+ }
178
+ $ ambiguous [] = $ constValue ;
179
+ }
180
+ throw new LogicException (sprintf (
181
+ 'All possible values needs to be unique. The following are ambiguous: %s ' ,
182
+ "' " . implode ("', ' " , $ ambiguous ) . "' "
183
+ ));
184
+ }
185
+
186
+ // This is required to make sure that constants of base classes will be the first
187
+ while ( ($ reflection = $ reflection ->getParentClass ()) ) {
188
+ $ constants = $ reflection ->getConstants () + $ constants ;
189
+ }
190
+
191
+ self ::$ constants [$ class ] = $ constants ;
192
+ return $ constants ;
193
+ }
194
+
128
195
/**
129
196
* Instantiate an enum by a name of a constant.
130
197
*
131
198
* This will be called automatically on calling a method
132
199
* with the same name of a defined constant.
133
200
*
134
- * NOTE: THIS WORKS FOR PHP >= 5.3 ONLY
135
- *
136
201
* @param string $const The name of the constant to instantiate the enum with
137
202
* @param array $args There should be no arguments
138
- * @throws BadMethodCallException
203
+ * @throws BadMethodCallException On an unknown constant name (method name)
204
+ * @throws LogicException On ambiguous constant values
139
205
*/
140
206
final public static function __callStatic ($ const , array $ args )
141
207
{
142
- $ class = get_called_class ();
143
- $ classConst = $ class . ':: ' . $ const ;
208
+ $ classConst = 'static:: ' . $ const ;
144
209
if (!defined ($ classConst )) {
145
- throw new BadMethodCallException ($ classConst . ' not defined ' );
210
+ $ class = get_called_class ();
211
+ throw new BadMethodCallException ($ class . ':: ' . $ const . ' not defined ' );
146
212
}
147
- return new $ class (constant ($ classConst ));
213
+ return static :: get (constant ($ classConst ));
148
214
}
149
215
}
0 commit comments