سؤال

أعلم أن PHP لا يحتوي على تعدادات أصلية.لكنني اعتدت عليهم من عالم جافا.أرغب في استخدام التعدادات كوسيلة لإعطاء قيم محددة مسبقًا يمكن أن تفهمها ميزات الإكمال التلقائي لـ IDEs.

الثوابت تقوم بالمهمة، ولكن هناك مشكلة تضارب مساحة الاسم و (أو في الواقع لأن) إنهم عالميون.لا تواجه المصفوفات مشكلة مساحة الاسم، لكنها غامضة جدًا، ويمكن الكتابة فوقها في وقت التشغيل، ونادرًا ما تعرف IDEs (أبدًا؟) كيفية الملء التلقائي لمفاتيحها.

هل هناك أي حلول/حلول بديلة تستخدمها عادة؟هل يتذكر أحد ما إذا كان لدى PHP أي أفكار أو قرارات بشأن التعدادات؟

هل كانت مفيدة؟

المحلول

اعتمادًا على حالة الاستخدام، عادةً ما أستخدم شيئًا ما بسيط مثل ما يلي:

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

$today = DaysOfWeek::Sunday;

ومع ذلك، قد تتطلب حالات الاستخدام الأخرى المزيد من التحقق من صحة الثوابت والقيم.استنادا إلى التعليقات أدناه حول التفكير، و بعض الملاحظات الأخرى, ، إليك مثالًا موسعًا قد يخدم نطاقًا أوسع من الحالات بشكل أفضل:

abstract class BasicEnum {
    private static $constCacheArray = NULL;

    private static function getConstants() {
        if (self::$constCacheArray == NULL) {
            self::$constCacheArray = [];
        }
        $calledClass = get_called_class();
        if (!array_key_exists($calledClass, self::$constCacheArray)) {
            $reflect = new ReflectionClass($calledClass);
            self::$constCacheArray[$calledClass] = $reflect->getConstants();
        }
        return self::$constCacheArray[$calledClass];
    }

    public static function isValidName($name, $strict = false) {
        $constants = self::getConstants();

        if ($strict) {
            return array_key_exists($name, $constants);
        }

        $keys = array_map('strtolower', array_keys($constants));
        return in_array(strtolower($name), $keys);
    }

    public static function isValidValue($value, $strict = true) {
        $values = array_values(self::getConstants());
        return in_array($value, $values, $strict);
    }
}

من خلال إنشاء فئة تعداد بسيطة تعمل على توسيع BasicEnum، لديك الآن القدرة على استخدام التوابع للتحقق من صحة الإدخال البسيط:

abstract class DaysOfWeek extends BasicEnum {
    const Sunday = 0;
    const Monday = 1;
    const Tuesday = 2;
    const Wednesday = 3;
    const Thursday = 4;
    const Friday = 5;
    const Saturday = 6;
}

DaysOfWeek::isValidName('Humpday');                  // false
DaysOfWeek::isValidName('Monday');                   // true
DaysOfWeek::isValidName('monday');                   // true
DaysOfWeek::isValidName('monday', $strict = true);   // false
DaysOfWeek::isValidName(0);                          // false

DaysOfWeek::isValidValue(0);                         // true
DaysOfWeek::isValidValue(5);                         // true
DaysOfWeek::isValidValue(7);                         // false
DaysOfWeek::isValidValue('Friday');                  // false

كملاحظة جانبية، في أي وقت أستخدم الانعكاس مرة واحدة على الأقل في فئة ثابتة/ثابتة حيث لن تتغير البيانات (كما هو الحال في التعداد)، أقوم بتخزين نتائج استدعاءات الانعكاس تلك، نظرًا لأن استخدام كائنات انعكاس جديدة في كل مرة سيكون له في النهاية تأثير ملحوظ على الأداء (يتم تخزينها في مصفوفة ترابطية للتعدادات المتعددة).

الآن بعد أن فعل معظم الناس أخيراً تمت ترقيته إلى 5.3 على الأقل، و SplEnum متاح، وهذا بالتأكيد خيار قابل للتطبيق أيضًا - طالما أنك لا تمانع في الفكرة غير البديهية التقليدية المتمثلة في وجود التعداد الفعلي إنشاء مثيلات في جميع أنحاء قاعدة التعليمات البرمجية الخاصة بك.في المثال أعلاه، BasicEnum و DaysOfWeek لا يمكن إنشاء مثيل لها على الإطلاق، ولا ينبغي أن تكون كذلك.

نصائح أخرى

هناك امتداد أصلي أيضًا.ال SplEnum

يمنح الطحال القدرة على محاكاة وإنشاء كائنات تعداد أصلاً في PHP.

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

وماذا عن الثوابت الدرجة؟

<?php

class YourClass
{
    const SOME_CONSTANT = 1;

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

echo YourClass::SOME_CONSTANT;

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

والجواب العليا فوق رائعة. ومع ذلك، إذا كنت extend ذلك بطريقتين مختلفتين، ثم أيهما التمديد يتم النتائج الأولى في استدعاء وظائف وإنشاء ذاكرة التخزين المؤقت. وبعد ذلك يتم استخدام هذا المخبأ من قبل جميع المكالمات اللاحقة، بغض النظر عن أيهما تمديد وبدأت دعوات ...

ليحل بذلك، استبدال المتغير والوظيفة الأولى مع:

private static $constCacheArray = null;

private static function getConstants() {
    if (self::$constCacheArray === null) self::$constCacheArray = array();

    $calledClass = get_called_class();
    if (!array_key_exists($calledClass, self::$constCacheArray)) {
        $reflect = new \ReflectionClass($calledClass);
        self::$constCacheArray[$calledClass] = $reflect->getConstants();
    }

    return self::$constCacheArray[$calledClass];
}

وأنا استخدم interface بدلا من class:

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

var $today = DaysOfWeek::Sunday;

واعتدت الطبقات مع الثوابت:

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

print Enum::NAME;

لقد علقت على بعض الإجابات الأخرى هنا، لذلك اعتقدت أنني سأعلق عليها أيضًا.في نهاية المطاف، نظرًا لأن PHP لا يدعم التعدادات المكتوبة، يمكنك اتباع إحدى طريقتين:اختراق التعدادات المكتوبة، أو التعايش مع حقيقة أنه من الصعب للغاية اختراقها بفعالية.

أفضّل أن أعيش مع الحقيقة، وأستخدم بدلاً من ذلك const الطريقة التي استخدمتها الإجابات الأخرى هنا بطريقة أو بأخرى:

abstract class Enum
{

    const NONE = null;

    final private function __construct()
    {
        throw new NotSupportedException(); // 
    }

    final private function __clone()
    {
        throw new NotSupportedException();
    }

    final public static function toArray()
    {
        return (new ReflectionClass(static::class))->getConstants();
    }

    final public static function isValid($value)
    {
        return in_array($value, static::toArray());
    }

}

مثال على التعداد:

final class ResponseStatusCode extends Enum
{

    const OK                         = 200;
    const CREATED                    = 201;
    const ACCEPTED                   = 202;
    // ...
    const SERVICE_UNAVAILABLE        = 503;
    const GATEWAY_TIME_OUT           = 504;
    const HTTP_VERSION_NOT_SUPPORTED = 505;

}

استخدام Enum كفئة أساسية تمتد منها جميع التعدادات الأخرى تسمح بالطرق المساعدة، مثل toArray, isValid, ، وما إلى ذلك وهلم جرا.بالنسبة لي، التعدادات المكتوبة (وإدارة حالاتهم) ينتهي الأمر بالفوضى للغاية.


افتراضية

لو, ، كان هناك أ __getStatic الطريقة السحرية(ويفضل أن __equals الطريقة السحرية ايضا) يمكن تخفيف الكثير من هذا بنوع من نمط متعدد الأطنان.

(ما يلي افتراضي؛هو - هي متعود العمل، على الرغم من أنه ربما سيفعل ذلك يومًا ما)

final class TestEnum
{

