Basic structure of variables
We all know that PHP variables are weakly typed, and there is no need to specify the type when declaring them. So how is this implemented? This has to start with the basic structure of variables.
Implementation of zval
In the source code file zend_type.h, you can see the definition of zval:
typedef struct _zval_struct zval; struct _zval_struct { zend_value value; /* value */ union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar type, /* active type */ zend_uchar type_flags, zend_uchar const_flags, zend_uchar reserved) /* call info for EX(This) */ } v; uint32_t type_info; } u1; union { uint32_t next; /* hash collision chain */ uint32_t cache_slot; /* literal cache slot */ uint32_t lineno; /* line number (for ast nodes) */ uint32_t num_args; /* arguments number for EX(This) */ uint32_t fe_pos; /* foreach position */ uint32_t fe_iter_idx; /* foreach iterator index */ uint32_t access_flags; /* class constant access flags */ uint32_t property_guard; /* single property guard */ uint32_t extra; /* not further specified */ } u2; }
The zval structure consists of a value or pointer that holds the variable type. The union zend_value and the two unions u1 and u2 form
- u1
u1 is used to save the variable type and its information, and the fields inside it The uses are as follows:
type: record variable type. You can access
type_flags through u2.v.type: flags corresponding to the unique types of variables (such as constant types, reference counting types, immutable types). Different types of variables have different flags.
const_flags: constant type flags
reserved: reserved fields
- u2
u2 is mainly an auxiliary function, due to the structure The memory is aligned, so the space of u2 has already occupied space with or without u2, so it is used. The auxiliary field of u2 records a lot of type information, which is of great benefit to internal functions, or improves cache friendliness or reduces memory addressing operations. Some of these fields are introduced here.
next: Used to solve hash conflict problems (hash conflicts are not understood yet) and record the position of the next element of the conflict.
cache_slot: Runtime cache. When executing a function, it will first search in the cache. If it is not found in the cache, it will then search in the global function table.
num_args: The number of parameters passed in when the function is called
access_flags: The access flag of the object class, such as public protected private.
- zend_value
typedef union _zend_value { zend_long lval; /* 整型*/ double dval; /* 浮点型 */ zend_refcounted *counted; zend_string *str; zend_array *arr; zend_object *obj; zend_resource *res; zend_reference *ref; zend_ast_ref *ast; zval *zv; void *ptr; zend_class_entry *ce; zend_function *func; struct { uint32_t w1; uint32_t w2; } ww; } zend_value;
As can be seen from zend__value, long and double types store values directly, while other types are pointers, pointing to their respective structures. Therefore, due to the structure of zval, PHP variables do not need to explicitly specify their type when declaring it, because no matter what type of value you assign to the variable, it can help you find the corresponding storage structure.
Take a variable whose value is a string as an example. Its structure is as follows:
Comparison of the zval structure of PHP5 and PHP7
- PHP5
- PHP7
You can see that the zval of php7 only occupies 16 bytes in total, which saves a lot of memory compared to the 48 bytes occupied by the zval of PHP5.
In addition, in PHP5, all variables are allocated in the heap, but for temporary variables, there is no need to apply in the heap. Therefore, this has been optimized in PHP7, and temporary variables are applied directly on the stack.
Common variable types
The following introduces several common types of variable structures. For more other types, you can view the source code yourself.
Integers and floating point types
For integers and floating point types, due to their small space, the integer values are directly stored in zval and are stored in lval. Floating point The type value is stored in dval.
typedef union _zend_value { zend_long lval; /* 整型*/ double dval; /* 浮点型 */ ... }
String
A new string structure is defined in PHP 7. The structure is as follows:
struct _zend_string { zend_refcounted_h ; zend_ulong h; /* hash value */ size_t len; char val[1]; };
The meaning of each field above:
gc: Variable reference information, all variable types that use reference counting will have this structure.
h: Hash value, used when calculating index in array. (It is said that this operation improves the performance of PHP7 by 5%)
len: String length, binary security is ensured through this value
val: String content, variable length struct, press when allocating len length applies for memory
array
array is a very powerful data structure in PHP. Its underlying implementation is an ordinary ordered HashTable. Here is a brief look at its structure. More details will be given later.
typedef struct _zend_array HashTable; struct _zend_array { zend_refcounted_h gc; union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar flags, zend_uchar nApplyCount, zend_uchar nIteratorsCount, zend_uchar consistency) } v; uint32_t flags; } u; uint32_t nTableMask; Bucket *arData; uint32_t nNumUsed; uint32_t nNumOfElements; uint32_t nTableSize; uint32_t nInternalPointer; zend_long nNextFreeElement; dtor_func_t pDestructor; }
Object
The object structure of PHP7 has also been redesigned, which is very different from the implementation of PHP5.
struct _zend_object { zend_refcounted_h gc; uint32_t handle; zend_class_entry *ce; const zend_object_handlers *handlers; HashTable *properties; zval properties_table[1]; };
Here are some of the fields:
gc: gc header
*ce: Class corresponding to the object Class
*properties: HashTable Structure, key is the attribute name of the object, value is the offset of the attribute value in the properties_tables array, and the corresponding attribute value is found in properties_talbe through the offset.
properties_talbe[1]: Store the attribute value of the object
ok, let’s write this here first.