如何在PHP中实现类似枚举的功能?


How to implement Enum like functionality in PHP?

如何在PHP中使用EnUM类功能(如Java和其他高级语言提供的)?我知道PHP目前不允许创建枚举,但是最接近的是什么?


可能使用const

1
2
3
4
class SomeClass {
    const FIRSTVAL = 1;
    const SECONDVAL = 2;
};


我使用类常量,还有一些反射技巧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<?php
/**
 * @package Red.Core
 * @author [email protected]
 *
 * Implements the abstract base for all enum types
 *
 * example of a typical enum:
 *
 *    class DayOfWeek extends Enum
 *    {
 *        const Sunday    = 0;
 *        const Monday    = 1;
 *        const Tuesday   = 2;
 *        const Wednesday = 3;
 *        const Thursday  = 4;
 *        const Friday    = 5;
 *        const Saturday  = 6;
 *    }
 *
 * usage examples:
 *
 *     $monday = Enum::FromString( 'DayOfWeek::Monday' );           // (int) 1
 *     $monday = DayOfWeek::Monday                                  // (int) 1
 *     $monday = Enum::ToString( 'DayOfWeek', DayOfWeek::Monday );  // (string)"DayOfWeek::Monday"
 *     $monday = Enum::Label( 'DayOfWeek', DayOfWeek::Monday );     // (string)"Monday"
 *
 **/

abstract class Enum
{
    // make sure there are never any instances created
    final private function __construct()
    {
        throw new Exception( 'Enum and Subclasses cannot be instantiated.' );
    }

    /**
     * Give the integer associated with the const of the given string in the format of"class:const"
     *
     * @param string $string
     * @return integer
     */

    final public static function FromString( $string )
    {
        if ( strpos( $string, '::' ) < 1 )
        {
            throw new Exception( 'Enum::FromString( $string ) Input string is not in the expected format.' );
        }
        list( $class, $const ) = explode( '::', $string );

        if ( class_exists( $class, false ) )
        {
            $reflector = new ReflectionClass( $class );
            if ( $reflector->IsSubClassOf( 'Enum' ) )
            {
                if ( $reflector->hasConstant( $const ) )
                {
                    return eval( sprintf( 'return %s;', $string ) );
                }
            }
        }
        throw new Excption( sprintf( '%s does not map to an Enum field', $string ) );
    }