    private static $_values = [
        'FOO' => 1,
        'BAR' => 2,
        'QUX' => 3,
    ];
    private static $_instances = [];

    public static function __getStatic($name)
    {
        if (isset(static::$_values[$name]))
        {
            if (empty(static::$_instances[$name]))
            {
                static::$_instances[$name] = new static($name);
            }
            return static::$_instances[$name];
        }
        throw new Exception(sprintf('Invalid enumeration value, "%s"', $name));
    }

    private $_value;

    public function __construct($name)
    {
        $this->_value = static::$_values[$name];
    }

    public function __equals($object)
    {
        if ($object instanceof static)
        {
            return $object->_value === $this->_value;
        }
        return $object === $this->_value;
    }

}

$foo = TestEnum::$FOO; // object(TestEnum)#1 (1) {
                       //   ["_value":"TestEnum":private]=>
                       //   int(1)
                       // }

$zap = TestEnum::$ZAP; // Uncaught exception 'Exception' with message
                       // 'Invalid enumeration member, "ZAP"'

$qux = TestEnum::$QUX;
TestEnum::$QUX == $qux; // true
'hello world!' == $qux; // false

حسنا، لجافا بسيط مثل التعداد في بي، وأنا استخدم:

class SomeTypeName {
    private static $enum = array(1 => "Read", 2 => "Write");

    public function toOrdinal($name) {
        return array_search($name, self::$enum);
    }

    public function toString($ordinal) {
        return self::$enum[$ordinal];
    }
}

وأن نسميها:

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

ولكن أنا مبتدئ PHP، تكافح مع بناء الجملة حتى هذا قد لا يكون أفضل وسيلة. أنا جربت بعض مع فئة الثوابت، وذلك باستخدام انعكاس للحصول على اسم ثابت من انها قيمة، قد يكون أكثر إتقانا.

وبعد أربع سنوات، صادفت هذا مرة أخرى.أسلوبي الحالي هو أنه يسمح بإكمال التعليمات البرمجية في IDE بالإضافة إلى أمان النوع:

الطبقة الأساسية:

abstract class TypedEnum
{
    private static $_instancedValues;

    private $_value;
    private $_name;

    private function __construct($value, $name)
    {
        $this->_value = $value;
        $this->_name = $name;
    }

    private static function _fromGetter($getter, $value)
    {
        $reflectionClass = new ReflectionClass(get_called_class());
        $methods = $reflectionClass->getMethods(ReflectionMethod::IS_STATIC | ReflectionMethod::IS_PUBLIC);    
        $className = get_called_class();

        foreach($methods as $method)
        {
            if ($method->class === $className)
            {
                $enumItem = $method->invoke(null);

                if ($enumItem instanceof $className && $enumItem->$getter() === $value)
                {
                    return $enumItem;
                }
            }
        }

        throw new OutOfRangeException();
    }

    protected static function _create($value)
    {
        if (self::$_instancedValues === null)
        {
            self::$_instancedValues = array();
        }

        $className = get_called_class();

        if (!isset(self::$_instancedValues[$className]))
        {
            self::$_instancedValues[$className] = array();
        }

        if (!isset(self::$_instancedValues[$className][$value]))
        {
            $debugTrace = debug_backtrace();
            $lastCaller = array_shift($debugTrace);

            while ($lastCaller['class'] !== $className && count($debugTrace) > 0)
            {
                $lastCaller = array_shift($debugTrace);
            }

            self::$_instancedValues[$className][$value] = new static($value, $lastCaller['function']);
        }

        return self::$_instancedValues[$className][$value];
    }

    public static function fromValue($value)
    {
        return self::_fromGetter('getValue', $value);
    }

    public static function fromName($value)
    {
        return self::_fromGetter('getName', $value);
    }

    public function getValue()
    {
        return $this->_value;
    }

    public function getName()
    {
        return $this->_name;
    }
}

التعداد المثال:

final class DaysOfWeek extends TypedEnum
{
    public static function Sunday() { return self::_create(0); }    
    public static function Monday() { return self::_create(1); }
    public static function Tuesday() { return self::_create(2); }   
    public static function Wednesday() { return self::_create(3); }
    public static function Thursday() { return self::_create(4); }  
    public static function Friday() { return self::_create(5); }
    public static function Saturday() { return self::_create(6); }      
}

مثال على الاستخدام:

function saveEvent(DaysOfWeek $weekDay, $comment)
{
    // store week day numeric value and comment:
    $myDatabase->save('myeventtable', 
       array('weekday_id' => $weekDay->getValue()),
       array('comment' => $comment));
}

// call the function, note: DaysOfWeek::Monday() returns an object of type DaysOfWeek
saveEvent(DaysOfWeek::Monday(), 'some comment');

لاحظ أن جميع مثيلات نفس إدخال التعداد هي نفسها:

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

يمكنك أيضًا استخدامه داخل بيان التبديل:

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

يمكنك أيضًا إنشاء إدخال تعداد حسب الاسم أو القيمة:

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

أو يمكنك فقط الحصول على الاسم (أي.اسم الوظيفة) من إدخال التعداد الموجود:

$wednesday = DaysOfWeek::Wednesday()
echo $wednesDay->getName(); // Wednesday

إذا كنت بحاجة إلى استخدام تتضمن التعدادات التي هي فريدة من نوعها على الصعيد العالمي (أي حتى عند المقارنة بين العناصر المختلفة بين تتضمن التعدادات) وهي سهلة الاستخدام، لا تتردد في استخدام التعليمات البرمجية التالية. أود أيضا أن أضيف بعض الأساليب التي أجد من المفيد. سوف تجد أمثلة في التعليقات في أعلى جدا من التعليمات البرمجية.

<?php

/**
 * Class Enum
 * 
 * @author Christopher Fox <christopher.fox@gmx.de>
 *
 * @version 1.0
 *
 * This class provides the function of an enumeration.
 * The values of Enum elements are unique (even between different Enums)
 * as you would expect them to be.
 *
 * Constructing a new Enum:
 * ========================
 *
 * In the following example we construct an enum called "UserState"
 * with the elements "inactive", "active", "banned" and "deleted".
 * 
 * <code>
 * Enum::Create('UserState', 'inactive', 'active', 'banned', 'deleted');
 * </code>
 *
 * Using Enums:
 * ============
 *
 * The following example demonstrates how to compare two Enum elements
 *
 * <code>
 * var_dump(UserState::inactive == UserState::banned); // result: false
 * var_dump(UserState::active == UserState::active); // result: true
 * </code>
 *
 * Special Enum methods:
 * =====================
 *
 * Get the number of elements in an Enum:
 *
 * <code>
 * echo UserState::CountEntries(); // result: 4
 * </code>
 *
 * Get a list with all elements of the Enum:
 *
 * <code>
 * $allUserStates = UserState::GetEntries();
 * </code>
 *
 * Get a name of an element:
 *
 * <code>
 * echo UserState::GetName(UserState::deleted); // result: deleted
 * </code>
 *
 * Get an integer ID for an element (e.g. to store as a value in a database table):
 * This is simply the index of the element (beginning with 1).
 * Note that this ID is only unique for this Enum but now between different Enums.
 *
 * <code>
 * echo UserState::GetDatabaseID(UserState::active); // result: 2
 * </code>
 */
class Enum
{

