4

Is it possible to unset (clean or un-instantiate) an object of a class by executing code of that particular class?

My scenario is one that, for some reason, I reach a point that the object has no meaning to exist anymore, so I want to make sure that is will not be used again by killing its reference in memory.

Here is a snippet example:

class Foo {
    public $bar = "doh!";
    function destroy() {
        // ???    
    }
}
$o = new Foo();
echo $o->bar . '\n';
$o->destroy();
echo $o->bar . '\n'; // I expect an error here...

If I do a "unset" command outside the class code, it works, obviously:

$o = new Foo();
unset($o);
echo $o->bar . '\n'; // PHP Notice:  Undefined property: Foo::$bar in ...

But I tried to do it inside the "destroy" method, by calling "unset", or creating a custom "__destruct", but none worked.

Any ideas are very welcome.

Cheers,

Jason
  • 15,017
  • 23
  • 85
  • 116
bruno.braga
  • 1,001
  • 12
  • 21

6 Answers6

6

Take into account that you can't explicitly destroy an object.

The ways to allow PHP Garbage Collector to act upon an object are:

$var = null; // set to null
unset($var); // unset

The object will stay there. However, if you unset the object and your script pushes PHP to the memory limits, the objects not needed will be garbage collected. I would go with unset() as it seams to have better performance (not tested but documented on one of the comments from the PHP official manual).

That said do keep in mind that PHP always destroys the objects as soon as the page is served. So this should only be needed on really long loops and/or heavy intensive pages.

