PHP源码阅读Part3变量

  注:这篇文章的内容出自ircmaxell的博客,这里只是翻译整理一下!

  这节我们将继续在上节的基础上,探讨PHP的内核实现,上节主要讲了如何在PHP源码中找到一个PHP函数的实现, 并以strpos函数为例,简要分析了它的实现过程。这节我们主要分析一下PHP中的变量,即源码中随处可见的zval类型。

走进zval

  PHP是一种弱类型语言,不需要变量的类型声明,解释器会根据上下文环境,来决定当前变量是什么类型。PHP有Intger、String、Boolean、Float四种基本类型;Array、Object两种复合类型;还有Resource、NULL两种特殊类型。那么这些“PHP类型”是如何映射到“C类型”的呢。上节我们简单提到,PHP的整型对应着C的long类型。

  这些都跟zval结构体有关,在lxr.php.net中,搜索zval,会有很多结果,随便选一个进入,点击zval便可以跳转到zval的定义(在zend目录下的zend.h文件中)

1 typedef struct _zval_struct zval;

   可见,zval是_zval_struct的别名,点击_zval_struct

1 struct _zval_struct {
2     /* Variable information */
3     zvalue_value value;     /* value */
4     zend_uint refcount__gc;
5     zend_uchar type;    /* active type */
6     zend_uchar is_ref__gc;
7 };

  这个结构体看起来很简单,只有四个成员变量,但PHP中所有的变量都出自这里,这个结构体就是我们接下来的重点,我将会挨个分析这四个成员变量。

Value

  这个变量是zvalue_value类型,用于存储PHP中变量的值,如:$var = 100;值100就存储在这里,点击跳到定义处。

 1 typedef union _zvalue_value {
 2     long lval;                  /* long value */
 3     double dval;                /* double value */
 4     struct {
 5         char *val;
 6         int len;
 7     } str;
 8     HashTable *ht;              /* hash table value */
 9     zend_object_value obj;
10 } zvalue_value;

  注意到这是一个union类型,大家知道C语言中的union类型是一种很有趣的类型,它里面的成员变量都使用同一块内存,系统也只为这些变量分配一块内存,这块内存的大小取决于成员变量中占内存最大的变量,这就保证了这块内存可以容下任何一个成员变量。所以,union类型可以让一个变量根据访问的不同,被解释为不同的类型,比如:zvalue_value.lval = 123.321,内存中就会存一个long类型的变量。

  现在,我们仔细看看zvalue_value里的成员变量,一共有5个。

  • long lval                       表示PHP中的整型
  • double dval                  表示PHP中的浮点型
  • struct{...}                   表示PHP中的字符串
  • HashTable *ht          表示PHP中的数组
  • zend_object_value obj  表示PHP中的对象

   似乎还有Boolean、Resource、NULL三种没有提到,OK,已有的类型已经足够存储了,Boolean、Resource被存储为long,NULL不需要存储。

TYPE

  OK,假设我们在PHP中定义了一个变量:$var = 'hello';对应到C代码,在给上面的value赋值的时候,我们怎么知道当前变量的类型的呢,这里的type成员变量就是实现这个功能的,如:tyle标识为String类型,就会通过value.str.char = 'hello'。

  zend目录下的zend.h文件定义了各种类型。

1 #define IS_NULL     0
2 #define IS_LONG     1
3 #define IS_DOUBLE   2
4 #define IS_BOOL     3
5 #define IS_ARRAY    4
6 #define IS_OBJECT   5
7 #define IS_STRING   6
8 #define IS_RESOURCE 7

IS_REF

  这个变量是否被引用,比如:$foo = &$mm,那么$foo对应的is_ref会被赋值为1,否则为0。

REFCOUNT

  引用次数,如果为1,表示只有一个变量指向这个zval示例,如果为2,表示有两个变量指向这个zval示例。is_ref和refcount结合底层优化和垃圾回收有重要的作用,想更深入的理解,可以参考这里

  至此,zval的成员变量介绍完毕。

如何操作zval

  zval应该是内核中操作最频繁的结构体了,对zval的增删改查操作,是通过定义了几个宏来实现的,这样做很方便,我们在上一节中也遇到过这些宏指令。

在zend目录下的zend_operators.h文件中,我们可以看到:

1 #define Z_LVAL(zval)            (zval).value.lval
2 #define Z_BVAL(zval)            ((zend_bool)(zval).value.lval)
3 #define Z_DVAL(zval)            (zval).value.dval
4 #define Z_STRVAL(zval)          (zval).value.str.val
5 #define Z_STRLEN(zval)          (zval).value.str.len
6 #define Z_ARRVAL(zval)          (zval).value.ht
7 #define Z_OBJVAL(zval)          (zval).value.obj
...

  这些宏指令很精简 ,这里就不再介绍了

类型转化

  PHP会自动为我们做类型转化的,因此你可以将一个字符串当作整型使用,在内核中是通过convert_to_type函数来完成的,它将会zval的type改为整型。

Zend_Parse_Parameters

  这个函数我们上节中提到过,如果你想了解内核中类型的转换过程,这个函数的源码肯定可以让你大饱眼福,详情可以在这里看到。

下一篇博文

  下一节,我们将会探讨PHP内核中数组的实现。

原文地址:https://www.cnblogs.com/smilealgernon/p/3053107.html