PHP और अंकन




enumeration (24)

मुझे पता है कि PHP में मूल गणना नहीं है। लेकिन मैं जावा दुनिया से उनके आदी हो गया हूं। मैं पूर्वनिर्धारित मान देने के तरीके के रूप में enums का उपयोग करना पसंद करूंगा जो आईडीई 'ऑटो पूर्णता सुविधाओं को समझ सकता है।

कॉन्स्टेंट चाल करते हैं, लेकिन नेमस्पेस टकराव की समस्या है और (या वास्तव में क्योंकि ) वे वैश्विक हैं। Arrays में नेमस्पेस समस्या नहीं है, लेकिन वे बहुत अस्पष्ट हैं, उन्हें रनटाइम पर ओवरराइट किया जा सकता है और आईडीई शायद ही कभी (कभी नहीं?) जानते हैं कि उनकी चाबियाँ ऑटोफिल कैसे करें।

क्या आपके पास आमतौर पर उपयोग किए जाने वाले कोई समाधान / कामकाज हैं? क्या किसी को याद है कि PHP लोगों के पास enums के आसपास कोई विचार या निर्णय था?


खैर, php में enum जैसे साधारण जावा के लिए, मैं इसका उपयोग करता हूं:

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 शुरुआतकर्ता हूं, जो सिंटैक्स के साथ संघर्ष कर रहा है, इसलिए यह सबसे अच्छा तरीका नहीं हो सकता है। मैंने क्लास कॉन्स्टेंट्स के साथ कुछ प्रयोग किया, प्रतिबिंब का उपयोग करके इसके मूल्य से निरंतर नाम प्राप्त करने के लिए, नीदर हो सकता है।



यदि आपको विश्वव्यापी अद्वितीय (यानी विभिन्न enums के बीच तत्वों की तुलना करते समय भी) का उपयोग करने की आवश्यकता है और उपयोग करने में आसान है, तो निम्न कोड का उपयोग करने के लिए स्वतंत्र महसूस करें। मैंने कुछ विधियों को भी जोड़ा जो मुझे उपयोगी लगता है। आपको कोड के शीर्ष पर टिप्पणियों में उदाहरण मिलेगा।

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

}


?>

मुझे जावा से भी enums पसंद है और इसी कारण से मैं अपने enums इस तरह लिखते हैं, मुझे लगता है कि जावा enums में यह सबसे समान व्यवहार है, निश्चित रूप से, अगर कुछ जावा से अधिक तरीकों का उपयोग करना चाहते हैं तो इसे यहां लिखना चाहिए, या में अमूर्त वर्ग लेकिन मूल विचार नीचे कोड में एम्बेड किया गया है


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


चार साल बाद मैं फिर से इस पर आया। मेरा वर्तमान दृष्टिकोण यह है क्योंकि यह आईडीई में कोड पूर्ण होने के साथ-साथ टाइप सुरक्षा की अनुमति देता है:

बेस क्लास:

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

उदाहरण 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); }      
}

उदाहरण का उपयोग:

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';
        // ...
}

आप नाम या मूल्य से enum प्रविष्टि भी बना सकते हैं:

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

या आप मौजूदा एनम प्रविष्टि से नाम (यानी फ़ंक्शन नाम) प्राप्त कर सकते हैं:

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

ऊपर का शीर्ष जवाब शानदार है। हालांकि, यदि आप इसे दो अलग-अलग तरीकों से 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];
}

Php में टाइप-सुरक्षित गणनाओं को संभालने के लिए यहां एक जिथब लाइब्रेरी है:

यह लाइब्रेरी कक्षा पीढ़ी, कक्षा कैशिंग को नियंत्रित करती है और यह एनम से निपटने के लिए कई सहायक तरीकों के साथ टाइप सेफ एन्युमरेशन डिज़ाइन पैटर्न लागू करती है, जैसे कि एनम्स सॉर्टिंग के लिए ऑर्डिनल को पुनर्प्राप्त करना या बाइनरी वैल्यू को पुनर्प्राप्त करना।

