PHP/Tutorials/Einführung in die Interna/Werte und Datenstrukturen in PHP

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Die zval-Datenstruktur

Die zval-Datenstruktur ist ein Container, der für alle Werte in PHP verwendet wird. Um jedoch zu verstehen, wie der aufgebaut ist, sollte vorher noch einmal kurz dargestellt werden, welche Datentypen es denn in PHP gibt:

Datentyp Beschreibung
null Der Wert, der Undefiniertheit darstellen soll.
integer Eine Ganzzahl
float Eine Gleitkommazahl
boolean Wahr (true) oder falsch (false)
array Ein indiziertes oder assoziatives Array
object Ein Objekt
string Eine Zeichenkette
resource Eine Ressource, d. h. ein Zeiger auf eine C-interne Datenstruktur

Die zval-Struktur ist nun in Zend/zend.h in Zeile 334 definiert:

Beispiel
 struct _zval_struct {
     /* Variable information */
     zvalue_value value;		/* value */
     zend_uint refcount__gc;
     zend_uchar type;	/* active type */
     zend_uchar is_ref__gc;
 };


Eintrag Beschreibung
value Der Wert, der angenommen wird (zvalue_value ist eine union)
refcount__gc Wie oft die Struktur referenziert wird
type Der Datentyp des Werts (eine Konstante, siehe unten)
is_ref__gc Ob die Struktur eine Referenz ist oder nur eine Lazy-Copy (siehe den Abschnitt zu Copy-on-write)

Die union, die den eigentlichen Wert enthält, ist wie folgt definiert (Zend/zend.h, Zeile 323):

Beispiel
 typedef union _zvalue_value {
      long lval;					/* long value */
      double dval;				/* double value */
      struct {
         char *val;
         int len;
     } str;
     HashTable *ht;				/* hash table value */
     zend_object_value obj;
 } zvalue_value;

In der Zend/zend.h ab Zeile 527 sind außerdem noch Konstanten definiert, die den Datentyp definieren. Das Feld type nimmt einen dieser Werte an. Im folgenden sind nur die Konstanten enthalten, die für Datentypen relevant sind, die anderen Konstanten, die für Spezialfälle zuständig sind, werden an geeigneter Stelle besprochen:

Beispiel
 /* data types */
 /* All data types <= IS_BOOL have their constructor/destructors skipped */
 #define IS_NULL		0
 #define IS_LONG		1
 #define IS_DOUBLE	2
 #define IS_BOOL		3
 #define IS_ARRAY	4
 #define IS_OBJECT	5
 #define IS_STRING	6
 #define IS_RESOURCE	7

Folgende Tabelle mapped nun die verschiedenen Datentypen auf die verschiedenen Felder der union und die verschiedenen Konstanten:

Datentyp Typ-Konstante Feld in der union
null IS_NULL -
integer IS_LONG lval
float IS_DOUBLE dval
boolean IS_BOOL lval
array IS_ARRAY ht
object IS_OBJECT obj
string IS_STRING str
resource IS_RESOURCE lval

Wie man hier sehen kann wird lval mehrfach verwendet, nämlich für die Datentypen integer, boolean (hier wird jede Zahl ungleich 0 als true interpretiert und 0 als false) sowie resource. Bei Ressourcen ist die Zahl ein Index, mit dem die eigentliche Datenstruktur hinter der Ressource abgerufen werden kann.

Zusätzlich ist anzumerken, dass PHP-Strings binärsicher sind, weil die Länge mitgespeichert wird. Es ist also für PHP-Strings problemlos möglich, dass sie 0-Bytes enthalten.

Zugriffsmakros auf zvals

Das Zend-API stellt etliche Makros zur Verfügung, mit denen man auf Teile der Datenstruktur zugreifen kann. Die Makros heißen alle Z_XXXX_SUFFIX, wobei XXXX das ist, worauf man zugreifen will während SUFFIX entweder weggelassen wird (dann nur Z_XXXX) oder wahlweise P oder PP sein kann. Wenn das Suffix weggelassen wird, ist das Argument des Makros eine zval-Struktur, wenn das Suffix ein P ist, dann ist das Argument ein Zeiger auf eine zval-Struktur. Und wenn das Suffix ein PP ist, dann ist das Argument ein Zeiger auf einen Zeiger auf eine zval-Struktur. Die Makros sind so konzipiert, dass man ihnen auch etwas zuweisen kann, d. h. Z_LVAL(zv) = 0; ist möglich.

Die Makros sind alle in Zend/zend_operators.h ab Zeile 389 definiert. Folgende Tabelle stellt dar, worauf sie zugreifen.

Makro Zugriff auf Beschreibung
Z_TYPE type Greift auf das Typ-Feld eines Wertes zu
Z_LVAL value.lval Greift auf den Wert einer Ganzzahl zu
Z_BVAL (zend_bool)value.lval Interpretiert value.lval als boolschen Wert
Z_DVAL value.dval Greift auf den Wert einer Gleitkommazahl zu
Z_STRVAL value.str.val Greift auf char*-Zeiger einer Zeichenkette zu
Z_STRLEN value.str.len Greift die Länge einer Zeichenkette zu
Z_ARRVAL value.ht Greift die HashTable*-Struktur eines Arrays zu
Z_OBJVAL value.obj Greift die zend_object_value-Struktur eines Objekts zu
Z_OBJ... value.obj.XXX Vereinfachungsmakros für Zugriff auf bestimmte Aspekte eines Objekts (siehe OOP für Details)
Z_RESVAL value.lval Greift auf den Index einer Ressource zu

