PHP und Aufzählungen

1149

Ich weiß, dass PHP keine nativen Aufzählungen hat. Aber ich habe mich aus der Java-Welt an sie gewöhnt. Ich würde gerne Aufzählungen verwenden, um vordefinierte Werte anzugeben, die die Funktionen zur automatischen Vervollständigung von IDEs verstehen könnten.

Konstanten machen den Trick, aber es gibt das Problem der Namespace-Kollision und (oder eigentlich, weil ) sie global sind. 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 ausfüllen.

Gibt es Lösungen / Problemumgehungen, die Sie üblicherweise verwenden? Erinnert sich jemand daran, ob die PHP-Leute irgendwelche Gedanken oder Entscheidungen in Bezug auf Aufzählungen hatten?

Henrik Paul
quelle
1
Ich habe eine Workaround-Funktion erstellt, die Konstanten als bitweise auflistet oder nicht. Ich habe nicht bemerkt, dass Sie dies zuvor gefragt haben, aber ich habe hier eine bessere Lösung als Klassenvariablen: stackoverflow.com/questions/3836385/…
NoodleOfDeath
github.com/myclabs/php-enum
Matthieu Napoli
Haben Sie etwas dagegen, etwas mehr über das Problem der Konstanten zu erzählen? "Konstanten machen den Trick, aber es gibt das Problem der Namespace-Kollision und (oder eigentlich, weil) sie global sind."
XuDing
github.com/githubjeka/enum
Evgeniy Tkachenko

Antworten:

1492

Abhängig vom Anwendungsfall würde ich normalerweise etwas Einfaches wie das folgende verwenden:

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

$today = DaysOfWeek::Sunday;

Andere Anwendungsfälle erfordern jedoch möglicherweise eine stärkere Validierung von Konstanten und Werten. Basierend auf den Kommentaren unten zur Reflexion und einigen anderen Anmerkungen ist hier ein erweitertes Beispiel, das möglicherweise eine viel größere Bandbreite von Fällen besser bedienen 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 auf diese Weise für die einfache 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: Jedes Mal, wenn ich mindestens einmal eine Reflexion für eine statische / konstante Klasse verwende, in der sich die Daten nicht ändern (z. B. in einer Aufzählung), werden die Ergebnisse dieser Reflexionsaufrufe zwischengespeichert, da jedes Mal neue Reflexionsobjekte verwendet werden wird sich irgendwann spürbar auf die Leistung auswirken (in einem assocciative Array für mehrere Aufzählungen gespeichert).

Jetzt, da die meisten Benutzer endlich ein Upgrade auf mindestens 5.3 durchgeführt haben und SplEnumverfügbar sind, ist dies sicherlich auch eine praktikable Option - solange Sie nichts gegen die traditionell nicht intuitive Vorstellung haben, tatsächliche Enum- Instanziierungen in Ihrer gesamten Codebasis zu haben. In dem obigen Beispiel BasicEnumund DaysOfWeekkann überhaupt nicht instanziert werden, noch sollten sie sein.

Brian Cline
quelle
70
Ich benutze das auch. Sie können auch in Betracht ziehen, die Klasse abstractund zu finalerstellen, damit sie nicht instanziiert oder erweitert werden kann.
Ryeguy
21
Sie können eine Klasse machen abstractund final? Ich weiß, dass dies in Java nicht erlaubt ist. Sie können das in PHP tun?
CorsiKa
20
@ryeguy Es scheint, dass Sie nicht beide abstract und machen können final. In diesem Fall würde ich mich für eine Zusammenfassung entscheiden.
Nicole
45
Über abstrakt oder endgültig; Ich mache sie endgültig und gebe ihnen einen leeren privaten Konstruktor
rael_kid
21
Seien Sie vorsichtig mit der Verwendung von 0, damit Sie nicht auf unerwartete Probleme mit falschen Vergleichen stoßen, z. B. auf die Gleichwertigkeit mit nullund Freunde in einer switchAussage. War dort.
Yitznewton
185

Es gibt auch eine native Erweiterung. Das SplEnum

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

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

Beachtung:

https://www.php.net/manual/en/spl-types.installation.php

Die PECL-Erweiterung ist nicht mit PHP gebündelt.

