$obj1 = new StdClass(); $obj2 = new StdClass(); $obj1->value = 1; $obj2->value = 1; function f1($o) { $o = 100; } function f2($o) { $o->value = 100; } f1($obj1); f2($obj2); var_dump($obj1); var_dump($obj2);
IS_TYPE
. For example, IS_NULL
corresponds to the data type null, and IS_STRING
to the string. typedef union _zvalue_value { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object_value obj; } zvalue_value;
value.lval
data member value.lval
been assigned a value, then you can use only value.lval
to access the data, access to other data members is invalid and can lead to unpredictable program behavior. The reason for this is that the unions store the data of all their members in the same memory area and interpret the value differently based on the name you are referring to. The size of the memory allocated for the union corresponds to the size of its largest data member.IS_NULL
: it should not store any value, since it is just null.IS_LONG
and IS_DOUBLE
, which use the members long lval
and double dval
respectively. The first is used to store integers, the second - for floating point numbers.LONG_MIN
and LONG_MAX
and the size of this type can be determined using the SIZEOF_LONG
macro (unlike the sizeof(long)
this macro can be used in #if
directives).double
data type is intended for storing floating point numbers and, usually, following the IEEE-754 specification, it is 8 bytes in size. Details of this format will not be discussed here, but you should at least be aware that this type has limited accuracy and often stores not exactly the value you are counting on.IS_BOOL
flag and are stored in the long val
field as 0
(false) and 1
(true). Since this type uses only 2 values, then, theoretically, it was enough to use a smaller type (for example, zend_bool), but since zvalue_value is a union and under it the memory size corresponding to the largest data member is allocated, the use of a more compact variable for boolean values will not save memory. Therefore, lval is reused in this case.IS_STRING
) are stored in a struct {char *val; int len; } str;
struct {char *val; int len; } str;
that is, the string is stored as a pointer to the char *
string and the integer length of the int
string. Strings in PHP must explicitly store their length in order to be able to contain NUL bytes (\ 0) and be binary safe (binary safe). But despite this, the lines used in PHP still end with a null byte (NUL-terminated) to ensure compatibility with library functions that do not accept an argument with a string length, but expect to find a zero byte at the end of the string. Of course, in such cases the strings can no longer be binary safe and will be truncated to the first occurrence of the zero byte. For example, many of the functions associated with the file system and most of the string functions from libc behave in this way.foo
is 3, despite the fact that 4 bytes are used to store it. If you define the length of a string using sizeof
you need to subtract the unit: strlen("foo") == sizeof("foo") - 1
.IS_ARRAY
label and are stored in the data member HashTable *ht
. How the HashTable data structure works is covered in another article.IS_OBJECT
) use the data member zend_object_value obj
, which consists of an “object handle” (an integer ID used to search for real data) and a set of “object handlers” that determine the behavior of the object. The system of classes and objects in PHP will be described in the “Classes and Objects” chapter.IS_RESOURCE
) are similar to objects, since they also store a unique ID used to look up the value. This ID is stored in the long lval member. Resources will be described in the relevant chapter, which has not yet been written.Type tag | Storage location |
IS_NULL | none |
IS_BOOL | long lval |
IS_LONG | long lval |
IS_DOUBLE | double dval |
IS_STRING | struct { char *val; int len; } str |
IS_ARRAY | HashTable *ht |
IS_OBJECT | zend_object_value obj |
IS_RESOURCE | long lval |
typedef struct _zval_struct { zvalue_value value; zend_uint refcount__gc; zend_uchar type; zend_uchar is_ref__gc; } zval;
zend_uchar type
. In addition, this structure contains 2 additional properties whose names end with __gc
, which are used by the garbage collection mechanism. These properties are discussed in more detail in the next section. <?php $a = 1; $b = $a; $a++; // $a 1, $b : var_dump($a, $b); // int(2), int(1) function inc($n) { $n++; } $c = 1; inc($c); // $c $n — var_dump($c); // int(1)
<?php $obj = (object) ['value' => 1]; function fnByVal($val) { // , object integer $val = 100; } function fnByRef(&$ref) { $ref = 100; } // , , $obj, — : fnByVal($obj); var_dump($obj); // stdClass(value => 1), fnByVal fnByRef($obj); var_dump($obj); // int(100)
<?php $obj = (object) ['value' => 1]; function fnByVal($val) { // , $val->value = 100; } var_dump($obj); // stdClass(value => 1) fnByVal($obj); var_dump($obj); // stdClass(value => 100), fnByVal
&
), but simply an indicator saying that someone (variable, function, etc.) uses this zval. The number of such links is called refcount
and it is stored in the data member refcount__gc
zval. <?php $a = 1; // $a = zval_1(value=1, refcount=1) $b = $a; // $a = $b = zval_1(value=1, refcount=2) $c = $b; // $a = $b = $c = zval_1(value=1, refcount=3) $a++; // $b = $c = zval_1(value=1, refcount=2) // $a = zval_2(value=2, refcount=1) unset($b); // $c = zval_1(value=1, refcount=1) // $a = zval_2(value=2, refcount=1) unset($c); // zval_1 , refcount=0 // $a = zval_2(value=2, refcount=1)
refcount
incremented by one, when the link is deleted - the refcount
decreases. When the value of refcount
reaches 0 - zval is deleted. <?php $a = []; // $a = zval_1(value=[], refcount=1) $b = []; // $b = zval_2(value=[], refcount=1) $a[0] = $b; // $a = zval_1(value=[0 => zval_2], refcount=1) // $b = zval_2(value=[], refcount=2) // refcount zval_2 // zval_1 $b[0] = $a; // $a = zval_1(value=[0 => zval_2], refcount=2) // $b = zval_2(value=[0 => zval_1], refcount=2) // refcount zval_1 // zval_2 unset($a); // zval_1(value=[0 => zval_2], refcount=1) // $b = zval_2(value=[0 => zval_1], refcount=2) // refcount zval_1 , zval // zval_2 unset($b); // zval_1(value=[0 => zval_2], refcount=1) // zval_2(value=[0 => zval_1], refcount=1) // refcount zval_2 , // zval_1
&$var
, and not those that were discussed above) that needs to be considered. To indicate that zval is used as a PHP reference, the is_ref__gc
flag is is_ref__gc
in the zval structure.is_ref=1
this is a signal that zval should not be copied before the modification, instead, the value of zval should be changed: <?php $a = 1; // $a = zval_1(value=1, refcount=1, is_ref=0) $b =& $a; // $a = $b = zval_1(value=1, refcount=2, is_ref=1) $b++; // $a = $b = zval_1(value=2, refcount=2, is_ref=1) // is_ref=1 PHP zval //
$a
has a refcount=1
before creating the link. Now consider a similar example with the number of links greater than 1: <?php $a = 1; // $a = zval_1(value=1, refcount=1, is_ref=0) $b = $a; // $a = $b = zval_1(value=1, refcount=2, is_ref=0) $c = $b // $a = $b = $c = zval_1(value=1, refcount=3, is_ref=0) $d =& $c; // $a = $b = zval_1(value=1, refcount=2, is_ref=0) // $c = $d = zval_2(value=1, refcount=2, is_ref=1) // $d $c, ** $a and $b, // zval . // zval is_ref=0 is_ref=1. $d++; // $a = $b = zval_1(value=1, refcount=2, is_ref=0) // $c = $d = zval_2(value=2, refcount=2, is_ref=1) // 2 zvals $d++ // $a $b ( ).
is_ref=0
and refcount>1
requires creating a copy. Similarly, when using zval with is_ref=1
and refcount>1
in context with passing by value, a copy operation is required. For this reason, using PHP links usually slows down the code. Almost all functions in PHP use the transfer semantics by value, so they create a copy when they receive a zval with the value is_ref=1
.Source: https://habr.com/ru/post/226707/
All Articles