Vordefinierter Wert: uninitialized_zval_ptr

Es gibt in PHP einen speziellen vordefinierten Wert für nichtinitialisierte Variablen. Der wird in EG(uninitialized_zval_ptr) abgelegt. Dieser Wert hat immer den Typ null. Dies hat den Vorteil, dass man im Code einfach auf diesen zval verweisen kann, wenn man eine uninitialisierte Variable irgendwo temporär ablegen muss (dies geschieht im Code an einigen Stellen), was Speicher spart. Der Nachteil ist, dass bei Zuweisungen kontrolliert werden muss, ob auf dieses spezielle zval gezeigt wird. Sollte dies der Fall sein, muss ein neuer zval angelegt werden. Die zuständigen PHP-Makros übernehmen das jedoch bereits für einen.

GDB-Makro zum Darstellen von zvals

Es gibt ein GDB-Makro in der .gdbinit-Datei von PHP welches bei der Darstellung von zvals hilft. Man übergibt ihm den Zeiger auf eine zval-Struktur und das Makro gibt den eigentlichen Inhalt des Werts aus. Folgendes Beispiel demonstriert dies an Hand einer Beispiel-GDB-Sitzung:

Beispiel
 PHP-Array: $x = array (100, 200, 300);
 
 GDB-Sitzung:
 (gdb) print $5
 $7 = (struct _zval_struct *) 0xa30cb78
 (gdb) printzv $5
 [0x0a30cb78] (refcount=1) array(3): {
     0 => [0x0a30b474] (refcount=1) long: 100
     1 => [0x0a30b4b8] (refcount=1) long: 200
     2 => [0x0a30ca88] (refcount=1) long: 300
   }

Werte allozieren und initialisieren

Wenn man eine zval-Struktur nur in der eigenen Funktion braucht, reicht es aus, sie auf dem Stack zu deklarieren und dann das Makro INIT_ZVAL (Zend/zend.h, Zeile 685) aufzurufen, das den Wert der zval mit einer Standard-zval überschreibt, die keine Referenz ist, deren Referenzierungscounter auf 1 gesetzt ist und deren Datentyp null ist. Beispiel:

Beispiel
 zval myval;
 INIT_ZVAL(myval);

Wenn der Wert länger leben soll oder man ihn bestimmten Funktionen übergeben will, die diesem Wert eventuell eine Referenz zuweisen, sollte man den Wert lieber auf dem Stack allozieren – und dafür gibt es ein vordefiniertes Makro ALLOC_INIT_ZVAL (Zend/zend.h, Zeile 687):

zval *p_myval;
ALLOC_INIT_ZVAL(p_myval);

Dieses Makro selbst ruft zuerst das Makro ALLOC_ZVAL auf (Zend/zend_alloc.h, Zeile 166), damit die Variable auf eine gültige zval-Datenstruktur zeigt, und dann INIT_ZVAL, um den Wert zu initialisieren.

Befüllen mit Werten

Um einen frisch allozierten Wert mit Daten zu füllen, stehen verschiedene Makros bereit, die das übernehmen. Sie sind in zend_API.h ab Zeile 513 definiert. Als ersten Parameter erwarten sie einen Zeiger auf eine zval-Struktur, alle weitere Parameter sind makrospezifisch. Die folgende Tabelle listet diese Makros auf:

Makro Beschreibung Beispiel
ZVAL_RESOURCE(z, l) Erzeugt eine Ressource vom Index l ZVAL_RESOURCE(p_myval, my_rsrc_index);
ZVAL_BOOL(z, b) Erzeugt einen boolschen Wert b ZVAL_BOOL(p_myval, 1); /* true */
ZVAL_NULL(z) Erzeugt einen null-Wert ZVAL_NULL(p_myval);
ZVAL_LONG(z, l) Erzeugt eine Ganzzahl mit Wert l ZVAL_LONG(p_myval, 42);
ZVAL_DOUBLE(z, d) Erzeugt eine Gleitkommazahl mit Wert d ZVAL_DOUBLE(p_myval, 42.0);
ZVAL_STRING(z, s, duplicate) Erzeugt eine Zeichenkette mit Wert s, wenn duplicate != 0, dann wird der Inhalt der Zeichenkette per estrdup kopiert. ZVAL_STRING(p_myval, "Hallo Welt!\n", 1);
ZVAL_STRINGL(z, s, l, duplicate) Erzeugt eine Zeichenkette mit Wert s und Länge l, wenn duplicate != 0, dann wird der Inhalt der Zeichenkette per estrndup kopiert. ZVAL_STRING(p_myval, "Hallo Welt!\n", 12, 1);
ZVAL_EMPTY_STRING(z) Erzeugt eine leere Zeichenkette ZVAL_EMPTY_STRING(p_myval);
ZVAL_ZVAL(z, zv, copy, dtor) Kopiert einen Wert (zv) in den Zielwert (z), allerdings auf eine spezifische Weise, die nicht allgemein funktioniert, siehe unten. Nur mit Bedacht anwenden! ZVAL_ZVAL(p_myval, &myval, 1, 0);
Beachten Sie: Diese Makros gehen davon aus, dass das Ziel-zval leer ist. Wenn vorher ein anderer Wert enthalten war, dann entsteht ein Speicherleck, da der andere Wert nicht korrekt vernichtet wird!

Weblinks