    final public static function IsValidValue( $enumType, $enumValue )
    {
        if ( class_exists( $enumType ) )
        {
            $reflector = new ReflectionClass( $enumType );
            if ( $reflector->IsSubClassOf( 'Enum' ) )
            {
                foreach( $reflector->getConstants() as $label => $value )
                {
                    if ( $value == $enumValue )
                    {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    final public static function IsValidLabel( $enumType, $enumValue )
    {
        if ( class_exists( $enumType ) )
        {
            $reflector = new ReflectionClass( $enumType );
            if ( $reflector->IsSubClassOf( 'Enum' ) )
            {
                foreach( $reflector->getConstants() as $label => $value )
                {
                    if ( $label == $enumValue )
                    {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * For a given $enumType, give the complete string representation for the given $enumValue (class::const)
     *
     * @param string $enumType
     * @param integer $enumValue
     * @return string
     */

    final public static function ToString( $enumType, $enumValue )
    {
        $result = 'NotAnEnum::IllegalValue';

        if ( class_exists( $enumType, false ) )
        {
            $reflector = new ReflectionClass( $enumType );
            $result = $reflector->getName() . '::IllegalValue';
            foreach( $reflector->getConstants() as $key => $val )
            {
                if ( $val == $enumValue )
                {
                    $result = str_replace( 'IllegalValue', $key, $result );
                    break;
                }
            }
        }
        return $result;
    }

    /**
     * For a given $enumType, give the label associated with the given $enumValue (const name in class definition)
     *
     * @param string $enumType
     * @param integer $enumValue
     * @return string
     */

    final public static function Label( $enumType, $enumValue )
    {
        $result = 'IllegalValue';

        if ( class_exists( $enumType, false ) )
        {
            $reflector = new ReflectionClass( $enumType );

            foreach( $reflector->getConstants() as $key => $val )
            {
                if ( $val == $enumValue )
                {
                    $result = $key;
                    break;
                }
            }
        }
        return $result;
    }
}
?>


这是@kris代码的一个更新版本,可以更好地与较新版本的PHP配合使用。它是根据@lassombra评论制作的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/**
 * Implements the abstract base for all enum types
 * @see http://stackoverflow.com/a/2324746/1003020
 * @see http://stackoverflow.com/a/254543/1003020
 *
 * Example of a typical enum:
 *
 *    class DayOfWeek extends Enum
 *    {
 *        const Sunday    = 0;
 *        const Monday    = 1;
 *        const Tuesday   = 2;
 *        const Wednesday = 3;
 *        const Thursday  = 4;
 *        const Friday    = 5;
 *        const Saturday  = 6;
 *    }
 *
 * Usage examples:
 *
 *     $monday = DayOfWeek::Monday                      // (int) 1
 *     DayOfWeek::isValidName('Monday')                 // (bool) true
 *     DayOfWeek::isValidName('monday', $strict = true) // (bool) false
 *     DayOfWeek::isValidValue(0)                       // (bool) true
 *     DayOfWeek::fromString('Monday')                  // (int) 1
 *     DayOfWeek::toString(DayOfWeek::Tuesday)          // (string)"Tuesday"
 *     DayOfWeek::toString(5)                           // (string)"Friday"
 **/

abstract class Enum
{
    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
eflectionClass($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);
    }

    public static function fromString($name)
    {
        if (self::isValidName($name, $strict = true)) {
            $constants = self::getConstants();
            return $constants[$name];
        }

        return false;
    }

    public static function toString($value)
    {
        if (self::isValidValue($value, $strict = true)) {
            return array_search($value, self::getConstants());
        }

        return false;
    }
}

您也可以使用这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Enum{

    private $m_valueName = NULL;

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

    public static function __callStatic($methodName, $arguments){
        $className = get_called_class();
        return new $className($methodName);
    }

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

class NotificationType extends Enum{
    const Notification = NULL;
    const Warning = NULL;
    const Error = NULL;
}

function Test(NotificationType $type){
    echo"Test function, type: $type";
}

Test(NotificationType::Warning());


提供了一个SplEnum类。

文档中的示例用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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;
}

上面的示例将输出

1
2
6
Value not a const in enum Month

另一种可能是使用myclabs/php枚举包。


可以使用常量

1
2
3
4
class myClass {
    const aValue = 123;
    const aString ="ABC";
};

但它不会提供一种很好的迭代方法,因此我可能会选择关联数组,因为这样更容易管理:

1
2
3
4
5
class myClass{
  $enum = array ("first" => 123,
               "second" =>"ABC");

}


在我的例子中,我需要存储整个应用程序中使用的权限名称。我最后得到了一个基本枚举抽象类,它为枚举定义了几个实用函数,然后对其进行了扩展。下面是基本枚举类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php

namespace App\Enums;


use ReflectionClass;

abstract class BasicEnum {
    private static $constCacheArray = NULL;

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

下面是通过扩展抽象类创建的枚举示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php

namespace App\Enums;


class Permissions extends BasicEnum
{
    const COMMENTS_CREATE = 'create comments';
    const COMMENTS_VIEW = 'view comments';
    const COMMENTS_EDIT = 'edit comments';
    const COMMENTS_DELETE = 'delete comments';
    const COMMENTS_RESTORE = 'restore comments';

    const REACTIONS_CREATE = 'create reactions';
    const REACTIONS_VIEW = 'view reactions';
    const REACTIONS_EDIT = 'edit reactions';
    const REACTIONS_DELETE = 'delete reactions';
    const REACTIONS_RESTORE = 'restore reactions';

    const QUESTIONS_CREATE = 'create questions';
    const QUESTIONS_VIEW = 'view questions';
    const QUESTIONS_EDIT = 'edit questions';
    const QUESTIONS_DELETE = 'delete questions';
    const QUESTIONS_RESTORE = 'restore questions';

    const PERMISSIONS_CREATE = 'create permissions';
    const PERMISSIONS_VIEW = 'view permissions';
    const PERMISSIONS_EDIT = 'edit permissions';
    const PERMISSIONS_DELETE = 'delete permissions';
    const PERMISSIONS_RESTORE = 'restore permissions';

}
1
2
3
4
5
6
7
8
9
10
11
<?php

namespace App\Enums;


class PostTypes extends BasicEnum
{
    const POST    = 'post';
    const NEWS    = 'news';
    const SERVICE    = 'service';
}

下面是权限枚举的一个示例用法:

1
2
3
4
5
6
7
8
9
10
/**
 * Determine whether the user can create reactions.
 *
 * @param User $user
 * @return mixed
 */

public function create(User $user)
{
    return $user->can(Permissions::REACTIONS_CREATE);
}

希望这有帮助。


一个便宜的技巧是用可能的值创建一个数组。但是,与上面的答案不同,我选择一个键/值对相等的数组,即:

1
2
3
4
5
6
7
<?php
$enum = Array(
'apple' => 'apple',
'pear' => 'pear',
'orange' => 'orange'
);
?>

这样,如果($enum[$value] != $value),您就知道给定的值不在集合中。

当然,如果您希望键/值对不同,可以使用常规数组。


作为数组。

1
2
3
4
5
$arr = array('A','B','C','D');

$find = 'A';
$key = array_search($find,$arr);
echo $arr[$key];