前提必看
我們這裏看的源碼都是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的