Я знаю, что для некоторых, которые могут показаться глупыми, но я думал, если я hava метод delete () в классе, который удаляет все данные объекта (из базы данных и файловой системы), как я могу уничтожить / удалить объект изнутри класс.
Это вопрос PHP. Что-то вроде unset($this);
Возможно ли и мудро? И каков правильный способ сделать это?
Развивая рамки, я столкнулся с такой проблемой. unset($this)
полностью невозможно, поскольку $this
просто специальный указатель, который позволяет вам получить доступ к свойствам и методам текущего объекта.
Единственный способ – инкапсулировать использование объектов в методах / функциях, чтобы при завершении метода / функции ссылка на объект терялась, а сборщик мусора автоматически освобождал память для других вещей.
См. Пример RaiseFile
, класс, представляющий файл:
http://code.google.com/p/phpraise/source/browse/trunk/phpraise/core/io/file/RaiseFile.php
В классе RaiseFile будет разумно, что после вызова метода delete()
и удаления файла объект RaiseFile также должен быть удален.
Однако из-за проблемы, о которой вы говорили, мне действительно нужно настаивать на том, что RaiseFile указывает на файл независимо от того, существует или нет файл. Существование файла можно отслеживать с помощью метода exists()
.
Скажем, у нас есть функция вырезания, которая использует представление RaiseFile:
/** * 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; }
Обратите внимание, как $f
удаляется и GC-ed после завершения функции, потому что больше нет ссылок на объект RaiseFile
$f
вне функции.
Вы не можете отменить $this
. Или более правильно: unset()
on $this
только оказывает локальное влияние на переменную. unset()
удаляет переменную из локальной области, что уменьшает количество ссылок для объекта. Если объект все еще упоминается где-то в другом месте, он останется в памяти и будет работать.
Как правило, значение свойства ID используется для определения того, хранится ли объект на задней панели. Если идентификатор имеет правильное значение, этот объект сохраняется. Если идентификатор равен null
, он еще не сохраняется. При успешном хранении вы затем устанавливаете идентификатор. При удалении вы снова установите для свойства ID значение null
.
Сейчас я нахожусь в этой же лодке.
Я создаю решение CMS с нуля и имею много ссылок между объектами; пользователи, группы, категории, форумы, темы, сообщения и т. д.
Я также использую загрузчик «Object :: getObject (id)» в каждом классе, который обеспечивает наличие только одного экземпляра объекта для каждого идентификатора, но также означает, что код кода еще проще потянуть ссылку на существующие объекты.
Когда данные, представляемые объектом, удаляются из источника данных, я бы хотел стереть объект из памяти и свернуть все ссылки на него, чтобы другой код не пытался использовать устаревший набор данных.
В идеале все ссылки должны быть удалены – ссылочный код может обеспечить обратный вызов, который запускается при удалении объекта, который впоследствии может удалить / обновить ссылку. Но если ссылочный код становится неаккуратным, я бы предпочел, чтобы он ошибся с ошибкой «Нет объекта», чем фактически работает с объектом.
Не зная, как заставить разрушение внутри объекта, сам, меня принуждают:
Начинайте почти каждый нестатический метод с проверки, чтобы увидеть, был ли объект помечен как «удаленный», выбрасывая исключение, если оно есть. Это гарантирует, что любой ссылочный код не может навредить, но это неприятная вещь, чтобы посмотреть в коде.
Уничтожьте каждую переменную объекта после удаления из базы данных, чтобы она не задерживалась в памяти. Неважно, но, опять же: противно смотреть.
Ни один из них не понадобился бы, если бы я мог просто уничтожить объект изнутри.
Это зависит от того, как вы структурировали свой класс.
Если вы следуете шаблонам DTO / DAO, ваши данные будут отделены от вашей модели, и вы можете просто удалить DTO. Если вы этого не сделаете, просто отключите часть данных класса.
Но на самом деле, я думаю, это не нужно, так как PHP будет автоматически очищаться в конце запроса. Если вы не работаете над гигантским объектом, который занимает огромное количество памяти, и это долгий процесс, это не стоит усилий.
Существует магический метод __destruct (), который является деструктором для класса PHP.
Возможно, вы можете поместить код удаления там, и как только вся ссылка на ваши объекты будет удалена, этот деструктор будет вызван и данные будут удалены.
Другой подход заключается в том, чтобы сделать статический метод delete, который затем может получить объект и данные PDO, который определяет, что нужно удалить. Таким образом, вам не нужно инициализировать объект.
Вот примерное решение, которое было бы «разумно полезным», когда оно было реализовано в четко определенных соотношениях / шаблонах ссылок, как правило, я бросаю что-то подобное в своих композитах. Фактически использовать текущий код, как я сделал в этом примере, в реальной жизни будет слишком много неприятностей, но это просто для иллюстрации метода «точка-точка».
Объект не может просто волшебным образом исчезнуть – его удалят только (собирают мусор), когда нет ничего, указывающего на него. Таким образом, «все» объект должен делать, это следить за всем, что относится к нему. Это довольно низкое трение, когда у вас есть все встроенные ссылки управления – объекты создаются и передаются только фиксированными методами.
Давайте начнем с простого интерфейса, чтобы мы могли сказать, безопасно ли передавать нашу ссылку на объект или нет.
interface removableChildInterface { public function removeChild($obj); }
Класс, который может смело содержать ссылку на наш объект.
class MyParent implements removableChildInterface { public $children = array(); public function removeChild($child) { $key = array_search($child, $this->children); unset($this->children[$key]); } }
неclass MyParent implements removableChildInterface { public $children = array(); public function removeChild($child) { $key = array_search($child, $this->children); unset($this->children[$key]); } }
И, наконец, класс со способностью к самоуничтожению aka инициирует процесс удаления всеми его родителями.
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>'; } }
с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>'; } }
,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>'; } }
И пример использования, содержащий ссылку в array
, в object
реализующем наш interface
и в $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>';
Вывод:
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