PHP와 열거


1149

PHP에는 네이티브 열거 형이 없습니다. 그러나 나는 자바 세계에서 그들에게 익숙해졌습니다. IDE의 자동 완성 기능이 이해할 수있는 미리 정의 된 값을 제공하는 방법으로 열거 형을 사용하고 싶습니다.

상수는 트릭을 할,하지만 거기에 네임 스페이스 충돌 문제이고 (또는 실제로 때문에 )가 세계입니다. 배열은 네임 스페이스 문제가 없지만 너무 모호하여 런타임에 덮어 쓸 수 있으며 IDE는 키를 자동으로 채우는 방법을 거의 알지 못합니다.

일반적으로 사용하는 솔루션 / 해결 방법이 있습니까? PHP 사람들이 열거 형에 대해 어떤 생각이나 결정을했는지 여부를 기억합니까?



1
상수를 비트 단위로 열거하거나 열거하지 않는 해결 방법 함수를 만들었습니다. 이전에이 질문을하지는 않았지만 여기에 클래스 변수보다 더 나은 해결책이 있습니다. stackoverflow.com/questions/3836385/…
NoodleOfDeath


Constants 문제에 대해 조금 더 공유 하시겠습니까? "정수는 트릭을 수행하지만 네임 스페이스 충돌 문제가 있으며 실제로는 전역 적입니다."
XuDing

답변:


1492

유스 케이스에 따라 일반적으로 다음과 같은 간단한 것을 사용합니다 .

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

참고로, 데이터가 변경되지 않는 정적 / 상수 클래스 (예 : 열거 형) 에서 적어도 한 번 리플렉션을 사용할 때마다 매번 새로운 리플렉션 객체를 사용하기 때문에 해당 리플렉션 호출의 결과를 캐시합니다 결국 눈에 띄는 성능 영향을 미칩니다 (여러 열거 형의 연관 배열에 저장 됨).

이제 대부분의 사람들이 최소한 5.3 으로 업그레이드되었으며 SplEnum사용 가능 합니다. 코드베이스 전체에서 실제로 열거 형 인스턴스화 라는 전통적으로 직관적이지 않은 개념에 신경 쓰지 않는 한 실제로 실행 가능한 옵션 입니다. 위의 예에서 BasicEnumDaysOfWeek전혀 인스턴스화 할 수 없으며이 있어야한다.


70
나도 이것을 사용합니다. 또한 클래스를 만드는 고려해 볼 수 있습니다 abstractfinal는 인스턴스 또는 확장 할 수 없습니다 수 있습니다.
ryeguy

21
당신은 클래스를 모두 만들 수 abstractfinal? Java에서는 이것이 허용되지 않는다는 것을 알고 있습니다. PHP에서 그렇게 할 수 있습니까?
corsiKa

20
@ryeguy 당신이 그것을 할 수없는 것 모두 abstractfinal. 이 경우, 나는 추상적으로 갈 것입니다.
Nicole

45
초록 또는 최종에 대하여; 나는 그들을 최종적으로 만들고 빈 개인 생성자를 제공합니다
rael_kid

21
0을 사용할 때는주의해야합니다. 예를 들어 진술 null에서 동등성 과 친구 와 같이 예상치 못한 거짓 비교 문제가 발생하지 않도록하십시오 switch. 거기에 있었다.
yitznewton

185

기본 확장도 있습니다. SplEnum

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은 현재 사용할 수 없습니다.


4
다음은 splenum의 예입니다 : dreamincode.net/forums/topic/201638-enum-in-php
Nordes

4
롤백했습니다. 링크를 볼 때 더 좋습니다. 컨텍스트 정보를 제공합니다.
markus

5
다시 롤백했습니다. 나는 너희들이 링크를 편집하기를 원하지 않는다.
markus

6
이것을 조심해서 사용하십시오. SPL 타입은 실험적입니다 : "이 확장은 실험적입니다.이 확장의 기능과 그 확장을 둘러싼 다른 문서를 포함한이 확장의 동작은 향후 PHP 릴리스에서 예고없이 변경 될 수 있습니다.이 확장은 사용자의 책임하에 사용해야합니다. "
bzeaman

