Frage PHP und Aufzählungen


Ich weiß, dass PHP keine nativen Enumerations hat. Aber ich habe mich aus der Java-Welt an sie gewöhnt. Ich würde gerne Enums verwenden, um vordefinierte Werte zu geben, die die Autovervollständigungsfunktionen von IDEs verstehen könnten.

Konstanten machen den Trick, aber da ist das Namespace-Kollisionsproblem und (oder tatsächlich weil) Sie sind global. Arrays haben kein Namespace-Problem, aber sie sind zu vage, sie können zur Laufzeit überschrieben werden, und IDEs wissen selten (nie?), Wie sie ihre Schlüssel automatisch füllen können.

Gibt es Lösungen / Workarounds, die Sie häufig verwenden? Erinnert sich jemand daran, dass die PHP-Leute irgendwelche Gedanken oder Entscheidungen um enums herum hatten?


980
2017-10-31 18:51


Ursprung


Antworten:


Je nach Anwendungsfall würde ich normalerweise etwas verwenden einfach wie folgt:

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

$today = DaysOfWeek::Sunday;

Andere Anwendungsfälle erfordern jedoch möglicherweise eine größere Validierung von Konstanten und Werten. Basierend auf den folgenden Kommentaren zur Reflektion und ein paar andere Notizen, hier ist ein erweitertes Beispiel, das einem viel größeren Bereich von Fällen besser dienen kann:

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

Durch das Erstellen einer einfachen Enum-Klasse, die BasicEnum erweitert, haben Sie jetzt die Möglichkeit, Methoden zur einfachen Eingabevalidierung zu verwenden:

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

Als Randnotiz benutze ich jedes Mal die Reflektion mindestens einmal in einer statischen / const-Klasse, in der sich die Daten nicht ändern (wie in einer Enumeration), cache ich die Ergebnisse dieser Reflektionsaufrufe, da die Verwendung von frischen Reflektionsobjekten jedes Mal einen merklichen Leistungseinfluss haben wird (gespeichert in einem assoziativen Array für mehrere Enums).

Jetzt haben die meisten Leute endlich Upgrade auf mindestens 5.3, und SplEnum verfügbar ist, das ist sicherlich auch eine praktikable Option - solange Sie die traditionell nicht intuitive Vorstellung von tatsächlichen enum nicht stört Instanziierungen in Ihrer Codebasis. Im obigen Beispiel BasicEnum und DaysOfWeek kann überhaupt nicht instanziiert werden und sollte auch nicht sein.


1320
2017-10-31 18:59



Es gibt auch eine native Erweiterung. Das SplEnum

SplEnum bietet die Möglichkeit, Aufzählungsobjekte zu emulieren und zu erstellen   nativ in PHP.

http://www.php.net/manual/en/class.splenum.php


156
2017-10-31 18:57



Was ist mit Klassenkonstanten?

<?php

class YourClass
{
    const SOME_CONSTANT = 1;

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

echo YourClass::SOME_CONSTANT;

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

35
2018-02-03 20:14



Die obere Antwort ist fantastisch. Wenn Sie jedoch extend es auf zwei verschiedene Arten, dann, welche Erweiterung zuerst ausgeführt wird, führt zu einem Aufruf der Funktionen wird den Cache erstellen. Dieser Cache wird dann von allen nachfolgenden Anrufen verwendet, unabhängig davon, welche Nebenstelle die Anrufe von ...

Ersetzen Sie dazu die Variable und die erste Funktion durch:

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

27
2017-10-31 18:56



Ich habe Klassen mit Konstanten verwendet:

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

print Enum::NAME;

25
2017-11-24 14:51



ich benutze interface Anstatt von class:

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

var $today = DaysOfWeek::Sunday;

24
2017-12-23 20:04



Nun, für ein einfaches Java wie Enum in PHP verwende ich:

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

Und es zu nennen:

SomeTypeName::toOrdinal("Read");
SomeTypeName::toString(1);

Aber ich bin ein PHP-Anfänger, der mit der Syntax kämpft, so dass dies nicht der beste Weg ist. Ich habe etwas mit Klassenkonstanten experimentiert, indem ich Reflection benutzt habe, um den konstanten Namen von seinem Wert zu bekommen, könnte es besser sein.


20
2018-06-11 13:17



Ich habe einige der anderen Antworten hier kommentiert, also dachte ich mir, ich würde auch einrechnen. Am Ende des Tages, da PHP keine typisierten Enumerationen unterstützt, können Sie eine von zwei Möglichkeiten wählen: Ausgegebene Enumerationen aushacken oder mit der Tatsache leben, dass sie extrem schwierig effektiv zu hacken sind.

Ich ziehe es vor, mit der Tatsache zu leben, und verwende stattdessen die const Methode, die andere Antworten hier auf die eine oder andere Weise benutzt haben:

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

}

Eine Beispielaufzählung:

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;

}

Verwenden Enum Als Basisklasse, von der alle anderen Aufzählungen abweichen, können Hilfsmethoden wie z toArray, isValid, und so weiter. Aufgetippte Aufzählungen (und Verwalten ihrer Instanzen) enden einfach zu unordentlich.


Hypothetisch

Ob, gab es a __getStatic magische Methode (und vorzugsweise ein __equals magische Methode auch) viel davon könnte mit einer Art Multitonmuster gemildert werden.

(Das Folgende ist hypothetisch; es Gewohnheit Arbeit, obwohl vielleicht eines Tages es wird)

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

18
2017-08-27 11:52