往期精选(欢迎转发~~)
- 如何看待程序员35岁职业危机?
- Java全套学习资料(14W字),耗时半年整理
- 我肝了三个月,为你写出了GO核心手册
- 消息队列:从选型到原理,一文带你全部掌握
- 肝了一个月的ETCD,从Raft原理到实践
- 更多…
在PHP中没有对常规变量的声明操作,如果要使用一个变量,直接进行赋值操作即可,因为PHP在赋值操作的同时已经进行声明操作,那么PHP是怎样在赋值前进行声明的呢?
在博文《深入理解PHP原理之变量赋值》中其实已经提到过变量的声明,但是讲述的不够透彻,下面主要通过词法分析、语法分析和获取左值和右值的过程,来讲述变量声明的原理。
下面是一个简单的变量赋值操作:
$a = 'hello world';
在赋值前,我们先看看变量a是怎样存储的,之前我们在《深入理解PHP原理之变量赋值》中讲过,是这么描述变量a的“因为变量名a其实有一个指针ptr_a,每次初始化一个变量时,系统会先开辟一块内存,将变量的值保持在zval中,然后将变量a和对应的指针ptr_a保持在数值中,同时让ptr_a指向zval的首地址”,其实在执行的过程中,变量名及指针主要存储_zend_executor_globals的符号表中,_zend_executor_globals的结构如下:
struct _zend_executor_globals {
...
/* symbol table cache */
HashTable *symtable_cache[SYMTABLE_CACHE_SIZE];
HashTable **symtable_cache_limit;
HashTable **symtable_cache_ptr;
zend_op **opline_ptr;
HashTable *active_symbol_table; /* active symbol table */
HashTable symbol_table; /* main symbol table */
HashTable included_files; /* files already included */
...
}
其中active_symbol_table和symbol_table中保存不同类型变量的变量名,opline_ptr是指向变量值的指针,变量a就保存在该数组中。
知道了变量的存储方法,我们还是回到之前的赋值语句,该语句经过词法分析和语法分析后,大致会解析为以下的条件语句:
FETCH_W local !0, 'a'
ASSIGN !0, 'hello+world'
第一行是将变量a保存到临时变量!0(前面的感叹号,表示数据放在缓存),第二行是进行赋值操作,在赋值前需要获取左值和右值,代码如下:
zval *value = &opline->op2.u.constant;
zval **variable_ptr_ptr = _get_zval_ptr_ptr_cv(&opline->op1,EX(Ts), BP_VAR_W TSRMLS_CC);
由于右值为一个数值,我们可以理解为一个常量,则直接取操作数存储的constant字段,左值是通过 _get_zval_ptr_ptr_cv函数获取zval值,代码如下:
static zend_always_inline zval **_get_zval_ptr_ptr_cv(const znode *node, const
temp_variable *Ts, int type TSRMLS_DC)
{
zval ***ptr = &CV_OF(node->u.var);
if (UNEXPECTED(*ptr == NULL)) {
return _get_zval_cv_lookup(ptr, node->u.var, type TSRMLS_CC);
}
return *ptr;
}
// 函数中的CV_OF宏定义
#define CV_OF(i) (EG(current_execute_data)->CVs[i])
该函数中先调用CV_OF(node->u.var),CV_OF是个宏,展开后其实调用的是CVs,那什么是CVs呢?它其实就是个缓存,以数组的形式缓存变量所在HashTable的值,获取变量的值前,先从缓存中取,用于提高效率(是不是可以和之前提到的“FETCH_W local !0, ‘a’”对应起来呢,!0表示数据在缓存)。如果在缓存中没有获取到,调用方法_get_zval_cv_lookup(),该方法通过EG(active_symbol_table)表查找变量。
上面是在变量赋值前进行的一些操作,我把他们定义为“变量的声明”,具体赋值操作,可以参考我之前的博文《深入理解PHP原理之变量赋值》,里面讲的非常详细。
可能大家感觉有点晕,我总结一下,当变量进行赋值时,如$a=1,先将变量a放在缓存中,这个变量包含变量名a和指针(此时的指针可以理解为空指针,因为还没有给它赋值),然后是获取左值和右值,右值的获取很easy,左值的获取分2步,即先到缓存中找,如果没有,就到哈希表中找,最后就是赋值操作,结束!
参考:http://www.php-internals.com/