编程语言
首页 > 编程语言> > PHP源码分析(基本变量)

PHP源码分析(基本变量)

作者:互联网

小而巧的zval

     zval可以表示PHP中任意一个变量

struct_zval_struct{
   zend_value  value;
   union  u1;
   union  u2;
}


typedef union_zend_value{
   zend_long  lval;  //整形
   double     dval;  //浮点型
   zend_refcounted *counted 
   zend_string  *str; //字符串
   zend_array   *arr; //数组 ->hashtable
   zend_object  *obj; //对象
   zend_resource *res; //资源类型
   zend_reference *ref; //引用类型
   zend_ast_ref   *ast; //抽象语法树
   zval  *zv;  //还可以指向一个zval
   void  *ptr; //不确定类型
   zend_class_entry *ce;//类
   zend_function  *func;

}zend_value;

源码图:文件zand_types.h

u1:  type 定义了不同的类型 

       type_flags 是变量类型特有的标记,可以表示常量、不可变的类型、需要引入计数的类型、可能包括循环引用的类型、可被复制的类型

      const_flags:是常量类型特有的标记,0变量 2常量

      reserved 是保留字段

u2:next 是主要解决hash冲突的

     cache_slot 主要是做运行时缓存的

    lineno 标记了哪一行,一般用作AST抽象语法树

    num_args:代表函数调用输入参数的个数

    fe_pos:代表我们foreach的时候位置,每foreach一次这个值+1

    fe_iter_idx:也在foreach中使用,代表游标索引的位置

    access_flags:主要用在类里面比如写代码用到的public、protected等

     property_guard:防止类中魔术方法的循环引用,在get和set中会用到

其中zval里面有三个变量,他们都是联合体,   value里面有各种类型的指针,它的类型判断是由u1里面的type来判断的,根据type来取里面不同的值,比如type里面是IS_LONG整形,可以直接取zend_value里面的lval,就得到值了,如果type是string,我们取*str,它是一个指向zend_string的指针

变量都挨在一起放在同一段内存中,每一个都占了16字节

 

 

从源码中我们看到虽然说PHP是弱语言类型,但是在真正的底层实现还是区分类型的,为什么需要区分类型呢?

           如果知道一个变量想知道它的长度的话有这么几种方法,一个是专门用一个长度的字段来记录长度比如我们字符串,数组的话我们有一个长度的字段,其他的我们用类型这个字段,而类型天然有长度这个字段,一个类型隐世的包含它的长度,比如我们定义 $a=1虽然我们没有定义类型,但是底层解析会把他定义为整形,会用到zend_value 的lval

   如何区分类型:看u1里面的结构体v的type,这个type就是代表不同的类型,类型定义了有IS_FALSE、IS_TRUE、IS_LONG、IS_ARRAY、IS_OBJECT等,另外还有一个type_info,可以快速的取出上面的四个char的值

 

Zend_string  字符串

 

gc对应一个结构体,主要是进行垃圾回收的,refcount为引用计数

h 为这个字符串对应的hash值,后面会用在数组里面

len和val[1]:是二进制安全的,不像c语言中如果/0就会被截断,两个就可以表示一个字符串,其中gc和h主要用作垃圾回收和以空间换时间做hash运算时存储的一个h值

写时复制:如果是整形或者其他简单的类型,用zval的16个字节就可以表示,所以是直接赋值的,比如zend_string,$a='string',$b=$a,他们的*str指向同一个zend_string,使用gc里面的refcount+1,当修改b的时候进行copy一份出来进行修改

对于7.1.0的话 refcount=0,flags=2常量  变量的话   refcount=1,flags=0,并且字符串是写时复制的,当没有修改前的话,他们的*str都指向同一个zend_string,修改后refcount-1,并且分配一个新的地址

 

引用类型:

 在zend_value里面 

 zend_reference    *ref  对应 IS_REFERENCE 

   当把$b=&$a的的时候,a的type也变成10,引用类型了,都是同一个地址

当$a复制string的时候,$a对应的zval的类型为IS_STRING,指向的是zend_string,当$b=&$a的时候,它们的类型都变成了zend_reference,它们指向同一个zend_reference,zend_reference里面有zval,它的类型为IS_STRING,指向zend_string。 当使用unset($b)时候,只是把b的zval的type类型改为NULL,甚至ref地址的指针都没有变,而a的zval的type还是为10,类型为ref没有变,指向的zend_string也没有变

 

Zend_array  数组

zend_array  还有个别名 HashTable

TableMask:计算索引值

*arDate:真正存储的是key-value对

NumUsed:已经用过的空间

TableSize:代表的是arDate的大小,初始化的话大小为8,当不够用的话进行扩容8-16-32

NextFreeElement:当我们直接赋值没有key的value

整个HashTable分为了Bucket arr和hash arr

 

Bucket array是后面存arrData的下标一个个往上加的,0123,我写进去的时候他们key一定是数字,这个arData前面有一个索引的数组,只用两个位置,分别是-1和-2,这个是Bucke array

Hash array是算出来的hash值& nTableMask得到一个值

 

标签:string,zend,value,源码,类型,zval,PHP,type,变量
来源: https://blog.csdn.net/ligupeng7929/article/details/90041631