Rolf B: Daten eintragen (Lösungsansatz)

Beitrag lesen

Hallo Jörg,

aus einem Tippfehler. Ich hab das ein paar mal geändert. Es muss natürlich immer $mj heißen.

Übernimm meinen Code nicht unverstanden. Wie Du siehst, ist das riskant 😂

Was heißt Referenz? Lektüre

Zusammengefasst:

PHP speichert jeden Wert, den es verarbeitet, in einer bestimmten Datenstruktur. Jeden. Zahlen, Bools, Strings, Arrays, Objekte. Deine Variablen enthalten nicht die Werte selbst, sondern einen Verweis auf eine solche Datenstruktur, die PHP irgendwo im Hintergrund verwaltet.

$a = 7;

Dadurch werden 2 Dinge erzeugt: Die Datenstruktur, die Werte speichern kann (hier vom Typ INT und dem Wert 7), sowie die Variable $a, in der nur steht, wo sich diese Datenstruktur befindet.

Wenn Du $a an eine Funktion übergibst, wird nicht die Datenstruktur übergeben. Sondern die Info, wo sie sich befindet.

$a = 7;
foo($a);
echo $a;

function foo($b) {
   $b = 9;
}

D.h. wenn foo aufgerufen wird, steht in $b erstmal (fast) die gleiche Info drin wie in $a, nämlich die Position dieses int-Wertes 7. Das wäre so nicht gut, denn dann würde die Zuweisung, die in foo geschieht, diesen Wert verändern und der echo den Wert 9 ausgeben.

Sowas kann Absicht sein. Man spricht dann von einer Datenübergabe "by reference". Aber in den gängigen Programmiersprachen ist diese Form der Datenübergabe unerwünscht. Man möchte vielmehr nur den Wert übergeben und der Funktion nicht erlauben, Daten des Aufrufers zu verändern. Dann nennt man Übergabe "per value".

PHP übergibt aber trotzdem nur die Info, wo die 7 steht. Nun kommt das "fast" zum Tragen, was ich eben in Klammern schrieb. In $b steht nämlich zum einen die Info, wo der Wert steht, und zum anderen die Info: "NICHT ÄNDERN". Und das führt dazu, dass beim Versuch, $b zu verändern, die Wertestruktur mit der 7 darin kopiert wird und die 9 in die Kopie geschrieben wird. PHP nennt das "copy on write".

Das ist effizient. Solange nämlich die Parameterwerte nicht verändert werden, braucht PHP keine neue Wertestruktur anzulegen. Denn das macht Arbeit, das belegt Heap-Speicher, der nur für einen Moment gebraucht wird. Und wenn ich ein Array mit drölftausend ELementen übergebe, dann kostet das richtig viel Speicher. Für nix, wenn es nur gelesen wird.

Wenn ich also NächsterMonat "einfach so" programmiere und aufrufe, kann ich $mj ändern, wie ich will, die Änderung kommt beim Aufrufer nicht an. Weil der copy-on-write dafür sorgt, dass meine Änderungen am Array nur auf einer Kopie stattfinden.

Lösung 1: Ich gebe das veränderte $mj mit return zurück. Ich müsste NächsterMonat dann so aufrufen:

$lfd = NächsterMonat($lfd);

Das würde gehen. Eine Inkarnation des Arrays geht hinein, würde kopiert, geändert und eine andere Inkarnation kommt heraus. Aber es ist nicht effizient, weil für jede Änderung ein neues Array angelegt werden muss.

Auftritt der Referenz. Hier wird diese Info "NICHT ÄNDERN" nicht gesetzt. Gesteuert wird das durch das & in der Definition von NächsterMonat. Wenn ich dann NächsterMonat($lfd) aufrufe, enthält $mj exakt das Gleiche wie $lfd, und es gibt keinen copy-on-write. Es passiert aber was anderes. Die Wertestruktur, die das Array speichert, enthält nämlich nicht nur den Wert, sondern auch eine Info, wieviele Leute diesen Wert ändern dürfen. Dort steht normalerweise 1, aber wenn eine Referenz erzeugt wird, erhöht sich das. PHP braucht diese Info, um zu wissen, wann der Speicherplatz für's Array freigegeben werden kann.

Und jetzt kann NächsterMonat das übergebene Array verändern, und die Änderung ist ohne eine Kopierflut der ZEND Engine beim Aufrufer verfügbar.

Alles weiter in der verlinkten Lektüre 😉

Rolf

--
sumpsi - posui - obstruxi