The problem

In the previous chapter, we said that REFERENCE was a flag bit in PHP5, but after PHP7 we changed it to a new type :IS_REFERNCE. However, reference is a very common application, so this change brought a lot of changes, and also caused a lot of bugs when we were doing PHP7 development, because we sometimes neglected to deal with this type, and brought a lot of bugs.

In the simplest case, when dealing with various types, we have to consider the new types from now on. In PHP7, for example, this form of code becomes common:

try_again: swtich (Z_TYPE_P(zv)) { case IS_TRING: break; case IS_ARRAY: break; . case IS_REFERENCE: zv = Z_REFVAL_P(zv); // Dereferences goto try_again; break; }Copy the code

If you write your own extensions and forget to take this new type into account, that can cause problems.

Why is that?

So with all the problems with this new type, why did it bother to make the reference a type? Why not just use a flag bit? In short, we have to do this. As mentioned earlier, Hashtable stores zVal directly, so how can two Zval share the same value in the symbol table? It looks like we can resolve some of the more complex types like strings by including a flag bit in the Zend_refcounted structure to indicate that it’s a reference, but this will also get copied by Change On Write, but as we know in PHP7, Some types are stored directly in zVal, such as IS_LONG, but reference types require reference counting. What about a Zval that is IS_LONG and is also IS_REFERNCE? To that end, we created this new genre:

For zval of IS_REFERNCE type, zval.value.ref is a pointer to zend_Reference, which contains the reference count and a Zval. Zval = zval.value.ref->val; So for an IS_LONG reference, we use a Zval of type IS_REFERNCE, which refers to a Zend_reference, and zend_Reference ->val is a ZVal of type IS_LONG.

Change On Write

PHP uses reference counting to do simple garbage collection. Consider the following code:

<? php 1\. $val = "laruence"; 2\. $ref = &$val; 3\. $copy = $val; ? >Copy the code

Ref and ref and ref and val are references to the same zval. In PHP5, we do this by having a reference count of 2 and a reference flag bit of 1. When val is copied to val and val is copied to copy(line 3), We find that $val is a reference with a count greater than 1, so we produce Change on write, which is separation. So we need to copy this zval.

In PHP7, the situation is much simpler. First, when the reference is assigned to $ref(line 2), it generates an IS_REFERNCE type, Zval.value.ref ->gc.refcount = 2; zval.value.ref->gc.refcount = 2;

$val = zval.value.ref->val; $val = zval.value.ref->val; $val = zval.value.ref->val Is the string value of laruence zval, then the reference count for the zval + 1, namely zval. Value. The ref – > val. Value. STR. Gc. Refcount to 2. There is no replication.

This solves the classic PHP5 problem described in the previous chapter. For example, when we run the problem from the previous chapter in PHP7, we get:

$ php-7.0/sapi/cli/php /tmp/1.php
Used 0.00021380008539
Used 0.00020173048281
Copy the code

You can see that replication really didn’t happen, so there were no performance problems.