6
SplEnum 은 PHP와 함께 제공되지 않으므로 SPL_Types 확장
Kwadz

46

클래스 상수는 어떻습니까?

<?php

class YourClass
{
    const SOME_CONSTANT = 1;

    public function echoConstant()
    {
        echo self::SOME_CONSTANT;
    }
}

echo YourClass::SOME_CONSTANT;

$c = new YourClass;
$c->echoConstant();

이 간단한 접근 방식을 선호합니다
David Lemon

echoConstant로 교체 할 수 있습니다 __toString. 그리고 간단히echo $c
Justinas

35

위의 최고 답변은 환상적입니다. 그러나 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];
}

2
이 문제가있었습니다. Brian 또는 편집 권한이있는 사람은 수락 된 답변에서 해당 항목을 터치해야합니다. getConstants () 함수에서 'self ::'대신 'static ::'메서드를 사용하고 자식 열거 형에서 $ constCache를 다시 선언하여 코드에서 해결했습니다.
Sp3igel

섹시하지는 않지만 인터페이스 상수를 사용하는 것이 PHP에서 가장 좋은 방법 일 수 있습니다.
Anthony Rutledge

27

상수가있는 클래스를 사용했습니다.

class Enum {
    const NAME       = 'aaaa';
    const SOME_VALUE = 'bbbb';
}

print Enum::NAME;

27

interface대신에 사용 합니다 class:

interface DaysOfWeek
{
    const Sunday = 0;
    const Monday = 1;
    // etc.
}

var $today = DaysOfWeek::Sunday;

6
class Foo implements DaysOfWeek { }그리고 Foo::Sunday... 뭐?
Dan Lugg

3
질문 작성자는 네임 스페이스와 IDE에 의한 자동 완성이라는 두 가지 솔루션을 요구합니다. 최고 답변이 제안했듯이 가장 쉬운 방법은 class(또는 interface, 선호도의 문제 인)를 사용하는 것입니다.
Andi T

4
인터페이스는 클래스 구현 무결성을 강화하는 데 사용되며 인터페이스의 범위를 벗어납니다.
user3886650

2
@ user3886650 인터페이스는 일정한 값을 유지하기 위해 Java에서 사용할 수 있습니다. 따라서 상수 값을 얻기 위해 클래스를 인스턴스화하지 않아도되고 IDE에서 코드 완성을 제공합니다. 또한 해당 인터페이스를 구현하는 클래스를 만들면 모든 상수를 상속받습니다. 때로는 매우 편리합니다.
Alex

@ user3886650 사실이지만 PHP에서는 인터페이스가 상수를 가질 수 있습니다. 또한 이러한 인터페이스 상수는 클래스 또는 해당 자식을 구현하여 재정의 할 수 없습니다. 사실상, 이것은 재정의 될 수있는 것은 상수처럼 작동하지 않기 때문에 PHP 측면에서 가장 좋은 대답입니다. 상수는 때가 아니라 상수를 의미해야합니다 (다형성이 때때로 유용 할 수 있음에도 불구하고).
Anthony Rutledge

25

나는 여기에 다른 답변 중 일부에 대해 언급 했으므로 무게도 달릴 것이라고 생각했습니다. 하루가 끝날 무렵 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

나는이 답변의 단순함을 정말로 좋아합니다. 나중에 다시 돌아와서 해킹 된 접근 방식처럼 보이게하지 않고 작동 방식을 신속하게 이해할 수있는 종류입니다. 안타깝게도 더 많은 투표권이 없습니다.
Reactgular

23

글쎄, PHP의 enum과 같은 간단한 Java의 경우 다음을 사용합니다.

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 초보자이며 구문에 어려움을 겪고 있으므로 이것이 최선의 방법이 아닐 수도 있습니다. 리플렉션을 사용하여 클래스 상수를 실험 해 보았습니다. 반사 값을 사용하여 상수 이름을 얻는 것이 더 깔끔 할 수 있습니다.


좋은 답변입니다. 다른 답변의 대부분은 수업을 사용하고 있습니다. 그래도 중첩 클래스를 가질 수 없습니다.
Keyo

