2

我正在努力使用全局状态来存储有关 PHP 中类元数据的运行时信息。由于显而易见的原因,全局状态被认为是“坏的”,所以我想尽可能地避免它。虽然,我仍然不知道在哪些情况下它是真正可以接受的,在哪些情况下不是。为了提供一个具体的例子,让我们以 PHP 中的 Enum 类型实现为例,其中每个具体的 Enum 类型都扩展了一个抽象 Enum 类型,它使用反射将子类的常量读取为有效的 Enum 值。

<?php

abstract class AbstractEnum
{
    /**
     * The constants currently resolved.
     *
     * Constants are resolved as flyweights into global state,
     * therefore they have to be distinguished by the class name they
     * refer to in this array.
     *
     * E.g.
     *
     * array(
     *     'My\Full\Qualified\Enum\Class' => array(
     *         'CONST1' => 'value1',
     *         'CONST2' => 'value2'
     *     ),
     *     'My\Other\Full\Qualified\Enum\Class' => array(
     *         'CONST1' => 'value1',
     *         'CONST2' => 'value2'
     *     )
     * )
     *
     * @var array
     */
    private static $constants = array();

    /**
     * The enumeration value of this instance.
     *
     * @var mixed
     */
    private $value;

    /**
     * Constructor.
     *
     * @param mixed $value The enumeration value of this instance.
     *
     * @throws \UnexpectedEnumValueException if given value is not defined
     *                                                                        in the enumeration class.
     */
    final public function __construct($value)
    {
        $values = static::getValues();

        if (!in_array($value, $values, true)) {
            throw new \UnexpectedEnumValueException($value, $values, get_called_class());
        }

        $this->value = $value;
    }

    /**
     * Returns the enumeration value of this instance as string representation.
     *
     * @return string
     */
    final public function __toString()
    {
        return (string) $this->value;
    }

    /**
     * Returns the enumeration value of this instance.
     *
     * @return mixed
     */
    final public function getValue()
    {
        return $this->value;
    }

    /**
     * Returns all enumeration values defined in this class.
     *
     * @return array
     */
    final public static function getValues()
    {
        $class = get_called_class();

        // Resolve class constants as flyweights.
        if (!isset(self::$constants[$class])) {
            self::resolveConstants($class);
        }

        return self::$constants[$class];
    }

    /**
     * Resolves the constants of a given full qualified class name.
     *
     * @param string $class The full qualified class name to resolve the constants for.
     */
    private static function resolveConstants($class)
    {
        $reflectionClass = new \ReflectionClass($class);
        self::$constants[$class] = $reflectionClass->getConstants();
    }
}

class Month extends AbstractEnum
{
    const JANUARY = 1;
    const FEBRUARY = 2;
    // ...
}

$month = new Month(Month::JANUARY);

我在这里看到的可能问题是 resolveConstants() 方法,它使用反射来解析子类的常量。考虑在运行时必须创建大量 Enum 实例的情况。在每个实例上使用反射可能会对性能产生严重影响,因此仅在某个 Enum 类型的第一个实例上“延迟加载”类元数据似乎是一种好方法。但是我必须使用全局状态才能完成,这似乎也是错误的。也许这不是最好的例子,但它表明了我对性能问题的关注。可能还有其他用例,必须广泛使用反射和类元数据自省才能将对象设置为状态。一般来说,拥有这种全球性状态是否可以接受,或者是否有其他选择可以实现这样的目标?我不喜欢使用单独的类来提供类元数据,因为像 Enum 这样的简单值对象的依赖注入似乎是开销。

4

0 回答 0