PHP และการแจกแจง


1149

ฉันรู้ว่า PHP ไม่มีการแจกแจงแบบดั้งเดิม แต่ฉันได้คุ้นเคยกับพวกเขาจากโลก Java ฉันชอบที่จะใช้ enums เป็นวิธีการให้ค่าที่กำหนดไว้ล่วงหน้าซึ่งคุณสมบัติการเติมภาพอัตโนมัติของ IDE สามารถเข้าใจได้

คงทำเคล็ดลับ แต่มีปัญหา namespace การปะทะกันและ (หรือจริงเพราะ ) พวกเขากำลังทั่วโลก อาร์เรย์ไม่มีปัญหาเนมสเปซ แต่มันคลุมเครือเกินไปพวกเขาสามารถถูกเขียนทับในขณะใช้งานจริงและ IDEs ไม่ค่อยจะรู้ว่าจะป้อนคีย์ของตนอย่างไร

มีวิธีแก้ปัญหา / วิธีแก้ปัญหาที่คุณใช้บ่อยหรือไม่? ไม่มีใครจำได้ว่าคน PHP มีความคิดหรือการตัดสินใจรอบ enums หรือไม่?



1
ฉันสร้างฟังก์ชั่นการทำงานที่ระบุค่าคงที่เป็นบิตหรือไม่ ไม่ได้สังเกตเห็นคุณถามก่อนหน้านี้ แต่ฉันมีทางออกที่ดีกว่าตัวแปรระดับที่นี่: stackoverflow.com/questions/3836385//
NoodleOfDeath


คุณคิดจะแบ่งปันปัญหาค่าคงที่อีกเล็กน้อยหรือไม่? "คงทำเคล็ด แต่มีปัญหาการชน namespace และ (หรือจริงเพราะ) พวกเขาเป็นสากล
XuDing

คำตอบ:


1492

ขึ้นอยู่กับกรณีการใช้งานปกติแล้วฉันจะใช้สิ่งที่เรียบง่ายเช่นนี้

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

ด้วยการสร้างคลาส enum แบบง่าย ๆ ที่ขยาย 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

ตามบันทึกด้านข้างทุกครั้งที่ฉันใช้การสะท้อนอย่างน้อยหนึ่งครั้งในคลาสแบบสแตติก / const ที่ข้อมูลจะไม่เปลี่ยนแปลง (เช่นใน enum) ฉันจะแคชผลลัพธ์ของการสะท้อนกลับเหล่านั้นเนื่องจากใช้วัตถุสะท้อนสดใหม่ทุกครั้ง ในที่สุดจะมีผลกระทบต่อประสิทธิภาพที่สังเกตเห็นได้ชัดเจน

ตอนนี้ที่คนส่วนใหญ่ได้ในที่สุดการปรับรุ่นเป็นอย่างน้อย 5.3 และSplEnumสามารถใช้ได้ที่แน่นอนเป็นตัวเลือกที่ทำงานได้เป็นอย่างดี - ตราบใดที่คุณไม่ทราบความคิด unintuitive ประเพณีของการมี enum จริงinstantiationsทั่ว codebase ของคุณ ในตัวอย่างด้านบนBasicEnumและDaysOfWeekไม่สามารถสร้างอินสแตนซ์ได้เลยไม่ควรเป็นเช่นนั้น


70
ฉันใช้สิ่งนี้ด้วย คุณอาจพิจารณาสร้างชั้นเรียนabstractและfinalไม่สามารถยกตัวอย่างหรือขยายเวลาได้
ryeguy

21
คุณสามารถเรียนได้ทั้งabstractและfinal? ฉันรู้ว่าใน Java นี้ไม่ได้รับอนุญาต คุณสามารถทำเช่นนั้นใน php?
corsiKa

20
@ryeguy ดูเหมือนว่าคุณจะไม่สามารถทำให้มันทั้งสอง และabstract finalในกรณีนี้ฉันจะใช้นามธรรม
Nicole

45
เกี่ยวกับนามธรรมหรือสุดท้าย ฉันทำให้พวกเขาสุดท้ายและให้พวกเขาสร้างส่วนตัวที่ว่างเปล่า
rael_kid