जेनरेट कोड एक सादे पुरानी PHP टेम्पलेट फ़ाइल का उपयोग करता है, जो कॉन्फ़िगर करने योग्य भी है, ताकि आप अपना टेम्पलेट प्रदान कर सकें।

यह phpunit के साथ पूरा परीक्षण है।

github पर php-enums (कांटा के लिए स्वतंत्र महसूस करें)

उपयोग: (@see use.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

मैंने github.com/myclabs/php-enum को जिथब पर पाया और मुझे लगता है कि यह यहां उत्तरों के लिए एक बहुत ही सभ्य विकल्प प्रदान करता है।

स्प्लेनम से प्रेरित PHP एनम कार्यान्वयन

  • आप टाइप-इशारा कर सकते हैं: function setAction(Action $action) {
  • आप विधियों के साथ enum समृद्ध कर सकते हैं (उदाहरण के लिए format , parse , ...)
  • आप नए मान जोड़ने के लिए enum का विस्तार कर सकते हैं (इसे रोकने के लिए अपना enum 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) {
    // ...
}

Some good solutions on here!

Here's my version.

  • It's strongly typed
  • It works with IDE auto-completion
  • Enums are defined by a code and a description, where the code can be an integer, a binary value, a short string, or basically anything else you want. The pattern could easily be extended to support orther properties.
  • It asupports value (==) and reference (===) comparisons and works in switch statements.

I think the main disadvantage is that enum members do have to be separately declared and instantiated, due to the descriptions and PHP's inability to construct objects at static member declaration time. I guess a way round this might be to use reflection with parsed doc comments instead.

The abstract enum looks like this:

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

Here's an example concrete enum:

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

Which can be used like this:

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

And produces this output:

1 : My first value

array(3) {
[1]=>
object(EMyEnum)#1 (2) {
["code":"AbstractEnum":private]=>
int(1)
["description":"AbstractEnum":private]=>
string(14) "My first value"
}
[2]=>
object(EMyEnum)#2 (2) {
["code":"AbstractEnum":private]=>
int(2)
["description":"AbstractEnum":private]=>
string(15) "My second value"
}
[3]=>
object(EMyEnum)#3 (2) {
["code":"AbstractEnum":private]=>
int(3)
["description":"AbstractEnum":private]=>
string(14) "My third value"
}
}

My second value


One of the aspects missing from some of the other answers here is a way to use enums with type hinting.

If you define your enum as a set of constants in an abstract class, eg

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

then you can't type hint it in a function parameter - for one, because it's not instantiable, but also because the type of ShirtSize::SMALL is int , not ShirtSize .

That's why native enums in PHP would be so much better than anything we can come up with. However, we can approximate an enum by keeping a private property which represents the value of the enum, and then restricting the initialization of this property to our predefined constants. To prevent the enum from being instantiated arbitrarily (without the overhead of type-checking a whitelist), we make the constructor private.

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

Then we can use ShirtSize like this:

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

This way, the biggest difference from the user's perspective is that you have to tack on a () on the constant's name.

One downside though is that === (which compares object equality) will return false when == returns true. For that reason, it's best to provide an equals method, so that users don't have to remember to use == and not === to compare two enum values.

EDIT: A couple of the existing answers are very similar, particularly: https://.com/a/25526473/2407870 .


This is my take on "dynamic" enum... so that i can call it with variables, ex. from a form.

look at updated verison below this codeblock...

$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: Better way of doing it...

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;

}

Call with

EnumCategory::${$category};

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

Don't use reflection. It makes it extremely difficult to reason about your code and track down where something is being used, and tends to break static analysis tools (eg what's built into your IDE).


My attempt to create an enum with PHP...it's extremely limited since it doesn't support objects as the enum values but still somewhat useful...

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

मैंने स्थिरांक वाले वर्गों का उपयोग किया:

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

print Enum::NAME;

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

I made a library based on Brian Cline's answer, it is named greg0ire/enum Enjoy!


मैंने यहां कुछ अन्य उत्तरों पर टिप्पणी की है, इसलिए मुझे लगा कि मैं भी वजन करूँगा। दिन के अंत में, चूंकि 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;

}

