Git viewing holonet/common / master / src/Enum.php


Filter

      <?php
/**
 * This file is part of the holonet common library
 * (c) Matthias Lantsch.
 *
 * @license http://opensource.org/licenses/gpl-license.php  GNU Public License
 * @author  Matthias Lantsch <[email protected]>
 */

namespace holonet\common;

use BadMethodCallException;
use UnexpectedValueException;

/**
 * Wrapper class around the standard enum to make all value classes singleton instances
 * Goal was to get closer to the Java implementation of enums including multiple data values for each enum value.
 * @psalm-immutable
 */
abstract class Enum extends \MyCLabs\Enum\Enum {
	protected static $instances = array();

	/**
	 * @psalm-suppress OverriddenMethodAccess
	 * We must override the constructor so we can ensure the existence of only our instances
	 * {@inheritDoc}
	 */
	protected function __construct($value) {
		parent::__construct($value);
	}

	/**
	 * {@inheritDoc}
	 * @return static
	 * @psalm-suppress MissingImmutableAnnotation
	 */
	public static function __callStatic($name, $arguments): self {
		return static::valueOf($name);
	}

	public static function fromValue($value): self {
		$name = static::search($value);
		if ($name === false) {
			/** @psalm-suppress InvalidCast */
			throw new UnexpectedValueException("Value '{$value}' is not part of the enum ".static::class);
		}

		return static::valueOf($name);
	}

	/**
	 * {@inheritDoc}
	 * @psalm-suppress MissingImmutableAnnotation
	 */
	public static function isValid($value): bool {
		return static::search($value) !== false;
	}

	/**
	 * {@inheritDoc}
	 * @psalm-suppress MissingImmutableAnnotation
	 */
	public static function search($value) {
		$toArray = parent::toArray();
		foreach ($toArray as $name => $constValue) {
			if (is_array($constValue)) {
				$constValue = array_shift($constValue);
			}
			if ((string)$constValue === (string)$value) {
				return $name;
			}
		}

		return false;
	}

	/**
	 * {@inheritDoc}
	 * In order to not break any code outside relying on this method, we will reorganise to only return the
	 * base values not the basic constant array.
	 * @psalm-suppress MissingImmutableAnnotation
	 */
	public static function toArray() {
		$array = parent::toArray();
		foreach ($array as &$value) {
			if (is_array($value)) {
				$value = array_shift($value);
			}
		}

		return $array;
	}

	/**
	 * Try to get the enum implementation closer to a useful one
	 * By ensuring all enum objects are singletons, we can just use reference comparison in
	 * userland code.
	 */
	public static function valueOf(string $name): self {
		$array = parent::toArray();
		$class = get_called_class();
		if (isset($array[$name]) || \array_key_exists($name, $array)) {
			if (!isset(static::$instances[$class][$name])) {
				if (is_array($array[$name])) {
					$params = $array[$name];
				} else {
					$params = array($array[$name]);
				}

				static::$instances[$class][$name] = new static(...$params);
			}

			return static::$instances[$class][$name];
		}

		throw new BadMethodCallException("No static method or enum constant '{$name}' in class ".static::class);
	}

	/**
	 * {@inheritDoc}
	 * @psalm-suppress MissingImmutableAnnotation
	 */
	public static function values(): array {
		// just make sure all instances where created
		foreach (static::keys() as $key) {
			static::valueOf($key);
		}

		return static::$instances[get_called_class()];
	}
}