nelexa / enum

PHP Enum 实现

1.1.1 2020-11-27 19:17 UTC

This package is auto-updated.

Last update: 2024-08-26 03:52:06 UTC


README

Packagist Version Packagist Build Status Scrutinizer Code Quality Code Coverage Build Status License

目录

安装

composer require nelexa/enum

Enum 声明

<?php

use Nelexa\Enum;

/**
 * @method static self PENDING()
 * @method static self ACTIVE()
 * @method static self INACTIVE()
 * @method static self DELETED()
 */
class UserStatus extends Enum
{
    public const
        PENDING = 1,
        ACTIVE = 1 << 1,
        INACTIVE = 1 << 2,
        DELETED = 1 << 3;
}

使用

$enum = UserStatus::ACTIVE();

assert($enum instanceof UserStatus);
assert($enum->name() === 'ACTIVE');
assert($enum->value() === 1 << 1);
assert($enum->ordinal() === 1);

遍历 enum 对象

foreach (UserStatus::values() as $userStatus) {
    printf('User status: %s, status id: %d, ordinal: %d' . PHP_EOL,
        $userStatus->name(),
        $userStatus->value(),
        $userStatus->ordinal()
    );
}

输出

User status: PENDING, status id: 1, ordinal: 0
User status: ACTIVE, status id: 2, ordinal: 1
User status: INACTIVE, status id: 4, ordinal: 2
User status: DELETED, status id: 8, ordinal: 3

比较 enum 值时,使用 === 操作符

$userStatus = UserStatus::DELETED();
if ($userStatus === UserStatus::DELETED()) {
    echo 'User status: ' . $userStatus->name() . PHP_EOL;
}

将字符串转换为 enum 对象

$enum = UserStatus::valueOf('ACTIVE');

assert(UserStatus::ACTIVE() === UserStatus::valueOf('ACTIVE'));

将值转换为 enum 对象

$enum = UserStatus::valueOf('ACTIVE');
$value = $enum->value();

assert(UserStatus::fromValue($value) === $enum);
assert(UserStatus::fromValue($value) === UserStatus::valueOf('ACTIVE'));

switch case

$enum = UserStatus::PENDING();
switch ($enum) {
    case UserStatus::ACTIVE():
        echo 'Active status';
        break;
    case UserStatus::PENDING():
        echo 'Pending status';
        break;
    case UserStatus::DELETED():
        echo 'Delete status';
        break;
    case UserStatus::INACTIVE():
        echo 'Inactive status';
        break;
    default:
        throw new \RuntimeException('Invalid value');
}

输出

Pending status

在类型提示中使用 enum

/**
 * @var UserStatus
 */
private $status;

public function setStatus(UserStatus $status): void
{
    $this->status = $status;
}

public function getStatus(): UserStatus
{
    return $this->status;
}

public function isActive(): bool
{
    return $this->status === UserStatus::ACTIVE();
}

示例

$user->setStatus(UserStatus::INACTIVE());
echo sprintf('User status is %s.' . PHP_EOL, $user->getStatus()->name());

输出

User status is INACTIVE

在 enum 中添加一些逻辑

<?php

/**
 * @method static self PLUS()
 * @method static self MINUS()
 * @method static self TIMES()
 * @method static self DIVIDE()
 */
class Operation extends \Nelexa\Enum
{
    private const
        PLUS = null,
        MINUS = null,
        TIMES = null,
        DIVIDE = null;

    /**
     * Do arithmetic op represented by this constant
     *
     * @param float $x
     * @param float $y
     * @return float
     */
    public function calculate(float $x, float $y): float
    {
        switch ($this) {
            case self::PLUS():
                return $x + $y;
            case self::MINUS():
                return $x - $y;
            case self::TIMES():
                return $x * $y;
            case self::DIVIDE():
                return $x / $y;
        }
        throw new \AssertionError('Unknown op: ' . $this->name());
    }
}

示例

echo Operation::PLUS()->calculate(4, 2);   // 6
echo Operation::TIMES()->calculate(4, 2);  // 8
echo Operation::MINUS()->calculate(4, 2);  // 2
echo Operation::DIVIDE()->calculate(4, 2); // 2

无构造函数初始化值

例如,考虑太阳系的行星。每个行星都知道其质量和半径,可以计算其表面重力和物体在行星上的重量。

以下是它的样子

<?php
declare(strict_types=1);

use Nelexa\Enum;

/**
 * Class Planet
 *
 * @method static self MERCURY()
 * @method static self VENUS()
 * @method static self EARTH()
 * @method static self MARS()
 * @method static self JUPITER()
 * @method static self SATURN()
 * @method static self URANUS()
 * @method static self NEPTUNE()
 * @method static self PLUTO()
 *
 * @see https://docs.oracle.com/javase/8/docs/technotes/guides/language/enums.html
 */