    /**
     * @var Enum $instance The only instance of Enum (Singleton)
     */
    private static $instance;

    /**
     * @var array $enums    An array of all enums with Enum names as keys
     *          and arrays of element names as values
     */
    private $enums;

    /**
     * Constructs (the only) Enum instance
     */
    private function __construct()
    {
        $this->enums = array();
    }

    /**
     * Constructs a new enum
     *
     * @param string $name The class name for the enum
     * @param mixed $_ A list of strings to use as names for enum entries
     */
    public static function Create($name, $_)
    {
        // Create (the only) Enum instance if this hasn't happened yet
        if (self::$instance===null)
        {
            self::$instance = new Enum();
        }

        // Fetch the arguments of the function
        $args = func_get_args();
        // Exclude the "name" argument from the array of function arguments,
        // so only the enum element names remain in the array
        array_shift($args);
        self::$instance->add($name, $args);
    }

    /**
     * Creates an enumeration if this hasn't happened yet
     * 
     * @param string $name The class name for the enum
     * @param array $fields The names of the enum elements
     */
    private function add($name, $fields)
    {
        if (!array_key_exists($name, $this->enums))
        {
            $this->enums[$name] = array();

            // Generate the code of the class for this enumeration
            $classDeclaration =     "class " . $name . " {\n"
                        . "private static \$name = '" . $name . "';\n"
                        . $this->getClassConstants($name, $fields)
                        . $this->getFunctionGetEntries($name)
                        . $this->getFunctionCountEntries($name)
                        . $this->getFunctionGetDatabaseID()
                        . $this->getFunctionGetName()
                        . "}";

            // Create the class for this enumeration
            eval($classDeclaration);
        }
    }

    /**
     * Returns the code of the class constants
     * for an enumeration. These are the representations
     * of the elements.
     * 
     * @param string $name The class name for the enum
     * @param array $fields The names of the enum elements
     *
     * @return string The code of the class constants
     */
    private function getClassConstants($name, $fields)
    {
        $constants = '';

        foreach ($fields as $field)
        {
            // Create a unique ID for the Enum element
            // This ID is unique because class and variables
            // names can't contain a semicolon. Therefore we
            // can use the semicolon as a separator here.
            $uniqueID = $name . ";" . $field;
            $constants .=   "const " . $field . " = '". $uniqueID . "';\n";
            // Store the unique ID
            array_push($this->enums[$name], $uniqueID);
        }

        return $constants;
    }

    /**
     * Returns the code of the function "GetEntries()"
     * for an enumeration
     * 
     * @param string $name The class name for the enum
     *
     * @return string The code of the function "GetEntries()"
     */
    private function getFunctionGetEntries($name) 
    {
        $entryList = '';        

        // Put the unique element IDs in single quotes and
        // separate them with commas
        foreach ($this->enums[$name] as $key => $entry)
        {
            if ($key > 0) $entryList .= ',';
            $entryList .= "'" . $entry . "'";
        }

        return  "public static function GetEntries() { \n"
            . " return array(" . $entryList . ");\n"
            . "}\n";
    }

    /**
     * Returns the code of the function "CountEntries()"
     * for an enumeration
     * 
     * @param string $name The class name for the enum
     *
     * @return string The code of the function "CountEntries()"
     */
    private function getFunctionCountEntries($name) 
    {
        // This function will simply return a constant number (e.g. return 5;)
        return  "public static function CountEntries() { \n"
            . " return " . count($this->enums[$name]) . ";\n"
            . "}\n";
    }

    /**
     * Returns the code of the function "GetDatabaseID()"
     * for an enumeration
     * 
     * @return string The code of the function "GetDatabaseID()"
     */
    private function getFunctionGetDatabaseID()
    {
        // Check for the index of this element inside of the array
        // of elements and add +1
        return  "public static function GetDatabaseID(\$entry) { \n"
            . "\$key = array_search(\$entry, self::GetEntries());\n"
            . " return \$key + 1;\n"
            . "}\n";
    }

    /**
     * Returns the code of the function "GetName()"
     * for an enumeration
     *
     * @return string The code of the function "GetName()"
     */
    private function getFunctionGetName()
    {
        // Remove the class name from the unique ID 
        // and return this value (which is the element name)
        return  "public static function GetName(\$entry) { \n"
            . "return substr(\$entry, strlen(self::\$name) + 1 , strlen(\$entry));\n"
            . "}\n";
    }

}


?>

وأنا أحب تتضمن التعدادات من جافا جدا، ولهذا السبب أنا أكتب تتضمن التعدادات بلدي بهذه الطريقة، وأعتقد أن هذا هو behawior الأكثر مماثلة كما هو الحال في تتضمن التعدادات جافا، وبطبيعة الحال، إذا كان بعض ترغب في استخدام أساليب أكثر من جافا يجب أن تكتب هنا ، أو في فئة مجردة ولكن الفكرة الأساسية مضمن في التعليمات البرمجية أدناه


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

وجدت هذه المكتبة على جيثب وأعتقد أنه يوفر بديلاً لائقًا جدًا للإجابات هنا.

تنفيذ PHP Enum مستوحى من SplEnum

