порядок вызова деструкторов

Andreika

"PHP for nubies" reader
про деструкторы

PHP5.1.0/5.1.2
собстно даж не знаю вопрос это или жалоба (кому: на деревню дедушке)

PHP:
  class MySQL extends mysqli {
     function __construct() {
        parent::__construct('localhost','root','','test');
     }

     function __destruct() {
       echo "MySQL::__destruct()<BR>";
     }
  }

  class X {
     function t() { echo "TTTT<BR>"; }
     function __destruct() { echo "X::__destruct()<BR>"; }
  }

  class Test {
     private $db;
     public $x;

     function __construct($db,$x) {
        $this->db = $db;
        $this->x = $x;
     }


     function __destruct() {
        echo "Test::__destruct()<BR>";
        $s = $this->db->real_escape_string('ddd');
        $this->x->t();
     }
  }

  $x= new X() ;
  $db = new MySQL();
  $t = new Test($db, $x);
X::__destruct()
MySQL::__destruct()
Test::__destruct()

Warning: mysqli::real_escape_string(): Couldn't fetch MySQL in php3E.tmp on line 34

/* пофиксил ошибку в коде - стало вообще ничего непонятно. */
деструкторы вызываются до "освобождения последней ссылки на объект", но при этом после вызова деструктора объект получается мертвым (деструктор все что надо почистил "вручную"/mysqli закрыла соединение), но доступ к телу разрешен. как это так и как с этим жить, если надо пользоваться mysql в деструкторе (без функций типа save/ $obj=null)
 
Andreika

Да, логично, т.к. первым был создан объект класса X, далее MySQL, и последним - Test, в обратном порядке вызываются и деструкторы.

По-видимому, Php не считает, что если объект переданный коструктору классу как параметр, ссылкой на этот объект, поэтому и "убиваются" сначала объекты классов X и MySQL, а после Test

з.ы. должен помочь &....

-~{}~ 26.01.06 10:45:

Упростил твой пример, имхо, так понятнее:
PHP:
class A 
{ 
     function __construct() {} 

     function __destruct() 
     { 
       echo "A::__destruct()<BR>"; 
     } 
} 

class B 
{ 
     function __construct() {}
     
     function t() 
     { 
         echo "function t();<BR>"; 
     } 
     
     function __destruct() 
     { 
         echo "B::__destruct()<BR>"; 
     } 
} 

  
  
class Test 
{ 
     private $b; 
     public  $c; 

     // Получаем не копии объектов, а ссылки на них.
     function __construct( & $b, & $c) 
     { 
        // В объкты класса записываем ссылки на глобальные объекты... 
        // (Но, тоже не помогло : ( )
        $this->b = & $b; 
        $this->c = & $c; 
     } 


     function __destruct() 
     { 
        echo "Test::__destruct()<BR>"; 
        $this->b->t(); 
     } 
} 

  $c = new B(); 
  $b = new A(); 
  $t = new Test( $b, $c );
 

Andreika

"PHP for nubies" reader
да вроде в 5ом пхп объекты и так должны по ссылкам передаваться... в любом случае - не помогло
 
Andreika

Сорри, нашел ошибку...
Получилось еще хуже: сначала вызывается деструктор объекта B, а потом метод этого объекта :(

Значит все равно в классе Test просиходит работа с копией объекта B...
PHP:
class A 
{ 
     function __construct() {} 

     function __destruct() 
     { 
       echo "A::__destruct()<BR>"; 
     } 
} 

class B 
{ 
     function __construct() {}
     
     function t() 
     { 
         echo "function t();<BR>"; 
     } 
     
     function __destruct() 
     { 
         echo "B::__destruct()<BR>"; 
     } 
} 

  
  
class Test 
{ 
     private $a; 
     public  $b; 

     // Получаем не копии объектов, а ссылки на них.
     function __construct( & $a, & $b ) 
     { 
        // В объкты класса записываем ссылки на глобальные объекты... 
        $this->a = & $a;
        $this->b = & $b;
     } 


     function __destruct() 
     { 
        echo "Test::__destruct()<BR>"; 
        $this->b->t(); 
     } 
} 


$b = new B(); 
$a = new A(); 
$t = new Test( $a, $b );
У меня выдает:

B::__destruct()
A::__destruct()
Test::__destruct()
function t();
 

tony2001

TeaM PHPClub
Чёткого гарантированного порядка вызова деструкторов нет.
Если один объект зависит от другого - ты должен сам заботиться об их правильном разрушении.
 
tony2001
да, это все понятно, но а данном случае происходит работа с объектом после вызова деструктора... Как это?

кстати, если изменить код деструктора класса Test вот так, то все равно работает... Даже после принудительного вызова деструктора....
PHP:
// В классе Test
     function __destruct()  
     {  
        echo "Test::__destruct()<BR>";

        // Вызываем деструктор объекта b
        $this->b->__destruct();

        // Наблюдаем "нормальную" работу с объекта???
        $this->b->t();  
     }  
}
 

