php-resource




Archiv verlassen und diese Seite im Standarddesign anzeigen :
destrutor problem


 
mo78
05-09-2011, 17:53 
 
Also ich versuche mal vereinfacht mein problem Darzustellen.
Ich habe eine klasse welche mir ein Bild erstellt und in ein Verzeichniss speichert.
Mit unset() möchte ich dann den Destructor aufrufen und das bild wieder löschen. Doch iwie löscht er das Bild sofort wieder.

So in etwa wird das objekt verwendet:

//NAch Post und $x=1
if($x==1){
// hier wird jetzt nochmal die klasse geladen und ein $obj erzeugt

}

//Erster Aufruf der Seite
if($x==0){
// in der datei wird die klasse geladen und das obj erzeugt und das bild //erstellt
include ('create_pic.php');
$x=1;
//per POST wird die seite neu geladen und $x auf 1 gesetzt
}


Naja aber irgendwie wird der destructor schon gleich beim ersten Aufruf der Seite ausgeführt. Was er aber erst soll nachdem $x==1 ist und die seite neu geladen wurde.

schonmal danke
mfg mo

 
h3ll
05-09-2011, 17:55 
 
Du kannst nicht steuern, wann der Destruktor aufgerufen wird. Das macht der Garbage Collector von PHP. Wichtige Tätigkeiten, auf die deine Applikation aufbaut, solltest du deshalb auf keinen Fall in den Destruktor stecken.

 
mo78
05-09-2011, 17:57 
 
ahh gut zu wissen :) :beer::beer:
besten dank

 
fireweasel
05-09-2011, 21:49 
 
Du kannst nicht steuern, wann der Destruktor aufgerufen wird. Das macht der Garbage Collector von PHP.

Kann man das irgendwo nachlesen? Bisher bin ich davon ausgegangen, dass Objekt-Destrukturen bei bestimmten Ereignissen aufgerufen werden, wie Script-Ende, Objekt-Zerstörung per unset() oder = NULL usw. Etwas anderes ist die Freigabe des belegten Speichers. Die kann man nicht steuern.

*einschieb*
Wollte gerade noch den Handbuchlink (http://de2.php.net/manual/en/language.oop5.decon.php#language.oop5.decon.destructor) + Zitat nachschieben, aber da war ja wer schneller. ;)

Wichtige Tätigkeiten, auf die deine Applikation aufbaut, solltest du deshalb auf keinen Fall in den Destruktor stecken.

In den Destruktor gehört prinzipiell die "Umkehrfunktionalität" des Konstruktors, sprich: Freigabe von belegten Ressourcen, Aufruf der Destruktoren von Unterobjekten usw.

 
wahsaga
05-09-2011, 21:58 
 
Bisher bin ich davon ausgegangen, dass Objekt-Destrukturen bei bestimmten Ereignissen aufgerufen werden, wie Script-Ende, Objekt-Zerstörung per unset() oder = NULL usw.
Das ist es ja auch im großen und ganzen ja auch schon.

PHP: Constructors and Destructors (http://www.php.net/manual/en/language.oop5.decon.php#language.oop5.decon.destructor):
The destructor method will be called as soon as all references to a particular object are removed or when the object is explicitly destroyed or in any order in shutdown sequence.
Wann wirklich alle Referenzen auf ein Objekt entfernt sind, weiß man u.U. nicht immer exakt;
was “explicitly destroyed” bedeutet, kann ich gerade auch nicht sagen; und
in der Shutdown-Phase kann man sich nicht mehr auf irgendeine bestimmte Reihenfolge verlassen.

Späße wie
Attempting to throw an exception from a destructor (called in the time of script termination) causes a fatal error.
kommen dann ggf. noch hinzu, um das ganze noch ein bisschen „spannender“ zu machen.

In den Destruktor gehört prinzipiell die "Umkehrfunktionalität" des Konstruktors, sprich: Freigabe von belegten Ressourcen, Aufruf der Destruktoren von Unterobjekten usw.
In der Theorie bzw. in anderen Sprachen wie bspw. JAVA ja - aber in PHP wäre ich mit letzterem auch vorsichtig, Reihenfolge und so.
So lange sich keine explizite Notwendigkeit ergibt, einen Destruktor zu nutzen, ist man m.E. immer noch am besten bedient, wenn man am Scriptende einfach dem GC das Aufräumen überlässt und da nicht mit den eigenen Wurstfingerchen drin herum fuhrwerkt ;-)

 
fireweasel
05-09-2011, 22:30 
 
... Wann wirklich alle Referenzen auf ein Objekt entfernt sind, weiß man u.U. nicht immer exakt; ...

Abgesehen von "cyclic references" sollte das aber möglich sein. Wenn nicht, ist irgendwas am Code unsauber.

... was “explicitly destroyed” bedeutet, kann ich gerade auch nicht sagen; ...

Jetzt enttäuschst du mich aber:

$object = null;

... und
in der Shutdown-Phase kann man sich nicht mehr auf irgendeine bestimmte Reihenfolge verlassen.

Späße wie

Attempting to throw an exception from a destructor (called in the time of script termination) causes a fatal error.

kommen dann ggf. noch hinzu, um das ganze noch ein bisschen „spannender“ zu machen.


Naja, anscheinend wieder so ein Fall, wo die PHP-Entwickler ein Feature eingebaut haben ohne sich über alle Konsequenzen klarzuwerden. Ist nichts neues.


In der Theorie bzw. in anderen Sprachen wie bspw. JAVA ja - ...

Das letzte mal, als ich mich mit Java beschäftigte, gabs da (noch) keine Destruktoren[0] ... ;)

