深入理解PHP原理之变量声明

原创 2016年08月30日 21:15:17

  在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/

版权声明:本文为博主原创文章,未经博主允许不得转载。

编译原理——变量声明语句(十)

常数表。 代码: Constant.java package per.eyuan.util; //常数表 public class Constant { String key="num"...

编译原理——变量声明语句(六)

GoTo表。 代码: GoTo.java package per.eyuan.util; public class GoTo { /* * SLR的goto表 * 接受传递...

编译原理——变量声明语句(四)

语法分析。 代码: Syntax.java package per.eyuan.compile; import per.eyuan.util.Action; import per.eyu...

编译原理——变量声明语句(七)

Action动作表。 代码: Action.java package per.eyuan.util; public class Action { /* * SLR的action表...

编译原理——变量声明语句(一)

代码: Compile.java: package per.eyuan.compile; import per.eyuan.util.Id; import per.eyuan.util.I...

编译原理——变量声明语句(三)

词法分析: 代码: Lexical.java: package per.eyuan.compile; import per.eyuan.util.*; public class Le...

深入理解变量声明提升和函数声明提升

变量声明提升1、变量定义可以使用var定义变量,变量如果没有赋值,那变量的初始值为undefined。2、变量作用域变量作用域指变量起作用的范围。变量分为全局变量和局部变量。全局变量在全局都拥有定义;...

理解复杂的c变量声明

1.前言 对于一些复杂的c语言变量声明,往往难以理解。 简单的,如:int foo[5] ; //foo is an array of 5 ints char *foo; //foo is a...
  • werweqg
  • werweqg
  • 2015年04月04日 12:25
  • 281

如何理解C语言中的变量声明

曾经碰到过让你迷惑不解、类似于int * (* (*fp1) (int) ) [10];这样的变量声明吗?本文将由易到难,一步一步教会你如何理解这种复杂的 C/C++声明:我们将从每天都能碰到的...

C++类变量声明

  • 2015年12月04日 17:04
  • 2.51MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深入理解PHP原理之变量声明
举报原因:
原因补充:

(最多只允许输入30个字)