Peter Thomassen: Pass-by-Reference automatisch?

Hallo ihr PHP-Spezialisten,

unter http://php.net/features schieb jemand vor drei Jahren Folgendes:

| PHP4 also have reference count feature. For example, memory for variables is
| shared when it assigned to other variable. If contents has been changed, PHP4
| allocate new memory for it.
|
| For example, programmer does not have to use pass by reference for large
| parameters for better performance with PHP4.

Ist das wirklich der Fall? Sonst liest man doch oft, dass man Pass-by-Reference verwenden solle, um zu verhindern, dass Variablendaten unnötig kopiert werden und so die Geschwindigkeit beeinträchtigen; hier steht nun, dass das automatisch passiert, sofern die neue Variable nicht verändert wird. Aber dass o.g. Beitrag ohne Beantstandung durch andere PHP-Leser schon 3 Jahre durchgehalten hat, macht mich stuzig ...

Simmt das?

Danke!
Peter

  1. Halihallo Peter

    PHP4 also have reference count feature. For example, memory for variables is
    shared when it assigned to other variable. If contents has been changed, PHP4
    allocate new memory for it.
    |
    For example, programmer does not have to use pass by reference for large
    parameters for better performance with PHP4.

    Ist das wirklich der Fall? Sonst liest man doch oft, dass man Pass-by-Reference verwenden solle, um zu verhindern, dass Variablendaten unnötig kopiert werden und so die Geschwindigkeit beeinträchtigen; hier steht nun, dass das automatisch passiert, sofern die neue Variable nicht verändert wird. Aber dass o.g. Beitrag ohne Beantstandung durch andere PHP-Leser schon 3 Jahre durchgehalten hat, macht mich stuzig ...

    Das ist IMHO schon richtig. Ich habe mich zwar noch nie durch den PHP
    Quellcode gekämpft, weiss jedoch in etwa wie dies alles funktioniert:

    Ein kleiner Exkurs gefällig? : ;)

    Eine Variable in PHP/Perl/... ist nichts anderes als ein Pointer auf
    einen Speicherbereich, welcher z.B. die aktuelle Länge, die Daten an
    sich, ob es eine Referenz ist, ob die Variable eine Klasseninstanz
    ist (wenn ja, von welcher) und den Reference Counter enthält. Falls
    man also z.B.

    $a=5;

    schreibt, wird ein Speicherbereich alloziiert (reserviert, geholt,
    erstellt), welcher den Wert '5' aufnehmen kann. Zudem wird der
    Reference Counter auf 1 gesetzt, da die Variable $a jetzt
    genau "einmal existiert". Falls nun folgendes geschieht:

    $a=5;
    $b=$a;

    Nun, es ist klar, dass $a und $b denselben Wert haben. Muss jetzt
    wirklich jeweils Speicher für $a _und_ $b reserviert werden? - Nein.
    Solange die beiden Variablen denselben Wert enthalten (also nicht
    geändert werden), können $a und $b auf denselben Speicherbereich
    verweisen, wo '5' definiert ist; PHP muss sich nur merken, wie viele
    "Variablen" (oder eben besser: Pointer/Zeiger) auf diesen
    Speicherbereich referenzieren, denn sonst kann PHP nicht wissen, wann
    der Speicher wieder freigegeben werden darf. Falls jedoch $b der
    Wert 7 zugewiesen wird, kann dies nicht einfach im gemeinsamen
    Speicherbereich von $a und $b geschehen, sonst würde $a auch 7
    enthalten. Folglich muss hier bei einer Änderung neuer Speicher
    alloziiert werden und $b weist nun auf diesen Speicher[1].

    So, nun was bringt dir das?

    1. Bei alten PHP-Versionen - wie ich aus dem Posting folgere - wird
       bei jeder Zuweisung neuer Speicher alloziiert.

    $a = 5;     // einmal Speicher für $a, sprich der Wert '5'
       $b = $a;    // einmal Speicher für $b, auch für Wert '5'

    2. Funktionsaufrufe:

    1. $a = 5;
    2. function getA($b) {
      4)   each $b
    3. }
    4. getA($a);
    5. undef($a);   // $a ist nun nicht mehr definiert

    was geschieht hier?

    1) Speicher für den Wert '5' wird alloziiert, $a entspricht diesem
        Speicherbereich (auf der Ebene von C ist es ein Pointer auf den
        Speicherbereich, s. [1])
     3) Das erste Argument wird in $b gespeichert. Nach der Kenntnis von
        oben braucht man also nicht den gesamten Inhalt von $a zu
        kopieren und in $b zu speichern, da $b nach getA($a) genau
        denselben Wert wie $a hat. $a und $b zeigen[1] einfach auf
        denselben Speicherbereich. Wenn man aber in getA() $b verändert,
        dann muss (frühestens) neuer Speicher alloziiert werden, um den
        neuen zugewiesenen Wert zu speichern.
     5) Die lokale Variable $b verlässt den Funktion-Scope und wird somit
        vernichtet. Der ReferenceCounter des Speicherbereichs für den
        Wert '5' wird also um 1 dekrementiert, da er nurmehr von $a
        "verwendet" wird.
     7) Funktionsaufruf. Der ReferenceCounter von $a wird um 1 erhöht,
        denn der Inhalt von $a wird nun auch von durch Variable $b
        "verwendet".
     8) Bei undef() (Perl-ish) wird eine Variable undefiniert, sprich
        sie existiert nachher nicht mehr. Zurück zu unserem PHP-Kontext
        hiesse dies jedoch nicht, dass der Wert nicht mehr anderswo
        noch definiert ist. Es findet hier lediglich ein weiterer
        decrement des Reference-Counters statt, in dem Beispiel oben
        würde er zurück auf 0 gehen. 0 heisst für PHP: Es gibt keine
        PHP-Variable mehr, die auf diesen Speicherbereich, wo '5'
        gespeichert ist, verweist und somit kann auch der gesamte
        Speicherbereich für den Wert '5' freigegeben werden.

    Als zusätzliche Lektüre empfehle ich:
    http://ch2.php.net/manual/de/language.references.php
    insbesondere:
      http://www.zend.com/zend/art/ref-count.php

    [1] Die Zuweisung oder die Referenz ist im PHP-Script selber
    unsichtbar, dies geschieht auf der Ebene von C. Also nicht mit einer
    PHP-Skalar-Referenz verwechseln. Ebenso will ich hier nicht den
    Glauben herbeiführen, dass jeder vorkommende Wert genau in einem
    Speicherbereich gespeichert ist, sondern jede Zuweisung einer
    Variable zu einer anderen wird einfach durch ReferenceCounter auf
    den neu-gemeinsamen Speicher gelöst.

    Also: Ja, "Call-By-Reference" sind implizit, weil jede
    Variablenzuweisung nur noch über inkrementieren und Zuweisung des
    selben Speicherbereichs gelöst ist und nicht mehr einfach der gesamte
    Inhalt kopiert wird, früher musste so ja bei jedem FunctionCall die
    Variable kopiert werden, da die Variable in der Funktion verändert
    werden konnte, _ohne_ Einfluss auf die Variable des caller-Kontextes,
    ohne ReferenceCounter oder anderem GarbageCollection artet dies
    eben in wilde Kopieraktionen bei jedem Funktionsaufruf aus (so wie
    bei Call-By-Value).

    Ich möchte dir jedoch darin wiedersprechen, dass dies "Call-By-
    Reference automatisch?" ist. Es ist eben nicht Call-By-Reference,
    sondern eine Konsequenz in der PHP-internen Repräsentation von Werten
    und Variablen.

    Hope That Helped ;)

    Viele Grüsse

    Philipp

    --
    If a project is completed on schedule, it wasn't debugged properly.