11

I know that for some that might sound stupid, but I was thinking if I hava a delete() method in a class that removes all the object data (from DB and file system), how can I destroy/remove the object from within the class.

This is a PHP question. Something like unset($this); Is it possible and wise? And what is the right way to do it?

Yasen Zhelev
  • 4,045
  • 3
  • 31
  • 56
  • are you looking for Destructor – Shakti Singh Mar 03 '11 at 12:06
  • I don't understand the question. THe object itself lives in RAM and is deleted by the garbage collector as soon as there is no reference to it. If **you** take care of storing information about this object elsewhere, say in a database or a file, it is up to you to remove these data. There are many ways of storing data, and PHP cannot just guess the one you implemented. – Andrea Mar 03 '11 at 12:07
  • I may have misunderstood - maybe you have the code to remove the data and you just do not know where to put it. In this case the method __destruct() will do. – Andrea Mar 03 '11 at 12:09
  • So I can call __destrict in my delete() method, right? I do not want to wait for the GC or end of the script. I want to destroy/destruct the object right after I delete its data. – Yasen Zhelev Mar 03 '11 at 12:12
  • 1
    I understand the OT's question, it refers to deleting the object itself from inside – mauris Mar 03 '11 at 12:16
  • Yes, is this possible at all? Or it is wrong conception at first place and then not possible? :) – Yasen Zhelev Mar 03 '11 at 12:26
  • So, if I understand this correctly you do konw how to remove the data from wherever it is stored, but you are afraid that if you just call this code manually, you will be left with an object that may be still referenced somewhere, but with no associated data. Is this the right interpretation? – Andrea Mar 03 '11 at 12:50
  • Yes, something like that. After all why should I have a object in the script thata was already deleted? – Yasen Zhelev Mar 03 '11 at 13:42

7 Answers7

6

Whilst developing on a framework, I came across such issue as well. unset($this) is totally not possible, as $this is just a special pointer that allows you to access the current object's properties and methods.

The only way is to encapsulate the use of objects in methods / functions so that when the method / function ends, the reference to the object is lost and garbage collector will automatically free the memory for other things.

See example RaiseFile, a class that represents a file:

http://code.google.com/p/phpraise/source/browse/trunk/phpraise/core/io/file/RaiseFile.php

In the RaiseFile class, it'll be sensible that after you call the delete() method and the file is deleted, the RaiseFile object should also be deleted.

However because of the problem you mentioned, I actually have to insist that RaiseFile points to a file whether or not the file exists or not. Existence of the file can be tracked through the exists() method.

Say we have a cut-paste function that uses RaiseFile representation:

/**
 * Cut and paste a file from source to destination
 * @param string $file Pathname to source file
 * @param string $dest Pathname to destination file
 * @return RaiseFile The destination file
 */
function cutpaste($file, $dest){
    $f = new RaiseFile($file);
    $d = new RaiseFile($dest);
    $f->copy($d);
    $f->delete();
    return $d;
}

Notice how $f is removed and GC-ed after the function ends because there is no more references to the RaiseFile object $f outside the function.

mauris
  • 42,982
  • 15
  • 99
  • 131
  • Ok I see what you are doing here, but I was wondering if I can somehow destroy/GC a object from itself. Like self-destroying for objects :) – Yasen Zhelev Mar 03 '11 at 13:47
  • 1
    the answer is already clear, no an object cannot delete itself. I'm already providing the workaround for what you're asking. – mauris Mar 03 '11 at 14:06
3

You cannot unset $this. Or more correctly: unset() on $this only has local effect on the variable. unset() removes the variable from the local scope, which reduces the ref count for the object. If the object is still referenced somewhere else, it will stay in memory and work.

Typically, the value of an ID property is used to determine if an object is stored in the back end. If the ID has a proper value, that object is stored. If the ID is null, it is not stored, yet. On successful storage you then set the ID accordingly. On delete you set the ID property to null again.

tobyS
  • 860
  • 1
  • 7
  • 15
1

I'm in this same boat now.

I'm building a CMS solution from the ground up and have a lot of references between objects; users, groups, categories, forums, topics, posts, etc.

I also use an "Object::getObject(id)" loader in each class which ensures there's only one instance of an object per ID, but also means it's even easier for code to pull a reference to existing objects.

When the data the object represents gets deleted from the data source, I'd like to wipe the object from memory and nullify all references to it to ensure other code doesn't try to use an obsolete data set.

Ideally all references should be removed--the referencing code can provide a callback that gets fired at object deletion that can subsequently remove/update the reference. But if the referencing code gets sloppy, I'd rather it error out with a "Not an object" error than actually work with the object.

Without knowing how to force the destruction from within the object, itself, I'm being forced to:

  1. Begin almost every non-static method with a check to see if the object has been flagged "deleted", throwing an exception if it is. This ensures any referencing code can't do any harm, but it's a nasty thing to look at in the code.

  2. Unset every object variable after deletion from the database, so it doesn't linger in memory. Not a big deal but, again: nasty to look at.

Neither would be needed if I could just destroy the object from within.

Rikaelus
  • 574
  • 1
  • 5
  • 15
0

