[php] PHP와 열거

PHP에는 네이티브 열거 형이 없습니다. 그러나 나는 자바 세계에서 그들에게 익숙해졌습니다. 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

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

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


답변

기본 확장도 있습니다. 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은 현재 사용할 수 없습니다.


답변

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

<?php

class YourClass
{
    const SOME_CONSTANT = 1;

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

echo YourClass::SOME_CONSTANT;

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


답변

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


답변

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

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

print Enum::NAME;


답변

interface대신에 사용 합니다 class:

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

var $today = DaysOfWeek::Sunday;


답변

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