Я хотел бы получить все экземпляры объекта определенного класса.
Например:
class Foo { } $a = new Foo(); $b = new Foo(); $instances = get_instances_of_class('Foo');
$instances
должен быть либо array($a, $b)
либо array($b, $a)
(порядок не имеет значения).
Плюсом является то, что функция вернет экземпляры с суперклассом запрошенного класса, хотя это необязательно.
Один из методов, о котором я могу думать, – это использовать статическую переменную-член класса, которая содержит массив экземпляров. В конструкторе класса и деструкторе я бы добавил или удалл $this
из массива. Это довольно хлопотно и подвержено ошибкам, если я должен делать это на многих классах.
Если вы выведете все объекты из класса TrackableObject, этот класс может быть настроен для обработки таких вещей (просто убедитесь, что вы вызываете parent::__construct()
и parent::__destruct()
при перегрузке в подклассах.
class TrackableObject { protected static $_instances = array(); public function __construct() { self::$_instances[] = $this; } public function __destruct() { unset(self::$_instances[array_search($this, self::$_instances, true)]); } /** * @param $includeSubclasses Optionally include subclasses in returned set * @returns array array of objects */ public static function getInstances($includeSubclasses = false) { $return = array(); foreach(self::$_instances as $instance) { if ($instance instanceof get_class($this)) { if ($includeSubclasses || (get_class($instance) === get_class($this)) { $return[] = $instance; } } } return $return; } }
Основная проблема заключается в том, что никакой объект не будет автоматически загружен сборкой мусора (поскольку ссылка на него все еще существует в TrackableObject::$_instances
), поэтому __destruct()
нужно будет вызывать вручную для уничтожения указанного объекта. (Circular Reference Garbage Collection была добавлена в PHP 5.3 и может содержать дополнительные возможности сбора мусора)
Вот возможное решение:
function get_instances_of_class($class) { $instances = array(); foreach ($GLOBALS as $value) { if (is_a($value, $class) || is_subclass_of($value, $class)) { array_push($instances, $value); } } return $instances; }
Изменить : обновлен код, чтобы проверить, является ли $class
суперклассом.
Редактировать 2 : Создал немного более грязную рекурсивную функцию, которая проверяет переменные каждого объекта вместо объектов верхнего уровня:
function get_instances_of_class($class, $vars=null) { if ($vars == null) { $vars = $GLOBALS; } $instances = array(); foreach ($vars as $value) { if (is_a($value, $class)) { array_push($instances, $value); } $object_vars = get_object_vars($value); if ($object_vars) { $instances = array_merge($instances, get_instances_of_class($class, $object_vars)); } } return $instances; }
Я не уверен, может ли он перейти в бесконечную рекурсию с определенными объектами, поэтому будьте осторожны …
Мне нужно это, потому что я создаю систему событий и должен иметь возможность отправлять события ко всем объектам определенного класса (глобальное уведомление, если хотите, которое динамически связано).
Я хотел бы предложить отдельный объект, где вы регистрируете объекты с помощью ( Шаблон наблюдателя ). PHP имеет встроенную поддержку для этого через spl; См. SplObserver и SplSubject .
Насколько я знаю, среда выполнения PHP не раскрывает базовое пространство объектов, поэтому было бы невозможно запросить ее для экземпляров объекта.