21
ระมัดระวังการใช้ 0 ดังนั้นคุณจะไม่พบปัญหาการเปรียบเทียบเท็จที่ไม่คาดคิดเช่นความเท่าเทียมกันกับnullเพื่อนในswitchข้อความ เคยไปที่นั่น.
yitznewton

185

มีนามสกุลเป็นพื้นเมืองเช่นกัน SplEnum

SplEnum ให้ความสามารถในการเลียนแบบและสร้างวัตถุการแจงนับใน PHP

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

เรียน

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

ส่วนขยาย PECL ไม่ได้รวมอยู่กับ PHP

DLL สำหรับส่วนขยาย PECL นี้ไม่สามารถใช้งานได้ในขณะนี้


4
นี่คือตัวอย่างของ splenum: dreamincode.net/forums/topic/201638-enum-in-php
Nordes

4
ฉันย้อนกลับไปฉันชอบดีกว่าเมื่อฉันเห็นลิงก์ มันให้ข้อมูลบริบทแก่ฉัน
markus

5
ฉันย้อนกลับไปอีกครั้ง ฉันไม่ต้องการให้พวกคุณแก้ไขลิงก์
markus

6
ระวังการใช้สิ่งนี้ ประเภท SPL กำลังทดลอง: "ส่วนขยายนี้เป็นค่าทดลองลักษณะการทำงานของส่วนขยายนี้รวมถึงชื่อฟังก์ชั่นและเอกสารอื่น ๆ ที่เกี่ยวข้องกับส่วนขยายนี้อาจเปลี่ยนแปลงได้โดยไม่ต้องแจ้งให้ทราบล่วงหน้าใน PHP รุ่นต่อไป "
bzeaman

6
SplEnumไม่ได้รวมอยู่กับ PHP แต่ก็ต้องมีการขยาย
SPL_Types

46

แล้วค่าคงของคลาสล่ะ

<?php

class YourClass
{
    const SOME_CONSTANT = 1;

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

echo YourClass::SOME_CONSTANT;

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

ฉันชอบวิธีการง่ายๆนี้
David Lemon

echoConstant__toStringสามารถถูกแทนที่ด้วย แล้วก็echo $c
Justinas

35

คำตอบด้านบนเป็นเลิศ อย่างไรก็ตามหากคุณ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];
}

2
มีปัญหานี้มาก ไบรอันหรือบางคนที่มีสิทธิ์แก้ไขควรแตะมันในคำตอบที่ยอมรับได้ ฉันแก้ไขมันในโค้ดของฉันโดยใช้วิธี 'static ::' แทน 'self ::' ในฟังก์ชั่น getConstants () และประกาศ $ constCache ใน enums เด็กอีกครั้ง
Sp3igel

อาจไม่เซ็กซี่ แต่การใช้ค่าคงที่ส่วนต่อประสานอาจเป็นวิธีที่ดีที่สุดใน PHP
Anthony Rutledge


27

ฉันใช้interfaceแทนclass:

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

var $today = DaysOfWeek::Sunday;

6
class Foo implements DaysOfWeek { }แล้วFoo::Sunday... อะไรนะ?
Dan Lugg

3
ผู้เขียนคำถามถามถึงทางออกสำหรับสองสิ่ง: เนมสเปซและการเติมข้อมูลอัตโนมัติโดย IDE ตามคำแนะนำที่ได้รับการจัดอันดับสูงสุดวิธีที่ง่ายที่สุดคือการใช้class(หรือinterfaceซึ่งเป็นเพียงเรื่องของการตั้งค่า)
Andi T

4
อินเตอร์เฟสถูกใช้เพื่อบังคับใช้ความสมบูรณ์ของการใช้คลาสซึ่งอยู่นอกขอบเขตของอินเทอร์เฟซ
user3886650

2
@ user3886650 การเชื่อมต่อสามารถและ / ใช้ใน Java เพื่อเก็บค่าคงที่ ดังนั้นคุณจะไม่ถูกบังคับให้ยกตัวอย่างคลาสเพื่อรับค่าคงที่และ IDE ใด ๆ ที่เสนอโค้ดให้เสร็จ นอกจากนี้ถ้าคุณสร้างคลาสที่ใช้อินเทอร์เฟซนั้นมันจะสืบทอดค่าคงที่เหล่านั้นทั้งหมด - บางครั้งก็ค่อนข้างมีประโยชน์
อเล็กซ์

