ifornew/enum

PHP的枚举实现。

1.0.1 2021-09-02 03:14 UTC

This package is not auto-updated.

Last update: 2024-09-24 19:19:08 UTC


README

PHP的枚举实现。

安装和文档

什么是枚举?

在软件开发方面,枚举(或“枚举类型”)本质上是一组固定的值。这些值被称为“成员”或“元素”。

枚举用于希望允许参数仅是一组特定值之一的场景,并且任何其他内容都被视为无效。

基本示例

Enum 可以像 [C++ 枚举类型] 一样使用。以下是一个例子,表示一组 HTTP 请求方法

use Ifornew\Enum\AbstractEnumeration;

/**
 * Class HttpMethod
 
 * @method static HttpMethod OPTIONS
 * @method static HttpMethod GET
 * @method static HttpMethod HEAD
 * @method static HttpMethod POST
 * @method static HttpMethod PUT
 * @method static HttpMethod DELETE
 * @method static HttpMethod TRACE
 * @method static HttpMethod CONNECT
 */
final class HttpRequestMethod extends AbstractEnumeration
{
    const OPTIONS = 'OPTIONS';
    const GET = 'GET';
    const HEAD = 'HEAD';
    const POST = 'POST';
    const PUT = 'PUT';
    const DELETE = 'DELETE';
    const TRACE = 'TRACE';
    const CONNECT = 'CONNECT';
}

现在这个类可以在类型提示中使用,以便轻松接受任何有效的 HTTP 请求方法

function handleHttpRequest(HttpRequestMethod $method, $url, $body = null)
{
    // handle request...
}

访问枚举成员

成员通过静态方法调用进行访问,如下所示

handleHttpRequest(HttpRequestMethod::GET(), 'http://example.org/');
handleHttpRequest(HttpRequestMethod::POST(), 'http://example.org/', 'foo=bar&baz=qux');

对于枚举的每个成员,都会实例化一个枚举类的单个实例(即,在上面的例子中是 HttpRequestMethod 的实例)。这意味着可以使用严格的比较(===)来确定传递给函数的是哪个成员

function handleHttpRequest(HttpRequestMethod $method, $url, $body = null)
{
    if ($method === HttpRequestMethod::POST()) {
        // handle POST requests...
    } else {
        // handle other requests...
    }
}

Java风格的枚举

Java的枚举类型比C++枚举类型功能更丰富。它们可以有额外的属性和/或方法,并且实际上是一种特殊类,其中有一组固定的实例。

这有时被称为Multiton 模式,实际上,在这个实现中的所有枚举都是 Multiton。AbstractEnumeration 类仅基于类常量定义其成员。

以下是一个从Java枚举类型文档中借用的示例。以下multiton描述了我们的太阳系中的所有行星,包括它们的质量和半径

use Ifornew\Enum\AbstractMultiton;

final class Planet extends AbstractMultiton
{
    /**
     * Universal gravitational constant.
     *
     * @var float
     */
    const G = 6.67300E-11;

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

    /**
     * @param float $otherMass
     *
     * @return float
     */
    public function surfaceWeight($otherMass)
    {
        return $otherMass * $this->surfaceGravity();
    }

    protected static function initializeMembers()
    {
        new static('MERCURY', 3.302e23,  2.4397e6);
        new static('VENUS',   4.869e24,  6.0518e6);
        new static('EARTH',   5.9742e24, 6.37814e6);
        new static('MARS',    6.4191e23, 3.3972e6);
        new static('JUPITER', 1.8987e27, 7.1492e7);
        new static('SATURN',  5.6851e26, 6.0268e7);
        new static('URANUS',  8.6849e25, 2.5559e7);
        new static('NEPTUNE', 1.0244e26, 2.4764e7);
        // new static('PLUTO',   1.31e22,   1.180e6);
    }

    /**
     * @param string $key
     * @param float  $mass
     * @param float  $radius
     */
    protected function __construct($key, $mass, $radius)
    {
        parent::__construct($key);

        $this->mass = $mass;
        $this->radius = $radius;
    }

    private $mass;
    private $radius;
}

上面的类可以用来接受地球上的已知重量(以任何单位),并计算所有行星上的重量(以相同的单位)

$earthWeight = 175;
$mass = $earthWeight / Planet::EARTH()->surfaceGravity();

foreach (Planet::members() as $planet) {
    echo sprintf(
        'Your weight on %s is %f' . PHP_EOL,
        $planet,
        $planet->surfaceWeight($mass)
    );
}

如果执行上述脚本,它将产生以下类似的输出

Your weight on MERCURY is 66.107480
Your weight on VENUS is 158.422560
Your weight on EARTH is 175.000000
Your weight on MARS is 66.279359
Your weight on JUPITER is 442.677903
Your weight on SATURN is 186.513785
Your weight on URANUS is 158.424919
Your weight on NEPTUNE is 199.055584

枚举和类继承

定义枚举时,通常的目的是定义一组不应改变的有效值,至少在程序执行的生命周期内。

由于PHP没有内置的枚举支持,因此此库将枚举作为常规PHP类实现。但是,类允许进行比真正枚举所需的更多扩展。

例如,一个简单的枚举实现可能允许开发人员从上面的例子扩展 HttpRequestMethod 类(假设移除了 final 关键字)

class CustomHttpMethod extends HttpRequestMethod
{
    const PATCH = 'PATCH';
}

这种情况的问题在于,现在所有编写来仅期望在 HttpRequestMethod 中定义的HTTP方法的代码现在都受到了损害。任何人都可以扩展 HttpRequestMethod 以添加自定义值,本质上就消除了定义 HttpRequestMethod 的初衷。

此库提供了内置的保护来防止这种情况。尝试定义扩展另一个枚举的枚举将抛出异常,除非“基”枚举是抽象的。

抽象枚举

假设确实有必要扩展 HttpRequestMethod,进行的方式是定义一个抽象基类,然后扩展这个类以创建所需的具体枚举。

use Ifornew\Enum\AbstractEnumeration;

abstract class AbstractHttpRequestMethod extends AbstractEnumeration
{
    const OPTIONS = 'OPTIONS';
    const GET = 'GET';
    const HEAD = 'HEAD';
    const POST = 'POST';
    const PUT = 'PUT';
    const DELETE = 'DELETE';
    const TRACE = 'TRACE';
    const CONNECT = 'CONNECT';
}

final class HttpRequestMethod extends AbstractHttpRequestMethod {}

final class CustomHttpMethod extends AbstractHttpRequestMethod
{
    const PATCH = 'PATCH';
}

这样,当开发者为HttpRequestMethod使用类型提示时,他们根本不可能接收到'PATCH'方法

function handleHttpRequest(HttpRequestMethod $method, $url, $body = null)
{
    // only handles normal requests...
}

function handleCustomHttpRequest(
    CustomHttpRequestMethod $method,
    $url,
    $body = null
) {
    // handles normal requests, and custom requests...
}