질문자 :Henrik Paul
PHP에는 아직 기본 열거형이 없다는 것을 알고 있습니다. 그러나 나는 Java 세계에서 그것들에 익숙해졌습니다. IDE의 자동 완성 기능이 이해할 수 있는 미리 정의된 값을 제공하는 방법으로 열거형을 사용하고 싶습니다.
상수는 트릭을 할,하지만 거기에 네임 스페이스 충돌 문제이고 (또는 실제로 때문에)가 세계입니다. 배열에는 네임스페이스 문제가 없지만 너무 모호하고 런타임에 덮어쓸 수 있으며 IDE는 추가 정적 분석 주석이나 속성 없이 키를 자동으로 채우는 방법을 거의 모릅니다.
일반적으로 사용하는 솔루션/해결 방법이 있습니까? PHP 사용자가 열거형에 대해 어떤 생각이나 결정을 내렸는지 기억하는 사람이 있습니까?
사용 사례에 따라 일반적으로 다음과 같은 간단한 것을 사용합니다.
abstract class DaysOfWeek { const Sunday = 0; const Monday = 1; // etc. } $today = DaysOfWeek::Sunday;
그러나 다른 사용 사례에서는 상수 및 값에 대한 더 많은 유효성 검사가 필요할 수 있습니다. 리플렉션에 대한 아래의 설명과 몇 가지 다른 참고 사항을 기반으로 다음은 훨씬 더 광범위한 경우에 더 나은 서비스를 제공할 수 있는 확장된 예입니다.
abstract class BasicEnum { private static $constCacheArray = NULL; private static function getConstants() { if (self::$constCacheArray == NULL) { self::$constCacheArray = []; } $calledClass = get_called_class(); if (!array_key_exists($calledClass, self::$constCacheArray)) { $reflect = new ReflectionClass($calledClass); self::$constCacheArray[$calledClass] = $reflect->getConstants(); } return self::$constCacheArray[$calledClass]; } public static function isValidName($name, $strict = false) { $constants = self::getConstants(); if ($strict) { return array_key_exists($name, $constants); } $keys = array_map('strtolower', array_keys($constants)); return in_array(strtolower($name), $keys); } public static function isValidValue($value, $strict = true) { $values = array_values(self::getConstants()); return in_array($value, $values, $strict); } }
BasicEnum을 확장하는 간단한 열거형 클래스를 만들면 이제 간단한 입력 유효성 검사를 위해 메서드를 사용할 수 있습니다.
abstract class DaysOfWeek extends BasicEnum { const Sunday = 0; const Monday = 1; const Tuesday = 2; const Wednesday = 3; const Thursday = 4; const Friday = 5; const Saturday = 6; } DaysOfWeek::isValidName('Humpday'); // false DaysOfWeek::isValidName('Monday'); // true DaysOfWeek::isValidName('monday'); // true DaysOfWeek::isValidName('monday', $strict = true); // false DaysOfWeek::isValidName(0); // false DaysOfWeek::isValidValue(0); // true DaysOfWeek::isValidValue(5); // true DaysOfWeek::isValidValue(7); // false DaysOfWeek::isValidValue('Friday'); // false
참고로 데이터가 변경되지 않는 static/const 클래스 (예: 열거형)에서 리플렉션을 한 번 이상 사용할 때마다 매번 새로운 리플렉션 객체를 사용하기 때문에 해당 리플렉션 호출의 결과를 캐시합니다. 결국 성능에 눈에 띄는 영향을 미칩니다(여러 열거형에 대한 연관 배열에 저장됨).
이제 대부분의 사람들이 마침내 최소 5.3 SplEnum
을 사용할 수 있으므로 코드베이스 전체에서 실제 열거형 인스턴스화 를 갖는 전통적으로 직관적이지 않은 개념에 신경 쓰지 않는 한 확실히 실행 가능한 옵션입니다. 위의 예에서 BasicEnum
및 DaysOfWeek
는 인스턴스화될 수 없으며 인스턴스화되어서도 안 됩니다.
Brian Cline기본 확장도 있습니다. 스플에넘
SplEnum은 PHP에서 기본적으로 열거 개체를 에뮬레이트하고 생성하는 기능을 제공합니다.
http://www.php.net/manual/en/class.splenum.php
주목:
https://www.php.net/manual/en/spl-types.installation.php
PECL 확장은 PHP와 함께 제공되지 않습니다.
이 PECL 확장에 대한 DLL은 현재 사용할 수 없습니다.
Community Wiki클래스 상수는 어떻습니까?
<?php class YourClass { const SOME_CONSTANT = 1; public function echoConstant() { echo self::SOME_CONSTANT; } } echo YourClass::SOME_CONSTANT; $c = new YourClass; $c->echoConstant();
Peter Bailey위의 최고 답변은 환상적입니다. 그러나 extend
하는 경우 먼저 확장이 수행된 결과 함수 호출이 캐시를 생성합니다. 이 캐시는 호출이 시작되는 확장에 관계없이 모든 후속 호출에서 사용됩니다.
이 문제를 해결하려면 변수와 첫 번째 함수를 다음으로 바꾸십시오.
private static $constCacheArray = null; private static function getConstants() { if (self::$constCacheArray === null) self::$constCacheArray = array(); $calledClass = get_called_class(); if (!array_key_exists($calledClass, self::$constCacheArray)) { $reflect = new \ReflectionClass($calledClass); self::$constCacheArray[$calledClass] = $reflect->getConstants(); } return self::$constCacheArray[$calledClass]; }
Neil Townsend상수가 있는 클래스를 사용했습니다.
class Enum { const NAME = 'aaaa'; const SOME_VALUE = 'bbbb'; } print Enum::NAME;
andy.gurin나는 여기에 다른 답변 중 일부에 대해 언급 했으므로 나도 저울질 할 것이라고 생각했습니다. 결국 PHP는 유형이 지정된 열거를 지원하지 않으므로 두 가지 방법 중 하나를 사용할 수 있습니다. 유형이 있는 열거를 해킹하거나 효과적으로 해킹하기가 극히 어렵다는 사실을 안고 살아가는 것입니다.
나는 사실과 함께 사는 것을 선호하고 대신 여기에 다른 답변이 어떤 식 으로든 const
abstract class Enum { const NONE = null; final private function __construct() { throw new NotSupportedException(); // } final private function __clone() { throw new NotSupportedException(); } final public static function toArray() { return (new ReflectionClass(static::class))->getConstants(); } final public static function isValid($value) { return in_array($value, static::toArray()); } }
예시 열거:
final class ResponseStatusCode extends Enum { const OK = 200; const CREATED = 201; const ACCEPTED = 202; // ... const SERVICE_UNAVAILABLE = 503; const GATEWAY_TIME_OUT = 504; const HTTP_VERSION_NOT_SUPPORTED = 505; }
다른 모든 열거가 확장되는 기본 클래스로 Enum
을 사용하면 toArray
, isValid
등과 같은 도우미 메서드를 사용할 수 있습니다. 나에게 입력된 열거형( 및 인스턴스 관리 )이 너무 지저분해집니다.
가상의
만약 __getStatic
매직 메소드가 존재 한다면(그리고 바람직하게는 __equals
매직 메소드도 ) 일종의 멀티톤 패턴으로 이 중 상당 부분을 완화할 수 있습니다.
( 다음은 가설입니다. 작동하지 않을 수도 있지만 언젠가는 작동할 것입니다 )
final class TestEnum { private static $_values = [ 'FOO' => 1, 'BAR' => 2, 'QUX' => 3, ]; private static $_instances = []; public static function __getStatic($name) { if (isset(static::$_values[$name])) { if (empty(static::$_instances[$name])) { static::$_instances[$name] = new static($name); } return static::$_instances[$name]; } throw new Exception(sprintf('Invalid enumeration value, "%s"', $name)); } private $_value; public function __construct($name) { $this->_value = static::$_values[$name]; } public function __equals($object) { if ($object instanceof static) { return $object->_value === $this->_value; } return $object === $this->_value; } } $foo = TestEnum::$FOO; // object(TestEnum)#1 (1) { // ["_value":"TestEnum":private]=> // int(1) // } $zap = TestEnum::$ZAP; // Uncaught exception 'Exception' with message // 'Invalid enumeration member, "ZAP"' $qux = TestEnum::$QUX; TestEnum::$QUX == $qux; // true 'hello world!' == $qux; // false
Dan LuggPHP 8.1부터 네이티브 열거를 사용할 수 있습니다.
기본 구문은 다음과 같습니다.
enum TransportMode { case Bicycle; case Car; case Ship; case Plane; case Feet; }
function travelCost(Vehicle $vehicle, int $distance): int { /* implementation */ } $mode = TransportMode::Boat; $bikeCost = travelCost(TransportMode::Bicycle, 90); $boatCost = travelCost($mode, 90); // this one would fail: (Enums are singletons, not scalars) $failCost = travelCost('Car', 90);
가치
기본적으로 열거형은 어떤 종류의 스칼라도 지원하지 않습니다. 따라서 TransportMode::Bicycle
0
이 아니며 열거 간에 >
또는 <
를 사용하여 비교할 수 없습니다.
그러나 다음이 작동합니다.
$foo = TransportMode::Car; $bar = TransportMode::Car; $baz = TransportMode::Bicycle; $foo === $bar; // true $bar === $baz; // false $foo instanceof TransportMode; // true $foo > $bar || $foo < $bar; // false either way
지원되는 열거
"backed" enum을 가질 수도 있습니다. 여기서 각 enumeration case는 int
또는 string
의해 "backed"됩니다.
enum Metal: int { case Gold = 1932; case Silver = 1049; case Lead = 1134; case Uranium = 1905; case Copper = 894; }
- 한 케이스에 지원되는 값이 있는 경우 모든 케이스에 지원되는 값이 있어야 하며 자동 생성된 값은 없습니다.
- 지원되는 값의 유형은 열거형 이름 바로 뒤에 선언됩니다.
- 지원되는 값은 읽기 전용입니다.
- 스칼라 값은 고유해야 합니다.
- 값은 리터럴 또는 리터럴 표현식이어야 합니다.
- 지원되는 값을 읽으려면
value
속성에 Metal::Gold->value
.
마지막으로 지원되는 열거형은 BackedEnum
인터페이스를 구현하여 두 가지 메서드를 제공합니다.
-
from(int|string): self
-
tryFrom(int|string): ?self
그것들은 거의 동일하지만 값이 발견되지 않으면 첫 번째 것은 예외를 throw하고 두 번째는 단순히 null
반환합니다.
// usage example: $metal_1 = Metal::tryFrom(1932); // $metal_1 === Metal::Gold; $metal_2 = Metal::tryFrom(1000); // $metal_2 === null; $metal_3 = Metal::from(9999); // throws Exception
행동 양식
열거형에는 메서드가 있을 수 있으므로 인터페이스를 구현합니다.
interface TravelCapable { public function travelCost(int $distance): int; public function requiresFuel(): bool; } enum TransportMode: int implements TravelCapable{ case Bicycle = 10; case Car = 1000 ; case Ship = 800 ; case Plane = 2000; case Feet = 5; public function travelCost(int $distance): int { return $this->value * $distance; } public function requiresFuel(): bool { return match($this) { TransportMode::Car, TransportMode::Ship, TransportMode::Plane => true, TransportMode::Bicycle, TransportMode::Feet => false } } } $mode = TransportMode::Car; $carConsumesFuel = $mode->requiresFuel(); // true $carTravelCost = $mode->travelCost(800); // 800000
가치 목록
Pure Enums 및 Backed Enums 모두 내부적으로 UnitEnum
인터페이스를 구현하며, 여기에는 (정적) 메서드 UnitEnum::cases()
가 포함되며 열거형에 정의된 케이스 배열을 검색할 수 있습니다.
$modes = TransportMode::cases();
이제 $modes
는 다음과 같습니다.
[ TransportMode::Bicycle, TransportMode::Car, TransportMode::Ship, TransportMode::Plane TransportMode::Feet ]
정적 메서드
열거형은 일반적으로 특수 생성자에 사용되는 static
메서드를 구현할 수 있습니다.
이것은 기본 사항을 다룹니다. 모든 것을 얻으려면 해당 기능이 릴리스되어 PHP 문서에 게시될 때까지 관련 RFC로 이동하십시오.
yiviclass
대신 interface
를 사용합니다.
interface DaysOfWeek { const Sunday = 0; const Monday = 1; // etc. } var $today = DaysOfWeek::Sunday;
Andi T글쎄, PHP의 enum과 같은 간단한 자바의 경우 다음을 사용합니다.
class SomeTypeName { private static $enum = array(1 => "Read", 2 => "Write"); public function toOrdinal($name) { return array_search($name, self::$enum); } public function toString($ordinal) { return self::$enum[$ordinal]; } }
그리고 그것을 부르려면 :
SomeTypeName::toOrdinal("Read"); SomeTypeName::toString(1);
그러나 저는 PHP 초보자이며 구문으로 어려움을 겪고 있으므로 이것이 최선의 방법이 아닐 수도 있습니다. 나는 그것의 값에서 상수 이름을 얻기 위해 Reflection을 사용하여 클래스 상수로 몇 가지를 실험했습니다. 더 깔끔할 수 있습니다.
aelg4년 후 나는 이것을 다시 만났다. 내 현재 접근 방식은 IDE에서 코드 완성과 유형 안전성을 허용하기 때문에 다음과 같습니다.
기본 클래스:
abstract class TypedEnum { private static $_instancedValues; private $_value; private $_name; private function __construct($value, $name) { $this->_value = $value; $this->_name = $name; } private static function _fromGetter($getter, $value) { $reflectionClass = new ReflectionClass(get_called_class()); $methods = $reflectionClass->getMethods(ReflectionMethod::IS_STATIC | ReflectionMethod::IS_PUBLIC); $className = get_called_class(); foreach($methods as $method) { if ($method->class === $className) { $enumItem = $method->invoke(null); if ($enumItem instanceof $className && $enumItem->$getter() === $value) { return $enumItem; } } } throw new OutOfRangeException(); } protected static function _create($value) { if (self::$_instancedValues === null) { self::$_instancedValues = array(); } $className = get_called_class(); if (!isset(self::$_instancedValues[$className])) { self::$_instancedValues[$className] = array(); } if (!isset(self::$_instancedValues[$className][$value])) { $debugTrace = debug_backtrace(); $lastCaller = array_shift($debugTrace); while ($lastCaller['class'] !== $className && count($debugTrace) > 0) { $lastCaller = array_shift($debugTrace); } self::$_instancedValues[$className][$value] = new static($value, $lastCaller['function']); } return self::$_instancedValues[$className][$value]; } public static function fromValue($value) { return self::_fromGetter('getValue', $value); } public static function fromName($value) { return self::_fromGetter('getName', $value); } public function getValue() { return $this->_value; } public function getName() { return $this->_name; } }
열거형 예:
final class DaysOfWeek extends TypedEnum { public static function Sunday() { return self::_create(0); } public static function Monday() { return self::_create(1); } public static function Tuesday() { return self::_create(2); } public static function Wednesday() { return self::_create(3); } public static function Thursday() { return self::_create(4); } public static function Friday() { return self::_create(5); } public static function Saturday() { return self::_create(6); } }
사용 예:
function saveEvent(DaysOfWeek $weekDay, $comment) { // store week day numeric value and comment: $myDatabase->save('myeventtable', array('weekday_id' => $weekDay->getValue()), array('comment' => $comment)); } // call the function, note: DaysOfWeek::Monday() returns an object of type DaysOfWeek saveEvent(DaysOfWeek::Monday(), 'some comment');
동일한 열거형 항목의 모든 인스턴스는 동일합니다.
$monday1 = DaysOfWeek::Monday(); $monday2 = DaysOfWeek::Monday(); $monday1 === $monday2; // true
switch 문 내에서 사용할 수도 있습니다.
function getGermanWeekDayName(DaysOfWeek $weekDay) { switch ($weekDay) { case DaysOfWeek::Monday(): return 'Montag'; case DaysOfWeek::Tuesday(): return 'Dienstag'; // ... }
이름이나 값으로 열거형 항목을 만들 수도 있습니다.
$monday = DaysOfWeek::fromValue(2); $tuesday = DaysOfWeek::fromName('Tuesday');
또는 기존 열거형 항목에서 이름(예: 함수 이름)을 가져올 수 있습니다.
$wednesday = DaysOfWeek::Wednesday() echo $wednesDay->getName(); // Wednesday
Buck Fixinggithub에서 이 라이브러리 를 찾았고 여기에 있는 답변에 대한 매우 적절한 대안을 제공한다고 생각합니다.
SplEnum에서 영감을 받은 PHP Enum 구현
- 다음과 같이 힌트를 입력할 수 있습니다.
function setAction(Action $action) {
- 열거형을 메서드(예:
format
, parse
, …)로 풍부하게 할 수 있습니다. - enum을 확장하여 새 값을 추가할 수 있습니다(이를 방지하기 위해
final
- 가능한 모든 값의 목록을 얻을 수 있습니다(아래 참조)
선언
<?php use MyCLabs\Enum\Enum; /** * Action enum */ class Action extends Enum { const VIEW = 'view'; const EDIT = 'edit'; }
용법
<?php $action = new Action(Action::VIEW); // or $action = Action::VIEW();
유형 힌트 열거형 값:
<?php function setAction(Action $action) { // ... }
Songo전역적으로 고유하고(예: 다른 Enum 간에 요소를 비교할 때도) 사용하기 쉬운 열거형을 사용해야 하는 경우 다음 코드를 자유롭게 사용하십시오. 또한 유용하다고 생각되는 몇 가지 방법을 추가했습니다. 코드 맨 위에 있는 주석에서 예제를 찾을 수 있습니다.
<?php /** * Class Enum * * @author Christopher Fox <christopher.fox@gmx.de> * * @version 1.0 * * This class provides the function of an enumeration. * The values of Enum elements are unique (even between different Enums) * as you would expect them to be. * * Constructing a new Enum: * ======================== * * In the following example we construct an enum called "UserState" * with the elements "inactive", "active", "banned" and "deleted". * * <code> * Enum::Create('UserState', 'inactive', 'active', 'banned', 'deleted'); * </code> * * Using Enums: * ============ * * The following example demonstrates how to compare two Enum elements * * <code> * var_dump(UserState::inactive == UserState::banned); // result: false * var_dump(UserState::active == UserState::active); // result: true * </code> * * Special Enum methods: * ===================== * * Get the number of elements in an Enum: * * <code> * echo UserState::CountEntries(); // result: 4 * </code> * * Get a list with all elements of the Enum: * * <code> * $allUserStates = UserState::GetEntries(); * </code> * * Get a name of an element: * * <code> * echo UserState::GetName(UserState::deleted); // result: deleted * </code> * * Get an integer ID for an element (eg to store as a value in a database table): * This is simply the index of the element (beginning with 1). * Note that this ID is only unique for this Enum but now between different Enums. * * <code> * echo UserState::GetDatabaseID(UserState::active); // result: 2 * </code> */ class Enum { /** * @var Enum $instance The only instance of Enum (Singleton) */ private static $instance; /** * @var array $enums An array of all enums with Enum names as keys * and arrays of element names as values */ private $enums; /** * Constructs (the only) Enum instance */ private function __construct() { $this->enums = array(); } /** * Constructs a new enum * * @param string $name The class name for the enum * @param mixed $_ A list of strings to use as names for enum entries */ public static function Create($name, $_) { // Create (the only) Enum instance if this hasn't happened yet if (self::$instance===null) { self::$instance = new Enum(); } // Fetch the arguments of the function $args = func_get_args(); // Exclude the "name" argument from the array of function arguments, // so only the enum element names remain in the array array_shift($args); self::$instance->add($name, $args); } /** * Creates an enumeration if this hasn't happened yet * * @param string $name The class name for the enum * @param array $fields The names of the enum elements */ private function add($name, $fields) { if (!array_key_exists($name, $this->enums)) { $this->enums[$name] = array(); // Generate the code of the class for this enumeration $classDeclaration = "class " . $name . " {\n" . "private static \$name = '" . $name . "';\n" . $this->getClassConstants($name, $fields) . $this->getFunctionGetEntries($name) . $this->getFunctionCountEntries($name) . $this->getFunctionGetDatabaseID() . $this->getFunctionGetName() . "}"; // Create the class for this enumeration eval($classDeclaration); } } /** * Returns the code of the class constants * for an enumeration. These are the representations * of the elements. * * @param string $name The class name for the enum * @param array $fields The names of the enum elements * * @return string The code of the class constants */ private function getClassConstants($name, $fields) { $constants = ''; foreach ($fields as $field) { // Create a unique ID for the Enum element // This ID is unique because class and variables // names can't contain a semicolon. Therefore we // can use the semicolon as a separator here. $uniqueID = $name . ";" . $field; $constants .= "const " . $field . " = '". $uniqueID . "';\n"; // Store the unique ID array_push($this->enums[$name], $uniqueID); } return $constants; } /** * Returns the code of the function "GetEntries()" * for an enumeration * * @param string $name The class name for the enum * * @return string The code of the function "GetEntries()" */ private function getFunctionGetEntries($name) { $entryList = ''; // Put the unique element IDs in single quotes and // separate them with commas foreach ($this->enums[$name] as $key => $entry) { if ($key > 0) $entryList .= ','; $entryList .= "'" . $entry . "'"; } return "public static function GetEntries() { \n" . " return array(" . $entryList . ");\n" . "}\n"; } /** * Returns the code of the function "CountEntries()" * for an enumeration * * @param string $name The class name for the enum * * @return string The code of the function "CountEntries()" */ private function getFunctionCountEntries($name) { // This function will simply return a constant number (eg return 5;) return "public static function CountEntries() { \n" . " return " . count($this->enums[$name]) . ";\n" . "}\n"; } /** * Returns the code of the function "GetDatabaseID()" * for an enumeration * * @return string The code of the function "GetDatabaseID()" */ private function getFunctionGetDatabaseID() { // Check for the index of this element inside of the array // of elements and add +1 return "public static function GetDatabaseID(\$entry) { \n" . "\$key = array_search(\$entry, self::GetEntries());\n" . " return \$key + 1;\n" . "}\n"; } /** * Returns the code of the function "GetName()" * for an enumeration * * @return string The code of the function "GetName()" */ private function getFunctionGetName() { // Remove the class name from the unique ID // and return this value (which is the element name) return "public static function GetName(\$entry) { \n" . "return substr(\$entry, strlen(self::\$name) + 1 , strlen(\$entry));\n" . "}\n"; } } ?>
Christopher Fox나도 자바의 열거형을 좋아하고 이런 이유로 열거형을 이런 식으로 작성합니다. 물론 이것이 Java 열거형에서와 같이 가장 유사한 동작이라고 생각합니다. 물론 일부 사람들이 Java에서 더 많은 메서드를 사용하려면 여기에 작성해야 합니다. 추상 클래스이지만 핵심 아이디어는 아래 코드에 포함되어 있습니다.
class FruitsEnum { static $APPLE = null; static $ORANGE = null; private $value = null; public static $map; public function __construct($value) { $this->value = $value; } public static function init () { self::$APPLE = new FruitsEnum("Apple"); self::$ORANGE = new FruitsEnum("Orange"); //static map to get object by name - example Enum::get("INIT") - returns Enum::$INIT object; self::$map = array ( "Apple" => self::$APPLE, "Orange" => self::$ORANGE ); } public static function get($element) { if($element == null) return null; return self::$map[$element]; } public function getValue() { return $this->value; } public function equals(FruitsEnum $element) { return $element->getValue() == $this->getValue(); } public function __toString () { return $this->value; } } FruitsEnum::init(); var_dump(FruitsEnum::$APPLE->equals(FruitsEnum::$APPLE)); //true var_dump(FruitsEnum::$APPLE->equals(FruitsEnum::$ORANGE)); //false var_dump(FruitsEnum::$APPLE instanceof FruitsEnum); //true var_dump(FruitsEnum::get("Apple")->equals(FruitsEnum::$APPLE)); //true - enum from string var_dump(FruitsEnum::get("Apple")->equals(FruitsEnum::get("Orange"))); //false
user667540abstract class Enumeration { public static function enum() { $reflect = new ReflectionClass( get_called_class() ); return $reflect->getConstants(); } } class Test extends Enumeration { const A = 'a'; const B = 'b'; } foreach (Test::enum() as $key => $value) { echo "$key -> $value<br>"; }
jglatre다음과 같이 간단할 수 있습니다.
enum DaysOfWeek { Sunday, Monday, // ... }
미래에.
PHP RFC: 열거형
mykhal내가 PHP에서 열거형에 대해 본 가장 일반적인 솔루션은 일반 열거형 클래스를 만든 다음 확장하는 것입니다. 당신은 한 번 봐 걸릴 수 있습니다 이 .
업데이트: 또는 phpclasses.org에서 이것을 찾았습니다.
Noah Goodrich다음은 PHP에서 유형 안전 열거를 처리하기 위한 github 라이브러리입니다.
이 라이브러리는 클래스 생성, 클래스 캐싱을 처리하고 열거형 정렬을 위한 서수 검색 또는 열거형 조합의 이진 값 검색과 같은 열거형을 처리하기 위한 여러 도우미 메서드와 함께 Type Safe Enumeration 디자인 패턴을 구현합니다.
생성된 코드는 구성 가능한 일반 이전 PHP 템플릿 파일을 사용하므로 고유한 템플릿을 제공할 수 있습니다.
그것은 phpunit으로 덮인 전체 테스트입니다.
github의 php-enums(자유롭게 포크)
사용법: (자세한 내용은 @ usage.php 또는 단위 테스트 참조)
<?php //require the library require_once __DIR__ . '/src/Enum.func.php'; //if you don't have a cache directory, create one @mkdir(__DIR__ . '/cache'); EnumGenerator::setDefaultCachedClassesDir(__DIR__ . '/cache'); //Class definition is evaluated on the fly: Enum('FruitsEnum', array('apple' , 'orange' , 'rasberry' , 'bannana')); //Class definition is cached in the cache directory for later usage: Enum('CachedFruitsEnum', array('apple' , 'orange' , 'rasberry' , 'bannana'), '\my\company\name\space', true); echo 'FruitsEnum::APPLE() == FruitsEnum::APPLE(): '; var_dump(FruitsEnum::APPLE() == FruitsEnum::APPLE()) . "\n"; echo 'FruitsEnum::APPLE() == FruitsEnum::ORANGE(): '; var_dump(FruitsEnum::APPLE() == FruitsEnum::ORANGE()) . "\n"; echo 'FruitsEnum::APPLE() instanceof Enum: '; var_dump(FruitsEnum::APPLE() instanceof Enum) . "\n"; echo 'FruitsEnum::APPLE() instanceof FruitsEnum: '; var_dump(FruitsEnum::APPLE() instanceof FruitsEnum) . "\n"; echo "->getName()\n"; foreach (FruitsEnum::iterator() as $enum) { echo " " . $enum->getName() . "\n"; } echo "->getValue()\n"; foreach (FruitsEnum::iterator() as $enum) { echo " " . $enum->getValue() . "\n"; } echo "->getOrdinal()\n"; foreach (CachedFruitsEnum::iterator() as $enum) { echo " " . $enum->getOrdinal() . "\n"; } echo "->getBinary()\n"; foreach (CachedFruitsEnum::iterator() as $enum) { echo " " . $enum->getBinary() . "\n"; }
산출:
FruitsEnum::APPLE() == FruitsEnum::APPLE(): bool(true) FruitsEnum::APPLE() == FruitsEnum::ORANGE(): bool(false) FruitsEnum::APPLE() instanceof Enum: bool(true) FruitsEnum::APPLE() instanceof FruitsEnum: bool(true) ->getName() APPLE ORANGE RASBERRY BANNANA ->getValue() apple orange rasberry bannana ->getValue() when values have been specified pig dog cat bird ->getOrdinal() 1 2 3 4 ->getBinary() 1 2 4 8
zanshine함수 매개변수에 대한 유형 안전성, NetBeans의 자동 완성 및 우수한 성능을 제공하는 아래 접근 방식을 사용했습니다. 내가 너무 좋아하지 않는 한 가지는 [extended class name]::enumerate();
클래스를 정의한 후
abstract class Enum { private $_value; protected function __construct($value) { $this->_value = $value; } public function __toString() { return (string) $this->_value; } public static function enumerate() { $class = get_called_class(); $ref = new ReflectionClass($class); $statics = $ref->getStaticProperties(); foreach ($statics as $name => $value) { $ref->setStaticPropertyValue($name, new $class($value)); } } } class DaysOfWeek extends Enum { public static $MONDAY = 0; public static $SUNDAY = 1; // etc. } DaysOfWeek::enumerate(); function isMonday(DaysOfWeek $d) { if ($d == DaysOfWeek::$MONDAY) { return true; } else { return false; } } $day = DaysOfWeek::$MONDAY; echo (isMonday($day) ? "bummer it's monday" : "Yay! it's not monday");
Brian Fisher아래 내 Enum 클래스 정의는 Strongly typed 이며 사용 및 정의하기가 매우 자연스럽습니다.
정의:
class Fruit extends Enum { static public $APPLE = 1; static public $ORANGE = 2; } Fruit::initialize(); //Can also be called in autoloader
열거형으로 전환
$myFruit = Fruit::$APPLE; switch ($myFruit) { case Fruit::$APPLE : echo "I like apples\n"; break; case Fruit::$ORANGE : echo "I hate oranges\n"; break; } >> I like apples
Enum을 매개변수로 전달(강력한 형식)
/** Function only accepts Fruit enums as input**/ function echoFruit(Fruit $fruit) { echo $fruit->getName().": ".$fruit->getValue()."\n"; } /** Call function with each Enum value that Fruit has */ foreach (Fruit::getList() as $fruit) { echoFruit($fruit); } //Call function with Apple enum echoFruit(Fruit::$APPLE) //Will produce an error. This solution is strongly typed echoFruit(2); >> APPLE: 1 >> ORANGE: 2 >> APPLE: 1 >> Argument 1 passed to echoFruit() must be an instance of Fruit, integer given
Echo Enum을 문자열로
echo "I have an $myFruit\n"; >> I have an APPLE
정수로 열거형 가져오기
$myFruit = Fruit::getByValue(2); echo "Now I have an $myFruit\n"; >> Now I have an ORANGE
이름으로 열거형 가져오기
$myFruit = Fruit::getByName("APPLE"); echo "But I definitely prefer an $myFruit\n\n"; >> But I definitely prefer an APPLE
열거형 클래스:
/** * @author Torge Kummerow */ class Enum { /** * Holds the values for each type of Enum */ static private $list = array(); /** * Initializes the enum values by replacing the number with an instance of itself * using reflection */ static public function initialize() { $className = get_called_class(); $class = new ReflectionClass($className); $staticProperties = $class->getStaticProperties(); self::$list[$className] = array(); foreach ($staticProperties as $propertyName => &$value) { if ($propertyName == 'list') continue; $enum = new $className($propertyName, $value); $class->setStaticPropertyValue($propertyName, $enum); self::$list[$className][$propertyName] = $enum; } unset($value); } /** * Gets the enum for the given value * * @param integer $value * @throws Exception * * @return Enum */ static public function getByValue($value) { $className = get_called_class(); foreach (self::$list[$className] as $propertyName=>&$enum) { /* @var $enum Enum */ if ($enum->value == $value) return $enum; } unset($enum); throw new Exception("No such enum with value=$value of type ".get_called_class()); } /** * Gets the enum for the given name * * @param string $name * @throws Exception * * @return Enum */ static public function getByName($name) { $className = get_called_class(); if (array_key_exists($name, static::$list[$className])) return self::$list[$className][$name]; throw new Exception("No such enum ".get_called_class()."::\$$name"); } /** * Returns the list of all enum variants * @return Array of Enum */ static public function getList() { $className = get_called_class(); return self::$list[$className]; } private $name; private $value; public function __construct($name, $value) { $this->name = $name; $this->value = $value; } public function __toString() { return $this->name; } public function getValue() { return $this->value; } public function getName() { return $this->name; } }
덧셈
물론 IDE에 대한 주석을 추가할 수도 있습니다.
class Fruit extends Enum { /** * This comment is for autocomplete support in common IDEs * @var Fruit A yummy apple */ static public $APPLE = 1; /** * This comment is for autocomplete support in common IDEs * @var Fruit A sour orange */ static public $ORANGE = 2; } //This can also go to the autoloader if available. Fruit::initialize();
Torge나는 이것이 아주 아주 아주 오래된 스레드라는 것을 알고 있지만 이것에 대해 생각했고 사람들이 어떻게 생각하는지 알고 싶었습니다.
참고: 나는 이것을 가지고 놀다가 __call()
enums
더 가까워질 수 있다는 것을 깨달았습니다. __call()
함수는 알 수 없는 모든 함수 호출을 처리합니다. 세 개의 enums
RED_LIGHT, YELLOW_LIGHT 및 GREEN_LIGHT를 만들고 싶다고 가정해 보겠습니다. 이제 다음을 수행하면 됩니다.
$c->RED_LIGHT(); $c->YELLOW_LIGHT(); $c->GREEN_LIGHT();
일단 정의되면 값을 얻기 위해 다시 호출하기만 하면 됩니다.
echo $c->RED_LIGHT(); echo $c->YELLOW_LIGHT(); echo $c->GREEN_LIGHT();
0, 1, 2를 얻어야 합니다. 즐겁게 보내세요! 이것은 이제 GitHub에도 있습니다.
__get()
및 __set()
함수가 모두 사용되도록 만들었습니다. 이렇게 하면 원하지 않는 한 함수를 호출하지 않아도 됩니다. 대신 이제 다음과 같이 말할 수 있습니다.
$c->RED_LIGHT; $c->YELLOW_LIGHT; $c->GREEN_LIGHT;
가치의 생성과 획득 모두를 위해. 변수가 처음에 정의되지 않았기 때문에 __get()
함수가 호출되어(지정된 값이 없기 때문에) 배열의 항목이 만들어지지 않았음을 확인합니다. 따라서 항목을 만들고 마지막 값에 1(+1)을 더한 값을 할당하고 마지막 값 변수를 증가시키고 TRUE를 반환합니다. 값을 설정하는 경우:
$c->RED_LIGHT = 85;
그런 다음 __set()
함수가 호출되고 마지막 값이 새 값에 1을 더한 값(+1)으로 설정됩니다. 이제 enum을 수행하는 상당히 좋은 방법이 있으며 즉시 생성할 수 있습니다.
<?php ################################################################################ # Class ENUMS # # Original code by Mark Manning. # Copyrighted (c) 2015 by Mark Manning. # All rights reserved. # # This set of code is hereby placed into the free software universe # via the GNU greater license thus placing it under the Copyleft # rules and regulations with the following modifications: # # 1. You may use this work in any other work. Commercial or otherwise. # 2. You may make as much money as you can with it. # 3. You owe me nothing except to give me a small blurb somewhere in # your program or maybe have pity on me and donate a dollar to # sim_sales@paypal.com. :-) # # Blurb: # # PHP Class Enums by Mark Manning (markem-AT-sim1-DOT-us). # Used with permission. # # Notes: # # VIM formatting. Set tabs to four(4) spaces. # ################################################################################ class enums { private $enums; private $clear_flag; private $last_value; ################################################################################ # __construct(). Construction function. Optionally pass in your enums. ################################################################################ function __construct() { $this->enums = array(); $this->clear_flag = false; $this->last_value = 0; if( func_num_args() > 0 ){ return $this->put( func_get_args() ); } return true; } ################################################################################ # put(). Insert one or more enums. ################################################################################ function put() { $args = func_get_args(); # # Did they send us an array of enums? # Ex: $c->put( array( "a"=>0, "b"=>1,...) ); # OR $c->put( array( "a", "b", "c",... ) ); # if( is_array($args[0]) ){ # # Add them all in # foreach( $args[0] as $k=>$v ){ # # Don't let them change it once it is set. # Remove the IF statement if you want to be able to modify the enums. # if( !isset($this->enums[$k]) ){ # # If they sent an array of enums like this: "a","b","c",... then we have to # change that to be "A"=>#. Where "#" is the current count of the enums. # if( is_numeric($k) ){ $this->enums[$v] = $this->last_value++; } # # Else - they sent "a"=>"A", "b"=>"B", "c"=>"C"... # else { $this->last_value = $v + 1; $this->enums[$k] = $v; } } } } # # Nope! Did they just sent us one enum? # else { # # Is this just a default declaration? # Ex: $c->put( "a" ); # if( count($args) < 2 ){ # # Again - remove the IF statement if you want to be able to change the enums. # if( !isset($this->enums[$args[0]]) ){ $this->enums[$args[0]] = $this->last_value++; } # # No - they sent us a regular enum # Ex: $c->put( "a", "This is the first enum" ); # else { # # Again - remove the IF statement if you want to be able to change the enums. # if( !isset($this->enums[$args[0]]) ){ $this->last_value = $args[1] + 1; $this->enums[$args[0]] = $args[1]; } } } } return true; } ################################################################################ # get(). Get one or more enums. ################################################################################ function get() { $num = func_num_args(); $args = func_get_args(); # # Is this an array of enums request? (ie: $c->get(array("a","b","c"...)) ) # if( is_array($args[0]) ){ $ary = array(); foreach( $args[0] as $k=>$v ){ $ary[$v] = $this->enums[$v]; } return $ary; } # # Is it just ONE enum they want? (ie: $c->get("a") ) # else if( ($num > 0) && ($num < 2) ){ return $this->enums[$args[0]]; } # # Is it a list of enums they want? (ie: $c->get( "a", "b", "c"...) ) # else if( $num > 1 ){ $ary = array(); foreach( $args as $k=>$v ){ $ary[$v] = $this->enums[$v]; } return $ary; } # # They either sent something funky or nothing at all. # return false; } ################################################################################ # clear(). Clear out the enum array. # Optional. Set the flag in the __construct function. # After all, ENUMS are supposed to be constant. ################################################################################ function clear() { if( $clear_flag ){ unset( $this->enums ); $this->enums = array(); } return true; } ################################################################################ # __call(). In case someone tries to blow up the class. ################################################################################ function __call( $name, $arguments ) { if( isset($this->enums[$name]) ){ return $this->enums[$name]; } else if( !isset($this->enums[$name]) && (count($arguments) > 0) ){ $this->last_value = $arguments[0] + 1; $this->enums[$name] = $arguments[0]; return true; } else if( !isset($this->enums[$name]) && (count($arguments) < 1) ){ $this->enums[$name] = $this->last_value++; return true; } return false; } ################################################################################ # __get(). Gets the value. ################################################################################ function __get($name) { if( isset($this->enums[$name]) ){ return $this->enums[$name]; } else if( !isset($this->enums[$name]) ){ $this->enums[$name] = $this->last_value++; return true; } return false; } ################################################################################ # __set(). Sets the value. ################################################################################ function __set( $name, $value=null ) { if( isset($this->enums[$name]) ){ return false; } else if( !isset($this->enums[$name]) && !is_null($value) ){ $this->last_value = $value + 1; $this->enums[$name] = $value; return true; } else if( !isset($this->enums[$name]) && is_null($value) ){ $this->enums[$name] = $this->last_value++; return true; } return false; } ################################################################################ # __destruct(). Deconstruct the class. Remove the list of enums. ################################################################################ function __destruct() { unset( $this->enums ); $this->enums = null; return true; } } # # Test code # # $c = new enums(); # $c->RED_LIGHT(85); # $c->YELLOW_LIGHT = 23; # $c->GREEN_LIGHT; # # echo $c->RED_LIGHT . "\n"; # echo $c->YELLOW_LIGHT . "\n"; # echo $c->GREEN_LIGHT . "\n"; ?>
Mark Manning마지막으로, 재정의할 수 없는 상수가 포함 된 PHP 7.1+ 답변입니다.
/** * An interface that groups HTTP Accept: header Media Types in one place. */ interface MediaTypes { /** * Now, if you have to use these same constants with another class, you can * without creating funky inheritance / is-a relationships. * Also, this gets around the single inheritance limitation. */ public const HTML = 'text/html'; public const JSON = 'application/json'; public const XML = 'application/xml'; public const TEXT = 'text/plain'; } /** * An generic request class. */ abstract class Request { // Why not put the constants here? // 1) The logical reuse issue. // 2) Single Inheritance. // 3) Overriding is possible. // Why put class constants here? // 1) The constant value will not be necessary in other class families. } /** * An incoming / server-side HTTP request class. */ class HttpRequest extends Request implements MediaTypes { // This class can implement groups of constants as necessary. }
네임스페이스를 사용하는 경우 코드 완성이 작동해야 합니다.
protected
) 또는 클래스 단독( private
) 내에서 상수를 숨길 수 있는 기능을 잃게 됩니다. 정의에 따르면 Interface
모든 것은 public
입니다.
PHP 매뉴얼: 인터페이스
Anthony Rutledge나는 이것이 오래된 스레드라는 것을 알고 있지만 거의 모든 해결 방법은 열거형 항목에 수동으로 값을 할당해야 하거나 열거형 키 배열을 기능. 그래서 이에 대한 나만의 솔루션을 만들었습니다.
내 솔루션을 사용하여 열거형 클래스를 만들려면 아래에서 이 열거형 클래스를 확장하고 정적 변수 묶음을 만들고(초기화할 필요 없음) 열거형 클래스 정의 바로 아래에서 yourEnumClass::init()를 호출하면 됩니다. .
편집: 이것은 php >= 5.3에서만 작동하지만 이전 버전에서도 작동하도록 수정할 수 있습니다.
/** * A base class for enums. * * This class can be used as a base class for enums. * It can be used to create regular enums (incremental indices), but it can also be used to create binary flag values. * To create an enum class you can simply extend this class, and make a call to <yourEnumClass>::init() before you use the enum. * Preferably this call is made directly after the class declaration. * Example usages: * DaysOfTheWeek.class.php * abstract class DaysOfTheWeek extends Enum{ * static $MONDAY = 1; * static $TUESDAY; * static $WEDNESDAY; * static $THURSDAY; * static $FRIDAY; * static $SATURDAY; * static $SUNDAY; * } * DaysOfTheWeek::init(); * * example.php * require_once("DaysOfTheWeek.class.php"); * $today = date('N'); * if ($today == DaysOfTheWeek::$SUNDAY || $today == DaysOfTheWeek::$SATURDAY) * echo "It's weekend!"; * * Flags.class.php * abstract class Flags extends Enum{ * static $FLAG_1; * static $FLAG_2; * static $FLAG_3; * } * Flags::init(Enum::$BINARY_FLAG); * * example2.php * require_once("Flags.class.php"); * $flags = Flags::$FLAG_1 | Flags::$FLAG_2; * if ($flags & Flags::$FLAG_1) * echo "Flag_1 is set"; * * @author Tiddo Langerak */ abstract class Enum{ static $BINARY_FLAG = 1; /** * This function must be called to initialize the enumeration! * * @param bool $flags If the USE_BINARY flag is provided, the enum values will be binary flag values. Default: no flags set. */ public static function init($flags = 0){ //First, we want to get a list of all static properties of the enum class. We'll use the ReflectionClass for this. $enum = get_called_class(); $ref = new ReflectionClass($enum); $items = $ref->getStaticProperties(); //Now we can start assigning values to the items. if ($flags & self::$BINARY_FLAG){ //If we want binary flag values, our first value should be 1. $value = 1; //Now we can set the values for all items. foreach ($items as $key=>$item){ if (!isset($item)){ //If no value is set manually, we should set it. $enum::$$key = $value; //And we need to calculate the new value $value *= 2; } else { //If there was already a value set, we will continue starting from that value, but only if that was a valid binary flag value. //Otherwise, we will just skip this item. if ($key != 0 && ($key & ($key - 1) == 0)) $value = 2 * $item; } } } else { //If we want to use regular indices, we'll start with index 0. $value = 0; //Now we can set the values for all items. foreach ($items as $key=>$item){ if (!isset($item)){ //If no value is set manually, we should set it, and increment the value for the next item. $enum::$$key = $value; $value++; } else { //If a value was already set, we'll continue from that value. $value = $item+1; } } } } }
Tiddoclass DayOfWeek { static $values = array( self::MONDAY, self::TUESDAY, // ... ); const MONDAY = 0; const TUESDAY = 1; // ... } $today = DayOfWeek::MONDAY; // If you want to check if a value is valid assert( in_array( $today, DayOfWeek::$values ) );
리플렉션을 사용하지 마십시오. 이는 코드에 대해 추론하고 무언가가 사용되는 위치를 추적하는 것을 극도로 어렵게 만들고 정적 분석 도구(예: IDE에 내장된 것)를 중단시키는 경향이 있습니다.
Jesse이제 SplEnum 클래스를 사용하여 기본적으로 빌드할 수 있습니다. 공식 문서에 따르면.
SplEnum은 PHP에서 기본적으로 열거 개체를 에뮬레이트하고 생성하는 기능을 제공합니다.
<?php class Month extends SplEnum { const __default = self::January; const January = 1; const February = 2; const March = 3; const April = 4; const May = 5; const June = 6; const July = 7; const August = 8; const September = 9; const October = 10; const November = 11; const December = 12; } echo new Month(Month::June) . PHP_EOL; try { new Month(13); } catch (UnexpectedValueException $uve) { echo $uve->getMessage() . PHP_EOL; } ?>
설치해야 하지만 기본적으로 사용할 수 없는 확장 프로그램입니다. 이는 PHP 웹 사이트 자체에 설명 된 특수 유형 아래에 있습니다. 위의 예는 PHP 사이트에서 가져온 것입니다.
Krishnadas PC이것은 "동적" 열거형에 대한 저의 견해입니다... 변수를 사용하여 호출할 수 있습니다. 예를 들면 다음과 같습니다. 양식에서.
이 코드 블록 아래의 업데이트된 버전을 보십시오...
$value = "concert"; $Enumvalue = EnumCategory::enum($value); //$EnumValue = 1 class EnumCategory{ const concert = 1; const festival = 2; const sport = 3; const nightlife = 4; const theatre = 5; const musical = 6; const cinema = 7; const charity = 8; const museum = 9; const other = 10; public function enum($string){ return constant('EnumCategory::'.$string); } }
업데이트: 더 나은 방법...
class EnumCategory { static $concert = 1; static $festival = 2; static $sport = 3; static $nightlife = 4; static $theatre = 5; static $musical = 6; static $cinema = 7; static $charity = 8; static $museum = 9; static $other = 10; }
통화
EnumCategory::${$category};
Anders받아 들여진 대답은 가야 할 길이며 실제로 단순함을 위해 내가하고있는 것입니다. 열거의 대부분의 장점이 제공됩니다(읽기, 빠름 등). 그러나 한 가지 개념이 빠져 있습니다. 바로 유형 안전성입니다. 대부분의 언어에서 열거형은 허용되는 값을 제한하는 데도 사용됩니다. 다음은 개인 생성자, 정적 인스턴스화 방법 및 유형 검사를 사용하여 유형 안전성을 확보하는 방법의 예입니다.
class DaysOfWeek{ const Sunday = 0; const Monday = 1; // etc. private $intVal; private function __construct($intVal){ $this->intVal = $intVal; } //static instantiation methods public static function MONDAY(){ return new self(self::Monday); } //etc. } //function using type checking function printDayOfWeek(DaysOfWeek $d){ //compiler can now use type checking // to something with $d... } //calling the function is safe! printDayOfWeek(DaysOfWeek::MONDAY());
더 나아가 DaysOfWeek 클래스에서 상수를 사용하면 오용으로 이어질 수 있습니다. 예를 들어 다음과 같이 실수로 사용할 수 있습니다.
printDayOfWeek(DaysOfWeek::Monday); //triggers a compiler error.
이것은 잘못된 것입니다(정수 상수 호출). 상수 대신 개인용 정적 변수를 사용하여 이를 방지할 수 있습니다.
class DaysOfWeeks{ private static $monday = 1; //etc. private $intVal; //private constructor private function __construct($intVal){ $this->intVal = $intVal; } //public instantiation methods public static function MONDAY(){ return new self(self::$monday); } //etc. //convert an instance to its integer value public function intVal(){ return $this->intVal; } }
물론 정수 상수에 액세스하는 것은 불가능합니다(이것이 실제로 목적이었습니다). intVal 메서드를 사용하면 DaysOfWeek 개체를 정수 표현으로 변환할 수 있습니다.
열거가 광범위하게 사용되는 경우 메모리를 절약하기 위해 인스턴스화 방법에서 캐싱 메커니즘을 구현하여 더 나아갈 수 있습니다.
이것이 도움이 되기를 바랍니다.
Vincent Pazeller지적한 솔루션이 잘 작동합니다. 깨끗하고 매끄럽습니다.
그러나 강력한 형식의 열거형을 원하는 경우 다음을 사용할 수 있습니다.
class TestEnum extends Enum { public static $TEST1; public static $TEST2; } TestEnum::init(); // Automatically initializes enum values
Enum 클래스는 다음과 같습니다.
class Enum { public static function parse($enum) { $class = get_called_class(); $vars = get_class_vars($class); if (array_key_exists($enum, $vars)) { return $vars[$enum]; } return null; } public static function init() { $className = get_called_class(); $consts = get_class_vars($className); foreach ($consts as $constant => $value) { if (is_null($className::$$constant)) { $constantValue = $constant; $constantValueName = $className . '::' . $constant . '_VALUE'; if (defined($constantValueName)) { $constantValue = constant($constantValueName); } $className::$$constant = new $className($constantValue); } } } public function __construct($value) { $this->value = $value; } }
이렇게 하면 열거형 값이 강력하게 형식화되고
TestEnum::$TEST1 === TestEnum::parse('TEST1') // true statement
Hervé Labas다른 답변에서 누락된 측면 중 하나는 유형 힌트와 함께 열거형을 사용하는 방법입니다.
열거형을 추상 클래스의 상수 집합으로 정의하는 경우, 예를 들어
abstract class ShirtSize { public const SMALL = 1; public const MEDIUM = 2; public const LARGE = 3; }
당신은 함수 매개 변수에 힌트 입력 할 수 없습니다 - 그것은 인스턴스화 아니기 때문에, 하나,뿐만 아니라의 종류 때문에 ShirtSize::SMALL
이다 int
,하지 ShirtSize
.
이것이 PHP의 기본 열거형이 우리가 생각해낼 수 있는 어떤 것보다 훨씬 나은 이유입니다. 그러나 열거형 값을 나타내는 전용 속성을 유지하고 이 속성의 초기화를 미리 정의된 상수로 제한하여 열거형을 근사화할 수 있습니다. 열거형이 임의로 인스턴스화되는 것을 방지하기 위해(화이트리스트 유형 검사의 오버헤드 없이) 생성자를 비공개로 만듭니다.
class ShirtSize { private $size; private function __construct ($size) { $this->size = $size; } public function equals (ShirtSize $s) { return $this->size === $s->size; } public static function SMALL () { return new self(1); } public static function MEDIUM () { return new self(2); } public static function LARGE () { return new self(3); } }
ShirtSize
과 같이 사용할 수 있습니다.
function sizeIsAvailable ($productId, ShirtSize $size) { // business magic } if(sizeIsAvailable($_GET["id"], ShirtSize::LARGE())) { echo "Available"; } else { echo "Out of stock."; } $s2 = ShirtSize::SMALL(); $s3 = ShirtSize::MEDIUM(); echo $s2->equals($s3) ? "SMALL == MEDIUM" : "SMALL != MEDIUM";
이렇게 하면 사용자 관점에서 가장 큰 차이점은 상수 이름에 ()
그러나 한 가지 단점은 ==
가 true를 반환 ===
(객체 평등을 비교함)가 false를 반환한다는 것입니다. 이러한 이유로, 그것은 제공하는 것이 가장 좋습니다 equals
사용자가 사용하는 기억할 필요가 없습니다 그래서, 방법을 ==
아니라 ===
이 개 열거 값을 비교.
편집: 몇 가지 기존 답변은 특히 https://stackoverflow.com/a/25526473/2407870 과 매우 유사합니다.
Chris Middleton@Brian Cline의 답변을 밟고 5센트를 줄 수 있다고 생각했습니다.
<?php /** * A class that simulates Enums behaviour * <code> * class Season extends Enum{ * const Spring = 0; * const Summer = 1; * const Autumn = 2; * const Winter = 3; * } * * $currentSeason = new Season(Season::Spring); * $nextYearSeason = new Season(Season::Spring); * $winter = new Season(Season::Winter); * $whatever = new Season(-1); // Throws InvalidArgumentException * echo $currentSeason.is(Season::Spring); // True * echo $currentSeason.getName(); // 'Spring' * echo $currentSeason.is($nextYearSeason); // True * echo $currentSeason.is(Season::Winter); // False * echo $currentSeason.is(Season::Spring); // True * echo $currentSeason.is($winter); // False * </code> * * Class Enum * * PHP Version 5.5 */ abstract class Enum { /** * Will contain all the constants of every enum that gets created to * avoid expensive ReflectionClass usage * @var array */ private static $_constCacheArray = []; /** * The value that separates this instance from the rest of the same class * @var mixed */ private $_value; /** * The label of the Enum instance. Will take the string name of the * constant provided, used for logging and human readable messages * @var string */ private $_name; /** * Creates an enum instance, while makes sure that the value given to the * enum is a valid one * * @param mixed $value The value of the current * * @throws \InvalidArgumentException */ public final function __construct($value) { $constants = self::_getConstants(); if (count($constants) !== count(array_unique($constants))) { throw new \InvalidArgumentException('Enums cannot contain duplicate constant values'); } if ($name = array_search($value, $constants)) { $this->_value = $value; $this->_name = $name; } else { throw new \InvalidArgumentException('Invalid enum value provided'); } } /** * Returns the constant name of the current enum instance * * @return string */ public function getName() { return $this->_name; } /** * Returns the value of the current enum instance * * @return mixed */ public function getValue() { return $this->_value; } /** * Checks whether this enum instance matches with the provided one. * This function should be used to compare Enums at all times instead * of an identity comparison * <code> * // Assuming EnumObject and EnumObject2 both extend the Enum class * // and constants with such values are defined * $var = new EnumObject('test'); * $var2 = new EnumObject('test'); * $var3 = new EnumObject2('test'); * $var4 = new EnumObject2('test2'); * echo $var->is($var2); // true * echo $var->is('test'); // true * echo $var->is($var3); // false * echo $var3->is($var4); // false * </code> * * @param mixed|Enum $enum The value we are comparing this enum object against * If the value is instance of the Enum class makes * sure they are instances of the same class as well, * otherwise just ensures they have the same value * * @return bool */ public final function is($enum) { // If we are comparing enums, just make // sure they have the same toString value if (is_subclass_of($enum, __CLASS__)) { return get_class($this) === get_class($enum) && $this->getValue() === $enum->getValue(); } else { // Otherwise assume $enum is the value we are comparing against // and do an exact comparison return $this->getValue() === $enum; } } /** * Returns the constants that are set for the current Enum instance * * @return array */ private static function _getConstants() { if (self::$_constCacheArray == null) { self::$_constCacheArray = []; } $calledClass = get_called_class(); if (!array_key_exists($calledClass, self::$_constCacheArray)) { $reflect = new \ReflectionClass($calledClass); self::$_constCacheArray[$calledClass] = $reflect->getConstants(); } return self::$_constCacheArray[$calledClass]; } }
Loupax여기에 몇 가지 좋은 솔루션이 있습니다!
여기 내 버전이 있습니다.
- 강력하게 입력됩니다
- IDE 자동 완성과 함께 작동합니다.
- 열거형은 코드와 설명으로 정의되며, 여기서 코드는 정수, 이진 값, 짧은 문자열 또는 기본적으로 원하는 모든 것이 될 수 있습니다. 패턴은 다른 속성을 지원하도록 쉽게 확장될 수 있습니다.
- 값(==) 및 참조(===) 비교를 지원하고 switch 문에서 작동합니다.
주요 단점은 설명과 PHP가 정적 멤버 선언 시 객체를 구성할 수 없기 때문에 열거형 멤버를 별도로 선언하고 인스턴스화해야 한다는 것입니다. 이 방법은 대신 구문 분석된 문서 주석과 함께 리플렉션을 사용하는 것일 수 있습니다.
추상 열거형은 다음과 같습니다.
<?php abstract class AbstractEnum { /** @var array cache of all enum instances by class name and integer value */ private static $allEnumMembers = array(); /** @var mixed */ private $code; /** @var string */ private $description; /** * Return an enum instance of the concrete type on which this static method is called, assuming an instance * exists for the passed in value. Otherwise an exception is thrown. * * @param $code * @return AbstractEnum * @throws Exception */ public static function getByCode($code) { $concreteMembers = &self::getConcreteMembers(); if (array_key_exists($code, $concreteMembers)) { return $concreteMembers[$code]; } throw new Exception("Value '$code' does not exist for enum '".get_called_class()."'"); } public static function getAllMembers() { return self::getConcreteMembers(); } /** * Create, cache and return an instance of the concrete enum type for the supplied primitive value. * * @param mixed $code code to uniquely identify this enum * @param string $description * @throws Exception * @return AbstractEnum */ protected static function enum($code, $description) { $concreteMembers = &self::getConcreteMembers(); if (array_key_exists($code, $concreteMembers)) { throw new Exception("Value '$code' has already been added to enum '".get_called_class()."'"); } $concreteMembers[$code] = $concreteEnumInstance = new static($code, $description); return $concreteEnumInstance; } /** * @return AbstractEnum[] */ private static function &getConcreteMembers() { $thisClassName = get_called_class(); if (!array_key_exists($thisClassName, self::$allEnumMembers)) { $concreteMembers = array(); self::$allEnumMembers[$thisClassName] = $concreteMembers; } return self::$allEnumMembers[$thisClassName]; } private function __construct($code, $description) { $this->code = $code; $this->description = $description; } public function getCode() { return $this->code; } public function getDescription() { return $this->description; } }
다음은 구체적인 열거형의 예입니다.
<?php require('AbstractEnum.php'); class EMyEnum extends AbstractEnum { /** @var EMyEnum */ public static $MY_FIRST_VALUE; /** @var EMyEnum */ public static $MY_SECOND_VALUE; /** @var EMyEnum */ public static $MY_THIRD_VALUE; public static function _init() { self::$MY_FIRST_VALUE = self::enum(1, 'My first value'); self::$MY_SECOND_VALUE = self::enum(2, 'My second value'); self::$MY_THIRD_VALUE = self::enum(3, 'My third value'); } } EMyEnum::_init();
다음과 같이 사용할 수 있습니다.
<?php require('EMyEnum.php'); echo EMyEnum::$MY_FIRST_VALUE->getCode().' : '.EMyEnum::$MY_FIRST_VALUE->getDescription().PHP_EOL.PHP_EOL; var_dump(EMyEnum::getAllMembers()); echo PHP_EOL.EMyEnum::getByCode(2)->getDescription().PHP_EOL;
그리고 다음과 같은 출력을 생성합니다.
1 : 나의 첫 번째 가치
array(3) { [1]=> object(EMyEnum)#1 (2) { ["code":"AbstractEnum":private]=> int(1) ["description":"AbstractEnum":private]=> string(14) "My first value" } [2]=> object(EMyEnum)#2 (2) { ["code":"AbstractEnum":private]=> int(2) ["description":"AbstractEnum":private]=> string(15) "My second value" } [3]=> object(EMyEnum)#3 (2) { ["code":"AbstractEnum":private]=> int(3) ["description":"AbstractEnum":private]=> string(14) "My third value" } }
나의 두 번째 가치
Dan King출처 : http:www.stackoverflow.com/questions/254514/enumerations-on-php