一种常见的方式是,使用常量来代表枚举类型
1
2
3
|
const YES = '是' ; const NO = '否' ; |
可以在这个基础上更进一步,将其封装成类,以便于管理
1
2
3
4
5
6
7
|
class BoolEnum { const YES = '是' ; const NO = '否' ; } |
现在,我们希望能通过方法来动态调用对应的枚举类型
1
2
3
|
BoolEnum::YES(); // 是 BoolEnum::NO(); // 否 |
也可以批量获取枚举类型
1
|
BoolEnum::toArray(); // ['Yes' => '是', 'No' => '否'] |
下面来实现上面列举的功能。
定义基本的枚举基类,让所有的枚举类都继承该抽象基类。
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
|
abstract class Enum { // 获取所有枚举类型 public static function toArray(){ // 通过反射获取常量 $reflection = new \ReflectionClass( static :: class ); $contants = $reflection ->getConstants(); // 返回对应的常量 return $contants ; } // 动态调用属性 public static function __callStatic( $name , $arguments ) { $arr = static ::toArray(); if (isset( $arr [ $name ])){ return $arr [ $name ]; } throw new \BadMethodCallException( "找不到对应的枚举值 {$name}" ); } } class BoolEnum extends Enum { const YES = '是' ; const NO = '否' ; } |
利用反射,可以获取到所有的枚举类型。同时,利用魔术方法则可以实现对属性的动态调用。这里要注意的是,反射会消耗较多的资源,因此,对 toArray 方法进行重构,增加一个缓存变量来缓存获取到的枚举类型,避免重复使用反射。
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
|
abstract class Enum { protected static $cache = []; public static function toArray(){ $class = static :: class ; // 第一次获取,就通过反射来获取 if (! isset( static :: $cache [ $class ])){ $reflection = new \ReflectionClass( static :: class ); static :: $cache [ $class ] = $reflection ->getConstants(); } return static :: $cache [ $class ]; } } |
现在考虑更多的使用场景,比如用实例来代表特定枚举类型
1
2
3
|
$yes = new BoolEnum( "是" ); echo $yes ; // "是" |
实现如下
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
|
abstract Enum { protected $value ; public function __construct( $value ) { if ( $value instanceof static ) { $value = $value ->getValue(); } if (! $this ->isValid( $value )){ throw new \UnexpectedValueException( "$value 不属于该枚举值" . static :: class ); } $this ->value = $value ; } // 获取实例对应的键 public function getKey(){ return array_search ( $this ->value, static ::toArray(), true); } // 获取实例对应的值 public function getValue() { return $this ->value; } // 允许字符串形式输出 public function __toString() { return $this ->value; } // 验证值是否合法 public function isValid( $value ) { $arr = static ::toArray(); return in_array( $value , $arr , true); } // 验证键是否合法 public function isValidKey( $key ) { $arr = static ::toArray(); return array_key_exists ( $key , $arr ); } } |
这样做可避免用户使用非法的枚举类型的值
1
2
3
4
5
|
$user ->banned = '非法值' ; // 可能不会报错 $yes = new BoolEnum( "非法值" ); // 将会抛出异常 $user ->banned = $yes ; |
或者作为参数类型限定
1
2
3
4
5
|
function setUserStatus(BoolEnum $boolEnum ){ $user ->banned = $boolEnum ; } |
PHP 作为一门弱类型语言,参数限定的不足会导致很多不可预期的错误发生,通过使用枚举类,我们进一步加强了参数限定的功能,同时,管理枚举类型也更加的方便统一。
以上就是本次介绍的全部相关知识点,感谢大家的学习和对服务器之家的支持。
原文链接:https://www.php.cn/php-weizijiaocheng-442653.html