  • يمكنك كتابة تلميح: function setAction(Action $action) {
  • يمكنك إثراء التعداد بالطرق (على سبيل المثال. format, parse, …)
  • يمكنك تمديد التعداد لإضافة قيم جديدة (اجعل التعداد الخاص بك final لمنع ذلك)
  • يمكنك الحصول على قائمة بجميع القيم الممكنة (انظر أدناه)

تصريح

<?php
use MyCLabs\Enum\Enum;

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

الاستخدام

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

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

قيم التعداد نوع تلميح:

<?php
function setAction(Action $action) {
    // ...
}

الحل الأكثر شيوعًا الذي رأيته للتعداد في PHP هو إنشاء فئة تعداد عامة ثم توسيعها.ربما يمكنك إلقاء نظرة على هذا.

تحديث: بدلا من ذلك، وجدت هذا من phpclasses.org.

وهنا هي مكتبة جيثب لالتعدادات من نوع آمن التعامل في بي:

وهذه المكتبة دروس التعامل مع جيل، والطبقات التخزين المؤقت وينفذ نوع آمن تعداد نمط التصميم، مع العديد من الطرق المساعد للتعامل مع تتضمن التعدادات، مثل استرجاع ترتيبي لتتضمن التعدادات الفرز، أو استرداد قيمة ثنائية، لتتضمن التعدادات المجموعات.

والشفرة التي تم إنشاؤها باستخدام القديم ملف قالب فب عادي، والذي هو أيضا شكلي، حتى تتمكن من تقديم نموذج الخاص بك.

ويتم اختبار كامل مغطاة phpunit.

فب-تتضمن التعدادات على جيثب (لا تتردد في مفترق)

الاستخدام: (see usage.php، أو وحدة الاختبارات لمزيد من التفاصيل)

<?php
//require the library
require_once __DIR__ . '/src/Enum.func.php';

//if you don't have a cache directory, create one
@mkdir(__DIR__ . '/cache');
EnumGenerator::setDefaultCachedClassesDir(__DIR__ . '/cache');

//Class definition is evaluated on the fly:
Enum('FruitsEnum', array('apple' , 'orange' , 'rasberry' , 'bannana'));

//Class definition is cached in the cache directory for later usage:
Enum('CachedFruitsEnum', array('apple' , 'orange' , 'rasberry' , 'bannana'), '\my\company\name\space', true);

echo 'FruitsEnum::APPLE() == FruitsEnum::APPLE(): ';
var_dump(FruitsEnum::APPLE() == FruitsEnum::APPLE()) . "\n";

echo 'FruitsEnum::APPLE() == FruitsEnum::ORANGE(): ';
var_dump(FruitsEnum::APPLE() == FruitsEnum::ORANGE()) . "\n";

echo 'FruitsEnum::APPLE() instanceof Enum: ';
var_dump(FruitsEnum::APPLE() instanceof Enum) . "\n";

echo 'FruitsEnum::APPLE() instanceof FruitsEnum: ';
var_dump(FruitsEnum::APPLE() instanceof FruitsEnum) . "\n";

echo "->getName()\n";
foreach (FruitsEnum::iterator() as $enum)
{
  echo "  " . $enum->getName() . "\n";
}

echo "->getValue()\n";
foreach (FruitsEnum::iterator() as $enum)
{
  echo "  " . $enum->getValue() . "\n";
}

echo "->getOrdinal()\n";
foreach (CachedFruitsEnum::iterator() as $enum)
{
  echo "  " . $enum->getOrdinal() . "\n";
}

echo "->getBinary()\n";
foreach (CachedFruitsEnum::iterator() as $enum)
{
  echo "  " . $enum->getBinary() . "\n";
}

إخراج:

FruitsEnum::APPLE() == FruitsEnum::APPLE(): bool(true)
FruitsEnum::APPLE() == FruitsEnum::ORANGE(): bool(false)
FruitsEnum::APPLE() instanceof Enum: bool(true)
FruitsEnum::APPLE() instanceof FruitsEnum: bool(true)
->getName()
  APPLE
  ORANGE
  RASBERRY
  BANNANA
->getValue()
  apple
  orange
  rasberry
  bannana
->getValue() when values have been specified
  pig
  dog
  cat
  bird
->getOrdinal()
  1
  2
  3
  4
->getBinary()
  1
  2
  4
  8
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>";
}

وقد تكون بسيطة مثل

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

وفي المستقبل.

RFC PHP: تعداد أنواع

ولقد اتخذت لاستخدام نهج أدناه كما يعطيني القدرة على الحصول على السلامة نوع لمعلمات وظيفة، الإكمال التلقائي في نتبيانس والأداء الجيد. شيء واحد أنا لا أحب كثيرا هو أن لديك لاستدعاء [extended class name]::enumerate(); بعد تحديد الفئة.

abstract class Enum {

    private $_value;

    protected function __construct($value) {
        $this->_value = $value;
    }

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

    public static function enumerate() {
        $class = get_called_class();
        $ref = new ReflectionClass($class);
        $statics = $ref->getStaticProperties();
        foreach ($statics as $name => $value) {
            $ref->setStaticPropertyValue($name, new $class($value));
        }
    }
}

class DaysOfWeek extends Enum {
    public static $MONDAY = 0;
    public static $SUNDAY = 1;
    // etc.
}
DaysOfWeek::enumerate();

function isMonday(DaysOfWeek $d) {
    if ($d == DaysOfWeek::$MONDAY) {
        return true;
    } else {
        return false;
    }
}

$day = DaysOfWeek::$MONDAY;
echo (isMonday($day) ? "bummer it's monday" : "Yay! it's not monday");

تعريف فئة التعداد الخاص بي أدناه هو كتبت بقوة, ، و جداً طبيعي لاستخدامها وتحديدها.

تعريف:

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

التبديل على التعداد

$myFruit = Fruit::$APPLE;

switch ($myFruit) {
    case Fruit::$APPLE  : echo "I like apples\n";  break;
    case Fruit::$ORANGE : echo "I hate oranges\n"; break;
}

>> I like apples

تمرير التعداد كمعلمة (مكتوب بقوة)

/** Function only accepts Fruit enums as input**/
function echoFruit(Fruit $fruit) {
    echo $fruit->getName().": ".$fruit->getValue()."\n";
}

/** Call function with each Enum value that Fruit has */
foreach (Fruit::getList() as $fruit) {
    echoFruit($fruit);
}

//Call function with Apple enum
echoFruit(Fruit::$APPLE)

//Will produce an error. This solution is strongly typed
echoFruit(2);

>> APPLE: 1
>> ORANGE: 2
>> APPLE: 1
>> Argument 1 passed to echoFruit() must be an instance of Fruit, integer given

صدى التعداد كسلسلة

echo "I have an $myFruit\n";

>> I have an APPLE

الحصول على التعداد بواسطة عدد صحيح

$myFruit = Fruit::getByValue(2);

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

>> Now I have an ORANGE

الحصول على التعداد بالاسم

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

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

>> But I definitely prefer an APPLE

فئة التعداد:

/**
 * @author Torge Kummerow
 */
class Enum {

    /**
     * Holds the values for each type of Enum
     */
    static private $list = array();

    /**
     * Initializes the enum values by replacing the number with an instance of itself
     * using reflection
     */
    static public function initialize() {
        $className = get_called_class();
        $class = new ReflectionClass($className);
        $staticProperties = $class->getStaticProperties();

        self::$list[$className] = array();

        foreach ($staticProperties as $propertyName => &$value) {
            if ($propertyName == 'list')
                continue;

            $enum = new $className($propertyName, $value);
            $class->setStaticPropertyValue($propertyName, $enum);
            self::$list[$className][$propertyName] = $enum;
        } unset($value);
    }


    /**
     * Gets the enum for the given value
     *
     * @param integer $value
     * @throws Exception
     *
     * @return Enum
     */
    static public function getByValue($value) {
        $className = get_called_class();
        foreach (self::$list[$className] as $propertyName=>&$enum) {
            /* @var $enum Enum */
            if ($enum->value == $value)
                return $enum;
        } unset($enum);

        throw new Exception("No such enum with value=$value of type ".get_called_class());
    }

    /**
     * Gets the enum for the given name
     *
     * @param string $name
     * @throws Exception
     *
     * @return Enum
     */
    static public function getByName($name) {
        $className = get_called_class();
        if (array_key_exists($name, static::$list[$className]))
            return self::$list[$className][$name];

        throw new Exception("No such enum ".get_called_class()."::\$$name");
    }


    /**
     * Returns the list of all enum variants
     * @return Array of Enum
     */
    static public function getList() {
        $className = get_called_class();
        return self::$list[$className];
    }


    private $name;
    private $value;

    public function __construct($name, $value) {
        $this->name = $name;
        $this->value = $value;
    }

    public function __toString() {
        return $this->name;
    }

    public function getValue() {
        return $this->value;
    }

