tags:

views:

870

answers:

4

I would like to get all the instances of an object of a certain class.

For example:

class Foo {
}

$a = new Foo();
$b = new Foo();

$instances = get_instances_of_class('Foo');

$instances should be either array($a, $b) or array($b, $a) (order does not matter).

A plus is if the function would return instances which have a superclass of the requested class, though this isn't necessary.

One method I can think of is using a static class member variable which holds an array of instances. In the class's constructor and destructor, I would add or remove $this from the array. This is rather troublesome and error-prone if I have to do it on many classes.

Thanks!

A: 

AFAIK, the PHP runtime does not expose the underlying object space, so it would not be possible to query it for instances of an object.

Cody Caughlan
Do you have any sources to confirm this?
strager
+2  A: 

Here's a possible solution:

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;
}

Edit: Updated the code to check if the $class is a superclass.

Edit 2: Made a slightly messier recursive function that checks each object's variables instead of just the top-level objects:

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;
}

I'm not sure if it can go into infinite recursion with certain objects, so beware...

yjerem
This doesn't work as well as I would like. Most of my instances are members of some other classes.
strager
I've updated my answer with a recursive function that looks for instances in all the members of all objects (and all the members of those members, ...).
yjerem
There's another problem: temporary variables. I don't think you can cover all corners with $GLOBALS, which would introduce unexpected bugs most likely. I'm sure your method would be useful for someone else, though, with less specific needs.
strager
+4  A: 

If you derive all your objects from a TrackableObject class, this class could be set up to handle such things (just be sure you call parent::__construct() and parent::__destruct() when overloading those in subclasses.

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;
    }
}

The major issue with this is that no object would be automatically picked up by garbage collection (as a reference to it still exists within TrackableObject::$_instances), so __destruct() would need to be called manually to destroy said object.

J. Kenzal Hunter Sr.
This was basically my initial idea. I'll try and implement it.
strager
Question: wouldn't getInstances be static?
strager
Yes, it would, it was quite late when I typed this up, but you are indeed correct, Sir.
J. Kenzal Hunter Sr.
+1  A: 

I need this because I am making an event system and need to be able to sent events to all objects of a certain class (a global notification, if you will, which is dynamically bound).

I would suggest having a separate object where you register objects with (An observer pattern). PHP has built-in support for this, through spl; See: SplObserver and SplSubject.

troelskn
I would like a system more like Qt's signals and slots. The behavior I want in particular is as such: I have ChatRoom classes, which fire an event messageSent when someone sends a message. I would like a MessageFilter class to filter ANY message sent through ANY ChatRoom.
strager
So you want a global observer? You can either create a global variable, or you can pass the same instance to all ChatRoom's
troelskn