PHP变量底层原理

前言

        PHP是解释型的语言,它的执行顺序主要会经过以下几步:

        1. 进行词法分析

        2. 进行语法分析

        3. 通过zend编译器,编译成opcode

        4. zend虚拟机执行opcode

        我们在写PHP代码的时候就知道,PHP是弱语言类型,而PHP底层又是由zend虚拟机来执行的,zend虚拟机又是C语言编写的,C语言又是强类型的语言,那么PHP是怎么做到弱语言变成强语言类型的虚拟机可执行的呢?这里就要先了解到变量的zval结构了。

变量的结构

1. synbol_table符号表

synbol_table全局符号表,使用hashtable数据结构用于存放变量名以及值地址。

当我们定义一个变量 $a =1 ,a是这个变量名,1是变量的值,synbol_table就是存放变量名a和对应1所在的地址。

2. zval结构体

变量值采用的就是zval结构体来实现的,查看PHP源码可以看到zval结构体如下:

struct _zval_struct {
    /* Variable information */
    zvalue_value value; /* value */
    zend_uint refcount_gc;
    zend_uchar type; /* active type */
    zend_uchar is_ref_gc;
};

value: 变量值,它是一个zvalue_value联合,下面会具体介绍它的结构。

refcount_gc: 引用次数,用于变量赋值和垃圾回收使用。

type: 活动的类型,体现这个变量操作时的真实类型,它的值主要有这8种: IS_NULL、IS_BOOL、IS_LONG、IS_DOUBLE IS_STRING、IS_ARRAY、IS_OBJECT、IS_RESOURCE

is_ref_gc: 是否引用变量

3. zvalue_value联合

zvalue_value联合列出了PHP语言所支持的所有变量类型,结构如下:

typedef union _zvalue_value {
    long lval;
    double dval;
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;
    zend_object_value obj;
} zvalue_value;

对于变量定义如下图:   

PHP变量底层原理_第1张图片

当我们定义了一个变量 $a =3时,全局hashtable表中存下了变量a和3值得地址,变量值则为struct_zval_struct结构体,value值为 long类型3,type是IS_LONG类型,refcount_gc为1只有变量a在引用,is_ref_gc为0 非引用类型。

变量赋值

1. 变量传值赋值

举例:$a = 10 ; $b = $a;

我们定义了一个变量a=1,接着我们将变量a赋值给变量b。

当我们进行传值赋值时,其实php底层并不会复制,因此这时所占用的内存只有一个,从上面例子中,当我们定义变量a时则会生成一个变量a的zval结构体,这个结构体中refcount_gc=1,is_ref_gc =0,当进行赋值$b = $a时,则原来变量a的结构体中refcount_gc+1 = 2,因为非引用赋值is_ref_gc=0;

2. COW(写时复制)

COW(写时复制)特性,当变量进行写操作时则会复制新的结构体。 

上面例子中$b=$a;而接下去当我们要进行 $b=20 对变量b进行修改成20时,那么会复制一份$b的结构体,此时a的结构体中 refcount_gc-1 =1 ,b的结构体refcount_gc =1。

3. 变量引用赋值

举例:$a = 10 ; $b = &$a;

同样的定义了一个变量a=10;同时变量b赋值的是变量a的地址,这时a和b都是公用同一个结构体,这个结构体中 refcount_gc=2,is_ref_gc =1。无论对a还是对b进行修改,都是修改同一个结构体。

4. 传值和引用赋值混合操作

举例:

$a=10
$b=$a 
$c=&$a
$c=20

第一步:$a=10时, 结构体中 value=10 ,refcount_gc=1 ,is_ref_gc=0;
第二步:$a赋值$b时,结构体为同一个 refcount_gc =2;
第三步:$a引用赋值给$c时,结构体为同一个 refcount_gc=3, is_ref_gc=1;
第四步:$c进行修改时,则分裂一个$b的结构体 ref_count_gc=1 is_ref_gc=0,原结构体ref_count_gc=2 is_ref_gc=1;

总结

        PHP虽然是解释型语言,但是实际上还是需要通过zend虚拟机进行编译成物理机所能执行的语言,所以解释型语言也是需要编译的。同时对于PHP弱语言来讲,其只是对编程人员表现出是弱语言,PHP底层依然还是强类型语言,它是通过zval来实现变量的定义和表现的。

        在变量的zval结构中有一个refcount_gc的字段,这个字段也是PHP垃圾回收机制中很重要的一个标识。当我们使用unset函数时,则会将变量的refcount_gc=0,同时也会将synbol_table表中的变量也删除,这时PHP就会认定这个变量之前的结构体为垃圾数据而进行清理。

你可能感兴趣的:(php,php)