/php-enum

php enum

Primary LanguagePHPMIT LicenseMIT

php-enum

PHP 枚举基类,使用类常量作为枚举值,每一项均含有 keyvaluelabel

安装

composer require lip/php-enum

概念

一个枚举,通常包含:keyvalue,如 typescript 中的下列枚举:

enum Direction {
  Up = 1,
  Down = 2,
  Left = 3,
  Right = 4
}

其中 UpDownLeftRightkey1234value

一些常见的情形,还需要一个文字描述,特别是中文环境,将上面代码改造一下:

enum Direction {
  Up = [1, '上'],
  Down = [2, '下'],
  Left = [3, '左'],
  Right = [4, '右']
}

本库将这种文字描述,定义为枚举的 label,即上面的

由于 PHP 本身不支持枚举,这里撰写一个类 lip\enum\Enum,继承该类,定义类常量,作为枚举值。

使用

标准用法(推荐)

每一个类常量均为一个数组,其中,第一项为 value,第二项为 label,顺序不能变。

下例中,One、Two、Three 为 key,1、2、3 是 value,一、二、三是 label

<?php
use lip\enum\Enum;

/**
 * 一个很有意思的枚举。
 * @method static self One()    One的函数说明
 * @method static self Two()    Two的函数说明
 * @method static self Three()  Three的函数说明
 */
final class Some extends Enum
{
    private const One = [1, ''];
    private const Two = [2, ''];
    private const Three = [3, ''];
}

定义一个使用 Some 枚举作为参数的函数,使用强类型进行标注:

function useSome(Some $some) {
    // ...
}

此时,如果用别的值,传递给上述函数 useSome,则会报错,比如传递 Some::One,由于它的值为 [1, '一'],类型为 array,而非 Some,故而会报错。

Some 继承自 lip\enum\Enum 类,该类赋予 Some 一个很重要的能力:

将类常量定义,转换成对应的静态方法,调用此方法,会获得对应的 Some 类实例。

例如 Some::One() 对应的就是含有常量 Some::One 值的 Some 类实例,可以无障碍地传给 useSome

useSome(Some::One());

注意 Some::One() 是一个静态方法,它通过类 lip\enum\Enum 里的魔术方法 __callStatic 添加,有多少个类常量,就有多少个对应名字的静态方法。

那么 useSome 里如何获取枚举相应的 keyvaluelabel 呢?调用相应的实例方法即可,例如:

function useSome(Some $some)
{
    $key = $some->key();
    $value = $some->value();
    $label => $some->label();
    // $some 可以直接参与字符串连接运算,相当于 $some->value() 参与了运算,例如:
    echo 'some value is: ' . $some;
    // 相当于
    echo 'some value is: ' . $some->value();
}

更多 lip\enum\Enum 类实例 & 静态方法参见下面的方法列表部分。

强烈建议将类常量定义为 private,以免被访问到,始终使用 Some::ConstName() 的形式获取相应的枚举实例。

简洁用法(不需要或者不关注 label 时使用)

此用法不关注 label,每个类常量均为单一值,此时 valuelabel 相同,均为该常量值,举例来说:

/**
 * 另一个很有意思的枚举。
 * @method static self Haha() 哈哈大笑
 * @method static self Bibi() You can you up, no can no bibi
 */
final class Other extends Enum
{
    private const Haha = 'hh';
    private const Bibi = 'bb';
}

// Other 实例的 value 与 label 是相同的,都是 hh
Other::Haha()->value() == Other::Haha()->label()   // true

语法提示

由于 lip\enum\Enum 类使用了魔术方法,导致在调用诸如 Some::One() 时,其结果不能正确地,被开发工具反射正确的类型,为使开发工具能正确地识别类型,可在枚举类的注释中,为每一个类常量增加一行 @method static self ConstName() 函数说明,具体参见上面的例子。

无法被实例化

基类 lip\enum\Enum 将构造函数设为 protected,禁止通过构造函数实例化枚举类,请始终使用 Some::ConstName() 的形式。

方法列表

实例方法

方法名 说明
key() 获取枚举 key。
value() 获取枚举 value。
label() 获取枚举 label。
isXxx() 动态方法,Xxx 为枚举 key。
__toString() 无法被主动调用,在对实例本身进行字符串连接时,会被调用,此处实现为:(string)$this->value

静态方法

子类定义的类常量名,不要与👇的静态方法同名。

方法名 说明
allConstants() 获取全部常量列表,格式:[ key1 => [value1, label1], ... ],例如:[ 'One' => [1, '一'], 'Two' => [2, '二'], 'Three' => [3, '三'] ]
asList() 作为列表返回,以便前端使用,返回值用类 js 格式表示:[ { key: key1, value: value1, label: label1 }, ... ]
parts() 将实例的一部分,作为列表返回,返回值用类 js 格式表示:{ value, label }。注意,这是实例方法。
allKeys() 获取全部 key 列表。
allValues() 获取全部 value 列表。
allLabels() 获取全部 label 列表。
valueToLabel(mixed $value = null, $default = null) 获取 $value 对应的 label,如不存在,返回 $default,若不传参数,返回整个 map,例如:[1 => '一', 2 => '二', 3 => '三']
valuesToLabels(array $values, $default = null) 将一批 value 转换成对应的 label,若不存在,使用 $default 取代。
labelToValue(string $label = null, $default = null) 获取 $label 对应的 value,如不存在,返回 $default,若不传参数,返回整个 map。例如:['一' => 1, '二' => 2, '三' => 3]
labelsToValues(array $labels, $default = null) 将一批 label 转换成对应的 value,若不存在,使用 $default 取代。
isValidKey(string $key) 判断是否为合法的 key。
isValidValue(mixed $value) 判断是否为合法的 value。
isValidLabel(string $label) 判断是否为合法的 label。
fromKey(string $key) 从 key 构建 Enum 实例,若不存在,则返回 null。
fromValue(mixed $value) 从 value 构建 Enum 实例,若不存在,则返回 null。
isXxx(mixed $value) 动态方法,Xxx 为枚举 key。

唯一实例

相同的类常量值,每次调用静态方法,均返回唯一的类实例,即:

Some::One() === Some::One()  // true
Some::One() !== Some::One()  // false

因此,判断一个枚举实例,是否为某个枚举时,可以这样:

if ($some === Some::One()) {
    // ...
} else if ($some === Some::Two()) {
    // ...
} else if ($some === Some::Three()) {
    // ...
} else {
    // ...
}
// 或者
switch ($some) {
    case Some::One():
        // ...
        break;
    case Some::Two():
        // ...
        break;
    case Some::Three():
        // ...
        break;
    default:
        // ...
        break;
}

同样,相等 == 也符合预期。

JsonSerializable

lip\enum\Enum 类实现了接口 JsonSerializable,例如:

json_encode(Some::One()); // '{"key":"One","value":1,"label":"\u4e00"}'

单元测试

运行 tests/runtest.sh 执行单元测试,已 100% 测试通过,请放心使用。

结语

有任何建议或改进,请发 issue

如何发布

release Tag,转到 php-enum,就可以看到新版本了。