Enum.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. <?php
  2. /**
  3. * @link http://github.com/myclabs/php-enum
  4. * @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
  5. */
  6. namespace MyCLabs\Enum;
  7. /**
  8. * Base Enum class
  9. *
  10. * Create an enum by implementing this class and adding class constants.
  11. *
  12. * @author Matthieu Napoli <matthieu@mnapoli.fr>
  13. * @author Daniel Costa <danielcosta@gmail.com>
  14. * @author Mirosław Filip <mirfilip@gmail.com>
  15. *
  16. * @psalm-template T
  17. * @psalm-immutable
  18. * @psalm-consistent-constructor
  19. */
  20. abstract class Enum implements \JsonSerializable, \Stringable
  21. {
  22. /**
  23. * Enum value
  24. *
  25. * @var mixed
  26. * @psalm-var T
  27. */
  28. protected $value;
  29. /**
  30. * Enum key, the constant name
  31. *
  32. * @var string
  33. */
  34. private $key;
  35. /**
  36. * Store existing constants in a static cache per object.
  37. *
  38. *
  39. * @var array
  40. * @psalm-var array<class-string, array<string, mixed>>
  41. */
  42. protected static $cache = [];
  43. /**
  44. * Cache of instances of the Enum class
  45. *
  46. * @var array
  47. * @psalm-var array<class-string, array<string, static>>
  48. */
  49. protected static $instances = [];
  50. /**
  51. * Creates a new value of some type
  52. *
  53. * @psalm-pure
  54. * @param mixed $value
  55. *
  56. * @psalm-param T $value
  57. * @throws \UnexpectedValueException if incompatible type is given.
  58. */
  59. public function __construct($value)
  60. {
  61. if ($value instanceof static) {
  62. /** @psalm-var T */
  63. $value = $value->getValue();
  64. }
  65. /** @psalm-suppress ImplicitToStringCast assertValidValueReturningKey returns always a string but psalm has currently an issue here */
  66. $this->key = static::assertValidValueReturningKey($value);
  67. /** @psalm-var T */
  68. $this->value = $value;
  69. }
  70. /**
  71. * This method exists only for the compatibility reason when deserializing a previously serialized version
  72. * that didn't had the key property
  73. */
  74. public function __wakeup()
  75. {
  76. /** @psalm-suppress DocblockTypeContradiction key can be null when deserializing an enum without the key */
  77. if ($this->key === null) {
  78. /**
  79. * @psalm-suppress InaccessibleProperty key is not readonly as marked by psalm
  80. * @psalm-suppress PossiblyFalsePropertyAssignmentValue deserializing a case that was removed
  81. */
  82. $this->key = static::search($this->value);
  83. }
  84. }
  85. /**
  86. * @param mixed $value
  87. * @return static
  88. */
  89. public static function from($value): self
  90. {
  91. $key = static::assertValidValueReturningKey($value);
  92. return self::__callStatic($key, []);
  93. }
  94. /**
  95. * @psalm-pure
  96. * @return mixed
  97. * @psalm-return T
  98. */
  99. public function getValue()
  100. {
  101. return $this->value;
  102. }
  103. /**
  104. * Returns the enum key (i.e. the constant name).
  105. *
  106. * @psalm-pure
  107. * @return string
  108. */
  109. public function getKey()
  110. {
  111. return $this->key;
  112. }
  113. /**
  114. * @psalm-pure
  115. * @psalm-suppress InvalidCast
  116. * @return string
  117. */
  118. public function __toString()
  119. {
  120. return (string)$this->value;
  121. }
  122. /**
  123. * Determines if Enum should be considered equal with the variable passed as a parameter.
  124. * Returns false if an argument is an object of different class or not an object.
  125. *
  126. * This method is final, for more information read https://github.com/myclabs/php-enum/issues/4
  127. *
  128. * @psalm-pure
  129. * @psalm-param mixed $variable
  130. * @return bool
  131. */
  132. final public function equals($variable = null): bool
  133. {
  134. return $variable instanceof self
  135. && $this->getValue() === $variable->getValue()
  136. && static::class === \get_class($variable);
  137. }
  138. /**
  139. * Returns the names (keys) of all constants in the Enum class
  140. *
  141. * @psalm-pure
  142. * @psalm-return list<string>
  143. * @return array
  144. */
  145. public static function keys()
  146. {
  147. return \array_keys(static::toArray());
  148. }
  149. /**
  150. * Returns instances of the Enum class of all Enum constants
  151. *
  152. * @psalm-pure
  153. * @psalm-return array<string, static>
  154. * @return static[] Constant name in key, Enum instance in value
  155. */
  156. public static function values()
  157. {
  158. $values = array();
  159. /** @psalm-var T $value */
  160. foreach (static::toArray() as $key => $value) {
  161. $values[$key] = new static($value);
  162. }
  163. return $values;
  164. }
  165. /**
  166. * Returns all possible values as an array
  167. *
  168. * @psalm-pure
  169. * @psalm-suppress ImpureStaticProperty
  170. *
  171. * @psalm-return array<string, mixed>
  172. * @return array Constant name in key, constant value in value
  173. */
  174. public static function toArray()
  175. {
  176. $class = static::class;
  177. if (!isset(static::$cache[$class])) {
  178. /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
  179. $reflection = new \ReflectionClass($class);
  180. /** @psalm-suppress ImpureMethodCall this reflection API usage has no side-effects here */
  181. static::$cache[$class] = $reflection->getConstants();
  182. }
  183. return static::$cache[$class];
  184. }
  185. /**
  186. * Check if is valid enum value
  187. *
  188. * @param $value
  189. * @psalm-param mixed $value
  190. * @psalm-pure
  191. * @psalm-assert-if-true T $value
  192. * @return bool
  193. */
  194. public static function isValid($value)
  195. {
  196. return \in_array($value, static::toArray(), true);
  197. }
  198. /**
  199. * Asserts valid enum value
  200. *
  201. * @psalm-pure
  202. * @psalm-assert T $value
  203. * @param mixed $value
  204. */
  205. public static function assertValidValue($value): void
  206. {
  207. self::assertValidValueReturningKey($value);
  208. }
  209. /**
  210. * Asserts valid enum value
  211. *
  212. * @psalm-pure
  213. * @psalm-assert T $value
  214. * @param mixed $value
  215. * @return string
  216. */
  217. private static function assertValidValueReturningKey($value): string
  218. {
  219. if (false === ($key = static::search($value))) {
  220. throw new \UnexpectedValueException("Value '$value' is not part of the enum " . static::class);
  221. }
  222. return $key;
  223. }
  224. /**
  225. * Check if is valid enum key
  226. *
  227. * @param $key
  228. * @psalm-param string $key
  229. * @psalm-pure
  230. * @return bool
  231. */
  232. public static function isValidKey($key)
  233. {
  234. $array = static::toArray();
  235. return isset($array[$key]) || \array_key_exists($key, $array);
  236. }
  237. /**
  238. * Return key for value
  239. *
  240. * @param mixed $value
  241. *
  242. * @psalm-param mixed $value
  243. * @psalm-pure
  244. * @return string|false
  245. */
  246. public static function search($value)
  247. {
  248. return \array_search($value, static::toArray(), true);
  249. }
  250. /**
  251. * Returns a value when called statically like so: MyEnum::SOME_VALUE() given SOME_VALUE is a class constant
  252. *
  253. * @param string $name
  254. * @param array $arguments
  255. *
  256. * @return static
  257. * @throws \BadMethodCallException
  258. *
  259. * @psalm-pure
  260. */
  261. public static function __callStatic($name, $arguments)
  262. {
  263. $class = static::class;
  264. if (!isset(self::$instances[$class][$name])) {
  265. $array = static::toArray();
  266. if (!isset($array[$name]) && !\array_key_exists($name, $array)) {
  267. $message = "No static method or enum constant '$name' in class " . static::class;
  268. throw new \BadMethodCallException($message);
  269. }
  270. return self::$instances[$class][$name] = new static($array[$name]);
  271. }
  272. return clone self::$instances[$class][$name];
  273. }
  274. /**
  275. * Specify data which should be serialized to JSON. This method returns data that can be serialized by json_encode()
  276. * natively.
  277. *
  278. * @return mixed
  279. * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
  280. * @psalm-pure
  281. */
  282. #[\ReturnTypeWillChange]
  283. public function jsonSerialize()
  284. {
  285. return $this->getValue();
  286. }
  287. }