    public function getName() {
        return $this->name;
    }

}

إضافة

يمكنك بالطبع أيضًا إضافة تعليقات لـ IDEs

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

وأنا أدرك هذا هو موضوع جدا-جدا-جدا القديمة ولكن لم يكن لدي فكرة عن هذا وأراد أن يعرف ما يعتقد الناس.

وملاحظات: كنت ألعب مع حولها هذا، وأدركت أنه إذا أنا فقط تعديل وظيفة __call() التي يمكنك الحصول عليها حتى أقرب إلى enums الفعلية. وظيفة __call() يعالج جميع المكالمات وظيفة غير معروفة. لذلك دعونا نقول كنت تريد أن تجعل ثلاثة RED_LIGHT enums، YELLOW_LIGHT، وGREEN_LIGHT. يمكنك أن تفعل ذلك الآن فقط عن طريق القيام بما يلي:

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

وعندما تعرف كل ما عليك القيام به هو الاتصال بهم مرة أخرى للحصول على القيم:

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

ويجب أن تحصل على 0 و 1 و 2. وقتا ممتعا! وهذا هو أيضا الآن حتى على جيثب.

تحديث: لقد جعلت من ذلك يتم استخدام كل من __get() و__set() وظائف الآن. هذه تسمح لك ليكن لديك لاستدعاء دالة إلا إذا كنت تريد. بدلا من ذلك، الآن يمكنك أن تقول فقط:

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

لكلا من الخلق والحصول على القيم. لأنه لم يتم تعريف المتغيرات في البداية، يتم استدعاء الدالة __get() (بسبب عدم وجود قيمة محددة) الذي يرى أن دخول في مجموعة ولم ينشر. لذلك يجعل دخول ويسند القيمة الأخيرة نظرا زائد واحد (+1)، بزيادة المتغير قيمة الماضي، وإرجاع TRUE. إذا قمت بتعيين القيمة:

$c->RED_LIGHT = 85;

وبعد ذلك يتم استدعاء الدالة __set() ثم يتم تعيين القيمة الأخيرة إلى القيمة الجديدة زائد واحد (+1). حتى الآن لدينا وسيلة جيدة للقيام تتضمن التعدادات وأنها يمكن أن تنشأ على الطاير.

<?php
################################################################################
#   Class ENUMS
#
#       Original code by Mark Manning.
#       Copyrighted (c) 2015 by Mark Manning.
#       All rights reserved.
#
#       This set of code is hereby placed into the free software universe
#       via the GNU greater license thus placing it under the Copyleft
#       rules and regulations with the following modifications:
#
#       1. You may use this work in any other work.  Commercial or otherwise.
#       2. You may make as much money as you can with it.
#       3. You owe me nothing except to give me a small blurb somewhere in
#           your program or maybe have pity on me and donate a dollar to
#           sim_sales@paypal.com.  :-)
#
#   Blurb:
#
#       PHP Class Enums by Mark Manning (markem-AT-sim1-DOT-us).
#       Used with permission.
#
#   Notes:
#
#       VIM formatting.  Set tabs to four(4) spaces.
#
################################################################################
class enums
{
    private $enums;
    private $clear_flag;
    private $last_value;

################################################################################
#   __construct(). Construction function.  Optionally pass in your enums.
################################################################################
function __construct()
{
    $this->enums = array();
    $this->clear_flag = false;
    $this->last_value = 0;

    if( func_num_args() > 0 ){
        return $this->put( func_get_args() );
        }

    return true;
}
################################################################################
#   put(). Insert one or more enums.
################################################################################
function put()
{
    $args = func_get_args();
#
#   Did they send us an array of enums?
#   Ex: $c->put( array( "a"=>0, "b"=>1,...) );
#   OR  $c->put( array( "a", "b", "c",... ) );
#
    if( is_array($args[0]) ){
#
#   Add them all in
#
        foreach( $args[0] as $k=>$v ){
#
#   Don't let them change it once it is set.
#   Remove the IF statement if you want to be able to modify the enums.
#
            if( !isset($this->enums[$k]) ){
#
#   If they sent an array of enums like this: "a","b","c",... then we have to
#   change that to be "A"=>#. Where "#" is the current count of the enums.
#
                if( is_numeric($k) ){
                    $this->enums[$v] = $this->last_value++;
                    }
#
#   Else - they sent "a"=>"A", "b"=>"B", "c"=>"C"...
#
                    else {
                        $this->last_value = $v + 1;
                        $this->enums[$k] = $v;
                        }
                }
            }
        }
#
#   Nope!  Did they just sent us one enum?
#
        else {
#
#   Is this just a default declaration?
#   Ex: $c->put( "a" );
#
            if( count($args) < 2 ){
#
#   Again - remove the IF statement if you want to be able to change the enums.
#
                if( !isset($this->enums[$args[0]]) ){
                    $this->enums[$args[0]] = $this->last_value++;
                    }
#
#   No - they sent us a regular enum
#   Ex: $c->put( "a", "This is the first enum" );
#
                    else {
#
#   Again - remove the IF statement if you want to be able to change the enums.
#
                        if( !isset($this->enums[$args[0]]) ){
                            $this->last_value = $args[1] + 1;
                            $this->enums[$args[0]] = $args[1];
                            }
                        }
                }
            }

    return true;
}
################################################################################
#   get(). Get one or more enums.
################################################################################
function get()
{
    $num = func_num_args();
    $args = func_get_args();
#
#   Is this an array of enums request? (ie: $c->get(array("a","b","c"...)) )
#
    if( is_array($args[0]) ){
        $ary = array();
        foreach( $args[0] as $k=>$v ){
            $ary[$v] = $this->enums[$v];
            }

        return $ary;
        }
#
#   Is it just ONE enum they want? (ie: $c->get("a") )
#
        else if( ($num > 0) && ($num < 2) ){
            return $this->enums[$args[0]];
            }
#
#   Is it a list of enums they want? (ie: $c->get( "a", "b", "c"...) )
#
        else if( $num > 1 ){
            $ary = array();
            foreach( $args as $k=>$v ){
                $ary[$v] = $this->enums[$v];
                }

            return $ary;
            }
#
#   They either sent something funky or nothing at all.
#
    return false;
}
################################################################################
#   clear(). Clear out the enum array.
#       Optional.  Set the flag in the __construct function.
#       After all, ENUMS are supposed to be constant.
################################################################################
function clear()
{
    if( $clear_flag ){
        unset( $this->enums );
        $this->enums = array();
        }

    return true;
}
################################################################################
#   __call().  In case someone tries to blow up the class.
################################################################################
function __call( $name, $arguments )
{
    if( isset($this->enums[$name]) ){ return $this->enums[$name]; }
        else if( !isset($this->enums[$name]) && (count($arguments) > 0) ){
            $this->last_value = $arguments[0] + 1;
            $this->enums[$name] = $arguments[0];
            return true;
            }
        else if( !isset($this->enums[$name]) && (count($arguments) < 1) ){
            $this->enums[$name] = $this->last_value++;
            return true;
            }

    return false;
}
################################################################################
#   __get(). Gets the value.
################################################################################
function __get($name)
{
    if( isset($this->enums[$name]) ){ return $this->enums[$name]; }
        else if( !isset($this->enums[$name]) ){
            $this->enums[$name] = $this->last_value++;
            return true;
            }

    return false;
}
################################################################################
#   __set().  Sets the value.
################################################################################
function __set( $name, $value=null )
{
    if( isset($this->enums[$name]) ){ return false; }
        else if( !isset($this->enums[$name]) && !is_null($value) ){
            $this->last_value = $value + 1;
            $this->enums[$name] = $value;
            return true;
            }
        else if( !isset($this->enums[$name]) && is_null($value) ){
            $this->enums[$name] = $this->last_value++;
            return true;
            }

    return false;
}
################################################################################
#   __destruct().  Deconstruct the class.  Remove the list of enums.
################################################################################
function __destruct()
{
    unset( $this->enums );
    $this->enums = null;

    return true;
}

}
#
#   Test code
#
#   $c = new enums();
#   $c->RED_LIGHT(85);
#   $c->YELLOW_LIGHT = 23;
#   $c->GREEN_LIGHT;
#
#   echo $c->RED_LIGHT . "\n";
#   echo $c->YELLOW_LIGHT . "\n";
#   echo $c->GREEN_LIGHT . "\n";

?>

وأنا أعلم أن هذا هو موضوع قديم، ولكن أيا من الحلول رأيت حقا يشبه تتضمن التعدادات، حيث أن جميع الحلول تقريبا يتطلب منك تعيين القيم يدويا إلى العناصر التعداد، أو أنه يتطلب منك لتمرير مجموعة من التعداد مفاتيح وظيفة. لذلك أنا خلقت حل بلدي لذلك.

لإنشاء فئة التعداد باستخدام بلدي حل واحد يمكن ببساطة تمديد هذه الفئة التعداد أدناه، وخلق مجموعة من المتغيرات ثابتة (لا حاجة لتهيئة منهم)، وإجراء مكالمة إلى yourEnumClass :: الحرف الأول () فقط دون تعريف الطبقة التعداد الخاص بك.

وتحرير: هذا يعمل فقط في بي> = 5.3، ولكن يمكن على الأرجح أن يتم تعديل للعمل في الإصدارات القديمة فضلا     

/**
 * A base class for enums. 
 * 
 * This class can be used as a base class for enums. 
 * It can be used to create regular enums (incremental indices), but it can also be used to create binary flag values.
 * To create an enum class you can simply extend this class, and make a call to <yourEnumClass>::init() before you use the enum.
 * Preferably this call is made directly after the class declaration. 
 * Example usages:
 * DaysOfTheWeek.class.php
 * abstract class DaysOfTheWeek extends Enum{
 *      static $MONDAY = 1;
 *      static $TUESDAY;
 *      static $WEDNESDAY;
 *      static $THURSDAY;
 *      static $FRIDAY;
 *      static $SATURDAY;
 *      static $SUNDAY;
 * }
 * DaysOfTheWeek::init();
 * 
 * example.php
 * require_once("DaysOfTheWeek.class.php");
 * $today = date('N');
 * if ($today == DaysOfTheWeek::$SUNDAY || $today == DaysOfTheWeek::$SATURDAY)
 *      echo "It's weekend!";
 * 
 * Flags.class.php
 * abstract class Flags extends Enum{
 *      static $FLAG_1;
 *      static $FLAG_2;
 *      static $FLAG_3;
 * }
 * Flags::init(Enum::$BINARY_FLAG);
 * 
 * example2.php
 * require_once("Flags.class.php");
 * $flags = Flags::$FLAG_1 | Flags::$FLAG_2;
 * if ($flags & Flags::$FLAG_1)
 *      echo "Flag_1 is set";
 * 
 * @author Tiddo Langerak
 */
abstract class Enum{