class Planet extends Enum
{
    private const
        MERCURY = [3.303e+23, 2.4397e6],
        VENUS = [4.869e+24, 6.0518e6],
        EARTH = [5.976e+24, 6.37814e6],
        MARS = [6.421e+23, 3.3972e6],
        JUPITER = [1.9e+27, 7.1492e7],
        SATURN = [5.688e+26, 6.0268e7],
        URANUS = [8.686e+25, 2.5559e7],
        NEPTUNE = [1.024e+26, 2.4746e7],
        PLUTO = [1.27e+22, 1.137e6];

    /**
     * @var double universal gravitational constant (m3 kg-1 s-2)
     */
    private static $G = 6.67300E-11;

    /**
     * @var double in kilograms
     */
    private $mass;
    /**
     * @var double in meters
     */
    private $radius;

    /**
     * In this method, you can initialize additional variables based on the
     * value of the constant. The method is called after the constructor.
     *
     * @param string|int|float|bool|array|null $value the enum scalar value of the constant
     */
    protected function initValue($value): void
    {
        [$this->mass, $this->radius] = $value;
    }

    public function mass(): float
    {
        return $this->mass;
    }

    public function radius(): float
    {
        return $this->radius;
    }

    public function surfaceGravity(): float
    {
        return self::$G * $this->mass / ($this->radius * $this->radius);
    }

    public function surfaceWeight(float $otherMass): float
    {
        return round($otherMass * $this->surfaceGravity(), 6);
    }
}

枚举 Planet 类包含 initValue($value) 方法,并且每个枚举常量都声明了一个要传递给此方法的值,当它被创建时。

以下是一个示例程序,它接受你在地球上的体重(以任何单位),并计算并打印你在所有行星上的体重(以相同的单位)。

$earthWeight = 175;
$mass = $earthWeight / Planet::EARTH()->surfaceGravity();
foreach (Planet::values() as $p) {
    printf("Your weight on %s is %f\n", $p->name(), $p->surfaceWeight($mass));
}

输出

Your weight on MERCURY is 66.107583
Your weight on VENUS is 158.374842
Your weight on EARTH is 175.000000
Your weight on MARS is 66.279007
Your weight on JUPITER is 442.847567
Your weight on SATURN is 186.552719
Your weight on URANUS is 158.397260
Your weight on NEPTUNE is 199.207413
Your weight on PLUTO is 11.703031

类概览

abstract class Nelexa\Enum {

    /* Methods */
    final public static valueOf ( string $name ) : static
    final public name ( void ) : string
    final public value ( void ) : string | int | float | bool | array | null
    final public static values ( void ) : static[]
    final public static containsKey ( string $name ) : bool
    final public static containsValue ( mixed $value [, bool $strict = true ] ) : bool
    final public static function fromValue( mixed $value ): static
    final public ordinal ( void ) : int
    public __toString ( void ) : string
    protected static function getEnumConstants(): array
}

使用技巧

  • 尽管不是强制要求用 大写字母 声明枚举常量,但这是最佳实践。
  • 枚举类可以具有字段、方法以及枚举常量。
  • 枚举构造函数是 私有的。枚举类只允许使用私有构造函数。这就是为什么你不能使用 new 操作符实例化枚举类型的原因。
  • 枚举常量在整个执行过程中只创建一次。所有枚举常量都是在首次在代码中引用任何枚举常量时创建的。
  • 枚举类型可以实现任何数量的接口。
  • 我们可以使用 === 操作符比较枚举常量。
  • 你可以使用 values() 静态方法检索任何枚举类型的枚举常量。该 values() 静态方法返回一个枚举常量的数组。
  • 使用 ordinal() 静态方法获取枚举常量在枚举类型中的顺序。
  • 当您想要允许一组有限且在整个执行过程中保持不变的选项,并且您知道所有可能的选项时,通常使用枚举。例如,这可以是 菜单选项组合框选项
  • 使用私有常量排除 IDE 中的提示。
  • 使用 PHPDoc 描述静态枚举初始化方法。

为 enum 类生成 PHPDoc

要使用 IDE 中的提示,您需要在类上声明特殊注释,列出静态方法列表。

应该使用 \Nelexa\enum_docblock($enumOrEnumClass) 函数来帮助实现这一点。

echo \Nelexa\enum_docblock(Planet::class);
// or
echo \Nelexa\enum_docblock(Planet::MERCURY());

输出

/**
 * @method static self MERCURY()
 * @method static self VENUS()
 * @method static self EARTH()
 * @method static self MARS()
 * @method static self JUPITER()
 * @method static self SATURN()
 * @method static self URANUS()
 * @method static self NEPTUNE()
 * @method static self PLUTO()
 */

变更日志

变更记录在 发布页面 中。

许可证

该存档中的文件在 MIT 许可证 下发布。

您可以在 LICENSE 文件中找到此许可证的副本。