@ user3886650 True แต่ใน PHP อินเตอร์เฟสสามารถมีค่าคงที่ได้ นอกจากนี้ค่าคงที่ของอินเทอร์เฟซเหล่านี้ไม่สามารถถูกแทนที่ด้วยการใช้คลาสหรือลูก ๆ ในความเป็นจริงนี่คือคำตอบที่ดีที่สุดในแง่ของ PHP เพราะสิ่งที่สามารถแทนที่ได้ไม่ทำงานอย่างที่ควรจะเป็น ค่าคงที่ควรหมายถึงค่าคงที่ไม่ใช่ในบางครั้ง (แม้ว่า polymorphism จะมีประโยชน์ในบางครั้ง)
Anthony Rutledge

25

ฉันได้แสดงความคิดเห็นกับคำตอบอื่น ๆ ที่นี่ดังนั้นฉันคิดว่าฉันจะชั่งน้ำหนักด้วย ในตอนท้ายของวันเนื่องจาก 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เป็นฐานชั้นที่ enumerations อื่น ๆ ทั้งหมดจะช่วยให้การขยายวิธีการช่วยเหลือเช่นtoArray, isValidและอื่น ๆ ให้ฉันพิมพ์ enumerations ( และจัดการอินสแตนซ์ของพวกเขา ) เพียงแค่จบยุ่งเกินไป


น่าสงสัย

หากมี__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

ฉันชอบความเรียบง่ายของคำตอบนี้ มันเป็นสิ่งที่คุณสามารถกลับมาใหม่ในภายหลังและเข้าใจได้อย่างรวดเร็วว่ามันทำงานอย่างไรโดยไม่ทำให้ดูเหมือนว่าคุณใช้วิธีการแฮ็ก ความอัปยศมันไม่มีคะแนนมากขึ้น
ซ้ำ

23

ดีสำหรับ java อย่างง่ายเช่น enum ใน PHP ฉันใช้:

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 ซึ่งกำลังดิ้นรนกับไวยากรณ์ดังนั้นนี่อาจไม่ใช่วิธีที่ดีที่สุด ฉันทดลองบางอย่างกับ Class Constants โดยใช้ Reflection เพื่อให้ได้ชื่อที่คงที่จากคุณค่าของมัน


คำตอบที่ดีคำตอบอื่น ๆ ส่วนใหญ่ใช้คลาส คุณไม่สามารถมีคลาสที่ซ้อนกันได้
Keyo

สิ่งนี้มีประโยชน์ในการสามารถวนซ้ำผ่านค่าต่างๆด้วย foreach และความเสียหายที่ไม่ได้เกิดจากมูลค่าที่ผิดกฎหมาย
Bob Stein

2
ไม่มีการทำให้สมบูรณ์อัตโนมัติใน IDE ดังนั้นจะกระตุ้นการเดางาน ค่าคงที่จะเปิดใช้งานการทำให้สมบูรณ์อัตโนมัติได้ดีขึ้น
KrekkieD

19

สี่ปีต่อมาฉันเจอสิ่งนี้อีกครั้ง แนวทางปัจจุบันของฉันคือสิ่งนี้เนื่องจากช่วยให้สามารถเขียนโค้ดใน 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;
    }
}

ตัวอย่าง 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');

โปรดทราบว่าอินสแตนซ์ทั้งหมดของรายการ enum เดียวกันเหมือนกัน:

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

คุณยังสามารถใช้ภายในข้อความสั่ง switch:

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

หรือคุณสามารถรับชื่อ (เช่นชื่อฟังก์ชัน) จากรายการ enum ที่มีอยู่:

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

+1 สำหรับผู้สร้างส่วนตัว ฉันจะไม่ทำคลาสผู้ช่วยที่เป็นนามธรรมเพียงแค่คลาสที่เรียบง่ายตัวสร้างส่วนตัวและบางส่วนของconst Monday = DaysOfWeek('Monday');
Kangur

9

ฉันพบห้องสมุดนี้ใน GitHub และฉันคิดว่ามันเป็นทางเลือกที่ดีมากสำหรับคำตอบที่นี่