tony2001

TeaM PHPClub
>да, это все понятно, но а данном случае происходит работа с объектом после вызова деструктора... Как это?

то есть, тебя смущает, что можно вызвать __destruct(), а потом foo() ?

а почему тебя не смущает, что можно вызывать foo(), потом bar(), а потом еще и t() ?
__destruct() - это метод, (почти) как и все остальные.
он магическим образом не делает unset($obj).
 
tony2001
> то есть, тебя смущает, что можно вызвать __destruct(), а потом foo() ?
да.

> __destruct() - это метод, (почти) как и все остальные.
> он магическим образом не делает unset($obj).
хм... я почему-то думал, что после вызова деструктора любая работа с объектом, будь-то обращение к свойству или вызов метода, будет вызывать ошибку...
 

Andreika

"PHP for nubies" reader
tony2001
а меня смущает то, что в мануале про "почти обычный метод" ни слова - когда освобождается последняя ссылка на объект, перед высвобождением памяти, занимаемой этим объектом, вызывается метод __destruct(), не принимающий параметров. ©мануал
с одной стороны он не врет - действительно метод вызывается ПЕРЕД (а не ПОСЛЕ) освобождения памяти :) с другой стороны не так уж и очевидно, что это ПЕРЕД очень растяжимое понятие, особенно по сравнению с тем что применяются в других ОО языках©

Чёткого гарантированного порядка вызова деструкторов нет.
печально... ну бум иметь в виду.. всем спасибо )
 
Andreika
Добавил просмотр вызовов конструкторов, по-видимому они вызываются соответственно вывозам конструкторов, хотя - это так .... гадание...
PHP:
class A  
{  
     function __construct() 
     {
        echo 'A::__construct()<BR>';
     } 

     function __destruct()  
     {  
       echo "A::__destruct()<BR>";  
     }  
}  

class B  
{  
     function __construct() 
     {
        echo 'B::__construct()<BR>';
     } 
      
     function t()  
     {  
         echo "function t();<BR>";  
     }  
      
     function __destruct()  
     {  
         echo "B::__destruct()<BR>";  
     }  
}  

   
   
class Test  
{  
     private $a;  
     public  $b;  

     // Получаем не копии объектов, а ссылки на них. 
     function __construct( & $a, & $b )  
     {  
        // В объкты класса записываем ссылки на глобальные объекты...  
        $this->a = & $a; 
        $this->b = & $b; 
        echo 'Test::__construct()<BR>';
     }  


     function __destruct()  
     {  
        echo "Test::__destruct()<BR>";  
        $this->b->t();  
     }  
}  


$b = new B();  
$a = new A();  
$t = new Test( $a, $b );
B::__construct()
A::__construct()
Test::__construct()
B::__destruct()
A::__destruct()
Test::__destruct()
function t();
 

Andreika

"PHP for nubies" reader
tony2001
к сожалению я не могу ничего посоветовать, ибо не понимаю когда будет вызван деструктор, а когда удален объект.
также не очень понятно про "последнюю ссылку на объект".
//точнее понимаю, но гдето на интуитивном уровне


Loshadka
было бы в обратном порядке - было бы лучше )
 
Andreika
Знаешь, имхо все работает как нужно.
Убедиться в этом можно вот так:
PHP:
class A  
{  
     function __construct() 
     {
        echo 'A::__construct()<BR>';
     } 

     function __destruct()  
     {  
       echo "A::__destruct()<BR>";  
     }  
}  

class B  
{  
     function __construct() 
     {
        echo 'B::__construct()<BR>';
     } 
      
     function t()  
     {  
         echo "function t();<BR>";  
     }  
      
     function __destruct()  
     {  
         echo "B::__destruct()<BR>";  
     }  
}  

   
   
class Test  
{  
     private $a;  
     public  $b;  

     function __construct( & $a, & $b )  
     {  
        $this->a = new A();        
        $this->b = new B();
        echo 'Test::__construct()<BR>';
     }  


     function __destruct()  
     {  
        echo "Test::__destruct()<BR>";  
        $this->b->t();  
     }  
}  

$t = new Test( $a, $b );
А в пердыдущих примерах - вообще не понятно, что проиходит, в классе Test происходит работа с глобальными объектами, но Php не считает, что ссылки на них есть, несмотря на то, что существует объект класса Test...

бред....

PHP:
class A  
{  
    function __construct() 
    {
       echo 'A::__construct()<BR>';
    } 

    function __destruct()  
    {  
        echo 'A::__destruct()<BR>';  
    }  
}  

