cloudstek / php-enum
PHP 的枚举支持
Requires
- php: >=7.1
Requires (Dev)
- php-coveralls/php-coveralls: ^2.2
- squizlabs/php_codesniffer: ^3.5
- symfony/phpunit-bridge: ^5.0
- vimeo/psalm: ^3.8
This package is auto-updated.
Last update: 2024-09-18 20:50:10 UTC
README
PHP 的枚举支持。
此包为 PHP 添加了对 枚举 的支持,遗憾的是 PHP 本身并没有原生支持。
⚠️ 自 PHP 8.1 以来,PHP 终于原生支持了枚举。如果您需要使用枚举,请考虑升级到 PHP 8.1+ 并迁移离开此包。
仅使用具有常量的简单类无法让您使用类型提示,这意味着您仍然需要进行大量的检查以确定值是否预期。此包允许您以相同的方式定义枚举,但允许对方法参数等使用类型提示。这样,您可以始终确保它包含一组具体的成员和值。
要求
- PHP 7.1+
- Composer*
* 由于该包仅由一个类组成,因此可以不使用 Composer 进行安装,但这显然不推荐。
安装
通过 composer 安装该包
composer require cloudstek/php-enum
使用方法
定义
Cloudstek\Enum\Enum
基类负责处理所有幕后工作,因此您只需从该枚举类扩展您的枚举类,并使用属性、常量、方法或这些的组合来定义您的成员。
例如,这个具有三个成员 TODO
、IN_PROGRESS
和 DONE
的 TaskStatus
枚举。在示例中,每个成员都有一个字符串值,但您可以自由分配任何您喜欢的值。
use Cloudstek\Enum\Enum; /** * @method static self TODO() * @method static self IN_PROGRESS() * @method static self DONE() */ class TaskStatus extends Enum { private const TODO = 'todo'; private const IN_PROGRESS = 'in_progress'; private const DONE = 'done'; }
文档类型仅用于 IDE 中的自动完成,对于枚举的功能不是必需的。
请确保将您的成员定义为 private
或 protected
,以避免混淆,直接访问成员值而不是实例,这可能导致在您的代码期望实例而不是值时(如以下示例所示)引发异常。
TaskStatus::TODO !== TaskStatus::TODO()
class Task { /** @var TaskStatus */ private $status; /** * Set status * * @param TaskStatus $status */ public function setStatus(TaskStatus $status) { $this->status = $status; } // .. }
或者,如果您需要更灵活,get
方法将智能地通过名称返回成员,或者如果提供了一个对象,检查它是否是正确的类型。
class Task { /** @var TaskStatus */ private $status; /** * Set status * * @param TaskStatus|string $status * * @throws \UnexpectedValueException On unknown status. */ public function setStatus($status) { $this->status = TaskStatus::get($status); } // .. }
有关如何定义您的成员以及如何命名它们的更多信息,请参阅 docs/definition.md。
比较
由于枚举每个成员都对应一个单独的实例,因此您可以直接比较它们。
// Compare by instance TaskStatus::TODO() === TaskStatus::TODO(); // true TaskStatus::TODO() === TaskStatus::get('todo'); // true TaskStatus::get('TODO') === TaskStatus::get('todo'); // true TaskStatus::TODO() === TaskStatus::get(TaskStatus::TODO()) // true TaskStatus::TODO() === TaskStatus::DONE(); // false TaskStatus::TODO() === TaskStatus::get('done'); // false // Compare by value (string) TaskStatus::TODO() === 'todo'; // true TaskStatus::TODO()->getValue() === 'todo'; // true
继承
您应该始终将枚举定义为 final
类,以防止其他类从它继承。如果您希望其他类继承它,请考虑将其定义为 abstract
,并编写继承自它的 final
实例类。
如果不将其定义为最终类,则您的代码可能会接受继承的枚举,而实际上您期望的是基类。这可能会导致糟糕的 bug。
例如,考虑以下枚举
use Cloudstek\Enum\Enum; class FooEnum extends Enum { private const FOO = 'foo'; } class BarEnum extends FooEnum { private const BAR = 'bar'; }
如果不将 FooEnum
定义为最终类,则您的代码可能会无意中接受 BarEnum
,尽管它期望的是 FooEnum
。
class Foo { public function doSomething(FooEnum $foo) { // Do something... } } $foo = new Foo(); $foo->doSomething(FooEnum::FOO()); // Allowed and OK, we were expecting FooEnum $foo->doSomething(BarEnum::BAR()); // Allowed but not OK, we got BarEnum!
为了防止这种情况,并确保我们始终得到 FooEnum
,我们应该将其标记为最终类。这并不意味着它不能继承其他任何内容。
use Cloudstek\Enum\Enum; abstract class BaseEnum extends Enum { private const HELLO = 'world'; } final class FooEnum extends BaseEnum { private const FOO = 'foo'; } final class BarEnum extends BaseEnum { private const BAR = 'bar'; }
现在我们确保只得到 FooEnum
的实例。
class Foo { public function doSomething(FooEnum $foo) { // Do something... } } $foo = new Foo(); $foo->doSomething(FooEnum::FOO()); // Allowed and OK, we were expecting FooEnum $foo->doSomething(BarEnum::BAR()); // Fatal error
但是,如果我们真的不在乎,只要它的基类型是 BaseEnum
,我们必须明确将参数类型更改为 BaseEnum
,如下所示
class Foo { public function doSomething(BaseEnum $foo) { // Do something... } } $foo = new Foo(); $foo->doSomething(FooEnum::FOO()); // OK $foo->doSomething(BarEnum::BAR()); // OK
存储数据
如果您存储了包含枚举类型的数据,并且希望稍后将其转换回枚举,请确保使用 getName()
存储成员名称,而不是存储其值。如果您只关心值,可以直接使用 getValue()
存储值,或者在可能的情况下将其转换为字符串。
// Update task $status = TaskStatus::TODO(); $db->update($task, [ 'status' => $status->getName() // 'status' => 'todo' ]);
// Fetch task $taskRow = $db->tasks->fetchOne(13); // [..., 'status' => 'todo', ...] $task = new Task(); // .. $task->setStatus(TaskStatus::get($taskRow['status'])); // or if you call TaskStatus::get() in Task::setStatus() $task->setStatus($taskRow['status']);
支持
您可以通过贡献到开放问题、提交拉取请求、给这个项目一个 ⭐ 或告诉您的朋友关于它来支持这个项目。
如果您有任何想法或问题,请打开一个问题!