Глобальное состояние PHP приемлемо для информации времени выполнения, такой как метаданные класса?
Я борюсь с использованием глобального состояния для хранения информации времени выполнения о метаданных класса в PHP. Поскольку глобальное состояние считается "плохим" по очевидным причинам, я хотел бы избегать его как можно больше. Хотя я до сих пор не знаю, в каких ситуациях это действительно приемлемо, а в каких нет. Чтобы привести конкретный пример, давайте возьмем реализацию типа Enum в PHP, где каждый конкретный тип 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);
Возможная проблема, которую я вижу здесь, - это метод resolConstants(), который использует отражение для разрешения констант подклассов. Рассмотрим ситуацию, когда огромное количество экземпляров Enum должно быть создано во время выполнения. Использование отражения здесь для каждого экземпляра может оказать серьезное влияние на производительность, поэтому "ленивая загрузка" метаданных класса только в первом экземпляре определенного типа Enum представляется хорошим подходом. НО я должен использовать глобальное состояние для этого, что тоже кажется неправильным. Возможно, это не лучший пример, но он демонстрирует мое беспокойство по поводу производительности. Могут быть и другие случаи использования, когда необходимо широко использовать самоанализ рефлексии и метаданных класса, чтобы привести объект в состояние. Вообще говоря, допустимо ли иметь такое состояние глобализма или есть ли альтернатива для достижения такой цели? Мне не нравится использовать отдельный класс, который предоставляет метаданные класса в такой ситуации, когда внедрение зависимостей для простых объектов значений, таких как Enum, кажется чрезмерным.