PHP/Tutorials/Einführung in die Interna/Werte und Datenstrukturen in PHP
Inhaltsverzeichnis
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:
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
):
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:
/* 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 zval
s
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 zval
s
Es gibt ein GDB-Makro in der .gdbinit
-Datei von PHP welches bei der Darstellung von zval
s 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:
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:
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);
|
zval
leer ist. Wenn vorher ein anderer Wert enthalten war, dann entsteht ein Speicherleck, da der andere Wert nicht korrekt vernichtet wird!Weblinks
- Nikita Popov: PHP's new hashtable implementation
- Nikita Popov: Internal value representation in PHP 7 - Part 1
- Nikita Popov: Internal value representation in PHP 7 - Part 2