PHP源码分析之变量的存储过程分解
PHP代码如下:
$php_var=1;
对应C的代码是:
zval*c_var; //定义PHP变量指针 MAKE_STD_ZVAL(c_var); //初始化PHP变量 ZVAL_LONG(c_var,1);//赋值 ZEND_SET_SYMBL(EG(active_symbol_table),"php_var",c_var);//注册到全局变量符号表
一.首先看第一行:zval*c_var;//申明一个zval指针c_var;zval的结构如下:
struct_zval_struct{ /*Variableinformation*/ zvalue_valuevalue; /*变量的值*/ zend_uintrefcount; /*引用计数,垃圾回收的时候用到*/ zend_uchartype; /*变量类型*/ zend_ucharis_ref; /*是否为引用变量*/ }; typedefstruct_zval_structzval;
其中值zvalue_value的结构如下:
typedefunion_zvalue_value{ longlval; /*长整形*/ doubledval; /*双精度类型*/ struct{ /*字符串类型的值*/ char*val; intlen; }str; HashTable*ht; /*数组类型的值*/ zend_object_valueobj; /*对象类型的值*/ }zvalue_value;
二.接下来看第二行:MAKE_STD_ZVAL(new_val);//变量初始化相关宏如下://初始化
#defineMAKE_STD_ZVAL(zv) \ ALLOC_ZVAL(zv);\ INIT_PZVAL(zv); #defineALLOC_ZVAL(z) \ ZEND_FAST_ALLOC(z,zval,ZVAL_CACHE_LIST) #defineZEND_FAST_ALLOC(p,type,fc_type) \ (p)=(type*)emalloc(sizeof(type)) #defineINIT_PZVAL(z) \ (z)->refcount=1; \ (z)->is_ref=0;
展开后为:
(c_var)=(zval*)emalloc(sizeof(zval)); //分配内存 (c_var)->refcount=1; //引用计数初始化 (c_var)->is_ref=0;//是否引用
可以看到其作用就是分配内存,初始化refcount,is_ref
三.下面看第三行ZVAL_LONG(c_var,1)相关宏为:
//定义值 #defineZVAL_LONG(z,l){ \ Z_TYPE_P(z)=IS_LONG; \ Z_LVAL_P(z)=l; \ } #defineZ_TYPE_P(zval_p) Z_TYPE(*zval_p) #defineZ_TYPE(zval) (zval).type #defineZ_LVAL_P(zval_p) Z_LVAL(*zval_p) #defineZ_LVAL(zval) (zval).value.lval
展开后为:
(*c_var).type=IS_LONG; (*c_var).value=1;
四:接下来看第四行:ZEND_SET_SYMBOL(EG(active_symbol_table),“php_var”,c_var);首先说明下PHP的变量是存在一个hashtable里的
struct_zend_executor_globals{ …. HashTablesymbol_table;//全局变量的符号表 HashTable*active_symbol_table;//局部变量的符号表 ….. };
Hashtable的Key为变量的名称,即php_var,值为指向PHP变量的指针,即c_var指针;相关宏为:
#defineZEND_SET_SYMBOL(symtable,name,var) \ { \ char*_name=(name); \ ZEND_SET_SYMBOL_WITH_LENGTH(symtable,_name,strlen(_name)+1,var,1,0); \ } //主要的实现为下面这个函数: #defineZEND_SET_SYMBOL_WITH_LENGTH(symtable,name,name_length,var,_refcount,_is_ref) \ { zval**orig_var; \ if(zend_hash_find(symtable,(name),(name_length),(void**)&orig_var)==SUCCESS \ &&PZVAL_IS_REF(*orig_var)){ \ (var)->refcount=(*orig_var)->refcount; \ (var)->is_ref=1; \ if(_refcount){ \ (var)->refcount+=_refcount-1; \ } \ zval_dtor(*orig_var); \ **orig_var=*(var); \ FREE_ZVAL(var); \ }else{ \ (var)->is_ref=_is_ref; \ if(_refcount){ \ (var)->refcount=_refcount; \ } \ zend_hash_update(symtable,(name),(name_length),&(var),sizeof(zval*),NULL); \ } \ }
该函数的功能是:
1.如果全局符号表已经存在该变量且是引用类型,则
a.将原来变量的引用计数refcount,is_ref信息赋给c_var;
b.释放掉原来变量zvalue的值,比如原来其值指向的是一个mysql连接资源,则释放该资源。
c.将c_var指向的变量赋值给原来的变量d.释放c_var的内存空间这样保证了,如果变量被应用,值一起改变。比如如果前面有$b=&a;
2.如果全局符号表不存在该变量或者存在该变量但不是引用变量,则直接改变其值。