4

I am following a tutorial & reading through some articles on how variables are stored in PHP using zval (zend value) container. Most of those articles seem to be following & getting information from the documentation on php.net doc. I got curious about how zval struct is implemented in PHP's core, so I opened up the source code from PHP8 & it looks different than what those articles or the php.net are stating. For example, in php.net it states that zval container contains refcount and is_ref but those two are not part of the source code when I look at it in PHP8. So after some digging, I found out that refcount & is_ref were part of zval struct back in PHP5 but then changed in PHP7 which introduced performance optimizations including better memory management.

This is the struct code for zval from PHP5:

struct _zval_struct {
    zvalue_value value; 
    zend_uint refcount__gc;
    zend_uchar type;    
    zend_uchar is_ref__gc;
};

This is what it looks like since PHP7 in PHP8:

struct _zval_struct {
    zend_value        value;            /* value */
    union {
        uint32_t type_info;
        struct {
            ZEND_ENDIAN_LOHI_3(
                zend_uchar    type,         /* active type */
                zend_uchar    type_flags,
                union {
                    uint16_t  extra;        /* not further specified */
                } u)
        } v;
    } u1;
    union {
        uint32_t     next;                 /* hash collision chain */
        uint32_t     cache_slot;           /* cache slot (for RECV_INIT) */
        uint32_t     opline_num;           /* opline number (for FAST_CALL) */
        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     property_guard;       /* single property guard */
        uint32_t     constant_flags;       /* constant flags */
        uint32_t     extra;                /* not further specified */
    } u2;
};

So I am trying to find some updated/relevant information about this and can't seem to find a good explanation that would include some illustrations on how it compares to PHP5 or is documentation on php.net still relevant? Does ref counting no longer matter since PHP7 or how does it handle the references now. Before it was relying on is_ref to determine whether it should copy on write or simply point to the same zval container. How does the copy on write mechanism work now?

For example:

$a = 1;
$b = $a;

This would in PHP5 have a single zval container where both $x & $y symbols would be pointing to, it would only create a copy of the container if either $a or $b changed the value but since PHP7 from my understanding it will have two two zval containers from the beginning since the value is a simple scalar type int. When does it do copy on write?

I don't know C language, which is why I'm having a hard time understanding whats going on by simply looking at the source code and the information I find online does not line up with the current state of PHP.

9
  • 1
    @prieber because it does not line up with the source code. I know little bit of C but not to the point where I can read & understand everything. But I know enough to tell that what documentation is stating is different from whats in the code. As I showed code above, the current code in PHP does not have refcount & is_ref fields so Im not sure how it does ref counting & how references work now
    – Fuze
    Commented May 16, 2021 at 2:51
  • 2
    If you think it’s wrong, tell them - php.net/get-involved.
    – prieber
    Commented May 16, 2021 at 3:45
  • 2
    phpinternalsbook.com/php7/zvals/memory_management.html (and the next page) may be of use. The core idea of refcounting stays the same, but gets shifted out of the zval into the held structure.
    – NikiC
    Commented May 16, 2021 at 19:04
  • 1
    Yeah, that's right.
    – NikiC
    Commented May 17, 2021 at 7:36
  • 1
    Each variable always has a separate zval, independent of the type. In this case, both $a and $b are separate zvals pointing to the same object.
    – NikiC
    Commented May 17, 2021 at 21:39

0

Browse other questions tagged or ask your own question.