การติดตั้ง PHP Enum ได้รับแรงบันดาลใจจาก SplEnum

  • คุณสามารถพิมพ์คำใบ้: 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();

ค่า enum ประเภทคำใบ้:

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

1
นี่เป็นคำตอบที่ถูกต้อง (สำหรับตอนนี้จนกว่าenumจะถูกเพิ่มใน PHP 7.x) เพราะสามารถพิมพ์คำใบ้ได้
Tobia

1
สิ่งนี้ไม่เพียง แต่จะช่วยให้การบอกกล่าวประเภท แต่เนื่องจาก__toString()เวทมนตร์ช่วยให้คุณทำสิ่งที่คุณต้องการ enums - ใช้พวกเขาในคำสั่งswitchหรือifคำสั่งเปรียบเทียบโดยตรงกับค่าของ const วิธีที่ดีที่สุดที่ไม่สนับสนุน IMO แบบดั้งเดิม
LinusR

7

หากคุณต้องการใช้ enums ที่ไม่ซ้ำกันทั่วโลก (เช่นเมื่อเปรียบเทียบองค์ประกอบระหว่าง Enums ที่แตกต่างกัน) และใช้งานง่ายอย่าลังเลที่จะใช้รหัสต่อไปนี้ ฉันยังเพิ่มวิธีการบางอย่างที่ฉันพบว่ามีประโยชน์ คุณจะพบตัวอย่างในความคิดเห็นที่ด้านบนสุดของรหัส

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

}


?>

1
ฉันทำแบบนี้มาก อย่างไรก็ตามหนึ่งในข้อร้องเรียนหลักคือความสามารถของ IDE ในการรับค่าสำหรับการทำให้สมบูรณ์อัตโนมัติ ฉันไม่แน่ใจว่าสิ่งนี้จะสามารถทำได้โดยไม่มี addon ที่กำหนดเองสำหรับ IDE ไม่ว่ามันจะไม่สามารถทำได้ แต่มันก็จะทำงาน
corsiKa

2
ใช้eval()เพียงเพื่อให้คุณสามารถประกาศรันไทม์ Enums ใหม่ได้หรือไม่ จี๊ด ฉันไม่รู้สึก คุณจะป้องกันคลาสอื่นจากการสร้างคลาส Enum ที่ไม่ถูกต้องได้อย่างไรก่อนที่คุณจะกำหนดคลาสที่เหมาะสม Enums ไม่รู้จักมาก่อนรันไทม์ใช่หรือไม่ และตามที่ @corsiKa บอกเป็นนัยไม่มีการเติมข้อความอัตโนมัติ IDE ประโยชน์เท่านั้นที่ฉันเห็นคือการเข้ารหัสขี้เกียจ
KrekkieD

7

ฉันชอบ enums จาก java ด้วยและด้วยเหตุนี้ฉันจึงเขียน enums ด้วยวิธีนี้ฉันคิดว่านี่เป็นพฤติกรรมที่คล้ายกันมากที่สุดเช่นใน Java enums แน่นอนถ้าบางคนต้องการใช้วิธีการเพิ่มเติมจาก java ควรเขียนที่นี่หรือใน คลาสนามธรรม แต่แนวคิดหลักถูกฝังอยู่ในโค้ดด้านล่าง


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

3
ฉันกำลังทำสิ่งเดียวกันกับสองส่วนเพิ่มเติมเล็กน้อย: ฉันได้ซ่อนค่าสแตติกหลังตัวรับแบบคงที่ เหตุผลหนึ่งคือที่ฉันชอบFruitsEnum::Apple()มากกว่าสายตาFruitsEnum::$Appleแต่เหตุผลที่สำคัญกว่าคือการป้องกันไม่ให้คนอื่นจากการตั้งค่า$APPLEจึงทำลาย enum สำหรับการใช้งานทั้งหมด อีกอันเป็นแฟล็กสแตติกส่วนตัวแบบง่าย$initializedที่ทำให้แน่ใจว่าการโทรinit()กลายเป็นไม่มีการเรียกหลังจากการเรียกเป็นครั้งแรก (ดังนั้นจึงไม่มีใครสามารถยุ่งกับสิ่งนั้นได้)
Martin Ender