... und ich komme aus einer Welt, in der die wichtigste Grundregel lautet: Gib belegte Ressourcen wieder frei, weil in einem Multitaskingsystem andere Tasks (Prozesse) sie benötigen könnten. Ähnlich wie in dem Spruch:

Verlassen Sie diesen Raum so, wie Sie ihn vorzufinden wünschen.

... - aber in PHP wäre ich mit letzterem auch vorsichtig, Reihenfolge und so. ...

Mit Umkehrfunktionalität meinte ich nicht die Reihenfolge, sondern bspw. fclose() im Destruktor, wenn fopen() im Konstruktor stand.

...
So lange sich keine explizite Notwendigkeit ergibt, einen Destruktor zu nutzen, ist man m.E. immer noch am besten bedient, wenn man am Scriptende einfach dem GC das Aufräumen überlässt und da nicht mit den eigenen Wurstfingerchen drin herum fuhrwerkt ;-)

Wenn man nichts freigeben möchte, braucht man auch keinen Destruktor, klar. Dem GC das Aufräumen des Hauptspeichers zu überlassen, ist was anderes. Wie schon erwähnt, die Speicher-Freigabe kann man nicht (vernünftig) steuern.

--
[0] drin before finalize() (http://stackoverflow.com/questions/158174/why-would-you-ever-implement-finalize)

 
wahsaga
05-09-2011, 22:51 
 
Jetzt enttäuschst du mich aber:

$object = null;
Gut, aber wenn $object nur eine Referenz von mehreren ist, destroyed das auch nicht wirklich.

... und ich komme aus einer Welt, in der die wichtigste Grundregel lautet: Gib belegte Ressourcen wieder frei, weil in einem Multitaskingsystem andere Tasks (Prozesse) sie benötigen könnten. Ähnlich wie in dem Spruch:

Verlassen Sie diesen Raum so, wie Sie ihn vorzufinden wünschen.
Klar, aber PHP im Web gewöhnlich nix Multitasking.

Insofern lautet die Regeln in dem Umfeld eher Crap anywhere you like, this toilet’s gonna explode in 30 seconds anyway …

 
fireweasel
05-09-2011, 22:55 
 
Gut, aber wenn $object nur eine Referenz von mehreren ist, destroyed das auch nicht wirklich.

$object = null;
ist was anderes als
unset ($object);


Klar, aber PHP im Web gewöhnlich nix Multitasking.

Auf einem Webserver können durchaus mehrere Scripts gleichzeitig laufen, die ein und diesselbe Ressource (Datei, Datenbankverbindung usw.) benutzen, oder?


Insofern lautet die Regeln in dem Umfeld eher Crap anywhere you like, this toilet’s gonna explode in 30 seconds anyway …
Wer wollte dir da widersprechen? :D

 
wahsaga
05-09-2011, 22:59 
 
Auf einem Webserver können durchaus mehrere Scripts gleichzeitig laufen, die ein und diesselbe Ressource (Datei, Datenbankverbindung usw.) benutzen, oder?
Ja, aber idR. sind die Scriptlaufzeiten doch so kurz, dass du mit der Freigabe einer Ressource dem Scriptende eh nur um Millisekundenbruchteile zuvor kämst - und in den Fällen halte ich es für unsinnig, mich da selber drum zu kümmern.

In den Sonderfällen, wo ich damit rechne, dass etwas länger dauert, gebe ich natürlich Ressourcen auch explizit wieder frei - bspw. mit einem session_write_close, wenn anchließend ein länger dauernder Dateidownload ausgegeben wird, o.ä.

 
mermshaus
05-09-2011, 23:02 
 
Destruktoren zum Abräumen von Member-Objekten sind nach meinem Verständnis ein Konzept von Sprachen ohne Garbage Collection, in denen es in der Verantwortung des Programmierers liegt, den Speicher von Objekten freizugeben. Ich habe das zuletzt in Delphi gemacht. Java würde ich in der Hinsicht in dieselbe Ecke wie PHP stellen: Das übernimmt die GC.

In den Destruktor gehört prinzipiell die "Umkehrfunktionalität" des Konstruktors, sprich: Freigabe von belegten Ressourcen, Aufruf der Destruktoren von Unterobjekten usw.

Das ist für mich demnach der „klassische“ Destruktor, der in einem Umfeld ohne GC von Bedeutung ist.

Threads wie dieser beschreiben das vielleicht ganz gut:

- Is there a destructor for Java? - Stack Overflow (http://stackoverflow.com/questions/171952/is-there-a-destructor-for-java)

There is an inherited method called finalize, but this is called entirely at the discretion of the garbage collector. So for classes that need explicit tidy up the convention is to define a close method and use finalize only for sanity checking (i.e. if close has not been called do it now and log an error).

In PHP ist das vermutlich in aller Regel alles nicht sonderlich spannend, da PHP-Anwendungen in dem Sinne keinen „Laufzeit-Zustand“ haben.

Naja aber irgendwie wird der destructor schon gleich beim ersten Aufruf der Seite ausgeführt. Was er aber erst soll nachdem $x==1 ist und die seite neu geladen wurde.

Das ist mehr oder weniger ein Verständnisfehler. Ein PHP-Programm lebt üblicherweise nur für den Zeitraum, den es benötigt, die HTTP-Response zu generieren. Danach explodiert die Toil… wird es mitsamt aller Objekte komplett aus dem Speicher geworfen und muss beim nächsten Request wieder von Grund auf neu initialisiert werden.

$object = null;
ist was anderes als
unset ($object);

Wenn $object der einzige Verweis auf das Objekt ist, dann glaube ich ja.

Edit: Grübel. Objektvariablen sind keine Pointer.

 
h3ll
06-09-2011, 08:27 
 
Abgesehen von "cyclic references" sollte das aber möglich sein. Wenn nicht, ist irgendwas am Code unsauber.

Das hat nichts mit unsauberen Code zu tun.

Jetzt enttäuschst du mich aber:

$object = null;

Ehrlich gesagt enttäuschst du mich :)

$object = new FooBar();

$blackbox->doSomething($object);

$object = null;

// Ob hier das Objekt zerstört wurde, weiß man nicht. Es könnte noch immer existieren.

 
ApoY2k
06-09-2011, 09:00 
 
Wo wir grade dabei sind, was passiert eigentlich bei

function cube(&$array)
{
foreach ($array as &$val)
unset($val);
unset($array);
}
$arr = array(1, 2, 3);

cube($arr);
Der Code is totaler Blödsinn, aber er kompiliert und wird problemlos ausgeführt. Aber was genau passiert da mit den Referenzen usw. Werden nur die Referenzen gelöscht? Wird das Array gelöscht?

Testausgaben gehen nicht, weil natürlich nach cube $arr im PHP-Scope nicht mehr existiert, aber was passiert hinter den Kulissen?

 
h3ll
06-09-2011, 09:05 
 
Testausgaben gehen nicht, weil natürlich nach cube $arr im PHP-Scope nicht mehr existiert

Wie kommst du zu der Annahme?

 
ApoY2k
06-09-2011, 09:36 
 
Najo es wird doch gelöscht in der Funktion. Oder nur die Referenz? Das ist ja genau was ich frage.

Bin grade leider nicht an einem gescheiten Rechner deshalb konnt ichs nicht selbst ausprobieren x)

 
h3ll
06-09-2011, 09:50 
 