बेस क्लास के रूप में isValid का उपयोग करके, जिसमें अन्य सभी isValid सहायक हैं, जैसे isValid , isValid , आदि। मेरे लिए, टाइप की गई गणनाएं ( और उनके उदाहरणों का प्रबंधन ) बस बहुत गन्दा हो जाती है।

काल्पनिक

यदि , वहां एक __getStatic जादू विधि ( और अधिमानतः एक __equals जादू विधि भी मौजूद थी ) इसमें से अधिकांश को __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

// My Enumeration Class
class Enum
{
    protected $m_actions = array();

    public function __construct($actions)
    {
        $this->init($actions);
    }

    public function init($actions)
    {
        $this->m_actions = array();
        for($i = 0; $i < count($actions); ++$i)
        {
            $this->m_actions[$actions[$i]] = ($i + 1); 
            define($actions[$i], ($i + 1));
        }
    }

    public function toString($index)
    {
        $keys = array_keys($this->m_actions);
        for($i = 0; $i < count($keys); ++$i)
        {
            if($this->m_actions[$keys[$i]] == $index)
            {
                return $keys[$i];
            }
        }

        return "undefined";
    }

    public function fromString($str)
    {
        return $this->m_actions[$str];
    }
}

// Enumeration creation
$actions = new Enum(array("CREATE", "READ", "UPDATE", "DELETE"));

// Examples
print($action_objects->toString(DELETE));
print($action_objects->fromString("DELETE"));

if($action_objects->fromString($_POST["myAction"]) == CREATE)
{
    print("CREATE");
}

Pointed out solution works well. Clean and smooth.

However, if you want strongly typed enumerations, you can use this:

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

With an Enum class looking like:

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

This way, enum values are strongly typed and

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


PHP में enum के लिए मैंने देखा है कि सबसे आम समाधान एक सामान्य enum वर्ग बनाने के लिए किया गया है और फिर इसे विस्तारित किया गया है। आप इसे देख सकते हैं।

अद्यतन: वैकल्पिक रूप से, मुझे this phpclasses.org से मिला।


कक्षा स्थिरांक के बारे में क्या?

<?php

class YourClass
{
    const SOME_CONSTANT = 1;

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

echo YourClass::SOME_CONSTANT;

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

Yesterday I wrote this class on my blog . I think it's maybe be easy for use in php scripts:

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

उपयोग के मामले के आधार पर, मैं आमतौर पर निम्नलिखित की तरह कुछ सरल उपयोग करता हूं:

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 को विस्तारित करने वाली एक सरल enum क्लास बनाकर, अब आपके पास सरल इनपुट सत्यापन के लिए विधियों का उपयोग करने की क्षमता है:

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

एक साइड नोट के रूप में, जब भी मैं एक स्थिर / कॉन्स क्लास पर कम से कम एक बार प्रतिबिंब का उपयोग करता हूं जहां डेटा नहीं बदलेगा (जैसे कि enum में), मैं उन प्रतिबिंब कॉल के परिणामों को कैश करता हूं, क्योंकि प्रत्येक बार ताजा प्रतिबिंब ऑब्जेक्ट्स का उपयोग करते हैं अंततः एक उल्लेखनीय प्रदर्शन प्रभाव होगा (एकाधिक enums के लिए एक assocciative सरणी में संग्रहीत)।

अब जब अधिकांश लोगों ने आखिरकार कम से कम 5.3 तक अपग्रेड किया है, और SplEnum उपलब्ध है, यह निश्चित रूप से एक व्यवहार्य विकल्प भी है - जब तक कि आप अपने कोडबेस में वास्तविक एनम तत्काल होने की पारंपरिक रूप से अनजान धारणा को ध्यान में रखते हैं। उपर्युक्त उदाहरण में, BasicEnum और DaysOfWeek को तत्काल नहीं किया जा सकता है, न ही वे होना चाहिए।


एक मूल विस्तार भी है। स्प्लेनम

स्प्लेनम PHP में मूल रूप से गणना वस्तुओं को अनुकरण और बनाने की क्षमता देता है।

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





enumeration