class B  
{  
    private $a;    
    
    function __construct( $a ) 
    {
        $this->a = $a;
        echo 'B::__construct()<BR>';
    } 
      
    function t()  
    {  
        echo 'function t('.$this->a.');<BR>';  
     }  
      
    function __destruct()  
    {  
        echo "B::__destruct()<BR>";  
    }  
}  

   
   
class Test  
{  
    private $a;  
    public  $b;  

    // Получаем не копии объектов, а ссылки на них. 
    function __construct( & $a, & $b )  
    {  
        // В объкты класса записываем ссылки на глобальные объекты...  

        $this->a = & $a; 
        $this->b = & $b; 
        echo 'Test::__construct()<BR>';
    }  


    function __destruct()  
    {  
        echo "Test::__destruct()<BR>";  
        $this->b->t();  
    }  
}  


$b = new B('global');  
$a = new A();  
$t = new Test( $a, $b );
 

Andreika

"PHP for nubies" reader
Loshadka
чего доказать пытаешся то? :)
B::__construct()
A::__construct()
Test::__construct()
B::__destruct()
A::__destruct()
Test::__destruct()
function t(global);

твой B::__destruct() ничего не делает, а у меня на его месте находится mysqli::close(), закрывающий нужное мне соединение с базой
 
Andreika
Да, сорри, иногда сегодня туплю.. :(

Вот этот код показывает, что объекту класса Test передаются ссылка на глобальный объект класса A...

Так вот меня смущает то, что интерпретатор считает, что на объект нет никаких ссылок, вызывает его деструктор, но сам объект не удаляет, т.е. с ним дальше можно работать....
PHP:
class A   
{   
    private $a;     
     
    function __construct( $a )  
    { 
        $this->a = isset($a) ? $a : 'default' ; 
        echo 'A::__construct()<BR>'; 
    }  
       
    function out()   
    {   
        echo 'function t('.$this->a.');<BR>';   
    }   
       
    function change()   
    {   
        $this->a = 'change';   
    }   

    function __destruct()   
    {   
        echo 'A::__destruct()<BR>';   
    }   
}   

    
    
class Test   
{   
    private $a;

    // Получаем не копии объектов, а ссылки на них.  
    function __construct( & $a )   
    {   
        $this->a = $a;

        // вот здесь происходит вызов метода глобального объекта.
        $this->a->change();

        echo 'Test::__construct()<BR>'; 
    }   


    function __destruct()   
    {   
        $this->a->out();       
        echo "Test::__destruct()<BR>";   
    }   
}   


$a = new A('global');   
$t = new Test( $a );

$a->out();
вывод:

A::__construct()
Test::__construct()
function t(change);
A::__destruct()
function t(change); // это, насколько я понимаю, вызов метода out из класса Test, после вызова деструктора...
Test::__destruct()

-~{}~ 26.01.06 16:20:

да, кстати, для сущей убедительности можно немножко видоизменить код:
PHP:
$a = new A('global');    
$t = new Test( $a ); 
echo '<br>Из глобального объекта<br>';
$a->out();
echo '<br>';
вывод:
A::__construct()
Test::__construct()

Из глобального объекта
function t(change);

A::__destruct()
function t(change);
Test::__destruct()
 

romy4

invoke [brain]
хм, а ради интереса, если сделать unset($a), то деструктор будет вызван неявно или нет?
 

tony2001

TeaM PHPClub
Loshadka
вот ты удивительный человек.
если тебе что-то непонятно - спроси, я более подробно объяснить могу.
но нет. идёт на bugs.php.net, репортит багу, даже не потрудившись поискать в архиве.
ты думал, я тебе вру, что-ли?
 
tony2001
Сорри, плохо искал... :(

ого, уже почти год этой ошибке :(

мда... не я один такой... :)

>ты думал, я тебе вру, что-ли?
нет. Я думал, что дело не в порядке вызовов деструкторов, а в том, что php не освобождает память после вызова деструктора...
 

AnToXa

prodigy-одаренный ребенок
господа, предлагаю объяснение. постараюсь попроще.
на примере C++, уж извините :)

1. объект начинает жить после выхода из его конструктора и живет до входа в деструктор.
2. любое использование объекта после вызова деструктора -> undefined behaviour, т.е. хоть форматирование hdd, хоть полет на луну.
я понимаю, что в garbage collected языках это не совсем все так, но следить за собой следует все-таки тщательнее.
наличие garbage collection не освобождает вас от обязанности следить за ресурсами.

спасибо за внимание.
 

Screjet

Новичок
romy4
Если это не ссылка, и ссылки на объект отсутствуют, то явно уничтожаешь, да.

По моемому ктото не учитывает $GLOBALS
 
Сверху