ฉันชอบมาร์ติน .init()แปลกและฉันไม่สนใจวิธีการทะเยอทะยาน
Sebas

7
abstract class Enumeration
{
    public static function enum() 
    {
        $reflect = new ReflectionClass( get_called_class() );
        return $reflect->getConstants();
    }
}


class Test extends Enumeration
{
    const A = 'a';
    const B = 'b';    
}


foreach (Test::enum() as $key => $value) {
    echo "$key -> $value<br>";
}


5

ทางออกที่พบบ่อยที่สุดที่ฉันเห็น enum ใน PHP คือการสร้างระดับทั่วไป enum แล้วขยาย คุณอาจจะดูที่นี้

อัปเดต:อีกทางหนึ่งฉันพบสิ่งนี้จาก phpclasses.org


1
แม้ว่าการนำไปใช้จะลื่นและอาจจะทำงานได้ แต่ข้อเสียของมันก็คือว่า IDEs อาจไม่ทราบวิธีการป้อน enums อัตโนมัติ ฉันไม่สามารถตรวจสอบจาก phpclasses.org เพราะต้องการให้ฉันลงทะเบียน
Henrik Paul

5

นี่คือไลบรารี github สำหรับการจัดการการแจงนับประเภทที่ปลอดภัยใน php:

ไลบรารีนี้จัดการการสร้างคลาส, การแคชคลาสและใช้รูปแบบการออกแบบการนับจำนวนอย่างปลอดภัยด้วยวิธีการช่วยเหลือหลายวิธีสำหรับจัดการกับ enums เช่นการดึงลำดับสำหรับการจัดเรียง enums หรือการเรียกค่าไบนารีสำหรับการรวมกันของ enums

รหัสที่สร้างขึ้นใช้ไฟล์เทมเพลต php แบบเก่าซึ่งสามารถกำหนดค่าได้เช่นกันดังนั้นคุณสามารถจัดเตรียมเทมเพลตของคุณเอง

มันคือการทดสอบเต็มรูปแบบที่ปกคลุมไปด้วย phpunit

PHP-enums บน GitHub (อย่าลังเลที่จะแยก)

การใช้งาน: (@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

4

ฉันใช้วิธีการด้านล่างนี้เพราะให้ความสามารถในการพิมพ์ความปลอดภัยสำหรับพารามิเตอร์ฟังก์ชั่นอัตโนมัติสมบูรณ์ใน NetBeans และประสิทธิภาพที่ดี สิ่งหนึ่งที่ฉันไม่ชอบมากเกินไปคือคุณต้องโทรหา[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");

ไม่มีอะไรขัดขวางคุณจากการกำหนดค่า enum อีกครั้ง: DaysOfWeek::$MONDAY = 3;
KrekkieD

@BrianFisher ฉันรู้ว่ามันสายไปแล้ว แต่ถ้าคุณไม่ชอบการโทร[extended class name]::enumerate();หลังจากนิยามทำไมคุณไม่ทำมันในการสร้าง?
สามารถ O 'สแปม

4

นิยามคลาส Enum ของฉันด้านล่างพิมพ์อย่างรุนแรงและเป็นธรรมชาติมากที่จะใช้และกำหนด

ความหมาย:

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

สลับไปที่ Enum

$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

ส่ง Enum เป็นพารามิเตอร์ (พิมพ์อย่างมาก)

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

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

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

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

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

Echo Enum เป็นสตริง

echo "I have an $myFruit\n";

>> I have an APPLE

รับ Enum ด้วยจำนวนเต็ม

$myFruit = Fruit::getByValue(2);

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

>> Now I have an ORANGE

รับ Enum ตามชื่อ

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

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

>> But I definitely prefer an APPLE

ชั้น Enum:

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

}

ส่วนที่เพิ่มเข้าไป

คุณสามารถ ofcourse ยังเพิ่มความคิดเห็นสำหรับ 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();

4

ฉันรู้ว่านี่เป็นสิ่งที่เก่าแก่มาก แต่ฉันมีความคิดเกี่ยวกับสิ่งนี้และต้องการที่จะรู้ว่าสิ่งที่ผู้คนคิด

หมายเหตุ: ฉันกำลังเล่นกับสิ่งนี้และรู้ว่าถ้าฉันเพิ่งปรับเปลี่ยน__call()ฟังก์ชั่นที่คุณสามารถเข้าใกล้จริงenumsมากขึ้น __call()ฟังก์ชั่นจัดการทุกฟังก์ชั่นการโทรที่ไม่รู้จัก สมมติว่าคุณต้องการสร้างสามenumsRED_LIGHT, 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 ขอให้สนุก! นี่ก็เป็นเช่นนี้บน GitHub

อัปเดต: ฉันได้สร้างมันขึ้นมาเพื่อใช้ทั้งฟังก์ชั่น__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";

?>

3

ฉันรู้ว่านี่เป็นเธรดเก่า แต่ไม่มีวิธีแก้ไขปัญหาใด ๆ ที่ฉันเคยเห็นดูเหมือน enums เนื่องจากการแก้ไขปัญหาเกือบทั้งหมดต้องการให้คุณกำหนดค่าด้วยตนเองให้กับรายการ enum หรือคุณต้องผ่านอาร์เรย์ของคีย์ enum ไปยัง a ฟังก์ชัน ดังนั้นฉันจึงสร้างโซลูชันของตัวเองสำหรับสิ่งนี้

ในการสร้างคลาส Enum โดยใช้โซลูชันของฉันเพียงแค่ขยายคลาส Enum ด้านล่างนี้สร้างตัวแปรสแตติก (ไม่ต้องกำหนดค่าเริ่มต้น) และทำการเรียกใช้ yourEnumClass :: init () ด้านล่างคำจำกัดความของคลาส enum ของคุณ .

แก้ไข: ใช้งานได้ใน php> = 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;
                }
            }
        }
    }
}

