前提必看
我们这里看的源码都是PHP 7.0.12版本的哟,其他的版本可能变量的实现机制在细节会有一些不同,但是我们主旨都是希望通过阅读源码,来帮我理解下我经常使用语言,在底层实现的原理。(哈哈,毕竟在我没打算换一下语言,PHP还是作为我的编程主语言的)
变量
在一门高级编程语言中,变量是我们经常用来使用的数据形式。
例如我们常常在C中这样使用变量
#include <stdio.h> int main () { int a, b, c; a = 10; b = 20; c = a + b; printf("value of c : %d \n", c); return 0; } |
但是我们在PHP却不是这样去使用变量,来让我们看看PHP是怎么使用变量的~
<?php $a = "Hello World"; //string 类型 echo $a; $a = 1; //int 类型 echo $a; $a = true; // bool 类型 echo $a; $a = [1,2]; //array 类型 echo $a[0]; $a = new object(); //object 类型 echo $a; .....等等数据类型 |
按照我们对变量的定义,变量应该要由三个部分组成:变量名+变量类型+变量值。
呀呀,PHP就是没有像其他的高级语言一样去先定义变量的数据类型,而是直接使用 $ 与变量名就定义了一个变量,哇,那PHP是用C作为底层语言支持的,那是怎么来使用$a这样一个变量可以支持这么多数据类型呢。我先给出一个答案,PHP在底层数据结构使用 zval ,zval_value 两个数据结构去实现支持这么复杂的类型支持和类型转换。
变量类型
PHP中的变量类型从宏观的角度可以分为以下几大类型
- 标量类型:字符串,整型,浮点类型,布尔类型
- 复合类型:数组,对象
- 特殊类型:资源,NULL
那我们直接去看看PHP在C底层是怎么定义这些的。下面定义数据类型对应的常量
//file:zend_types.h /* regular data types */ #define IS_UNDEF 0 #define IS_NULL 1 #define IS_FALSE 2 #define IS_TRUE 3 #define IS_LONG 4 #define IS_DOUBLE 5 #define IS_STRING 6 #define IS_ARRAY 7 #define IS_OBJECT 8 #define IS_RESOURCE 9 #define IS_REFERENCE 10 /* constant expressions */ #define IS_CONSTANT 11 #define IS_CONSTANT_AST 12 /* fake types */ #define _IS_BOOL 13 #define IS_CALLABLE 14 /* internal types */ #define IS_INDIRECT 15 #define IS_PTR 17 |
内部实现
让我们来看下一个变量怎么实现的。便于大家理解可以把zval = 变量名 zend_value = 变量值 。 我们先看zval的数据结构定义
//file zend_types.h typedef struct _zval_struct zval; struct _zval_struct { zend_value value; /* value */ union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar type, /* active type */ zend_uchar type_flags, zend_uchar const_flags, zend_uchar reserved) /* call info for EX(This) */ } v; uint32_t type_info; } u1; union { uint32_t var_flags; uint32_t next; /* hash collision chain */ uint32_t cache_slot; /* literal cache slot */ 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 */ } u2; }; |
zend_value 的数据结构定义
//file zend_types.h typedef union _zend_value { zend_long lval; /* long value */ double dval; /* double value */ zend_refcounted *counted; zend_string *str; zend_array *arr; zend_object *obj; zend_resource *res; zend_reference *ref; zend_ast_ref *ast; zval *zv; void *ptr; zend_class_entry *ce; zend_function *func; struct { uint32_t w1; uint32_t w2; } ww; } zend_value; |
通过看了zend_value的数据结构,大家有没有发现少了我们经常使用的bool类型的数据,PHP对于bool类型拆分为了True,False类型两个,是直接使用zval中type直接存储IS_TRUE,IS_FALSE的