可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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.
回答2:
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.
回答3:
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:
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.
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.
回答4:
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.
回答5:
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.
回答6:
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.
回答7:
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