| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sat Jun 13 09:38:55 2026 | Cross-referenced by PHPXref |