이는 foreach를 사용하여 값을 반복 할 수 있다는 이점이 있습니다. 그리고 불법적 인 가치가 잡히지 않는다는 단점.
밥 스타

2
IDE에서 자동 완성이 없으므로 추측 작업을 자극합니다. 상수는 자동 완성을 가능하게하며 소리가 더 좋습니다.
KrekkieD

19

4 년 후 나는 이것을 다시 만났다. 현재 접근 방식은 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

개인 생성자의 경우 +1 나는 도우미 추상 클래스, 단순한 클래스, 개인 생성자 및 일부를 const Monday = DaysOfWeek('Monday');
만들지 않을 것

9

나는 github 에서이 라이브러리 를 발견 했으며 여기에 대한 답변에 대한 훌륭한 대안을 제공한다고 생각합니다.

SplEnum에서 영감을 얻은 PHP Enum 구현

  • 힌트를 입력 할 수 있습니다. function setAction(Action $action) {
  • 당신은 방법과 열거를 풍부하게 할 수 있습니다 (예를 들어 format, parse...)
  • 열거 형을 확장하여 새 값을 추가 할 수 있습니다 (열거 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) {
    // ...
}

1
이것은 enum유형 힌트를 허용하기 때문에 정답입니다 (현재로서는 PHP 7.x에서 추가 될 때까지 ).
Tobia

1
뿐만 아니라이 유형 암시하지만, 때문에의 수 있습니까 __toString()A의 사용을 - 마법, 당신이 열거 형과 함께 당신은 일반적으로 정말하고 싶은 일을 할 수 있습니다 switch또는 if문을의 consts의 값과 직접 비교. 네이티브 열거 형 지원, IMO에 대한 최선의 접근 방식.
LinusR

7

전역 적으로 고유 한 (즉, 다른 열거 형 간의 요소를 비교할 때도) 열거 형을 사용해야하고 사용하기 쉬운 경우 다음 코드를 자유롭게 사용하십시오. 또한 유용하다고 생각되는 몇 가지 방법을 추가했습니다. 코드 맨 위에있는 주석에서 예제를 찾을 수 있습니다.

<?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 (e.g. 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 (e.g. 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";
    }

}


?>

1
나는 이것을 좋아한다. 그러나 주요 불만 중 하나는 IDE가 자동 완성 값을 선택할 수 있다는 것입니다. IDE에 대한 사용자 정의 애드온이 없으면 이것이 가능할 것이라고 확신하지 않습니다. 할 수 없었던 것이 아니라 단지 약간의 작업이 필요할 것입니다.
corsiKa

2
eval()그냥 사용하여 새로운 Enums 런타임을 선언 할 수 있습니까? Eek. 나는 그것을 느끼지 않는다. 적절한 클래스를 정의하기 전에 다른 클래스가 잘못된 Enum 클래스를 작성하지 못하게하려면 어떻게해야합니까? 런타임 전에 Enum을 알고 있지 않습니까? 그리고 @corsiKa가 암시 한 것처럼 IDE 자동 완성은 없습니다. 내가 보는 이점은 게으른 코딩입니다.
KrekkieD

7

나는 자바에서 열거 형을 좋아하고 이런 이유로 나는 열거 형을 이런 식으로 쓴다. 이것은 자바 열거 형과 같이 가장 유사한 거동이라고 생각한다. 물론 자바에서 더 많은 메소드를 사용하려면 여기에 쓰거나 추상 클래스이지만 핵심 아이디어는 아래 코드에 포함되어 있습니다.


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

3
나는 두 가지 작은 추가로 거의 똑같은 일을하고 있습니다 : 정적 게터 뒤에 정적 값을 숨겼습니다. 한 가지 이유는 내가 시각적 선호입니다 FruitsEnum::Apple()이상 FruitsEnum::$Apple하지만, 더 중요한 이유는 설정에서 다른 사람을 방지하는 것입니다 $APPLE, 따라서 전체 응용 프로그램에 대한 열거를 깨고. 다른 하나는 처음 호출 한 후 호출이 작동하지 $initialized않도록 하는 간단한 개인 정적 플래그 입니다 init()(아무도 어느 쪽도 엉망으로 만들 수 없음).
마틴 엔더

나는 마틴을 좋아했다. .init()이상하고 게터 접근 방식은 신경 쓰지 않습니다.
Sebas

7
abstract 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>";
}