Eine DLL für diese PECL-Erweiterung ist derzeit nicht verfügbar.

Jasie
quelle
4
Hier ist ein Beispiel mit Splenum: dreamincode.net/forums/topic/201638-enum-in-php
Nordes
4
Ich rollte zurück, es gefällt mir besser, wenn ich den Link sehen kann. Es gibt mir Kontextinformationen.
Markus
5
Ich rollte mich zurück. Ich möchte nicht, dass ihr den Link herausarbeitet.
Markus
6
Seien Sie vorsichtig damit. SPL-Typen sind experimentell: "Diese Erweiterung ist EXPERIMENTELL. Das Verhalten dieser Erweiterung, einschließlich der Namen ihrer Funktionen und anderer Dokumentationen zu dieser Erweiterung, kann sich in einer zukünftigen Version von PHP ohne vorherige Ankündigung ändern. Diese Erweiterung sollte auf eigenes Risiko verwendet werden. ""
Bzeaman
6
SplEnum ist nicht mit PHP gebündelt, es benötigt SPL_Types Erweiterung
Kwadz
46

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();
Peter Bailey
quelle
Ich bevorzuge diesen einfachen Ansatz
David Lemon
echoConstantkann durch ersetzt werden __toString. Und dann einfachecho $c
Justinas
35

Die Top-Antwort oben ist fantastisch. Wenn Sie extendes jedoch auf zwei verschiedene Arten tun, wird der Cache erstellt, unabhängig davon, welche Erweiterung zuerst ausgeführt wird. Dieser Cache wird dann von allen nachfolgenden Aufrufen verwendet, unabhängig davon, von welcher Nebenstelle die Aufrufe initiiert werden ...

Um dies zu lösen, ersetzen Sie 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];
}
Neil Townsend
quelle
2
Hatte genau dieses Problem. Brian oder jemand mit Bearbeitungsrechten sollte dies in der akzeptierten Antwort berühren. Ich habe es in meinem Code mit der Methode 'static ::' anstelle von 'self ::' in der Funktion getConstants () gelöst und den $ constCache in den untergeordneten Aufzählungen erneut deklariert.
Sp3igel
Es mag nicht sexy sein, aber die Verwendung einer Schnittstellenkonstante ist möglicherweise der beste Weg, um in PHP vorzugehen.
Anthony Rutledge
27

Ich habe Klassen mit Konstanten verwendet:

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

print Enum::NAME;
andy.gurin
quelle
27

Ich benutze interfaceanstelle von class:

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

var $today = DaysOfWeek::Sunday;
Und es
quelle
6
class Foo implements DaysOfWeek { }und dann Foo::Sunday... was?
Dan Lugg
3
Der Autor der Frage fragt nach einer Lösung für zwei Dinge: Namespace und automatische Vervollständigung durch IDEs. Wie aus der am besten bewerteten Antwort hervorgeht, ist der einfachste Weg die Verwendung von class(oder interface, was nur eine Frage der Präferenz ist).
Andi T
4
Schnittstellen werden verwendet, um die Integrität der Klassenimplementierung zu erzwingen. Dies liegt außerhalb des Bereichs einer Schnittstelle
user3886650
2
@ user3886650 Schnittstellen können und wurden / wurden in Java verwendet, um konstante Werte zu halten. Sie sind also nicht gezwungen, eine Klasse nur zu instanziieren, um konstante Werte zu erhalten, und jede IDE bietet Code-Vervollständigung für sie. Auch wenn Sie eine Klasse erstellen, die diese Schnittstelle implementiert, erbt sie alle diese Konstanten - manchmal sehr praktisch.
Alex
@ user3886650 True, aber in PHP können Schnittstellen Konstanten haben. Darüber hinaus können diese Schnittstellenkonstanten nicht durch die Implementierung von Klassen oder deren untergeordneten Elementen überschrieben werden. In der Tat ist dies die beste Antwort in Bezug auf PHP, da alles, was überschrieben werden kann, nicht wirklich so funktioniert, wie es eine Konstante sollte. Konstante sollte nicht manchmal Konstante bedeuten (obwohl Polymorphismus manchmal nützlich sein kann).
Anthony Rutledge
25