3

ตอนนี้คุณสามารถใช้คลาสSplEnumเพื่อสร้างได้เอง ตามเอกสารอย่างเป็นทางการ

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


3

สุดท้ายคำตอบPHP 7.1+กับค่าคงที่ที่ไม่สามารถแทนที่ได้

/**
 * An interface that groups HTTP Accept: header Media Types in one place.
 */
interface MediaTypes
{
    /**
    * Now, if you have to use these same constants with another class, you can
    * without creating funky inheritance / is-a relationships.
    * Also, this gets around the single inheritance limitation.
    */

    public const HTML = 'text/html';
    public const JSON = 'application/json';
    public const XML = 'application/xml';
    public const TEXT = 'text/plain';
}

/**
 * An generic request class.
 */
abstract class Request
{
    // Why not put the constants here?
    // 1) The logical reuse issue.
    // 2) Single Inheritance. 
    // 3) Overriding is possible.

    // Why put class constants here?
    // 1) The constant value will not be necessary in other class families.
}

/**
 * An incoming / server-side HTTP request class.
 */
class HttpRequest extends Request implements MediaTypes
{
    // This class can implement groups of constants as necessary.
}

หากคุณกำลังใช้เนมสเปซการทำให้โค้ดสมบูรณ์ควรทำงาน

อย่างไรก็ตามในการทำเช่นนี้คุณจะสูญเสียความสามารถในการซ่อนค่าคงที่ภายในตระกูล class ( protected) หรือ class alone ( private) ตามคำนิยามทุกอย่างในInterfaceคือpublicเป็น

คู่มือ PHP: การเชื่อมต่อ


นี่ไม่ใช่จาวา ใช้งานได้ในกรณีที่ไม่จำเป็นต้องใช้รูปแบบ polymorphism / กลยุทธ์ในการแทนที่ค่าคงที่ในคลาสผู้ปกครอง
Anthony Rutledge

2

นี่คือสิ่งที่ฉันทำใน "ไดนามิก" enum ... เพื่อให้ฉันสามารถเรียกมันด้วยตัวแปรเช่น จากแบบฟอร์ม

ดู verison ที่อัปเดตด้านล่าง 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);
    }
}

อัปเดต: วิธีที่ดีกว่าในการทำ ...

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

5
ปัญหาเกี่ยวกับสิ่งมีชีวิตนี้ EnumCategory::$sport = 9;. ยินดีต้อนรับสู่พิพิธภัณฑ์กีฬา const เป็นวิธีที่ดีกว่าในการทำมัน
Dan Lugg