5

내가 PHP에서 열거하는 가장 일반적인 해결책은 일반 열거 클래스를 만든 다음 확장하는 것입니다. 당신은 이것을 볼 수 있습니다 .

업데이트 : 또한, 나는 발견 phpclasses.org에서.


1
구현이 매끄럽고 아마도 작업을 수행 할 것이지만, 단점은 IDE가 열거 형을 자동 완성하는 방법을 모른다는 것입니다. 내가 등록하기를 원했기 때문에 phpclasses.org에서 하나를 검사 할 수 없었습니다.
헨릭 폴

5

다음은 PHP에서 형식 안전 열거를 처리하기위한 github 라이브러리입니다.

이 라이브러리는 클래스 생성, 클래스 캐싱을 처리하고 열거 형 정렬을위한 서수 검색 또는 열거 형 조합에 대한 이진 값 검색과 같은 열거자를 처리하기위한 여러 가지 도우미 메서드를 사용하여 Type Safe Enumeration 디자인 패턴을 구현합니다.

생성 된 코드는 구성 가능한 구성 가능한 일반 PHP 템플릿 파일을 사용하므로 고유 한 템플릿을 제공 할 수 있습니다.

phpunit으로 가득 찬 전체 테스트입니다.

github의 PHP 열거 형 (포크 자유롭게)

사용법 : (@ 자세한 내용은 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

4

함수 매개 변수에 대한 유형 안전, 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");

아무것도 열거 형 값을 재정의하는 데 방해가되지 않습니다.DaysOfWeek::$MONDAY = 3;
KrekkieD

@BrianFisher, 나는 그것이 늦게 늦다는 것을 알고 있지만, [extended class name]::enumerate();정의를 부르는 것을 좋아하지 않는다면 왜 구조체에서 그것을하지 않습니까?
Can O 'Spam

4

아래의 Enum 클래스 정의는 강력하게 입력 되며 매우 자연 스럽습니다. 사용 및 정의하기가 습니다.

정의:

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 "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();

4

나는 이것이 매우 오래된 스레드라는 것을 알고 있지만 이것에 대해 생각하고 사람들이 어떻게 생각하는지 알고 싶었습니다.

참고 :이 문제를 해결하고 방금 __call()함수를 수정하면 실제에 더 가까이 갈 수 있음을 깨달았습니다 enums. 이 __call()함수는 알려지지 않은 모든 함수 호출을 처리합니다. 3 개의 enumsRED_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)으로 설정됩니다. 이제 열거 형을 수행하는 매우 좋은 방법이 있으며 즉시 만들 수 있습니다.

<?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";

?>

3

나는 이것이 오래된 스레드라는 것을 알고 있지만, 실제로 본 모든 해결 방법은 실제로 열거 형처럼 보이지 않습니다. 거의 모든 해결 방법은 수동으로 열거 형 항목에 값을 할당해야하거나 열거 형 키 배열을 함수. 그래서 이것을 위해 나만의 솔루션을 만들었습니다.

내 솔루션을 사용하여 열거 형 클래스를 만들려면 간단히이 Enum 클래스를 확장하고 여러 정적 변수 (초기화 할 필요 없음)를 만들고 열거 형 정의 바로 아래에 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;
                }
            }
        }
    }
}

3

이제 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 사이트에서 가져온 것입니다.


3

마지막으로, 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 매뉴얼 : 인터페이스


이것은 Java가 아닙니다. 다형성 / 전략 패턴이 부모 클래스의 상수를 재정의하지 않아도되는 경우에 작동합니다.
Anthony Rutledge

2

이것은 "dynamic"열거 형에 대한 나의 취향입니다 ... 그래서 변수로 호출 할 수 있습니다. 양식에서.

이 코드 블록 아래에서 업데이트 된 버전을보십시오 ...

$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};

5
이것에 대한 문제; EnumCategory::$sport = 9;. 스포츠 박물관에 오신 것을 환영합니다. const 이다 그 일의 더 좋은 방법.
Dan Lugg

2

