1317 Stimmen

Aufzählungen in PHP

Ich weiß, dass PHP noch keine nativen Enumerationen hat. Aber ich habe mich an sie aus der Java-Welt gewöhnt. Ich würde Enums gerne als eine Möglichkeit verwenden, vordefinierte Werte anzugeben, die die Autovervollständigungsfunktionen von IDEs verstehen könnten.

Konstanten tun den Trick, aber es gibt das Problem der Namespace-Kollision und (oder eigentlich denn ) sie sind global. Arrays haben das Namespace-Problem nicht, aber sie sind zu vage, sie können zur Laufzeit überschrieben werden und IDEs wissen selten, wie sie ihre Schlüssel ohne zusätzliche statische Analyse-Annotationen oder Attribute automatisch ausfüllen können.

Gibt es Lösungen/Workarounds, die Sie häufig verwenden? Erinnert sich jemand daran, ob die PHP-Leute irgendwelche Gedanken oder Entscheidungen zu Aufzählungen getroffen haben?

0 Stimmen

1 Stimmen

Ich habe eine Umgehungsfunktion erstellt, die Konstanten als bitweise oder nicht aufzählt. Ich habe nicht bemerkt, dass Sie das vorher gefragt haben, aber ich habe hier eine bessere Lösung als Klassenvariablen: stackoverflow.com/questions/3836385/

0 Stimmen

5voto

Brian Fisher Punkte 22591

Ich habe mich für den folgenden Ansatz entschieden, da er mir die Möglichkeit gibt, Typsicherheit für Funktionsparameter, automatische Vervollständigung in NetBeans und gute Leistung zu bieten. Das einzige, was mir nicht so gut gefällt, ist, dass man die [extended class name]::enumerate(); nachdem Sie die Klasse definiert haben.

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

4voto

Tiddo Punkte 5810

Ich weiß, dies ist ein altes Thema, jedoch keine der Umgehungen, die ich gesehen habe, sah wirklich wie Enums, da fast alle Umgehungen erfordert Sie manuell Werte zu den Enum-Elemente zuweisen, oder es erfordert Sie ein Array von Enum-Schlüssel an eine Funktion übergeben. Also habe ich meine eigene Lösung für dieses Problem entwickelt.

Um eine Enum-Klasse mit meiner Lösung zu erstellen, kann man einfach diese Enum-Klasse unten erweitern, eine Reihe von statischen Variablen erstellen (die nicht initialisiert werden müssen) und einen Aufruf von yourEnumClass::init() direkt unter der Definition Ihrer Enum-Klasse machen.

edit: Dies funktioniert nur in php >= 5.3, aber es kann wahrscheinlich modifiziert werden, um auch in älteren Versionen zu funktionieren

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

4voto

Krishnadas PC Punkte 4850

Jetzt können Sie die SplEnum Klasse, um sie nativ zu erstellen. Wie in der offiziellen Dokumentation beschrieben.

SplEnum bietet die Möglichkeit, Aufzählungsobjekte zu emulieren und zu erstellen 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 nicht standardmäßig verfügbar ist. Das kommt unter Besondere Typen die auf der PHP-Website selbst beschrieben sind. Das obige Beispiel ist der PHP-Website entnommen.

4voto

Jesse Punkte 5980
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. Es macht es extrem schwierig, über Ihren Code zu folgern und aufzuspüren, wo etwas verwendet wird, und neigt dazu, statische Analyse-Tools zu brechen (z. B. was in Ihre IDE eingebaut ist).

3voto

Vincent Pazeller Punkte 1248

Die akzeptierte Antwort ist der richtige Weg, und das ist es auch, was ich der Einfachheit halber tue. Die meisten Vorteile der Aufzählung sind gegeben (lesbar, schnell, usw.). Ein Konzept fehlt jedoch: die Typsicherheit. In den meisten Sprachen werden Aufzählungen auch zur Einschränkung zulässiger Werte verwendet. Nachstehend ein Beispiel dafür, wie Typsicherheit auch durch die Verwendung privater Konstruktoren, statischer Instanziierungsmethoden und Typüberprü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 könnte zu einer falschen Verwendung führen: z.B. könnte man sie fälschlicherweise so verwenden:

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

was falsch ist (ruft ganzzahlige 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 Integer-Konstanten zuzugreifen (das war eigentlich der Zweck). Die intVal-Methode ermöglicht die Umwandlung eines DaysOfWeek-Objekts in seine Integer-Darstellung.

Wir könnten sogar noch weiter gehen, indem wir einen Zwischenspeichermechanismus in den Instanziierungsmethoden implementieren, um Speicher zu sparen, falls Aufzählungen ausgiebig genutzt werden...

Ich hoffe, das hilft

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X