一种常见的方式是,使用常量来代表枚举类型
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