받아 들여진 대답은 갈 길이며 실제로 단순성을 위해하고있는 일입니다. 열거의 대부분의 장점이 제공됩니다 (읽기, 빠름 등). 그러나 유형 안전이라는 개념이 하나도 없습니다. 대부분의 언어에서 열거 형은 허용 된 값을 제한하는 데에도 사용됩니다. 다음은 개인 생성자, 정적 인스턴스화 방법 및 유형 검사를 사용하여 유형 안전성을 얻는 방법의 예입니다.

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 오브젝트를 정수 표현으로 변환 할 수 있습니다.

열거가 광범위하게 사용되는 경우 메모리를 절약하기 위해 인스턴스화 방법으로 캐싱 메커니즘을 구현하여 더 나아가는 것도 가능합니다 ...

이것이 도움이되기를 바랍니다.


2

여기에 좋은 해결책이 있습니다!

여기 내 버전이 있습니다.

  • 강력하게 입력되었습니다
  • 그것은 IDE 자동 완성과 함께 작동
  • 열거 형은 코드와 설명으로 정의되며 코드는 정수, 이진 값, 짧은 문자열 또는 기본적으로 원하는 다른 것이 될 수 있습니다. 패턴을 쉽게 확장하여 다른 속성을 지원할 수 있습니다.
  • 값 (==) 및 참조 (===) 비교를 지원하며 스위치 문에서 작동합니다.

가장 큰 단점은 정적 멤버 선언 시간에 객체를 생성 할 수없는 설명과 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) "내 첫 값"
}
[2] =>
object (EMyEnum) # 2 (2) {
[ "code": "AbstractEnum": private] =>
int (2)
[ "description": "AbstractEnum" : private] =>
string (15) "나의 두 번째 값"
}
[3] =>
object (EMyEnum) # 3 (2) {
[ "code": "AbstractEnum": private] =>
int (3) string (14) "내 세번째 값" } }
[ "description": "AbstractEnum": private] =>


내 두 번째 가치


2
class 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에 내장 된 도구)를 깨뜨리는 경향이 있습니다.


2

다른 답변 중 일부에서 누락 된 측면 중 하나는 유형 힌트와 함께 열거 형을 사용하는 방법입니다.

열거 형을 추상 클래스에서 상수 세트로 정의하는 경우 (예 :

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 입니다.


2

@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];
    }
}

어떤 이유로 나는이 기능을 호출 할 수 없습니다. 그것은 그러한 기능들이 선언되지 않았다는 것을 말해줍니다. 내가 뭘 잘못하고 있니? [다른 파일에있는 기본 Enum 클래스이며 사용하고 있습니다 include('enums.php');]. 어떤 이유로 자식 클래스에 대해 Enum에 선언 된 함수가 보이지 않습니다.
Andrew

또한 ... 문자열에서 설정하는 방법? sth$currentSeason.set("Spring");
앤드류

1

PHP로 열거 형을 만들려는 시도는 ... 객체를 열거 형 값으로 지원하지 않지만 다소 유용하기 때문에 매우 제한적입니다 ...

class ProtocolsEnum {

    const HTTP = '1';
    const HTTPS = '2';
    const FTP = '3';

    /**
     * Retrieve an enum value
     * @param string $name
     * @return string
     */
    public static function getValueByName($name) {
        return constant('self::'. $name);
    } 

    /**
     * Retrieve an enum key name
     * @param string $code
     * @return string
     */
    public static function getNameByValue($code) {
        foreach(get_class_constants() as $key => $val) {
            if($val == $code) {
                return $key;
            }
        }
    }

    /**
     * Retrieve associate array of all constants (used for creating droplist options)
     * @return multitype:
     */
    public static function toArray() {      
        return array_flip(self::get_class_constants());
    }

    private static function get_class_constants()
    {
        $reflect = new ReflectionClass(__CLASS__);
        return $reflect->getConstants();
    }
}

여러 방향으로 제한되어 있으며 기존 답변이 그 이상을 제공합니다. 이것이 실제로 유용한 것을 추가하지는 않는다고 말하고 싶습니다.
hakre

1

