PHP zvals und Referenzen erklärt

Tags:

Heute schauen wir uns kurz an, wie PHP Variablen speichert und wie Referenzen funktionieren. Los geht's.

Symbols und zvals

Jede PHP Variable besteht aus einem Symbol (dem Name der Variable) und einer zval (Zend Value), einem speziellen Daten-Container. Diese zval speichert nebst dem Datentyp und dem Wert, auch wie viele Symbols sie hat (refcount) und ob diese Symbols als Referenzen definiert wurden (is_ref).

Im PHP Souce Code ist die zval einfach eine simple Struktur:

typedef struct _zval_struct {
    zvalue_value value;
    zend_uint refcount;
    zend_uchar type;
    zend_uchar is_ref;
}

refcount

refcount ist wichtig, damit PHP für gleiche Variablen (im Sinne von $a = $b = 42) dieselben Daten nicht mehrmals speichern muss.

Zwei Variabeln

Der refcount-Zähler wird also inkrementiert, wenn ein neues Symbol hinzugefügt wird und dekrementiert, falls ein Symbol gelöscht wird (z.B. mit unset()). Wenn refcount Null (0) erreicht, kann die zval aus dem Speicher gekickt werden. Achtung: Die zval weiss nicht welche Symbols auf sie zeigen.

is_ref

Das is_ref-Flag wird gesetzt, fall eine Variable einer anderen per Referenz zugewiesen wurde.

Zwei Variabeln per Referenz

Wieso dieses Flag so wichtig ist, sehen wir gleich.

Copy-on-Write

Wir haben also gesehen, dass PHP versucht Arbeitspeicher zu sparen und unnötiges Kopieren zu verhindern. Doch wann wird kopiert? Nun, zvals (oder die "Daten") werden dann kopiert, wenn nicht mehr sichergestellt werden kann, dass alle Symbols auf die selben Daten verweisen werden oder können. Also z.B. wenn $a und $b zuerst gleich sind, danach aber $b geändert wird.

Zwei Variablen erst gleich, danach eine geändert

Solange $a und $b gleich sind, gibt es keinen Grund die Daten zu kopieren. Wird $b aber verändert, muss die zval kopiert werden. Daneben gibt es noch zwei andere Szenarien, wann zvals aufgeteilt werden müssen. Bei beiden geht es darum, wenn ein drittes Symbol ins Spiel kommt.

Referenz auf is_ref = 0

Nehmen wir an, dass $a und $b "gleich" sind. Würde $c nun ebenfalls $a zugewiesen, würde einfach ein weiteres Symbol erstellt werden. Wenn aber $c per Referenz an $a (oder $b) zugewiesen wird, muss PHP die zval kopieren. Denn $c soll ja nur die selben Daten wie $a referenzieren, nicht aber wie $b.

Zwei Variablen, geteilt durch neue Referenz

Zuweisung auf is_ref = 1

Und dann gibt es noch das Gegenteil davon: Zwei Variablen $a und $b referenzieren sich, und eine dritte Variable $c wird normal $a (oder $b) zugewiesen. Da die Referenz zwischen $a und $b nicht zerstört werden darf, muss $c einen eigenen Daten-Container nutzen.

Zwei referenzierende Variablen, geteilt durch neue Variable

Dieses Verfahren, dass Daten nicht kopiert werden, solange es nicht nötig ist, nennt sich copy-on-write oder copy-on-change.

Wann wird kopiert?

Bei PHP gibt es dazu ein paar einfache Grundregeln.

Wenn Variablen einander zugewiesen werden, sehen die Regeln so aus:

Wenn refcount = 1:

  • Erfolgt die Zuweisung normal wird refcount auf 2 gesetzt und is_ref bleibt 0
  • Erfolgt die Zuweisung per Referenz wird refcount auf 2 gesetzt und is_ref auf 1

Wenn refcount grösser 1:

  • Erfolgt die Zuweisung normal und is_ref ist 0, kann refcount um 1 erhöht werden. Ist is_ref aber 1, dann muss eine neue zval erstellt werden.
  • Erfolgt die Zuweisung per Referenz und is_ref ist 1, kann refcount um 1 erhöht werden. Ist is_ref aber 0, dann muss die bestehende zval aufgeteilt werden.

Werden Variablen neue Werte zugewiesen, sehen die Regeln so aus:

Wenn refcount = 1:

  • Der bestehende Wert und Datentyp kann überschrieben werden.

Wenn refcount grösser 1:

  • Wenn is_ref gleich 0 ist, dann muss eine neue zval erstellt werden.
  • Wenn is_ref gleich 1 ist, dann kann der bestehenden Wert und Datentyp überschrieben werden.

Eigentlich ganz simpel, und doch können damit so viele Kombinationen abgedeckt werden. :)

Ähnliche Artikel

Kommentare