Frankie
  • 24,627
  • 10
  • 79
  • 121
  • Using `unset` can **never** lead to better performance. It is always faster to not call the function rather than call it – zerkms Jan 19 '12 at 06:31
  • @zerkms unless your script is so memory intensive it will, at some point, require to clean up during execution. On that particular scenario... unset should be used. Nevertheless if you don't need it, you shouldn't use it as it will NOT speed things up. – Frankie Jan 19 '12 at 06:33
  • "unless your script is so memory intensive it will, at some point, require to clean up during execution" --- this all is about the fact that otherwise script will just not work at all. Using `unset` to free some memory != using `unset` to make the script faster. – zerkms Jan 19 '12 at 06:39
  • @zerkms agreed. But I'm confused, does the OP refer to speed? And on my question I do say "this should only be used on... etc". – Frankie Jan 19 '12 at 06:42
  • Performance isn't only about speed, but also about computer resources consumption ;) – meze Jan 19 '12 at 06:44
  • @meze: `unset` won't decrease current script's memory allocation – zerkms Jan 19 '12 at 06:48
  • @zerkms: but it will free memory. – meze Jan 19 '12 at 06:50
  • @meze: uhm, which memory? After that memory is collected by GC (not immediately after `unset`) - it is still allocated by php process, it will not be returned back to the kernel – zerkms Jan 19 '12 at 06:51
  • @zerkms: I meant the script can reuse allocated memory immediately after unset. PHP would need to allocate more memory -> worse performance without unset, wouldn't it? – meze Jan 19 '12 at 06:54
  • @meze, nop. The script won't reuse allocated memory **immediately**. That will only happen (and if) the garbage collector decides to act. It's all there in my answer. – Frankie Jan 19 '12 at 06:56
  • @Frankie: my experience says it will ;) `not tested but documented on one of the comments from the PHP official manual` is a poor argument – meze Jan 19 '12 at 06:58
  • @meze: memory for php is being allocated by reasonable big pieces. So you wouldn't experience any issues even if you initialize a lot of small variables. – zerkms Jan 19 '12 at 06:59
  • @meze, on the `not tested but documented...` I posted a link to the source where the test was written extensively. And do notice that the test refers to the difference between `unset` and `null`; which has little to do with your experiencing in *noticing* PHP freeing memory immediately. – Frankie Jan 19 '12 at 07:03
  • @zerkms: what small variables? – meze Jan 19 '12 at 07:07
  • @Frankie: I set memory_limit to 128M, this code: `$b = str_repeat('a', 131223422); /*unset($b);*/ $z = str_repeat('b', 131223422);` runs out of memory. If you uncomment unset, it will work. How can you explain that? – meze Jan 19 '12 at 07:08
  • @meze: The second assignment `$z = ...` just triggers GC to collect some garbage because of that assignment cannot be evaluated in the available memory space. If you changed your strings to 1-char length ones then in both cases you'd see the same memory consumption – zerkms Jan 19 '12 at 07:10
  • @meze I'm not exactly sure if we're on the same page. My answer depicts that exactly. Have you read it? – Frankie Jan 19 '12 at 07:11
  • @zerkms: [Example 1](http://www.php.net/manual/en/function.memory-get-usage.php) says the memory consumption will be different. But this function can trigger GC. Even if unset doesn't free memory immediately, it still helps to use less memory. – meze Jan 19 '12 at 07:17
  • @zerkms: http://ideone.com/HQtLX with unset, http://ideone.com/ps3bS without unset. they are different ;) – meze Jan 19 '12 at 07:28
  • @meze: it is the numbers from php perspective. If you checked the script's memory consumption from the OS perspective you'd see it is the same – zerkms Jan 19 '12 at 07:45
  • @zerkms: agree, on small variables yes. But what about heavy objects? – meze Jan 19 '12 at 07:58
  • @meze: with heavy objects: I'm not sure which is cheaper: to allocate new memory or to collect the garbage. I don't think one operation is significantly cheaper than another – zerkms Jan 19 '12 at 08:32
1

Since php 5.2, it's possible using spl_object_hash but it shouldn't be used...

class MyClass {
    public function destroy(){
        foreach($GLOBALS as $k => $v){
            // bucabay is totally right ! It's faster this way
            //if(is_object($v) && (spl_object_hash($v) == spl_object_hash($this))){
            if($v === $this){
                    $GLOBALS[$k] = null;
                    return;
            }
        }
    }
}
$a = new MyClass;
$b = new MyClass;

var_dump($a, $b); // class MyClass#1 (0) {}class MyClass#2 (0) {}
echo "\n";

$a->destroy();
var_dump($a, $b); // NULL class MyClass#2 (0) {}
echo "\n";

$b->destroy();
var_dump($a, $b); // NULL NULL
zenko
  • 147
  • 1
  • 8
1

What's wrong with unset($o)? You could wrap that behaviour up in a static class method if it really offends you:

class Foo {
    public static function destroy(&$foo) {
        unset($foo);
    }
}
Foo::destroy($o);
Finbarr
  • 31,350
  • 13
  • 63
  • 94
  • It won't work. `unset` in this case just removes one reference, not the object itself. But probably `$foo = null;` would do the work – zerkms Jan 19 '12 at 06:29
  • The idea is that there is a particular situation in which the object should cease to exist, but this decision was taken by the code that lies in the class itself... so the point is that you do not know that you should kill the object from the instantiated object's perspective. – bruno.braga Jan 19 '12 at 06:29
0

You can use:

public function delete()
    {
        foreach($GLOBALS as $name => $value) {
            if ($GLOBALS[$name] === $this) {
                unset($GLOBALS[$name]);
            }
        }
    }

any PHP version. This only works if the object is in the global scope.

bucabay
  • 5,235
  • 2
  • 26
  • 37
0

It's impossible. You can unset a variable or set it to null but there's no guarantee that you won't have any references to this object ($b = $o; $b won't be destroyed with unset($o);).

An ugly solution is add a field in your class, e.g. destroyed, check in every method that it's not true and throw an exception if it is.

meze
  • 14,975
  • 4
  • 47
  • 52
0

Yeah, it seems impossible to achieve... another dirty way could be:

class Foo {
    public $bar = "doh!";
    function destroy() {
        unset($this->bar); // and do this for EVERY property of this class
    }
}
$o = new Foo();
echo $o->bar . '\n';
$o->destroy();
echo $o->bar . '\n'; // PHP Notice:  Undefined property: Foo::$bar in...

However, the object would still exists, and methods would still be accessible.

bruno.braga
  • 1,001
  • 12
  • 21