Here is a sample solution which would be "reasonably usable" when implemented in well defined relationship/reference patterns, usually I drop something like this in my composites. To actually use the current code as I've done in this example in a real life would be going through too much trouble but it's just to illustrate the how-to-point.

An object can't just magically disappear - it will only be deleted (garbage collected) once there is nothing pointing at it. So "all" an object has to do is to keep track of everything that refers to it. It is fairly low friction when you have all the reference management built in - objects are created and passed on only by fixed methods.

Lets begin with a simple interface so we would be able to tell if it is safe to pass our reference to an object or not.

interface removableChildInterface
{

    public function removeChild($obj);

}

A class which can safely hold a reference to our object.

class MyParent implements removableChildInterface
{

    public $children = array();

    public function removeChild($child)
    {
        $key = array_search($child, $this->children);
        unset($this->children[$key]);
    }

}

And finally a class with the ability to self-destruct aka trigger the process of being removed by all of its parents.

class Suicidal
{

    private $parents = array(); // Store all the reference holders
    private $id; // For example only
    private $memory = ''; // For example only
    public static $counter = 0; // For example only

    public function __construct(&$parent)
    {
        // Store a parent on creation
        $this->getReference($parent);
        // For the example lets assing an id
        $this->id = 'id_' . ++self::$counter;
        // and generate some weight for the object.
        for ($i = 0; $i < 100000; $i++) {
            $this->memory .= md5(mt_rand() . $i . self::$counter);
        }
    }

    // A method to use for passing the object around after its creation.
    public function getReference(&$parent)
    {
        if (!in_array($parent, $this->parents)) {
            $this->parents[] = &$parent;
        }
        return $this;
    }

    // Calling this method will start the removal of references to this object.
    // And yes - I am not actually going to call this method from within this
    // object in the example but the end result is the same.
    public function selfDestruct()
    {
        foreach ($this->parents as &$parent) {
            if (is_array($parent)) {
                $key = array_search($this, $parent);
                unset($parent[$key]);
                echo 'removing ' . $this->id . ' from an array<br>';
            } elseif ($parent instanceof removableChildInterface) {
                $parent->removeChild($this);
                echo 'removing ' . $this->id . ' from an object<br>';
            }
            // else throw your favourite exception
        }
    }

    // A final shout out right before being garbage collected.
    public function __destruct()
    {
        echo 'destroying ' . $this->id . '<br>';
    }

}

And for the example of usage, holding the reference in an array, in an object implementing our interface and the $GLOBALS array.

// Define collectors
$array = array();
$parent = new MyParent();

// Store objects directly in array
$array['c1'] = new Suicidal($array);
$array['c2'] = new Suicidal($array);

// Make a global reference and store in object
$global_refrence = $array['c1']->getReference($GLOBALS);
$parent->children[] = $array['c1']->getReference($parent);

// Display some numbers and blow up an object.
echo 'memory usage with 2 items ' . memory_get_usage() . ' bytes<br>';
$array['c1']->selfDestruct();
echo 'memory usage with 1 item ' . memory_get_usage() . ' bytes<br>';

// Second object is GC-d the natural way after this line
echo '---- before eof ----' . '<br>';

Output:

memory usage with 2 items 6620672 bytes
removing id_1 from an array
removing id_1 from an array
removing id_1 from an object
destroying id_1
memory usage with 1 item 3419832 bytes
---- before eof ----
destroying id_2
Jaak Kütt
  • 2,566
  • 4
  • 31
  • 39
0

This depends on how you've structured your class.

If you're following DTO/DAO patterns, your data would be separate from your model and you can simply remove the DTO. If you're not, simply unsetting the data part of the class should do it.

But in actual terms, I think this is unnecessary since PHP will automatically cleanup at the end of the request. Unless you're working on a giant object that takes up massive amounts of memory, and it's a long process it's not really worth the effort.

JohnP
  • 49,507
  • 13
  • 108
  • 140
  • I think I am following them, but still at the end I want the object to self-destroy itself. I do not want to have a object which data was removed from DB and file system already. – Yasen Zhelev Mar 03 '11 at 12:10
  • It's too difficult to give a correct answer without looking at your code. This entirely depends on how you're storing your data and how your models and data are structured – JohnP Mar 03 '11 at 12:14
  • Basically I am using a ActiveRecord approach. Storing object data in protected $_data = array(); – Yasen Zhelev Mar 03 '11 at 13:44
  • well if memory is your biggest worry you could have an interface or a parent method `deleteData()` that would do some clean up on the data array. The model itself would be left untouched for you to carry out more operations. – JohnP Mar 03 '11 at 15:17
0

There is a __destruct() magic method which is a destructor for a PHP Class.

Maybe you can put your deletion code in there and as soon as all reference to your objects are deleted, this destructor will be called and the data deleted.

krtek
  • 26,334
  • 5
  • 56
  • 84
  • Maybe I'm wrong, but the __destruct method is also called if the script is completed. Which means that after each request all your used objects will be automatically deleted from the database. Also see http://stackoverflow.com/questions/151660/php-destruct-method#answer-151673 – Rene Terstegen Mar 03 '11 at 12:24
0

Another approach is to make the delete-method static, which then could receive a PDO object and data, that determines what to delete. This way you don't need to initialise the object.

feeela
  • 29,399
  • 7
  • 59
  • 71