    static $BINARY_FLAG = 1;
    /**
     * This function must be called to initialize the enumeration!
     * 
     * @param bool $flags If the USE_BINARY flag is provided, the enum values will be binary flag values. Default: no flags set.
     */ 
    public static function init($flags = 0){
        //First, we want to get a list of all static properties of the enum class. We'll use the ReflectionClass for this.
        $enum = get_called_class();
        $ref = new ReflectionClass($enum);
        $items = $ref->getStaticProperties();
        //Now we can start assigning values to the items. 
        if ($flags & self::$BINARY_FLAG){
            //If we want binary flag values, our first value should be 1.
            $value = 1;
            //Now we can set the values for all items.
            foreach ($items as $key=>$item){
                if (!isset($item)){                 
                    //If no value is set manually, we should set it.
                    $enum::$$key = $value;
                    //And we need to calculate the new value
                    $value *= 2;
                } else {
                    //If there was already a value set, we will continue starting from that value, but only if that was a valid binary flag value.
                    //Otherwise, we will just skip this item.
                    if ($key != 0 && ($key & ($key - 1) == 0))
                        $value = 2 * $item;
                }
            }
        } else {
            //If we want to use regular indices, we'll start with index 0.
            $value = 0;
            //Now we can set the values for all items.
            foreach ($items as $key=>$item){
                if (!isset($item)){
                    //If no value is set manually, we should set it, and increment the value for the next item.
                    $enum::$$key = $value;
                    $value++;
                } else {
                    //If a value was already set, we'll continue from that value.
                    $value = $item+1;
                }
            }
        }
    }
}

الآن يمكنك استخدام SplEnum فئة لبنائه محليا.حسب الوثائق الرسمية.

يمنح الطحال القدرة على محاكاة وإنشاء كائنات تعداد أصلاً في PHP.

<?php
class Month extends SplEnum {
    const __default = self::January;

    const January = 1;
    const February = 2;
    const March = 3;
    const April = 4;
    const May = 5;
    const June = 6;
    const July = 7;
    const August = 8;
    const September = 9;
    const October = 10;
    const November = 11;
    const December = 12;
}

echo new Month(Month::June) . PHP_EOL;

try {
    new Month(13);
} catch (UnexpectedValueException $uve) {
    echo $uve->getMessage() . PHP_EOL;
}
?>

يرجى ملاحظة أنه امتداد يجب تثبيته، ولكنه غير متاح افتراضيًا.الذي يأتي تحت أنواع خاصة الموصوفة في موقع php نفسه.المثال أعلاه مأخوذ من موقع PHP.

والجواب المقبول هو الطريق للذهاب وهو في الواقع ما أقوم به لالبساطة. وتقدم معظم مزايا تعداد (للقراءة، وبسرعة، وما إلى ذلك). مفهوم واحد مفقود، ولكن: نوع السلامة. في معظم اللغات، وتستخدم التعدادات أيضا لتقييد القيم المسموح بها. فيما يلي مثال على الكيفية التي يمكن أيضا الحصول على السلامة نوع باستخدام منشئات خاصة وطرق مثيل ثابت ونوع التحقق:

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

 private $intVal;
 private function __construct($intVal){
   $this->intVal = $intVal;
 }

 //static instantiation methods
 public static function MONDAY(){
   return new self(self::Monday);
 }
 //etc.
}

//function using type checking
function printDayOfWeek(DaysOfWeek $d){ //compiler can now use type checking
  // to something with $d...
}

//calling the function is safe!
printDayOfWeek(DaysOfWeek::MONDAY());

ونحن يمكن حتى تذهب أبعد من ذلك: استخدام الثوابت في فئة DaysOfWeek قد تؤدي إلى استعمال خاطئ: على سبيل المثال يمكن للمرء أن استخدام بطريق الخطأ أنه بهذه الطريقة:

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

والذي هو الخطأ (يدعو ثابت صحيح). يمكننا منع ذلك باستخدام متغيرات ثابتة خاصة بدلا من الثوابت:

class DaysOfWeeks{

  private static $monday = 1;
  //etc.

  private $intVal;
  //private constructor
  private function __construct($intVal){
    $this->intVal = $intVal;
  }

  //public instantiation methods
  public static function MONDAY(){
    return new self(self::$monday);
  }
  //etc.


  //convert an instance to its integer value
  public function intVal(){
    return $this->intVal;
  }

}

وبطبيعة الحال، فإنه من غير الممكن الوصول إلى ثوابت عدد صحيح (كان هذا في الواقع الغرض). طريقة intVal يسمح لتحويل كائن DaysOfWeek إلى تمثيل صحيح لل.

ملحوظة نتمكن حتى تذهب أبعد من ذلك من خلال تنفيذ آلية التخزين المؤقت في أساليب مثيل لحفظ الذاكرة في التعدادات حالة وتستخدم على نطاق واسع ...

وهذا الأمل سيساعد

بعض الحلول الجيدة هنا!

