@@ -37,6 +37,14 @@ abstract class Enum implements \JsonSerializable
3737 */
3838 protected static $ cache = [];
3939
40+ /**
41+ * Cache of instances of the Enum class
42+ *
43+ * @var array
44+ * @psalm-var array<class-string, array<string, static>>
45+ */
46+ protected static $ instances = [];
47+
4048 /**
4149 * Creates a new value of some type
4250 *
@@ -53,15 +61,24 @@ public function __construct($value)
5361 $ value = $ value ->getValue ();
5462 }
5563
56- if (!$ this ->isValid ($ value )) {
57- /** @psalm-suppress InvalidCast */
58- throw new \UnexpectedValueException ("Value ' $ value' is not part of the enum " . static ::class);
59- }
64+ static ::assertValidValue ($ value );
6065
6166 /** @psalm-var T */
6267 $ this ->value = $ value ;
6368 }
6469
70+ /**
71+ * @param mixed $value
72+ * @return static
73+ * @psalm-return static<T>
74+ */
75+ public static function from ($ value ): self
76+ {
77+ static ::assertValidValue ($ value );
78+
79+ return new static ($ value );
80+ }
81+
6582 /**
6683 * @psalm-pure
6784 * @return mixed
@@ -167,13 +184,27 @@ public static function toArray()
167184 * @param $value
168185 * @psalm-param mixed $value
169186 * @psalm-pure
187+ * @psalm-assert-if-true T $value
170188 * @return bool
171189 */
172190 public static function isValid ($ value )
173191 {
174192 return \in_array ($ value , static ::toArray (), true );
175193 }
176194
195+ /**
196+ * Asserts valid enum value
197+ *
198+ * @psalm-pure
199+ * @psalm-assert T $value
200+ */
201+ public static function assertValidValue ($ value ): void
202+ {
203+ if (!static ::isValid ($ value )) {
204+ throw new \UnexpectedValueException ("Value ' $ value' is not part of the enum " . static ::class);
205+ }
206+ }
207+
177208 /**
178209 * Check if is valid enum key
179210 *
@@ -210,17 +241,20 @@ public static function search($value)
210241 * @param array $arguments
211242 *
212243 * @return static
213- * @psalm-pure
214244 * @throws \BadMethodCallException
215245 */
216246 public static function __callStatic ($ name , $ arguments )
217247 {
218- $ array = static ::toArray ();
219- if (isset ($ array [$ name ]) || \array_key_exists ($ name , $ array )) {
220- return new static ($ array [$ name ]);
248+ $ class = static ::class;
249+ if (!isset (self ::$ instances [$ class ][$ name ])) {
250+ $ array = static ::toArray ();
251+ if (!isset ($ array [$ name ]) && !\array_key_exists ($ name , $ array )) {
252+ $ message = "No static method or enum constant ' $ name' in class " . static ::class;
253+ throw new \BadMethodCallException ($ message );
254+ }
255+ return self ::$ instances [$ class ][$ name ] = new static ($ array [$ name ]);
221256 }
222-
223- throw new \BadMethodCallException ("No static method or enum constant ' $ name' in class " . static ::class);
257+ return clone self ::$ instances [$ class ][$ name ];
224258 }
225259
226260 /**
0 commit comments