2Gas
а зачем вообще этого хотеть в приложениях, которые должны отрабатывать за десятые/сотые доли секунды, где по окончании освобождаются все ресурсы и вызываются необходимые деструкторы.
Если вопрос в контексте консольных долгоживущих скриптов, то прежде чем делать - нужно хорошо подумать.
Вот именно я разрабатываю долгоживущие серверные приложения, а не домашние паги. Сразу скажу, почему именно на PHP это разговор другой и к данной теме не относится.
И не всегда даже проблема в цикличности. Может быть даже проблема в невидимости ссылки данного объекта из данного объекта. И тут уже ручной деструктор и суперобнул не помогут из данного объекта.
Вот пример.
PHP:
<?php
class B{
private $just_a_var;
public function test($var){
$var = strrev($var);
$global_c->just_a_var = $var;
//echo "Test var='".$var."' \n";
return $var;
}
public function __construct($name){
$this->name = $name;
echo "Construct Class B name='".$this->name."'\n";
}
public function __destruct(){
echo "Desctruct class B name='".$this->name."'\n";
}
}
class D{
public $s_class;
public function add_class(&$class){
$this->s_class = &$class;
}
public function test($var){
$var = strtoupper($var);
return $this->s_class->test($var);
}
public function __construct($name){
$this->name = $name;
echo "Construct Class D name='".$this->name."'\n";
}
public function __destruct(){
echo "Desctruct class D name='".$this->name."'\n";
}
}
echo "construc class B\n";
$test_cl = new B('name_one');
echo "construc class D\n";
$test_D = new D('name_two');
echo "add in class D class B\n";
$test_D->add_class(&$test_cl);
echo "run func from D\n";
$ret = $test_D->test('abc');
echo "return ret='".$ret."' \n";
echo "Try unset class B\n";
unset($test_cl);
echo "Try unset class D\n";
unset($test_D);
echo "End Programm\n";
?>
Любая функция обнуления или ещё чего-нибудь в классе B никак не сможет добраться до $test_D, так-как находится не в пределах видимости данного объекта.
Ещё скажу что это пример НЕ хорошего программирование. Но чтобы показать данную проблему, пришлось написать так.
Кроме того, не только объект подвисает в памяти.
Допустим, объект на протяжении своего существования должен каждый тик вызывать какую-то функцию. После уничтожения, не должен её вызвать.
Вот пример
PHP:
<?php
class B{
private $child_class;
public $name;
private $memory;
public function add_children(&$class, $counter){
$t_counter = $counter;
echo "Add child class counter='".$counter."' \n";
if($counter<=0){
echo "Exit by counter\n";
return true;
}
$this->child_class = &$class;
$counter--;
$this->child_class->add_children($class, $counter);
echo "End add child class couner='".$t_counter."' \n";
}
public function __construct($name, $count_child = 5){
$this->name = $name;
echo "Construct Class B name='".$this->name."'\n";
$this->add_children(&$this, $count_child);
register_tick_function('eche_ticks', $this->name);
declare(ticks=1);
}
public function __destruct(){
echo "Desctruct class B name='".$this->name."'\n";
unregister_tick_function('eche_ticks');
}
}
function eche_ticks($name){
///to do somthing at each ticks
echo "---------------Ticks name='".$name."' \n";
}
declare(ticks=1);
echo "Init \n";
$test_cl = new B('name_one', 1);
echo "Try unset\n";
unset($test_cl);
echo "Now variable is='".var_export($test_cl, true)."' \n";
echo "Init \n";
$test_cl = new B('name_two', 1);
echo "Try NULL\n";
$test_cl = NULL;
echo "Now variable is='".var_export($test_cl, true)."' \n";
for($x=1; $x<=3; $x++){
echo "x='".$x."'\n";
}
echo "end script\n";
?>
Так как в объекте есть цикл, то он не будет уничтожен. и на момент выполнения for будет выполнятся тики от двух объектов. А должно быть не одного тика.
Далее
Типа сделать метод который бы обходил все переменные и смотрел объекты это или нет и уничтожал их, но при этом нужно ещё зайти в этот подобъект и в нём уничтожить всё по такому же принципу. Но это не получится.
Во первых, я уже описал выше, что ссылка может быть не в пределах видимости уничтожаемого объекта.
Во вторых представим ситуацию, когда есть объект которые в себе хранит ссылки на другие объекты и при этом общий для всех этих объектов. И при этом есть обратная ссылка на общий объект в данных объекта. И по этому правилу нудно уничтожить и его, во первых не получится, во вторых не нужно.
Вроде сам понимаю что написал
-~{}~ 18.02.08 20:10:
Ну чё пришлось свой каунтер ссылок написать. Правда сыровать, но вроде работает со всеми примерами.
PHP:
<?php
class obj_links_works_cl{
public $all_class = array();
public $all_links = array();
public function links(&$where, $parametr, &$who){
$id_where = $this->search_id_obj_in_class(&$where);
if($id_where===false){
$this->add_obj_in_class(&$where);
$id_where = $this->search_id_obj_in_class(&$where);
}
$id_who = $this->search_id_obj_in_class(&$who);
if($id_who===false){
$this->add_obj_in_class(&$who);
$id_who = $this->search_id_obj_in_class(&$who);
}
if(empty($this->all_links[$id_who])){
$this->all_links[$id_who] = array();
}
//echo "id_where='".$id_where."' id_who='".$id_who."' \n";
if($id_where!==false and $id_who!==false){
$info['id_where'] = $id_where;
$info['parametr'] = $parametr;
$this->all_links[$id_who][] = $info;
/*echo "now all_links\n";
var_export($this->all_links);
echo "\n";*/
//return $who;
}else{
//SOMTHING wrong with add object
}
}
public function unset_all_links(&$object){
$id_obj = $this->search_id_obj_in_class(&$object);
if($id_obj!==false){
//echo "found id to unlinks='".$id_obj."' \n";
if(!empty($this->all_links[$id_obj])){
$all_links_obj = $this->all_links[$id_obj];
if(is_array($all_links_obj) or is_object($all_links_obj)){
krsort($all_links_obj);
reset($all_links_obj);
while (list($key, $val) = each($all_links_obj)){
//echo "id_key='".$key."' param='".$val['parametr']."' \n";
//echo "try unset='".$val['id_where']."' \n";
$temp_obj = &$this->all_class[$val['id_where']];
unset($temp_obj->$val['parametr']);
unset($temp_obj);
}
}
}
//echo "remove from global all_links\n";
unset($this->all_links[$id_obj]);
//echo "remove from global all_class\n";
unset($this->all_class[$id_obj]);
}else{
//object not found in array
}
}
public function search_id_obj_in_class(&$object){
$ret = false;
/*$all_key = array_keys($this->all_class, $object);
for($x=0; $x<count($all_key); $x++){
if($this->all_class[$all_key[$x]]===$object){
$ret = $all_key[$x];
break;
}
}*/
if(is_array($this->all_class) or is_object($this->all_class)){
reset($this->all_class);
while (list($key, $val) = each($this->all_class)){
if($val===$object){
$ret = $key;
break;
}
}
}
return $ret;
}
public function add_obj_in_class(&$object){
//echo "add_one_object\n";
$this->all_class[] = &$object;
}
}
$obj_links_works = new obj_links_works_cl();
function &add_links_obj(&$where, $paramter, &$who){
global $obj_links_works;
$obj_links_works->links(&$where, $paramter, &$who);
return $who;
}
function unset_all_links_obj(&$who){
global $obj_links_works;
$obj_links_works->unset_all_links(&$who);
}
?>
Как это работает.
Вместо
$this->child_class = &$class;
Указываем
$this->child_class = &add_links_obj(&$this, 'child_class', &$class);
Вместо
$global_c->temp_class = &$this;
Указваем
$global_c->temp_class = &add_links_obj(&$global_c, 'temp_class', &$this);
И обязательное условие, чтобы переменная в объекте которой присваевается ссылка (child_class, temp_class) была паблик.
И т.д.
А когда нужно уничтожить, то делаем так.
unset_all_links_obj(&$test_cl);
unset($test_cl);
И деструктор срабатывает нормально.
Кто, что может сказать по этому поводу? Может чё-то пропустил, какие-то варианты. А то понедельник день тяжёлый.