ها هي نسختي.

  • انها مكتوبة بقوة
  • يعمل مع الإكمال التلقائي IDE
  • يتم تعريف التعدادات بواسطة رمز ووصف، حيث يمكن أن يكون الرمز عددًا صحيحًا أو قيمة ثنائية أو سلسلة قصيرة أو أي شيء آخر تريده.يمكن بسهولة توسيع النمط لدعم خصائص أخرى.
  • وهو يدعم مقارنات القيمة (==) والمرجعية (===) ويعمل في عبارات التبديل.

أعتقد أن العيب الرئيسي هو أنه يجب الإعلان عن أعضاء التعداد بشكل منفصل وإنشاء مثيل لهم، وذلك بسبب الأوصاف وعدم قدرة PHP على إنشاء كائنات في وقت إعلان العضو الثابت.أعتقد أن إحدى الطرق للتغلب على ذلك قد تكون استخدام الانعكاس مع تعليقات المستند التي تم تحليلها بدلاً من ذلك.

يبدو التعداد الملخص كما يلي:

<?php

abstract class AbstractEnum
{
    /** @var array cache of all enum instances by class name and integer value */
    private static $allEnumMembers = array();

    /** @var mixed */
    private $code;

    /** @var string */
    private $description;

    /**
     * Return an enum instance of the concrete type on which this static method is called, assuming an instance
     * exists for the passed in value.  Otherwise an exception is thrown.
     *
     * @param $code
     * @return AbstractEnum
     * @throws Exception
     */
    public static function getByCode($code)
    {
        $concreteMembers = &self::getConcreteMembers();

        if (array_key_exists($code, $concreteMembers)) {
            return $concreteMembers[$code];
        }

        throw new Exception("Value '$code' does not exist for enum '".get_called_class()."'");
    }

    public static function getAllMembers()
    {
        return self::getConcreteMembers();
    }

    /**
     * Create, cache and return an instance of the concrete enum type for the supplied primitive value.
     *
     * @param mixed $code code to uniquely identify this enum
     * @param string $description
     * @throws Exception
     * @return AbstractEnum
     */
    protected static function enum($code, $description)
    {
        $concreteMembers = &self::getConcreteMembers();

        if (array_key_exists($code, $concreteMembers)) {
            throw new Exception("Value '$code' has already been added to enum '".get_called_class()."'");
        }

        $concreteMembers[$code] = $concreteEnumInstance = new static($code, $description);

        return $concreteEnumInstance;
    }

    /**
     * @return AbstractEnum[]
     */
    private static function &getConcreteMembers() {
        $thisClassName = get_called_class();

        if (!array_key_exists($thisClassName, self::$allEnumMembers)) {
            $concreteMembers = array();
            self::$allEnumMembers[$thisClassName] = $concreteMembers;
        }

        return self::$allEnumMembers[$thisClassName];
    }

    private function __construct($code, $description)
    {
        $this->code = $code;
        $this->description = $description;
    }

    public function getCode()
    {
        return $this->code;
    }

    public function getDescription()
    {
        return $this->description;
    }
}

فيما يلي مثال على التعداد الملموس:

<?php

require('AbstractEnum.php');

class EMyEnum extends AbstractEnum
{
    /** @var EMyEnum */
    public static $MY_FIRST_VALUE;
    /** @var EMyEnum */
    public static $MY_SECOND_VALUE;
    /** @var EMyEnum */
    public static $MY_THIRD_VALUE;

    public static function _init()
    {
        self::$MY_FIRST_VALUE = self::enum(1, 'My first value');
        self::$MY_SECOND_VALUE = self::enum(2, 'My second value');
        self::$MY_THIRD_VALUE = self::enum(3, 'My third value');
    }
}

EMyEnum::_init();

والتي يمكن استخدامها مثل هذا:

<?php

require('EMyEnum.php');

echo EMyEnum::$MY_FIRST_VALUE->getCode().' : '.EMyEnum::$MY_FIRST_VALUE->getDescription().PHP_EOL.PHP_EOL;

var_dump(EMyEnum::getAllMembers());

echo PHP_EOL.EMyEnum::getByCode(2)->getDescription().PHP_EOL;

وينتج هذا الإخراج:

1 :قيمتي الأولى

صفيف (3) {
[1]=>
كائن (EMyEnum)#1 (2) {
["code":"AbstractEnum":private]=>
كثافة العمليات (1)
["description":"AbstractEnum":private]=>
السلسلة(14) "قيمتي الأولى"
}
[2]=>
كائن (EMyEnum)#2 (2) {
["code":"AbstractEnum":private]=>
كثافة العمليات (2)
["description":"AbstractEnum":private]=>
السلسلة(15) "قيمتي الثانية"
}
[3]=>
الكائن (EMyEnum)#3 (2) {
["code":"AbstractEnum":private]=>
كثافة العمليات (3)
["description":"AbstractEnum":private]=>
السلسلة(14) "قيمتي الثالثة"
}
}

قيمتي الثانية

class DayOfWeek {
    static $values = array(
        self::MONDAY,
        self::TUESDAY,
        // ...
    );

    const MONDAY  = 0;
    const TUESDAY = 1;
    // ...
}

$today = DayOfWeek::MONDAY;

// If you want to check if a value is valid
assert( in_array( $today, DayOfWeek::$values ) );

لا تستخدم التفكير. فإنه يجعل من الصعب للغاية أن يرشد حول التعليمات البرمجية وتعقب حيث يتم استخدام شيء ما، ويميل إلى تحطيم أدوات التحليل ثابتة (على سبيل المثال ما في صلب IDE الخاص بك).

واحد من الجوانب الناقصة من بعض الأجوبة الأخرى هنا هو وسيلة لاستخدام تتضمن التعدادات مع نوع ملمحا.

إذا تعريف التعداد ك مجموعة من الثوابت في فئة مجردة، منها مثلا.

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

وبعد ذلك لا يمكنك كتابة التلميح في معلمة وظيفة - لأحد، لأنها ليست instantiable، ولكن أيضا بسبب نوع من ShirtSize::SMALL هو int، وليس ShirtSize

وهذا هو السبب تتضمن التعدادات الأم في PHP سيكون أفضل بكثير من أي شيء يمكن أن تصل. ومع ذلك، يمكننا تقريب والتعداد عن طريق الحفاظ على الملكية الخاصة والتي تمثل قيمة التعداد، ومن ثم الحد من تهيئة هذه الخاصية إلى الثوابت المعرفة مسبقا لدينا. لمنع التعداد من يجري مثيل تعسفا (بدون النفقات العامة من القائمة البيضاء التحقق من نوع)، يمكننا أن نجعل من القطاع الخاص المنشئ.

class ShirtSize {
    private $size;
    private function __construct ($size) {
        $this->size = $size;
    }
    public function equals (ShirtSize $s) {
        return $this->size === $s->size;
    }
    public static function SMALL () { return new self(1); }
    public static function MEDIUM () { return new self(2); }
    public static function LARGE () { return new self(3); }
}

وبعد ذلك يمكننا استخدام ShirtSize مثل هذا:

function sizeIsAvailable ($productId, ShirtSize $size) {
    // business magic
}
if(sizeIsAvailable($_GET["id"], ShirtSize::LARGE())) {
    echo "Available";
} else {
    echo "Out of stock.";
}
$s2 = ShirtSize::SMALL();
$s3 = ShirtSize::MEDIUM();
echo $s2->equals($s3) ? "SMALL == MEDIUM" : "SMALL != MEDIUM";