2

คำตอบที่ได้รับการยอมรับเป็นวิธีที่จะไปและเป็นจริงสิ่งที่ฉันทำเพื่อความเรียบง่าย ข้อดีของการแจงนับส่วนใหญ่มีให้ (อ่านเร็ว ฯลฯ ) อย่างไรก็ตามแนวคิดหนึ่งหายไป: ความปลอดภัยของประเภท ในภาษาส่วนใหญ่การแจกแจงยังใช้เพื่อ จำกัด ค่าที่อนุญาต ด้านล่างนี้เป็นตัวอย่างของวิธีที่ความปลอดภัยของประเภทสามารถรับได้โดยใช้ตัวสร้างส่วนตัววิธีการสร้างอินสแตนซ์แบบคงที่และการตรวจสอบประเภท:

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 เป็นตัวแทนจำนวนเต็ม

โปรดทราบว่าเราสามารถดำเนินการต่อไปได้ด้วยการใช้กลไกการแคชในวิธีการสร้างอินสแตนซ์เพื่อบันทึกหน่วยความจำในกรณีการนับจำนวนที่ใช้อย่างกว้างขวาง ...

หวังว่าจะช่วยได้


2

คำตอบที่ดีที่นี่!

นี่คือรุ่นของฉัน

  • มันพิมพ์อย่างยิ่ง
  • มันทำงานร่วมกับการเติมข้อมูลอัตโนมัติของ IDE
  • Enums ถูกกำหนดโดยโค้ดและคำอธิบายโดยที่โค้ดสามารถเป็นจำนวนเต็มค่าไบนารีสตริงสั้น ๆ หรือโดยทั่วไปสิ่งอื่นที่คุณต้องการ รูปแบบสามารถขยายได้อย่างง่ายดายเพื่อสนับสนุนคุณสมบัติที่เหนือกว่า
  • มันรองรับค่า (==) และการอ้างอิง (===) การเปรียบเทียบและทำงานในงบเปลี่ยน

ฉันคิดว่าข้อเสียเปรียบหลักคือสมาชิก enum จะต้องประกาศแยกต่างหากและอินสแตนซ์เนื่องจากคำอธิบายและ PHP ไม่สามารถที่จะสร้างวัตถุในการประกาศสมาชิกคงที่ ฉันเดาว่าวิธีนี้อาจเป็นการใช้การสะท้อนพร้อมกับความคิดเห็นของเอกสารแยกวิเคราะห์แทน

Enum นามธรรมมีลักษณะเช่นนี้:

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

นี่คือตัวอย่างคอนกรีต 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();

ซึ่งสามารถใช้ดังนี้:

<?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) {
["รหัส": "AbstractEnum": ส่วนตัว] =>
int (1)
["คำอธิบาย": "AbstractEnum": ส่วนตัว] =>
สตริง (14) "ค่าแรกของฉัน"
}
[2] =>
วัตถุ (EMyEnum) # 2 (2) {
["รหัส": "AbstractEnum": ส่วนตัว] =>
int (2)
["คำอธิบาย": "AbstractEnum" : ส่วนตัว] =>
string (15) "ค่าที่สองของฉัน"
}
[3] =>
วัตถุ (EMyEnum) # 3 (2) {
["รหัส": "AbstractEnum": ส่วนตัว] =>
(3)
["description": "AbstractEnum": private] =>
สตริง (14) "ค่าที่สามของฉัน"
}
}

ค่าที่สองของฉัน


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

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

$today = DayOfWeek::MONDAY;

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

อย่าใช้การไตร่ตรอง ทำให้ยากอย่างยิ่งที่จะให้เหตุผลเกี่ยวกับรหัสของคุณและติดตามสิ่งที่มีการใช้งานและมีแนวโน้มที่จะทำลายเครื่องมือวิเคราะห์แบบคงที่ (เช่นสิ่งที่สร้างไว้ใน IDE ของคุณ)


2

ด้านหนึ่งที่ขาดหายไปจากคำตอบอื่น ๆ ที่นี่เป็นวิธีการใช้ enums ด้วยคำใบ้ประเภท

หากคุณกำหนด enum ของคุณเป็นชุดของค่าคงที่ในคลาสนามธรรมเช่น

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

