[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/php-ai-client/src/Common/ -> AbstractEnum.php (source)

   1  <?php
   2  
   3  declare (strict_types=1);
   4  namespace WordPress\AiClient\Common;
   5  
   6  use BadMethodCallException;
   7  use JsonSerializable;
   8  use ReflectionClass;
   9  use WordPress\AiClient\Common\Exception\InvalidArgumentException;
  10  use WordPress\AiClient\Common\Exception\RuntimeException;
  11  /**
  12   * Abstract base class for enum-like behavior in PHP 7.4.
  13   *
  14   * This class provides enum-like functionality for PHP versions that don't support native enums.
  15   * Child classes should define uppercase snake_case constants for enum values.
  16   *
  17   * @example
  18   * class PersonEnum extends AbstractEnum {
  19   *     public const FIRST_NAME = 'first';
  20   *     public const LAST_NAME = 'last';
  21   * }
  22   *
  23   * // Usage:
  24   * $enum = PersonEnum::from('first'); // Creates instance with value 'first'
  25   * $enum = PersonEnum::tryFrom('invalid'); // Returns null
  26   * $enum = PersonEnum::firstName(); // Creates instance with value 'first'
  27   * $enum->name; // 'FIRST_NAME'
  28   * $enum->value; // 'first'
  29   * $enum->equals('first'); // Returns true
  30   * $enum->is(PersonEnum::firstName()); // Returns true
  31   * PersonEnum::cases(); // Returns array of all enum instances
  32   *
  33   * @property-read string $value The value of the enum instance.
  34   * @property-read string $name The name of the enum constant.
  35   *
  36   * @since 0.1.0
  37   */
  38  abstract class AbstractEnum implements JsonSerializable
  39  {
  40      /**
  41       * @var string The value of the enum instance.
  42       */
  43      private string $value;
  44      /**
  45       * @var string The name of the enum constant.
  46       */
  47      private string $name;
  48      /**
  49       * @var array<string, array<string, string>> Cache for reflection data.
  50       */
  51      private static array $cache = [];
  52      /**
  53       * @var array<string, array<string, self>> Cache for enum instances.
  54       */
  55      private static array $instances = [];
  56      /**
  57       * Constructor is private to ensure instances are created through static methods.
  58       *
  59       * @since 0.1.0
  60       *
  61       * @param string $value The enum value.
  62       * @param string $name The constant name.
  63       */
  64      final private function __construct(string $value, string $name)
  65      {
  66          $this->value = $value;
  67          $this->name = $name;
  68      }
  69      /**
  70       * Provides read-only access to properties.
  71       *
  72       * @since 0.1.0
  73       *
  74       * @param string $property The property name.
  75       * @return mixed The property value.
  76       * @throws BadMethodCallException If property doesn't exist.
  77       */
  78      final public function __get(string $property)
  79      {
  80          if ($property === 'value' || $property === 'name') {
  81              return $this->{$property};
  82          }
  83          throw new BadMethodCallException(sprintf('Property %s::%s does not exist', static::class, $property));
  84      }
  85      /**
  86       * Prevents property modification.
  87       *
  88       * @since 0.1.0
  89       *
  90       * @param string $property The property name.
  91       * @param mixed $value The value to set.
  92       * @throws BadMethodCallException Always, as enum properties are read-only.
  93       */
  94      final public function __set(string $property, $value): void
  95      {
  96          throw new BadMethodCallException(sprintf('Cannot modify property %s::%s - enum properties are read-only', static::class, $property));
  97      }
  98      /**
  99       * Creates an enum instance from a value, throws exception if invalid.
 100       *
 101       * @since 0.1.0
 102       *
 103       * @param string $value The enum value.
 104       * @return static The enum instance.
 105       * @throws InvalidArgumentException If the value is not valid.
 106       */
 107      final public static function from(string $value): self
 108      {
 109          $instance = self::tryFrom($value);
 110          if ($instance === null) {
 111              throw new InvalidArgumentException(sprintf('%s is not a valid backing value for enum %s', $value, static::class));
 112          }
 113          return $instance;
 114      }
 115      /**
 116       * Tries to create an enum instance from a value, returns null if invalid.
 117       *
 118       * @since 0.1.0
 119       *
 120       * @param string $value The enum value.
 121       * @return static|null The enum instance or null.
 122       */
 123      final public static function tryFrom(string $value): ?self
 124      {
 125          $constants = static::getConstants();
 126          foreach ($constants as $name => $constantValue) {
 127              if ($constantValue === $value) {
 128                  return self::getInstance($constantValue, $name);
 129              }
 130          }
 131          return null;
 132      }
 133      /**
 134       * Gets all enum cases.
 135       *
 136       * @since 0.1.0
 137       *
 138       * @return static[] Array of all enum instances.
 139       */
 140      final public static function cases(): array
 141      {
 142          $cases = [];
 143          $constants = static::getConstants();
 144          foreach ($constants as $name => $value) {
 145              $cases[] = self::getInstance($value, $name);
 146          }
 147          return $cases;
 148      }
 149      /**
 150       * Checks if this enum has the same value as the given value.
 151       *
 152       * @since 0.1.0
 153       *
 154       * @param string|self $other The value or enum to compare.
 155       * @return bool True if values are equal.
 156       */
 157      final public function equals($other): bool
 158      {
 159          if ($other instanceof self) {
 160              return $this->is($other);
 161          }
 162          return $this->value === $other;
 163      }
 164      /**
 165       * Checks if this enum is the same instance type and value as another enum.
 166       *
 167       * @since 0.1.0
 168       *
 169       * @param self $other The other enum to compare.
 170       * @return bool True if enums are identical.
 171       */
 172      final public function is(self $other): bool
 173      {
 174          return $this === $other;
 175          // Since we're using singletons, we can use identity comparison
 176      }
 177      /**
 178       * Gets all valid values for this enum.
 179       *
 180       * @since 0.1.0
 181       *
 182       * @return string[] List of all enum values.
 183       */
 184      final public static function getValues(): array
 185      {
 186          return array_values(static::getConstants());
 187      }
 188      /**
 189       * Checks if a value is valid for this enum.
 190       *
 191       * @since 0.1.0
 192       *
 193       * @param string $value The value to check.
 194       * @return bool True if value is valid.
 195       */
 196      final public static function isValidValue(string $value): bool
 197      {
 198          return in_array($value, self::getValues(), \true);
 199      }
 200      /**
 201       * Gets or creates a singleton instance for the given value and name.
 202       *
 203       * @since 0.1.0
 204       *
 205       * @param string $value The enum value.
 206       * @param string $name The constant name.
 207       * @return static The enum instance.
 208       */
 209      private static function getInstance(string $value, string $name): self
 210      {
 211          $className = static::class;
 212          if (!isset(self::$instances[$className])) {
 213              self::$instances[$className] = [];
 214          }
 215          if (!isset(self::$instances[$className][$name])) {
 216              $instance = new $className($value, $name);
 217              self::$instances[$className][$name] = $instance;
 218          }
 219          /** @var static */
 220          return self::$instances[$className][$name];
 221      }
 222      /**
 223       * Gets all constants for this enum class.
 224       *
 225       * @since 0.1.0
 226       *
 227       * @return array<string, string> Map of constant names to values.
 228       * @throws RuntimeException If invalid constant found.
 229       */
 230      final protected static function getConstants(): array
 231      {
 232          $className = static::class;
 233          if (!isset(self::$cache[$className])) {
 234              self::$cache[$className] = static::determineClassEnumerations($className);
 235          }
 236          return self::$cache[$className];
 237      }
 238      /**
 239       * Determines the class enumerations by reflecting on class constants.
 240       *
 241       * This method can be overridden by subclasses to customize how
 242       * enumerations are determined (e.g., to add dynamic constants).
 243       *
 244       * @since 0.1.0
 245       *
 246       * @param class-string $className The fully qualified class name.
 247       * @return array<string, string> Map of constant names to values.
 248       * @throws RuntimeException If invalid constant found.
 249       */
 250      protected static function determineClassEnumerations(string $className): array
 251      {
 252          $reflection = new ReflectionClass($className);
 253          $constants = $reflection->getConstants();
 254          // Validate all constants
 255          $enumConstants = [];
 256          foreach ($constants as $name => $value) {
 257              // Check if constant name follows uppercase snake_case pattern
 258              if (!preg_match('/^[A-Z][A-Z0-9_]*$/', $name)) {
 259                  throw new RuntimeException(sprintf('Invalid enum constant name "%s" in %s. Constants must be UPPER_SNAKE_CASE.', $name, $className));
 260              }
 261              // Check if value is valid type
 262              if (!is_string($value)) {
 263                  throw new RuntimeException(sprintf('Invalid enum value type for constant %s::%s. ' . 'Only string values are allowed, %s given.', $className, $name, gettype($value)));
 264              }
 265              $enumConstants[$name] = $value;
 266          }
 267          return $enumConstants;
 268      }
 269      /**
 270       * Handles dynamic method calls for enum checking.
 271       *
 272       * @since 0.1.0
 273       *
 274       * @param string $name The method name.
 275       * @param array<mixed> $arguments The method arguments.
 276       * @return bool True if the enum value matches.
 277       * @throws BadMethodCallException If the method doesn't exist.
 278       */
 279      final public function __call(string $name, array $arguments): bool
 280      {
 281          // Handle is* methods
 282          if (str_starts_with($name, 'is')) {
 283              $constantName = self::camelCaseToConstant(substr($name, 2));
 284              $constants = static::getConstants();
 285              if (isset($constants[$constantName])) {
 286                  return $this->value === $constants[$constantName];
 287              }
 288          }
 289          throw new BadMethodCallException(sprintf('Method %s::%s does not exist', static::class, $name));
 290      }
 291      /**
 292       * Handles static method calls for enum creation.
 293       *
 294       * @since 0.1.0
 295       *
 296       * @param string $name The method name.
 297       * @param array<mixed> $arguments The method arguments.
 298       * @return static The enum instance.
 299       * @throws BadMethodCallException If the method doesn't exist.
 300       */
 301      final public static function __callStatic(string $name, array $arguments): self
 302      {
 303          $constantName = self::camelCaseToConstant($name);
 304          $constants = static::getConstants();
 305          if (isset($constants[$constantName])) {
 306              return self::getInstance($constants[$constantName], $constantName);
 307          }
 308          throw new BadMethodCallException(sprintf('Method %s::%s does not exist', static::class, $name));
 309      }
 310      /**
 311       * Converts camelCase to CONSTANT_CASE.
 312       *
 313       * @since 0.1.0
 314       *
 315       * @param string $camelCase The camelCase string.
 316       * @return string The CONSTANT_CASE version.
 317       */
 318      private static function camelCaseToConstant(string $camelCase): string
 319      {
 320          $snakeCase = preg_replace('/([a-z])([A-Z])/', '$1_$2', $camelCase);
 321          if ($snakeCase === null) {
 322              return strtoupper($camelCase);
 323          }
 324          return strtoupper($snakeCase);
 325      }
 326      /**
 327       * Returns string representation of the enum.
 328       *
 329       * @since 0.1.0
 330       *
 331       * @return string The enum value.
 332       */
 333      final public function __toString(): string
 334      {
 335          return $this->value;
 336      }
 337      /**
 338       * Converts the enum to a JSON-serializable format.
 339       *
 340       * @since 0.1.0
 341       *
 342       * @return string The enum value.
 343       */
 344      #[\ReturnTypeWillChange]
 345      public function jsonSerialize()
 346      {
 347          return $this->value;
 348      }
 349  }


Generated : Sat Jun 13 09:38:55 2026 Cross-referenced by PHPXref