Получить все экземпляры класса в PHP
Я хочу получить все экземпляры объекта определенного класса.
например:
class Foo {
}
$a = new Foo();
$b = new Foo();
$instances = get_instances_of_class('Foo');
$instances
должно быть array($a, $b)
или array($b, $a)
(порядок не имеет значения).
плюс, если функция будет возвращать экземпляры, которые имеют суперкласс запрошенного класса, хотя это не обязательно.
один из методов, о котором я могу думать, - это использование статической переменной-члена класса, которая содержит массив экземпляров. В классе конструктор и деструктор, я бы добавил или удалил $this
из массива. Это довольно хлопотно и подвержено ошибкам, если мне приходится делать это на многих классах.
4 ответов
если вы производите все свои объекты из класса 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()
необходимо будет вызвать вручную, чтобы уничтожить указанный объект. (Круговая справочная сборка мусора была добавлена в 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;
}
Я не уверен, что он может перейти в бесконечную рекурсию с определенными объектами, поэтому будьте осторожны...
мне это нужно, потому что я создаю систему событий и должен иметь возможность отправлять события всем объектам определенного класса (глобальное уведомление, если хотите, которое динамически связано).
Я бы предложил иметь отдельный объект, где вы регистрируете объекты с (шаблон Observer). PHP имеет встроенную поддержку для этого, через spl; см.:SplObserver и SplSubject.
насколько я знаю, среда выполнения PHP не предоставляет базовое пространство объектов, поэтому было бы невозможно запросить его для экземпляров объекта.