Najo es wird doch gelöscht in der Funktion. Oder nur die Referenz? Das ist ja genau was ich frage.

Bin grade leider nicht an einem gescheiten Rechner deshalb konnt ichs nicht selbst ausprobieren x)

unset() löscht die Referenz. Steht aber auch im Handbuch.

 
fireweasel
07-09-2011, 00:03 
 
Abgesehen von "cyclic references" sollte das aber möglich sein. Wenn nicht, ist irgendwas am Code unsauber.

Das hat nichts mit unsauberen Code zu tun.


"Unsauber" ist vielleicht nicht das passende Wort.
(PHP-)Referenzen bleiben "zählbar", wenn man sie vernünftig einsetzt. Für Objekte und "Resourcen" benötigt man keine (expliziten) Referenzen. Bei Arrays kommts auf den Einzelfall an.

 
mermshaus
07-09-2011, 01:03 
 
Ninja-Edit @fireweasel: Ich habe mit dieser Antwort gerade übelste Race Conditions bezüglich deiner Edits. Sorry dafür. :)

Hast du den Satz mit Stack Overflow wieder rauseditiert? Na ja, egal.

(Verlinkter Artikel: Parameter passing in Java - by reference or by value? (http://www.yoda.arachsys.com/java/passing.html). Durchaus interessant zu lesen.)

Das (http://phpforum.de/forum/showpost.php?p=1328762&postcount=11) predige (http://www.php.de/php-tipps-2010/64887-erledigt-zuweisungen-funktionsaufrufe-kopie-der-werte-oder-referenz.html#post495277) (auch) ich (http://www.php.de/php-fortgeschrittene/78028-performance-problem.html#post584995) seit (http://www.php.de/614202-post11.html) langem (http://www.php.de/adventskalender-2010/75163-4-1-dont-panic-hitchhikers-guide-php-oddities.html).[1]

In allen anderen Fällen auch. Sämtliche anderen Variablen, die dieses Objekt referenzieren, zeigen dann auch auf null.

Ja, die es (besser gesagt die Variable) (im PHP-Sinne) referenzieren… Gut, so gesehen ist deine Aussage völlig richtig. Ich bin aber nicht sicher, dass wahsaga wirklich Referenzen meinte.

Referenzen spielen bei Objekten in aller Regel absolut keine Rolle.

Es ist schon bemerkenswert, wie sehr man sich auch als gestandener PHP-Programmierer dabei immer wieder in Definitionen oder ungenauer Ausdrucksweise verheddert.[2]



[1] Schamlos!
[2] Die Aussage beziehe ich natürlich zuerst auf mich selbst.

 
h3ll
07-09-2011, 07:33 
 
"Unsauber" ist vielleicht nicht das passende Wort.
(PHP-)Referenzen bleiben "zählbar", wenn man sie vernünftig einsetzt. Für Objekte und "Resourcen" benötigt man keine (expliziten) Referenzen. Bei Arrays kommts auf den Einzelfall an.

Objekte werden in PHP immer als Referenz übergeben. OK, es ist keine richtige Referenz, aber trotzdem werden Objekte nicht immer gelöscht, nur weil man eine Variable auf NULL setzt oder unset() verwendet. Das sollte einem bewusst sein.

Auch macht es selten Sinn Variablen auf NULL zu setzen, weil sie am Ende jeder Funktion sowieso automatisch gelöscht werden. Wie gesagt, die Variablen, und nicht unbedingt die Objekte. Es ist nur unnötige Schreibarbeit. Und globale Variablen sollte man sowieso nicht verwenden.

Das selbe gilt für Objektvariablen. Wenn ein Objekt gelöscht wird, werden auch automatisch alle Objektvariablen gelöscht. Deswegen ist es auch ziemlich sinnlos sie im Destruktor nochmal explizit zu löschen bzw. auf NULL zu setzen.

 
mermshaus
07-09-2011, 14:22 
 
Objekte werden in PHP immer als Referenz übergeben. OK, es ist keine richtige Referenz, …

Ich seh's so:

(„Objektvariable“ meint hier: eine Variable, deren Wert ein Object Identifier ist. Public/private/protected Variablen innerhalb einer Klasse sind für mich Instanzvariablen.)

Die Parameterübergabe sowohl von primitiven Datentypen (float, string, …) als auch von Objekten geschieht standardmäßig als Call-by-value. Auch Objekte werden nur dann by-reference übergeben, wenn dies durch das &-Zeichen in der Funktionssignatur explizit vermerkt ist. Der Inhalt (im Sinne von value) einer Objektvariable sind nicht die Instanzdaten selbst, sondern eine Art Ressourcen-Kennung (Object-Identifier), über die PHP auf die Instanzdaten zugreift.

// Call-by-value
function f($a) {
$a = null;
}

// Call-by-reference
function g(&$a) {
$a = null;
}

$s = 'foo'; f($s); var_dump($s); // string(3) "foo"
$s = 'foo'; g($s); var_dump($s); // NULL

$o = new stdClass(); f($o); var_dump($o); // object(stdClass)#1 (0) { }
$o = new stdClass(); g($o); var_dump($o); // NULL

- 4.1: Don't panic! The Hitchhiker's Guide to PHP oddities - php.de (http://www.php.de/adventskalender-2010/75163-4-1-dont-panic-hitchhikers-guide-php-oddities.html)

Noch kurz zur Verdeutlichung:

…aber trotzdem werden Objekte nicht immer gelöscht, nur weil man eine Variable auf NULL setzt…

Es sei denn, alle Variablen, die auf ein Objekt zeigen, sind Referenzen. (Nein, das ist nicht sonderlich realistisch.)

$a = new stdClass();
$b = $a;

$b = null;

var_dump($a, $b); // object(stdClass), NULL


$c = new stdClass();
$d = &$c;

$d = null;

var_dump($c, $d); // NULL, NULL

…oder unset() verwendet. Das sollte einem bewusst sein.

unset entfernt auch bei Referenzen lediglich das Symboltabellenalias, hat aber keinerlei Auswirkung auf den Inhalt.

$e = new stdClass();
$f = $e;

unset($f);

var_dump($e, $f); // object(stdClass), NULL


$g = new stdClass();
$h = &$g;

unset($h);

var_dump($g, $h); // object(stdClass), NULL

 
h3ll
07-09-2011, 14:40 
 
Ich kenn den Unterschied zwischen Referenz und Object-Identifier und zwischen unset und Zuweisung. Aber trotzdem danke für die ausführliche Schilderung. Dafür war ich selber zu bequem :)

Wie du schön gesagt hast, wenn man nur Referenzen verwendet, kann man Objekte (fast) zuverlässig löschen. Nur haben Referenzen so viele mögliche Seiteneffekte, dass man besser die Finger davon lässt.

 
mermshaus
07-09-2011, 14:47 
 
Offtopic:
Ja, ist mir klar, dass du den kennst. Ich dachte, weil wir hier die Begrifflichkeiten teilweise durcheinander gewürfelt haben, schreibe ich's mal einmal sauber auf.


Alle Zeitangaben in WEZ +2. Es ist jetzt 03:26 Uhr.