어제 나는이 수업 을 내 블로그에 썼습니다 . PHP 스크립트에서 사용하기 쉽다고 생각합니다.

final class EnumException extends Exception{}

abstract class Enum
{
    /**
     * @var array ReflectionClass
     */
    protected static $reflectorInstances = array();
    /**
     * Массив конфигурированного объекта-константы enum
     * @var array
     */
    protected static $enumInstances = array();
    /**
     * Массив соответствий значение->ключ используется для проверки - 
     * если ли константа с таким значением
     * @var array
     */
    protected static $foundNameValueLink = array();

    protected $constName;
    protected $constValue;

    /**
     * Реализует паттерн "Одиночка"
     * Возвращает объект константы, но но как объект его использовать не стоит, 
     * т.к. для него реализован "волшебный метод" __toString()
     * Это должно использоваться только для типизачии его как параметра
     * @paradm Node
     */
    final public static function get($value)
    {
        // Это остается здесь для увеличения производительности (по замерам ~10%)
        $name = self::getName($value);
        if ($name === false)
            throw new EnumException("Неизвестая константа");
        $className = get_called_class();    
        if (!isset(self::$enumInstances[$className][$name]))
        {
            $value = constant($className.'::'.$name);
            self::$enumInstances[$className][$name] = new $className($name, $value);
        }

        return self::$enumInstances[$className][$name];
    }

    /**
     * Возвращает массив констант пар ключ-значение всего перечисления
     * @return array 
     */
    final public static function toArray()
    {
        $classConstantsArray = self::getReflectorInstance()->getConstants();
        foreach ($classConstantsArray as $k => $v)
            $classConstantsArray[$k] = (string)$v;
        return $classConstantsArray;
    }

    /**
     * Для последующего использования в toArray для получения массива констант ключ->значение 
     * @return ReflectionClass
     */
    final private static function getReflectorInstance()
    {
        $className = get_called_class();
        if (!isset(self::$reflectorInstances[$className]))
        {
            self::$reflectorInstances[$className] = new ReflectionClass($className);
        }
        return self::$reflectorInstances[$className];
    }

    /**
     * Получает имя константы по её значению
     * @param string $value
     */
    final public static function getName($value)
    {
        $className = (string)get_called_class();

        $value = (string)$value;
        if (!isset(self::$foundNameValueLink[$className][$value]))
        {
            $constantName = array_search($value, self::toArray(), true);
            self::$foundNameValueLink[$className][$value] = $constantName;
        }
        return self::$foundNameValueLink[$className][$value];
    }

    /**
     * Используется ли такое имя константы в перечислении
     * @param string $name
     */
    final public static function isExistName($name)
    {
        $constArray = self::toArray();
        return isset($constArray[$name]);
    }

    /**
     * Используется ли такое значение константы в перечислении
     * @param string $value
     */
    final public static function isExistValue($value)
    {
        return self::getName($value) === false ? false : true;
    }   


    final private function __clone(){}

    final private function __construct($name, $value)
    {
        $this->constName = $name;
        $this->constValue = $value;
    }

    final public function __toString()
    {
        return (string)$this->constValue;
    }
}

용법:

class enumWorkType extends Enum
{
        const FULL = 0;
        const SHORT = 1;
}

2
그러나 좋은 클래스와 함수 이름은 기본입니다. 또한 translate.google.ru도 도움이 될 수 있습니다.
Arturgspb

2
크롬 녀석을 사용하고 번역하십시오. 프로그래머라면 코드를 읽으십시오!
markus

8
일반적으로 'n'개월 / 년 등에있을 수도 있고 없을 수도있는 외부 리소스에 연결하는 것보다 항상 답변 내에 코드를 포함시키는 것이 좋습니다.
John Parker

수업이 너무 커서이 게시물을 읽는 것이 불편할 것이라고 생각합니다.
Arturgspb

나는 여기에 두 가지 나쁜 점이 있다고 생각합니다 : 그것은 러시아어로되어 있습니다 (모든 프로그래머는 영어를 알아야하고 심지어 주석으로 사용해야합니다). 그리고 여기에 포함되어 있지 않습니다. 거대한 코드를 포함시키는 방법에 대한 도움말을 참조하십시오.
gaRex
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.