我正在努力使用全局状态来存储有关 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 这样的简单值对象的依赖注入似乎是开销。