Ich habe einige der anderen Antworten hier kommentiert, also dachte ich, ich würde auch abwägen. Letztendlich können Sie, da PHP keine typisierten Aufzählungen unterstützt, auf zwei Arten vorgehen: typisierte Aufzählungen hacken oder damit leben, dass es äußerst schwierig ist, sie effektiv zu hacken.

Ich lebe lieber mit der Tatsache und verwende stattdessen die constMethode, die andere Antworten hier auf die eine oder andere Weise verwendet 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 beispielhafte Aufzä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;

}

Die Verwendung Enumals Basisklasse , von der alle anderen Aufzählungen erweitern ermöglicht Hilfsmethoden, wie toArray, isValidund so weiter. Für mich sind getippte Aufzählungen ( und die Verwaltung ihrer Instanzen ) einfach zu chaotisch.


Hypothetisch

Wenn es eine __getStaticmagische Methode gäbe ( und vorzugsweise auch eine __equalsmagische Methode ), könnte ein Großteil davon mit einer Art Multitonenmuster gemildert werden.

( Das Folgende ist hypothetisch; es wird nicht funktionieren, obwohl es vielleicht eines Tages funktionieren 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
Dan Lugg
quelle
Ich mag die Einfachheit dieser Antwort sehr. Auf diese Art von Dingen können Sie später zurückkommen und schnell verstehen, wie es funktioniert, ohne dass es so aussieht, als hätten Sie einen gehackten Ansatz gewählt. Schade, dass es nicht mehr Stimmen gibt.
Reactgular
23

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 um es zu nennen:

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

Aber ich bin ein PHP-Anfänger und habe Probleme mit der Syntax, daher ist dies möglicherweise nicht der beste Weg. Ich habe einige mit Klassenkonstanten experimentiert, wobei Reflection verwendet wurde, um den Konstantennamen aus seinem Wert zu ermitteln.

aelg
quelle
Gute Antwort, die meisten anderen Antworten verwenden Klassen. Sie können jedoch keine verschachtelten Klassen haben.
Keyo
Dies hat den Vorteil, dass die Werte mit foreach durchlaufen werden können. Und der Nachteil, dass ein illegaler Wert nicht gefangen wird.
Bob Stein
2
Keine automatische Vervollständigung in der IDE, würde also die Vermutungsarbeit anregen. Die Konstanten würden die automatische Vervollständigung ermöglichen, klingt besser.
KrekkieD
19

Vier Jahre später bin ich wieder darauf gestoßen. Mein aktueller Ansatz ist dies, da er die Vervollständigung des Codes in der IDE sowie die Typensicherheit ermöglicht:

Basisklasse:

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

Beispiel Enum:

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

Anwendungsbeispiel:

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

Beachten Sie, dass alle Instanzen desselben Enum-Eintrags gleich sind:

$monday1 = DaysOfWeek::Monday();
$monday2 = DaysOfWeek::Monday();
$monday1 === $monday2; // true

Sie können es auch in einer switch-Anweisung verwenden:

function getGermanWeekDayName(DaysOfWeek $weekDay)
{
    switch ($weekDay)
    {
        case DaysOfWeek::Monday(): return 'Montag';
        case DaysOfWeek::Tuesday(): return 'Dienstag';
        // ...
}

Sie können einen Aufzählungseintrag auch nach Name oder Wert erstellen:

$monday = DaysOfWeek::fromValue(2);
$tuesday = DaysOfWeek::fromName('Tuesday');

Oder Sie können den Namen (dh den Funktionsnamen) einfach aus einem vorhandenen Aufzählungseintrag abrufen:

$wednesday = DaysOfWeek::Wednesday()
echo $wednesDay->getName(); // Wednesday
Buck Fixing
quelle
+1 für einen privaten Konstruktor. Ich würde keine abstrakte Helferklasse machen, nur eine einfache Klasse, einen privaten Konstruktor und einige vonconst Monday = DaysOfWeek('Monday');
Kangur
9

Ich habe diese Bibliothek auf Github gefunden und denke, sie bietet eine sehr anständige Alternative zu den Antworten hier.

PHP Enum Implementierung implementiert von SplEnum

  • Sie können einen Tipp eingeben: function setAction(Action $action) {
  • Sie können die Enum mit Methoden bereichern (zB format, parse, ...)
  • Sie können die Aufzählung erweitern, um neue Werte hinzuzufügen (machen Sie Ihre Aufzählung final, um dies zu verhindern).
  • Sie können eine Liste aller möglichen Werte erhalten (siehe unten)

Erklärung

<?php
use MyCLabs\Enum\Enum;

/**
 * Action enum
 */
class Action extends Enum
{
    const VIEW = 'view';
    const EDIT = 'edit';
}

Verwendungszweck

<?php
$action = new Action(Action::VIEW);

// or
$action = Action::VIEW();

Typ-Hinweis-Enum-Werte:

<?php
function setAction(Action $action) {
    // ...
}
Songo
quelle
1
Dies ist die richtige Antwort (vorerst, bis sie enumin PHP 7.x hinzugefügt wird), da sie Typhinweise ermöglicht.
Tobia
1
Dies ermöglicht nicht nur Typangaben, sondern __toString()ermöglicht es Ihnen aufgrund der Magie, mit Aufzählungen das zu tun, was Sie im Allgemeinen wirklich wollen - verwenden Sie sie in einer switchoder- ifAnweisung und vergleichen Sie sie direkt mit den Werten der Konstanten. Der beste Ansatz ohne native Enum-Unterstützung, IMO.
LinusR
7

Wenn Sie Aufzählungen verwenden müssen, die global eindeutig sind (dh auch beim Vergleichen von Elementen zwischen verschiedenen Aufzählungen) und einfach zu verwenden sind, können Sie den folgenden Code verwenden. Ich habe auch einige Methoden hinzugefügt, die ich nützlich finde. Beispiele finden Sie in den Kommentaren ganz oben im Code.

<?php

/**
 * Class Enum
 * 
 * @author Christopher Fox <[email protected]>
 *
 * @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";
    }

}


?>
Christopher Fox
quelle
1
Ich mag das sehr. Eine der Hauptbeschwerden ist jedoch die Fähigkeit der IDE, die Werte für die automatische Vervollständigung zu erfassen. Ich bin mir nicht sicher, ob dies ohne ein benutzerdefiniertes Addon für die IDE möglich wäre. Nicht, dass es nicht möglich wäre, es würde nur etwas Arbeit kosten.
CorsiKa
2
Verwenden Sie eval()nur, damit Sie neue Enums-Laufzeit deklarieren können? Eek. Ich fühle es nicht. Wie verhindern Sie, dass andere Klassen eine falsche Enum-Klasse erstellen, bevor Sie die richtige definieren? Sind die Enums nicht vor der Laufzeit bekannt? Und wie @corsiKa angedeutet hat, keine automatische Vervollständigung der IDE. Der einzige Vorteil, den ich sehe, ist die verzögerte Codierung.
KrekkieD
7

Ich mag auch Aufzählungen von Java und aus diesem Grund schreibe ich meine Aufzählungen auf diese Weise. Ich denke, dies ist das ähnlichste Verhalten wie in Java-Aufzählungen. Wenn einige mehr Methoden aus Java verwenden möchten, sollten Sie es hier oder in schreiben abstrakte Klasse, aber Kernidee ist in Code unten eingebettet


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
user667540
quelle
3
Ich mache so ziemlich das Gleiche, mit zwei kleinen Ergänzungen: Ich habe die statischen Werte hinter statischen Gettern versteckt. Ein Grund dafür ist, dass ich visuell lieber FruitsEnum::Apple()über FruitsEnum::$Apple, aber der wichtigere Grund ist , damit niemand sonst von Einstellung $APPLE, so dass die Enum für die gesamte Anwendung zu brechen. Das andere ist ein einfaches privates statisches Flag $initialized, das sicherstellt, dass das Anrufen init()nach dem ersten Aufruf nicht mehr möglich ist (damit kann sich auch niemand damit anlegen).
Martin Ender
Ich mochte Martin. .init()ist komisch, und mir macht der Getter-Ansatz nichts aus.
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>";
}
jglatre
quelle
6

Es könnte so einfach sein wie

enum DaysOfWeek {
    Sunday,
    Monday,
    // ...
}

in der Zukunft.

PHP RFC: Aufzählungstypen

Mykhal
quelle
Zu
Ihrer Information ab
5

Die häufigste Lösung, die ich für Aufzählungen in PHP gesehen habe, bestand darin, eine generische Aufzählungsklasse zu erstellen und diese dann zu erweitern. Vielleicht haben Sie einen Blick auf nehmen diese .

UPDATE: Alternativ habe ich dies von phpclasses.org gefunden.

Noah Goodrich
quelle
1
Obwohl die Implementierung raffiniert ist und wahrscheinlich die Arbeit erledigen würde, besteht der Nachteil darin, dass IDEs wahrscheinlich nicht wissen, wie die Aufzählungen automatisch ausgefüllt werden sollen. Ich konnte den von phpclasses.org nicht inspizieren, weil ich mich registrieren sollte.
Henrik Paul
5

Hier ist eine Github-Bibliothek für die Behandlung typsicherer Aufzählungen in PHP:

Diese Bibliothek verwaltet die Generierung von Klassen, das Zwischenspeichern von Klassen und implementiert das Entwurfsmuster Type Safe Enumeration mit verschiedenen Hilfsmethoden für den Umgang mit Aufzählungen, z. B. das Abrufen einer Ordnungszahl zum Sortieren von Aufzählungen oder das Abrufen eines Binärwerts für Aufzählungskombinationen.

Der generierte Code verwendet eine einfache alte PHP-Vorlagendatei, die ebenfalls konfigurierbar ist, sodass Sie Ihre eigene Vorlage bereitstellen können.

Es ist ein vollständiger Test, der mit phpunit abgedeckt ist.

PHP-Enums auf Github (zögern Sie nicht)

Verwendung: (@see usage.php oder Unit-Tests für weitere Details)

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

Ausgabe:

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
quelle
4

Ich habe mich für den folgenden Ansatz entschieden, da ich die Möglichkeit habe, Typensicherheit für Funktionsparameter, automatische Vervollständigung in NetBeans und gute Leistung zu gewährleisten. Das einzige, was ich nicht besonders mag, ist, dass Sie [extended class name]::enumerate();nach der Definition der Klasse aufrufen müssen .

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
quelle
Nichts hindert Sie daran, die Enum-Werte neu zu definieren:DaysOfWeek::$MONDAY = 3;
KrekkieD
@BrianFisher, ich weiß, dass es jetzt spät ist, aber wenn Sie nicht gerne [extended class name]::enumerate();nach der Definition aufrufen , warum tun Sie das nicht im Konstrukt?
Kann O 'Spam
4

Meine unten stehende Enum-Klassendefinition ist stark typisiert und sehr natürlich zu verwenden und zu definieren.

Definition:

class Fruit extends Enum {
    static public $APPLE = 1;
    static public $ORANGE = 2;
}
Fruit::initialize(); //Can also be called in autoloader

Enum umschalten

$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

Übergabe von Enum als Parameter (stark typisiert)

/** 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 als String

echo "I have an $myFruit\n";

>> I have an APPLE

Holen Sie sich Enum durch Ganzzahl

$myFruit = Fruit::getByValue(2);

echo "Now I have an $myFruit\n";

>> Now I have an ORANGE

Holen Sie sich Enum nach Namen

$myFruit = Fruit::getByName("APPLE");

echo "But I definitely prefer an $myFruit\n\n";

>> But I definitely prefer an APPLE

Die Enum-Klasse:

/**
 * @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;
    }

}

Zusatz

Sie können natürlich auch Kommentare für IDEs hinzufügen

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
quelle
4

Mir ist klar, dass dies ein sehr, sehr, sehr alter Thread ist, aber ich hatte einen Gedanken darüber und wollte wissen, was die Leute dachten.

Anmerkungen: Ich habe damit herumgespielt und festgestellt, __call()dass Sie der tatsächlichen Funktion noch näher kommen können , wenn ich sie nur modifiziere enums. Die __call()Funktion verarbeitet alle unbekannten Funktionsaufrufe. enumsAngenommen, Sie möchten drei RED_LIGHT, YELLOW_LIGHT und GREEN_LIGHT erstellen. Sie können dies jetzt tun, indem Sie einfach Folgendes tun:

$c->RED_LIGHT();
$c->YELLOW_LIGHT();
$c->GREEN_LIGHT();

Einmal definiert, müssen Sie sie nur noch einmal aufrufen, um die Werte zu erhalten:

echo $c->RED_LIGHT();
echo $c->YELLOW_LIGHT();
echo $c->GREEN_LIGHT();

und du solltest 0, 1 und 2 bekommen. Viel Spaß! Dies ist jetzt auch auf GitHub verfügbar.

Update: Ich habe es so gemacht, dass jetzt sowohl die __get()als auch die __set()Funktionen verwendet werden. Mit diesen können Sie keine Funktion aufrufen müssen, es sei denn, Sie möchten. Stattdessen können Sie jetzt einfach sagen:

$c->RED_LIGHT;
$c->YELLOW_LIGHT;
$c->GREEN_LIGHT;

Sowohl für die Schaffung als auch für das Erhalten der Werte. Da die Variablen anfangs nicht definiert wurden, wird die __get()Funktion aufgerufen (da kein Wert angegeben ist), wodurch festgestellt wird, dass der Eintrag im Array nicht vorgenommen wurde. Es macht also den Eintrag, weist ihm den zuletzt angegebenen Wert plus eins (+1) zu, erhöht die letzte Wertvariable und gibt TRUE zurück. Wenn Sie den Wert einstellen:

$c->RED_LIGHT = 85;

Dann wird die __set()Funktion aufgerufen und der letzte Wert auf den neuen Wert plus eins (+1) gesetzt. Jetzt haben wir also eine ziemlich gute Möglichkeit, Aufzählungen zu erstellen, und sie können im laufenden Betrieb erstellt werden.

<?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
#           [email protected].  :-)
#
#   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
quelle
3

Ich weiß, dass dies ein alter Thread ist, aber keine der Problemumgehungen, die ich gesehen habe, sah wirklich wie Aufzählungen aus, da bei fast allen Problemumgehungen Sie den Aufzählungselementen manuell Werte zuweisen müssen oder ein Array von Aufzählungsschlüsseln an a übergeben müssen Funktion. Also habe ich meine eigene Lösung dafür geschaffen.

Um eine Enum-Klasse mit meiner Lösung zu erstellen, können Sie diese Enum-Klasse einfach unten erweitern, eine Reihe statischer Variablen erstellen (ohne sie initialisieren zu müssen) und yourEnumClass :: init () direkt unterhalb der Definition Ihrer Enum-Klasse aufrufen .

edit: Dies funktioniert nur in php> = 5.3, kann aber wahrscheinlich auch in älteren Versionen geändert werden

/**
 * 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;
                }
            }
        }
    }
}
Tiddo
quelle
3

Jetzt können Sie die SplEnum- Klasse verwenden, um sie nativ zu erstellen. Gemäß der offiziellen Dokumentation.

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

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

Bitte beachten Sie, dass es sich um eine Erweiterung handelt, die installiert werden muss, aber standardmäßig nicht verfügbar ist. Welches fällt unter spezielle Typen, die auf der PHP-Website selbst beschrieben sind. Das obige Beispiel stammt von der PHP-Site.

Krishnadas PC
quelle
3

Schließlich eine Antwort von PHP 7.1+ mit Konstanten, die nicht überschrieben werden können.

/**
 * 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.
}

Wenn Sie Namespaces verwenden, sollte die Code-Vervollständigung funktionieren.

Dadurch verlieren Sie jedoch die Fähigkeit, die Konstanten innerhalb der Klassenfamilie ( protected) oder der Klasse allein ( private) auszublenden . Per Definition ist alles in einem Interfaceistpublic .

PHP-Handbuch: Schnittstellen

Anthony Rutledge
quelle
Dies ist kein Java. Dies funktioniert in Fällen, in denen kein Polymorphismus / Strategiemuster erforderlich ist, um Konstanten in einer übergeordneten Klasse zu überschreiben.
Anthony Rutledge
2

Dies ist meine Einstellung zu "dynamischer" Aufzählung ... damit ich sie mit Variablen aufrufen kann, z. aus einem Formular.

Schauen Sie sich die aktualisierte Version unter diesem Codeblock an ...

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

UPDATE: Besser so ...

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;

}

Rufen Sie an mit

EnumCategory::${$category};
Anders
quelle
5
Das Problem mit diesem Wesen; EnumCategory::$sport = 9;. Willkommen im Sportmuseum. const ist der bessere Weg, es zu tun.
Dan Lugg
2

Die akzeptierte Antwort ist der richtige Weg und ist eigentlich das, was ich der Einfachheit halber tue. Die meisten Vorteile der Aufzählung werden angeboten (lesbar, schnell usw.). Ein Konzept fehlt jedoch: Typensicherheit. In den meisten Sprachen werden Aufzählungen auch verwendet, um zulässige Werte einzuschränken. Im Folgenden finden Sie ein Beispiel dafür, wie die Typensicherheit auch mithilfe privater Konstruktoren, statischer Instanziierungsmethoden und Typprüfung erreicht werden kann:

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

Wir könnten sogar noch weiter gehen: Die Verwendung von Konstanten in der DaysOfWeek-Klasse kann zu Missbrauch führen: z. B. könnte man sie fälschlicherweise so verwenden:

printDayOfWeek(DaysOfWeek::Monday); //triggers a compiler error.

was falsch ist (ruft Integer-Konstante auf). Wir können dies verhindern, indem wir private statische Variablen anstelle von Konstanten verwenden:

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

}

Natürlich ist es nicht möglich, auf ganzzahlige Konstanten zuzugreifen (dies war eigentlich der Zweck). Mit der intVal-Methode kann ein DaysOfWeek-Objekt in seine Ganzzahldarstellung konvertiert werden.

Beachten Sie, dass wir sogar noch weiter gehen könnten, indem wir einen Caching-Mechanismus in Instanziierungsmethoden implementieren, um Speicherplatz zu sparen, falls Aufzählungen häufig verwendet werden ...

Hoffe das wird helfen

Vincent Pazeller
quelle
2

Einige gute Lösungen hier!

Hier ist meine Version.

  • Es ist stark getippt
  • Es funktioniert mit der automatischen IDE-Vervollständigung
  • Aufzählungen werden durch einen Code und eine Beschreibung definiert, wobei der Code eine Ganzzahl, ein Binärwert, eine kurze Zeichenfolge oder im Grunde alles andere sein kann, was Sie wollen. Das Muster könnte leicht erweitert werden, um andere Eigenschaften zu unterstützen.
  • Es unterstützt Werte- (==) und Referenzvergleiche (===) und funktioniert in switch-Anweisungen.

Ich denke, der Hauptnachteil ist, dass Enum-Mitglieder aufgrund der Beschreibungen und der Unfähigkeit von PHP, Objekte zum Zeitpunkt der Deklaration statischer Mitglieder zu erstellen, separat deklariert und instanziiert werden müssen. Ich denke, ein Weg, dies zu umgehen, könnte darin bestehen, stattdessen Reflexion mit analysierten Dokumentkommentaren zu verwenden.

Die abstrakte Aufzählung sieht folgendermaßen aus:

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

Hier ist ein konkretes Beispiel:

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

Welches kann so verwendet werden:

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

Und erzeugt diese Ausgabe:

1: Mein erster Wert

Array (3) {
[1] =>
Objekt (EMyEnum) # 1 (2) {
["Code": "AbstractEnum": privat] =>
int (1)
["Beschreibung": "AbstractEnum": privat] =>
Zeichenfolge (14) "Mein erster Wert"
}
[2] =>
Objekt (EMyEnum) # 2 (2) {
["Code": "AbstractEnum": privat] =>
int (2)
["Beschreibung": "AbstractEnum" : private] =>
string (15) "Mein zweiter Wert"
}
[3] =>
Objekt (EMyEnum) # 3 (2) {
["Code": "AbstractEnum": privat] =>
int (3)
["description": "AbstractEnum": private] =>
Zeichenfolge (14) "Mein dritter Wert"
}
}

Mein zweiter Wert

Dan King
quelle
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 ) );

Verwenden Sie keine Reflexion. Dies macht es äußerst schwierig, über Ihren Code nachzudenken und festzustellen, wo etwas verwendet wird, und führt dazu, dass statische Analysewerkzeuge beschädigt werden (z. B. was in Ihre IDE integriert ist).

Jesse
quelle
2

Einer der Aspekte, die in einigen der anderen Antworten hier fehlen, ist die Verwendung von Aufzählungen mit Typhinweisen.

Wenn Sie Ihre Aufzählung als eine Menge von Konstanten in einer abstrakten Klasse definieren, z

abstract class ShirtSize {
    public const SMALL = 1;
    public const MEDIUM = 2;
    public const LARGE = 3;
}

dann können Sie es nicht in einem Funktionsparameter geben andeuten - für einen, weil es nicht instantiable ist, sondern auch , weil die Art der ShirtSize::SMALList int, nicht ShirtSize.

Deshalb wären native Aufzählungen in PHP so viel besser als alles, was wir uns einfallen lassen können. Wir können eine Aufzählung jedoch approximieren, indem wir eine private Eigenschaft beibehalten, die den Wert der Aufzählung darstellt, und dann die Initialisierung dieser Eigenschaft auf unsere vordefinierten Konstanten beschränken. Um zu verhindern, dass die Aufzählung willkürlich instanziiert wird (ohne den Aufwand für die Typprüfung einer Whitelist), machen wir den Konstruktor privat.

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

Dann können wir so verwenden 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";

Auf diese Weise besteht der größte Unterschied aus der Sicht des Benutzers darin, dass Sie ()den Namen der Konstante anheften müssen.

Ein Nachteil ist jedoch, dass ===(der die Objektgleichheit vergleicht) false ==zurückgibt, wenn true zurückgegeben wird. Aus diesem Grund ist es am besten, eine equalsMethode anzugeben, damit Benutzer nicht daran denken müssen, zwei Aufzählungswerte zu verwenden ==und nicht ===zu vergleichen.

BEARBEITEN: Einige der vorhandenen Antworten sind sehr ähnlich, insbesondere: https://stackoverflow.com/a/25526473/2407870 .

Chris Middleton
quelle
2

Als ich auf die Antwort von @Brian Cline trat, dachte ich, ich könnte meine 5 Cent geben

<?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
quelle
Aus irgendeinem Grund kann ich diese Funktionen nicht nennen. Es sagt mir, dass solche Funktionen nicht deklariert sind. Was mache ich falsch? [grundlegende Enum-Klasse befindet sich in einer anderen Datei und ich verwende include('enums.php');]. Aus irgendeinem Grund werden in Enum keine Funktionen für
Andrew
Auch ... wie man es von String setzt? etw wie$currentSeason.set("Spring");
Andrew
1

Mein Versuch, eine Aufzählung mit PHP zu erstellen ... ist äußerst begrenzt, da Objekte als Aufzählungswerte nicht unterstützt werden, aber dennoch etwas nützlich sind ...

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();
    }
}
KDog
quelle
Es ist in viele Richtungen begrenzt und die vorhandenen Antworten bieten weit mehr darüber. Ich würde sagen, dass dies nicht wirklich etwas Nützliches hinzufügt.
hakre
1

Gestern habe ich diese Klasse in meinem Blog geschrieben . Ich denke, es ist vielleicht einfach für die Verwendung in PHP-Skripten:

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

Verwendungszweck:

class enumWorkType extends Enum
{
        const FULL = 0;
        const SHORT = 1;
}
Arturgspb
quelle
2
Aber es ist eine gute Klasse und der Name der Funktionen ist nativ. Und auch translate.google.ru vielleicht helfen.
Arturgspb
2
Verwenden Sie Chrome-Typen und übersetzen Sie sie. Wenn Sie Programmierer sind, lesen Sie Code!
Markus
8
Im Allgemeinen ist es immer besser, den Code in die Antwort aufzunehmen, als eine Verknüpfung zu einer externen Ressource herzustellen, die in 'n' Monaten / Jahren usw. vorhanden sein kann oder nicht
John Parker
Meine Klasse ist so groß und ich denke, dass das Lesen dieses Beitrags unpraktisch sein wird.
Arturgspb
Ich denke hier zwei schlechte Dinge: Es ist auf Russisch (jeder Programmierer muss Englisch sprechen und es verwenden, auch in Kommentaren) und es ist hier nicht enthalten. Weitere Informationen zum Einfügen von großem Code finden Sie in der Hilfe.
gaRex