แล้วคุณจะไม่สามารถพิมพ์คำแนะนำในพารามิเตอร์ฟังก์ชั่น - หนึ่งเพราะมันไม่ instantiable แต่ยังเพราะประเภทของการShirtSize::SMALLเป็นไม่ได้intShirtSize

นั่นเป็นเหตุผลที่ enums ดั้งเดิมใน PHP จะดีกว่าทุกอย่างที่เราสามารถหาได้ อย่างไรก็ตามเราสามารถประมาณค่า enum ได้โดยการรักษาสมบัติส่วนตัวซึ่งแสดงถึงค่าของ enum แล้ว จำกัด การเริ่มต้นของคุณสมบัตินี้ให้คงที่ของเรา เพื่อป้องกันไม่ให้ enum ถูกสร้างอินสแตนซ์โดยพลการ (โดยไม่มีค่าใช้จ่ายในการตรวจสอบรายการที่อนุญาต) เราจะสร้างคอนสตรัคเตอร์ให้เป็นส่วนตัว

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วิธีการเพื่อให้ผู้ใช้ไม่จำเป็นต้องจำไว้ว่าต้องใช้==และไม่===เปรียบเทียบค่า enum สองค่า

แก้ไข: คู่ของคำตอบที่มีอยู่จะคล้ายกันมากโดยเฉพาะอย่างยิ่ง: https://stackoverflow.com/a/25526473/2407870


2

การเหยียบคำตอบของ @Brian Cline ฉันคิดว่าฉันอาจให้ 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];
    }
}

ด้วยเหตุผลบางอย่างฉันไม่สามารถเรียกฟังก์ชั่นนี้ได้ มันบอกฉันว่าฟังก์ชั่นดังกล่าวไม่ได้ประกาศ ฉันทำอะไรผิด [คลาส Enum ขั้นพื้นฐานอยู่ในไฟล์อื่นและฉันกำลังใช้include('enums.php');] ด้วยเหตุผลบางอย่างมันไม่เห็นฟังก์ชั่นที่ประกาศใน Enum สำหรับชั้นเรียนของเด็ก ...
Andrew

นอกจากนี้ ... วิธีการตั้งค่าจากสตริง? sth เช่นเดียวกับ$currentSeason.set("Spring");
แอนดรู

1

ความพยายามของฉันในการสร้าง enum ด้วย PHP ... มันมีข้อ จำกัด อย่างยิ่งเนื่องจากไม่รองรับวัตถุเป็นค่า enum แต่ก็มีประโยชน์บ้าง ...

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

มันมีข้อ จำกัด ในหลาย ๆ ทิศทางและคำตอบที่มีอยู่ให้มากกว่านั้น ฉันจะบอกว่านี่ไม่ใช่การเพิ่มสิ่งที่มีประโยชน์จริงๆ
hakre

1

เมื่อวานนี้ผมเขียนชั้นนี้บนบล็อกของฉัน ฉันคิดว่าอาจใช้งานง่ายในสคริปต์ 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;
}

2
แต่มันเป็นคลาสที่ดีและฟังก์ชั่นชื่อเป็นของพื้นเมือง และ translate.google.ru ก็อาจช่วยได้เช่นกัน
Arturgspb

2
ใช้พวกโครมและแปลมันถ้าคุณเป็นโปรแกรมเมอร์คุณก็อ่านโค้ด!
markus

8
โดยทั่วไปจะดีกว่าเสมอในการรวมรหัสไว้ในคำตอบแทนที่จะเชื่อมโยงไปยังแหล่งข้อมูลภายนอกที่อาจมีหรือไม่มีในเดือน / ปี 'n' เป็นต้น
John Parker

ชั้นเรียนของฉันใหญ่มากและฉันคิดว่าการอ่านโพสต์นี้จะไม่สะดวก
Arturgspb

ฉันคิดว่าสองสิ่งที่ไม่ดีที่นี่: มันเป็นภาษารัสเซีย (โปรแกรมเมอร์ทุกคนต้องรู้จักภาษาอังกฤษและใช้มันแม้ในความคิดเห็น) และมันไม่รวมอยู่ที่นี่ ดูวิธีใช้เพื่อรวมรหัสขนาดใหญ่
gaRex
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.