marc-mabe / php-enum
使用原生PHP实现枚举的简单快速方法
Requires
- php: ^7.1 | ^8.0
- ext-reflection: *
Requires (Dev)
- phpbench/phpbench: ^0.16.10 || ^1.0.4
- phpstan/phpstan: ^1.3.1
- phpunit/phpunit: ^7.5.20 | ^8.5.22 | ^9.5.11
- vimeo/psalm: ^4.17.0
- v4.7.0
- dev-master / 4.6.x-dev
- v4.6.1
- v4.6.0
- v4.6.0-rc1
- v4.4.0
- v4.3.0
- v4.3.0-rc1
- v4.2.0
- v4.1.1
- v4.1.0
- v4.0.0
- v4.0.0-rc1
- 3.x-dev
- v3.2.0
- v3.1.1
- v3.1.0
- v3.1.0-rc1
- v3.0.2
- v3.0.1
- v3.0.0
- v3.0.0-rc3
- v3.0.0-rc2
- v3.0.0-rc1
- 2.x-dev
- v2.3.2
- v2.3.1
- v2.3.0
- v2.3.0-rc1
- v2.2.1
- v2.2.0
- v2.1.0
- v2.0.0
- v2.0.0-beta1
- 1.x-dev
- v1.3.0
- v1.2.0
- v1.1.0
- v1.0.0
- 1.0.0-beta2
- 1.0.0-beta1
This package is auto-updated.
Last update: 2024-09-19 07:41:47 UTC
README
这是一个为PHP添加枚举支持的本地PHP实现。它是一个需要扩展才能使用的抽象类。
什么是枚举?
在计算机编程中,枚举类型(也称为枚举或enum)是由一组称为元素、成员或枚举器的命名值组成的类型。枚举器名称通常是作为语言中的常量行为的标识符。已声明为枚举类型的变量可以被分配任何枚举器作为值。换句话说,枚举类型具有不同的值,可以进行比较和赋值,但在计算机内存中没有特定的具体表示;编译器和解释器可以任意表示它们。
使用方法
基础
use MabeEnum\Enum; // define an own enumeration class class UserStatus extends Enum { const INACTIVE = 'i'; const ACTIVE = 'a'; const DELETED = 'd'; // all scalar data types and arrays are supported as enumerator values const NIL = null; const BOOLEAN = true; const INT = 1234; const STR = 'string'; const FLOAT = 0.123; const ARR = ['this', 'is', ['an', 'array']]; // Enumerators will be generated from public constants only public const PUBLIC_CONST = 'public constant'; // this will be an enumerator protected const PROTECTED_CONST = 'protected constant'; // this will NOT be an enumerator private const PRIVATE_CONST = 'private constant'; // this will NOT be an enumerator // works since PHP-7.0 - see https://wiki.php.net/rfc/context_sensitive_lexer const TRUE = 'true'; const FALSE = 'false'; const NULL = 'null'; const PUBLIC = 'public'; const PRIVATE = 'private'; const PROTECTED = 'protected'; const FUNCTION = 'function'; const TRAIT = 'trait'; const INTERFACE = 'interface'; // Doesn't work - see https://wiki.php.net/rfc/class_name_scalars // const CLASS = 'class'; } // ways to instantiate an enumerator $status = UserStatus::get(UserStatus::ACTIVE); // by value or instance $status = UserStatus::ACTIVE(); // by name as callable $status = UserStatus::byValue('a'); // by value $status = UserStatus::byName('ACTIVE'); // by name $status = UserStatus::byOrdinal(1); // by ordinal number // basic methods of an instantiated enumerator $status->getValue(); // returns the selected constant value $status->getName(); // returns the selected constant name $status->getOrdinal(); // returns the ordinal number of the selected constant // basic methods to list defined enumerators UserStatus::getEnumerators(); // returns a list of enumerator instances UserStatus::getValues(); // returns a list of enumerator values UserStatus::getNames(); // returns a list of enumerator names UserStatus::getOrdinals(); // returns a list of ordinal numbers UserStatus::getConstants(); // returns an associative array of enumerator names to enumerator values // same enumerators (of the same enumeration class) holds the same instance UserStatus::get(UserStatus::ACTIVE) === UserStatus::ACTIVE() UserStatus::get(UserStatus::DELETED) != UserStatus::INACTIVE() // simplified way to compare two enumerators $status = UserStatus::ACTIVE(); $status->is(UserStatus::ACTIVE); // true $status->is(UserStatus::ACTIVE()); // true $status->is(UserStatus::DELETED); // false $status->is(UserStatus::DELETED()); // false
类型提示
use MabeEnum\Enum; class User { protected $status; public function setStatus(UserStatus $status) { $this->status = $status; } public function getStatus() { if (!$this->status) { // initialize default $this->status = UserStatus::INACTIVE(); } return $this->status; } }
类型提示问题
因为在正常的OOP中,上面的示例允许使用 UserStatus
及其派生类型。
请考虑以下示例
class ExtendedUserStatus extends UserStatus { const EXTENDED = 'extended'; } $user = new User(); $user->setStatus(ExtendedUserStatus::EXTENDED());
现在setter接收到一个它不知道的状态但允许它。
解决方案1:最终化枚举
final class UserStatus extends Enum { // ... } class User { protected $status; public function setStatus(UserStatus $status) { $this->status = $status; } }
-
这是一个好且明显的方法
-
结果行为与大多数其他语言的本地枚举实现(如Java)相匹配
但作为这个库模拟枚举,它也有一些缺点
-
枚举值不能直接使用
$user->setStatus(UserStatus::ACTIVE)
失败$user->setStatus(UserStatus::ACTIVE())
工作
-
如果枚举是在外部库中定义的,这没有帮助
解决方案2:使用 Enum::get()
class User { public function setStatus($status) { $this->status = UserStatus::get($status); } }
-
确保结果枚举器与枚举完全匹配。(不允许继承枚举器)
-
允许直接使用枚举值
$user->setStatus(UserStatus::ACTIVE)
工作$user->setStatus(UserStatus::ACTIVE())
工作
-
这也适用于在外部库中定义的枚举
但当然,这个解决方案也有缺点
-
丢失声明性类型提示
-
稍微慢一些
EnumSet
一个用于枚举类型的专用Set实现,称为 EnumSet
。在 EnumSet
中的所有枚举器必须来自创建集合时指定的单个枚举类型。
枚举集在内部表示为位向量。位向量是整数类型或二进制字符串类型,这取决于枚举类型中定义了多少枚举器。这种表示非常紧凑和高效。批量操作将非常快。根据设计,EnumSet
的枚举器是唯一的,并按其序号排序。
它实现了 IteratorAggregate
和 Countable
,可以直接使用 foreach
进行迭代,并使用 count()
进行计数。
EnumSet
有一个可变和不可变接口。可变方法以 set
、add
或 remove
开头,而不可变方法以 with
开头。
use MabeEnum\EnumSet; // create a new EnumSet and initialize with the given enumerators $enumSet = new EnumSet('UserStatus', [UserStatus::ACTIVE()]); // modify an EnumSet (mutable interface) // add enumerators (by value or by instance) $enumSet->addIterable([UserStatus::INACTIVE, UserStatus::DELETED()]); // or $enumSet->add(UserStatus::INACTIVE); $enumSet->add(UserStatus::DELETED()); // remove enumerators (by value or by instance) $enumSet->removeIterable([UserStatus::INACTIVE, UserStatus::DELETED()]); // or $enumSet->remove(UserStatus::INACTIVE); $enumSet->remove(UserStatus::DELETED()); // The immutable interface will create a new EnumSet for each modification // add enumerators (by value or by instance) $enumSet = $enumSet->withIterable([UserStatus::INACTIVE, UserStatus::DELETED()]); // or $enumSet = $enumSet->with(UserStatus::INACTIVE); $enumSet = $enumSet->with(UserStatus::DELETED()); // remove enumerators (by value or by instance) $enumSet->withoutIterable([UserStatus::INACTIVE, UserStatus::DELETED()]); // or $enumSet = $enumSet->without(UserStatus::INACTIVE); $enumSet = $enumSet->without(UserStatus::DELETED()); // Test if an enumerator exists (by value or by instance) $enumSet->has(UserStatus::INACTIVE); // bool // count the number of enumerators $enumSet->count(); count($enumSet); // test for elements $enumSet->isEmpty(); // convert to array $enumSet->getValues(); // List of enumerator values $enumSet->getEnumerators(); // List of enumerator instances $enumSet->getNames(); // List of enumerator names $enumSet->getOrdinals(); // List of ordinal numbers // iterating over the set foreach ($enumSet as $ordinal => $enum) { gettype($ordinal); // int (the ordinal number of the enumerator) get_class($enum); // UserStatus (enumerator object) } // compare two EnumSets $enumSet->isEqual($other); // Check if the EnumSet is the same as other $enumSet->isSubset($other); // Check if the EnumSet is a subset of other $enumSet->isSuperset($other); // Check if the EnumSet is a superset of other // union, intersect, difference and symmetric difference // ... the mutable interface will modify the set $enumSet->setUnion($other); // Enumerators from both this and other (this | other) $enumSet->setIntersect($other); // Enumerators common to both this and other (this & other) $enumSet->setDiff($other); // Enumerators in this but not in other (this - other) $enumSet->setSymDiff($other); // Enumerators in either this and other but not in both (this ^ other) // ... the immutable interface will produce a new set $enumSet = $enumSet->withUnion($other); // Enumerators from both this and other (this | other) $enumSet = $enumSet->withIntersect($other); // Enumerators common to both this and other (this & other) $enumSet = $enumSet->withDiff($other); // Enumerators in this but not in other (this - other) $enumSet = $enumSet->withSymDiff($other); // Enumerators in either this and other but not in both (this ^ other)
EnumMap
一个 EnumMap
将相同类型的枚举器映射到分配的数据。
它实现了 ArrayAccess
、Countable
和 IteratorAggregate
,因此可以使用 $enumMap[$key]
、foreach
和 count()
像正常数组一样访问、迭代和计数。
use MabeEnum\EnumMap; // create a new EnumMap $enumMap = new EnumMap('UserStatus'); // read and write key-value-pairs like an array $enumMap[UserStatus::INACTIVE] = 'inaktiv'; $enumMap[UserStatus::ACTIVE] = 'aktiv'; $enumMap[UserStatus::DELETED] = 'gelöscht'; $enumMap[UserStatus::INACTIVE]; // 'inaktiv'; $enumMap[UserStatus::ACTIVE]; // 'aktiv'; $enumMap[UserStatus::DELETED]; // 'gelöscht'; isset($enumMap[UserStatus::DELETED]); // true unset($enumMap[UserStatus::DELETED]); isset($enumMap[UserStatus::DELETED]); // false // ... no matter if you use enumerator values or enumerator objects $enumMap[UserStatus::INACTIVE()] = 'inaktiv'; $enumMap[UserStatus::ACTIVE()] = 'aktiv'; $enumMap[UserStatus::DELETED()] = 'gelöscht'; $enumMap[UserStatus::INACTIVE()]; // 'inaktiv'; $enumMap[UserStatus::ACTIVE()]; // 'aktiv'; $enumMap[UserStatus::DELETED()]; // 'gelöscht'; isset($enumMap[UserStatus::DELETED()]); // true unset($enumMap[UserStatus::DELETED()]); isset($enumMap[UserStatus::DELETED()]); // false // count number of attached elements $enumMap->count(); count($enumMap); // test for elements $enumMap->isEmpty(); // support for null aware exists check $enumMap[UserStatus::NULL] = null; isset($enumMap[UserStatus::NULL]); // false $enumMap->has(UserStatus::NULL); // true // iterating over the map foreach ($enumMap as $enum => $value) { get_class($enum); // UserStatus (enumerator object) gettype($value); // mixed (the value the enumerators maps to) } // get a list of keys (= a list of enumerator objects) $enumMap->getKeys(); // get a list of values (= a list of values the enumerator maps to) $enumMap->getValues();
序列化
因为这个枚举实现基于单例模式,在PHP中目前无法在不创建新实例的情况下反序列化单例,所以这个特性不支持,除非进行额外的工作。
由于这是一个经常请求的功能,您可以在枚举定义中添加一个特性。该特性增加了序列化功能,并在是第一个实例的情况下注入反序列化的枚举实例。这减少了单例行为中断,但如果不它是第一个实例,仍可能导致相同的枚举有两个不同的实例。
请谨慎使用!
PS:只要您没有设置其他非序列化值,EnumSet
和 EnumMap
默认可序列化。
使用 EnumSerializableTrait 的示例
use MabeEnum\Enum; use MabeEnum\EnumSerializableTrait; use Serializable; class CardinalDirection extends Enum implements Serializable { use EnumSerializableTrait; const NORTH = 'n'; const EAST = 'e'; const WEST = 'w'; const SOUTH = 's'; } $north1 = CardinalDirection::NORTH(); $north2 = unserialize(serialize($north1)); var_dump($north1 === $north2); // returns FALSE as described above var_dump($north1->is($north2)); // returns TRUE - this way the two instances are treated equal var_dump($north2->is($north1)); // returns TRUE - equality works in both directions
泛型和静态代码分析器
在版本 4.3 中,我们增加了对泛型的支持,并增加了更好的类型支持。
EnumSet<T of Enum>
EnumMap<T of Enum>
此外,我们开发了一个 PHPStan 扩展,以使枚举访问器方法已知。
为什么不使用 SplEnum
SplEnum
不是 PHP 内置的,需要安装 pecl 扩展。- 相同的
SplEnum
值的实例不是相同的实例。 - 不支持
EnumMap
或EnumSet
。
变更日志
变更记录在 发布页面 上。
安装
Composer
将 marc-mabe/php-enum
添加到项目的 composer.json 依赖项中,并运行 php composer.phar install
GIT
git clone git://github.com/marc-mabe/php-enum.git
ZIP / TAR
从 Github 下载最新版本并解压缩。
版本和发布
本项目遵循 SemVer 规范。
没有 LTS 发布,我们没有基于时间的发布窗口。相反,根据需要发布。
我们至少支持所有维护的 PHP 版本。
错误修复将被回溯到最新的维护次要版本。
关键错误修复和安全相关修复也可以回溯到旧版本。
新 BSD 许可证
此存档中的文件根据新 BSD 许可证发布。您可以在 LICENSE.txt 文件中找到此许可证的副本。