Variables in PHP are some containers that store the type of the variable, its value, the amount of referring variables to this container, and the flag - whether this variable is referenced.
Structures and Pointers
Structures are very similar to classes, but they can not have methods, just data, pointers to data and pointers to functions. Declaring a structure in C, you define the data type. And when defining a variable, you can write the name of this structure in place of the type of that variable, like so:
my_super_struct super_struct_instance;
Pointers are like variables, but their values store an address in memory. Reference variables behave like dereferenced pointers, that means they access values of the pointer. Let's look at an example:
// defining pointer `foo`, that will points to the variable with `int` type
int *foo;
// defining variable with `int` type
int bar = 3;
// taking reference to variable `bar` and assigning it to pointer.
// `foo` stores memory address, where `bar` stores
foo = &bar;
// with an asterisk we dereference the pointer (take the value at its address) and increment the value
(*foo)++;
// we increment the pointer itself, that means the pointer will refer at another value
foo++;
Containers
The container is a structure called zval
(short for “Zend value”), it represents an arbitrary PHP value and looks like this:
struct zval {
zvalue_value value;
zend_uchar type;
zend_uchar is_ref;
zend_ushort refcount;
};
As we can see, there is a value, a type, a flag and an amount of referring variables. PHP zval
supports the following 8 types:
BOOL
-
LONG
(signed integer type) -
DOUBLE
(used to store floating point numbers) STRING
ARRAY
OBJECT
RESOURCE
NULL
zvalue_value
is an union
. A union is a special type that may have several members declarations of different types, but only one will be used. That how it defines:
typedef union _zvalue_value {
long lval; // integer
double dval; // float
struct {
char *val;
int len;
} str; // string
HashTable *ht; // array
zend_object obj; // object
} zvalue_value;
As a result, when you create a variable of this type, it will take in memory exactly as much as occupies the heaviest element of unions.
Why do we need all this
First, let's figure out why we need refcount. That's very simple: when you assign to a variable value of another variable, they both refer to one zval
, and refcount
increments.
Now, if you change the value of one of these variables, PHP seeing the refcount
greater than 1, will copy this zval
, make the changes there, and your variable will point already to the new zval
. It will look something like this:
PHP | Under the hood |
---|---|
|
|
|
|
This technique is called "copy on write" and it allows to reduce memory consumption quite well. Also, refcount
is needed for the garbage collector, which removes from memory all zval
s, which have refcount = 0
.
And what happens with references? And is_ref
is working? That's very simple: if you create a reference from a variable, the is_ref flag becomes 1, and the above optimization for this zval will not be applied.
PHP | Under the hood |
---|---|
|
|
|
|
|
|
Interesting in reading more about PHP? Follow this link to find more articles 😉
Top comments (0)