وبهذه الطريقة، أكبر الفرق من وجهة نظر المستخدم هو أن لديك لتك على () على اسم ثابت ل.

واحد السلبي هو على الرغم من أن === (الذي يقارن المساواة وجوه) سيعود كاذبة عندما يعود == صحيح. لهذا السبب، فمن الأفضل لتوفير وسيلة equals، بحيث لا يحتاج المستخدمون إلى تذكر لاستخدام == وليس === لمقارنة قيمتين التعداد.

وتحرير: زوجان من الإجابات الموجودة متشابهة جدا، ولا سيما: https://stackoverflow.com/a/25526473/2407870 .

وندوس على إجابة لBrian كلاين ظننت أنني قد تعطي لي 5 سنتات

<?php 
/**
 * A class that simulates Enums behaviour
 * <code>
 * class Season extends Enum{
 *    const Spring  = 0;
 *    const Summer = 1;
 *    const Autumn = 2;
 *    const Winter = 3;
 * }
 * 
 * $currentSeason = new Season(Season::Spring);
 * $nextYearSeason = new Season(Season::Spring);
 * $winter = new Season(Season::Winter);
 * $whatever = new Season(-1);               // Throws InvalidArgumentException
 * echo $currentSeason.is(Season::Spring);   // True
 * echo $currentSeason.getName();            // 'Spring'
 * echo $currentSeason.is($nextYearSeason);  // True
 * echo $currentSeason.is(Season::Winter);   // False
 * echo $currentSeason.is(Season::Spring);   // True
 * echo $currentSeason.is($winter);          // False
 * </code>
 * 
 * Class Enum
 * 
 * PHP Version 5.5
 */
abstract class Enum
{
    /**
     * Will contain all the constants of every enum that gets created to 
     * avoid expensive ReflectionClass usage
     * @var array
     */
    private static $_constCacheArray = [];
    /**
     * The value that separates this instance from the rest of the same class
     * @var mixed
     */
    private $_value;
    /**
     * The label of the Enum instance. Will take the string name of the 
     * constant provided, used for logging and human readable messages
     * @var string
     */
    private $_name;
    /**
     * Creates an enum instance, while makes sure that the value given to the 
     * enum is a valid one
     * 
     * @param mixed $value The value of the current
     * 
     * @throws \InvalidArgumentException
     */
    public final function __construct($value)
    {
        $constants = self::_getConstants();
        if (count($constants) !== count(array_unique($constants))) {
            throw new \InvalidArgumentException('Enums cannot contain duplicate constant values');
        }
        if ($name = array_search($value, $constants)) {
            $this->_value = $value;
            $this->_name = $name;
        } else {
            throw new \InvalidArgumentException('Invalid enum value provided');
        }
    }
    /**
     * Returns the constant name of the current enum instance
     * 
     * @return string
     */
    public function getName()
    {
        return $this->_name;
    }
    /**
     * Returns the value of the current enum instance
     * 
     * @return mixed
     */
    public function getValue()
    {
        return $this->_value;
    }
    /**
     * Checks whether this enum instance matches with the provided one.
     * This function should be used to compare Enums at all times instead
     * of an identity comparison 
     * <code>
     * // Assuming EnumObject and EnumObject2 both extend the Enum class
     * // and constants with such values are defined
     * $var  = new EnumObject('test'); 
     * $var2 = new EnumObject('test');
     * $var3 = new EnumObject2('test');
     * $var4 = new EnumObject2('test2');
     * echo $var->is($var2);  // true
     * echo $var->is('test'); // true
     * echo $var->is($var3);  // false
     * echo $var3->is($var4); // false
     * </code>
     * 
     * @param mixed|Enum $enum The value we are comparing this enum object against
     *                         If the value is instance of the Enum class makes
     *                         sure they are instances of the same class as well, 
     *                         otherwise just ensures they have the same value
     * 
     * @return bool
     */
    public final function is($enum)
    {
        // If we are comparing enums, just make
        // sure they have the same toString value
        if (is_subclass_of($enum, __CLASS__)) {
            return get_class($this) === get_class($enum) 
                    && $this->getValue() === $enum->getValue();
        } else {
            // Otherwise assume $enum is the value we are comparing against
            // and do an exact comparison
            return $this->getValue() === $enum;   
        }
    }

    /**
     * Returns the constants that are set for the current Enum instance
     * 
     * @return array
     */
    private static function _getConstants()
    {
        if (self::$_constCacheArray == null) {
            self::$_constCacheArray = [];
        }
        $calledClass = get_called_class();
        if (!array_key_exists($calledClass, self::$_constCacheArray)) {
            $reflect = new \ReflectionClass($calledClass);
            self::$_constCacheArray[$calledClass] = $reflect->getConstants();
        }
        return self::$_constCacheArray[$calledClass];
    }
}

هذا هو رأيي في التعداد "الديناميكي" ...حتى أتمكن من تسميته مع المتغيرات، على سبيل المثال.من النموذج.

انظر إلى النسخة المحدثة أسفل مجموعة التعليمات البرمجية هذه...

$value = "concert";
$Enumvalue = EnumCategory::enum($value);
//$EnumValue = 1

class EnumCategory{
    const concert = 1;
    const festival = 2;
    const sport = 3;
    const nightlife = 4;
    const theatre = 5;
    const musical = 6;
    const cinema = 7;
    const charity = 8;
    const museum = 9;
    const other = 10;

    public function enum($string){
        return constant('EnumCategory::'.$string);
    }
}

تحديث:طريقة أفضل للقيام بذلك ...

class EnumCategory {

    static $concert = 1;
    static $festival = 2;
    static $sport = 3;
    static $nightlife = 4;
    static $theatre = 5;
    static $musical = 6;
    static $cinema = 7;
    static $charity = 8;
    static $museum = 9;
    static $other = 10;

}

الاتصال مع

EnumCategory::${$category};

وأمس كتبت هذه الفئة على بلدي بلوق . أعتقد أنه ربما يكون من السهل للاستخدام في البرامج النصية PHP:

final class EnumException extends Exception{}

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

    protected $constName;
    protected $constValue;

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

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

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

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

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

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

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

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


    final private function __clone(){}

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

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

والاستعمال:

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

وأشار إلى حل يعمل بشكل جيد. نظيفة وناعمة.

ولكن، إذا أردت كتابة بشدة التعدادات، يمكنك استخدام هذا:

class TestEnum extends Enum
{
    public static $TEST1;
    public static $TEST2;
}
TestEnum::init(); // Automatically initializes enum values

ومع فئة التعداد تبدو وكأنها:

class Enum
{
    public static function parse($enum)
    {
        $class = get_called_class();
        $vars = get_class_vars($class);
        if (array_key_exists($enum, $vars)) {
            return $vars[$enum];
        }
        return null;
    }

    public static function init()
    {
        $className = get_called_class();
        $consts = get_class_vars($className);
        foreach ($consts as $constant => $value) {
            if (is_null($className::$$constant)) {
                $constantValue = $constant;
                $constantValueName = $className . '::' . $constant . '_VALUE';
                if (defined($constantValueName)) {
                    $constantValue = constant($constantValueName);
                }
                $className::$$constant = new $className($constantValue);
            }
        }
    }

    public function __construct($value)
    {
        $this->value = $value;
    }
}

وبهذه الطريقة، يتم كتابتها القيم التعداد بقوة و

وTestEnum::$TEST1 === TestEnum::parse('TEST1') // true statement

ولقد تقدمت مكتبة على أساس الإجابة براين كلاين، فإنه يدعى greg0ire / التعداد استمتع!

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top