4

I have a class with a method that returns a new instance of itself. This allows me to use one single object without having to declare new ones over and over with the same values. For those wondering why not extending the class or something else, it just makes my life easier and it works like a charm, but my final purpose is to remove them all in one line.

for example:

$foo = new myClass('name1','ab','cd');
$bar = $foo->duplicate();

//duplicate() saves the new object in itself in an array:
array_push($this->instance,new myClass('name1','ab','cd'));
//and returns a pointer to the saved instance.
return end($this->instance);

$foo and $bar now share the same methods and values but those values can be modified separately.

$foo->changeName('newName'); 

$bar->changeName('newName2');

My question here is, if I unset the first class created unset($foo) will PHP automatically unset the other instances ($bar) or will the garbage collector remove them eventually?

I have tested it by unsettling $foo and gives me an error when I call $foo but not when I call $bar.

What I am trying to do is to unset all the instances of that class at once.

Thanks.

multimediaxp
  • 9,348
  • 13
  • 49
  • 80
  • 2
    If you want deterministic destruction of a 'collection' of objects, then keep them in a real collection of some sort and `unset` them all at once (in a loop). – David-SkyMesh Jan 10 '14 at 07:44

3 Answers3

3

Automatic cleanup?

No, PHP isn't awared about your architecture. It will not remove objects "cascade", they are independent entities - and, more, can belong to different scopes, for example. Simple code:

class Test
{
   protected $id = null;
   protected $uniqid = null;

   public function __construct($id)
   {
      $this->id = $id;//user-passed variable
      $this->uniqid = uniqid();//internal: to identify instance 
   }

   public function getCopy()
   {
      return new self($this->id);
   }

   public function getIdentity()
   {
      return $this->uniqid;
   }
}

$foo = new Test(3);
$bar = $foo->getCopy();
var_dump($foo->getIdentity());//valid

unset($foo);
//still valid: bar has nothing to do with foo
var_dump($bar->getIdentity());

By the way, for copying you can use clone in PHP (that, however, will result in object cloning, obviously)

Simple way

Most simple way to resolve a matter is to iterate through $GLOBALS, checking it with instanceof. This has serious weakness: inner function/method scopes would not be affected:

//static since doesn't belong to any instance:
public static function cleanup()
{
   foreach($GLOBALS as $name=>$var)
   {
      if($var instanceof self)
      {
         unset($GLOBALS[$name]);
      }
   }
}

-and

$foo = new Test(3);
$bar = $foo->getCopy();
var_dump($foo->getIdentity(), $bar->getIdentity());//valid
Test::cleanup();
//2 x notice:
var_dump($foo, $bar);

Note, that is has nothing to do with "child" mechanics (i.e. it will clean all instances in global scope - no matter which was copied from which).

Common case

Sample above will not do the stuff in common case. Why? Imagine that you'll have holder class:

class Holder
{
   protected $obj = null;

   public function __construct($obj)
   {
      $this->obj = $obj;
   }

   public function getData()
   {
      return $this->obj;
   }
}

and you'll pass instance to it:

$foo = new Test(3);
$bar = $foo->getCopy();
$baz = new Holder($bar);

-so then you'll have no chances to handle even this simple situation in common case. And with more complex situations you will also be stuck.

What to do?

I'd recommend: destroy objects explicitly when you need to do that. Implicit unset is a side-effect, and even if you'll maintain that somehow (I can imagine Observer pattern + some global registry for that) - it will be horrible side-effect, that will kill readability for your code. And same is about code, that uses $GLOBALS I've written above - I do not recommend to act such way in any case.

Alma Do
  • 37,009
  • 9
  • 76
  • 105
  • Thanks a lot, this looks very useful and well explained, quick question, just because I want to keep what I have done so far, if not I do understand it is not possible this way: What if I return a pass reference or pointer? &$this->instance? Obviously if this works Holder($bar) will return an error, but the idea is to be careful and know what we are doing. Thanks! – multimediaxp Jan 10 '14 at 13:39
  • It still is unreliable because you can't be sure what's going on inside class/function. May be it's copying data somehow? (via `clone`, for example). In common case you'll __loose__ tracking - that's for sure – Alma Do Jan 10 '14 at 13:41
0

From what I understand from the PHP reference here :

http://www.php.net/manual/en/features.gc.refcounting-basics.php

destroying the main instance will not automatically destroy all other instances since they do not ultimately point to the same 'zval'.

However the garbage collection will eventually destroy all instances if they are not referenced anymore. Since in your example $bar still references the second instance, it will not be destroyed.

What you could do if you want to unset them all at the same time :

  1. Use a static array referencing all the instances of your object
  2. Every time you create a new object add a reference to this object in the static array of the class
  3. Use a static function unsetAll() which loops through this array and unset one by one all instances
  • I forgot to mention that all the instances are saved in the main object and returns a pointer to the saved instance. – multimediaxp Jan 10 '14 at 07:44
  • I am not sure, as I mentioned before it doesn't give me an error when calling the new instances after unsettling the main object. Look at my question I added more specific code example. Thanks! – multimediaxp Jan 10 '14 at 07:53
  • My bad, poor explanation skills, edited the answer for more clarity – Arthur Hemery Jan 10 '14 at 08:23
0

Try the below code to clear all php objects.

public function clearAllVars() 
   { 
      $vars = get_object_vars($this); 
      foreach($vars as $key => $val) 
      { 
         $this->$key = null; 
      } 
   } 
}