常量是一个简单值的标识符(名字)。如同其名称所暗示的,在脚本执行期间该值不能改变。常量默认为大小写敏感。通常常量标识符总是大写的。
常量名和其它任何 PHP 标签遵循同样的命名规则。合法的常量名以字母或下划线开始,后面跟着任何字母,数字或下划线。
PHP中的常量通过define()
函数定义:
define('CONST_VAR_1', 1234);
在内核中常量存储在EG(zend_constants)
哈希表中,访问时也是根据常量名直接到哈希表中查找,其实现比较简单。
常量的数据结构:
typedef struct _zend_constant { zval value; //常量值 zend_string *name; //常量名 int flags; //常量标识位 int module_number; //所属扩展、模块 } zend_constant;
常量的几个属性都比较直观,这里只介绍下flags,它的值可以是以下三个中任意组合:
#define CONST_CS (1<<0) //大小写敏感 #define CONST_PERSISTENT (1<<1) //持久化的 #define CONST_CT_SUBST (1<<2) //允许编译时替换
介绍下三种flag代表的含义:
非持久化常量在request请求结束时销毁,具体销毁操作在:php_request_shutdown()->zend_deactivate()->shutdown_executor()->clean_non_persistent_constants()
。
void clean_non_persisten
PHP中把定义在函数、类之外的变量称之为全局变量,也就是定义在主脚本中的变量,这些变量可以在函数、成员方法中通过global关键字引入使用。
function test() { global $id; $id++; } $id = 1; test(); echo $id;
全局变量在整个请求执行期间始终存在,它们保存在EG(symbol_table)
中,也就是全局变量符号表,与静态变量的存储一样,这也是一个哈希表,主脚本(或include、require)在zend_execute_ex
执行开始之前会把当前作用域下的所有局部变量添加到EG(symbol_table)
中,这一步操作后面介绍zend执行过程时还会讲到,这里先简单提下:
ZEND_API void zend_execute(zend_op_array *op_array, zval *return_value) { ... i_init_execute_data(execute_data, op_array, return_value); zend_execute_ex(execute_data); ... }
i_init_execute_data()
这个函数中会把局部变量插入到EG(symbol_table):
ZEND_API void zend_attach_symbol_table(zend_execute_data *execute_data) { zend_op_array *op_array = &execute_data->func->op_array; HashTable *ht = execute_data->symbol_table; if (!EXPECTED(op_array->last_var)) { return; } zend_string **str = op_array->vars; zend_string **end = str + op_array->last_var; //局部变量数组起始位置 zval *var = EX_VAR_NUM(0); do{ zval *zv
PHP中局部变量分配在zend_execute_data结构上,每次执行zend_op_array都会生成一个新的zend_execute_data,局部变量在执行之初分配,然后在执行结束时释放,这是局部变量的生命周期,而局部变量中有一种特殊的类型:静态变量,它们不会在函数执行完后释放,当程序执行离开函数域时静态变量的值被保留下来,下次执行时仍然可以使用之前的值。
PHP中的静态变量通过static
关键词创建:
function my_func(){ static $count = 4; $count++; echo $count,"\n"; } my_func(); my_func(); =========================== 5 6
静态变量既然不会随执行的结束而释放,那么很容易想到它的保存位置:zend_op_array->static_variables
,这是一个哈希表,所以PHP中的静态变量与普通局部变量不同,它们没有分配在执行空间zend_execute_data上,而是以哈希表的形式保存在zend_op_array中。
静态变量只会初始化一次,注意:它的初始化发生在编译阶段而不是执行阶段,上面这个例子中:
static $count = 4;
是在编译阶段发现定义了一个静态变量,然后插进了zend_op_array->static_variables中,并不是执行的时候把static_variables中的值修改为4,所以上面执行的时候会输出5、6,再次执行并没有重置静态变量的值。这个特性也意味着静态变量初始的值不能是变量,比如:
static $count = $xxx;
这样定义将会报错。
局部变量通过编译时确定的编号进行读写操作,而静态变量通过哈希表保存,这就使得其不能像普通变量那样有一个固定的编号,有一种可能是通过变量名索引的,那么究竟是否如此呢?我们分析下其编译过程。
静态变量编译的语法规则:
statement: ... | T_STATIC static_var_list ';' { $$ = $2; } ... ; static_var_list: static_var_list ',' static
数组是PHP中非常强大、灵活的一种数据类型,它的底层实现为散列表(HashTable,也称作:哈希表),除了我们熟悉的PHP用户空间的Array类型之外,内核中也随处用到散列表,比如函数、类、常量、已include文件的索引表、全局符号表等都用的HashTable存储。
散列表是根据关键码值(Key value)而直接进行访问的数据结构,它的key - value之间存在一个映射函数,可以根据key通过映射函数直接索引到对应的value值,它不以关键字的比较为基本操作,采用直接寻址技术(就是说,它是直接通过key映射到内存地址上去的),从而加快查找速度,在理想情况下,无须任何比较就可以找到待查关键字,查找的期望时间为O(1)。
存放记录的数组称做散列表,这个数组用来存储value,而value具体在数组中的存储位置由映射函数根据key计算确定,映射函数可以采用取模的方式,key可以通过一些譬如“times 33”的算法得到一个整形值,然后与数组总大小取模得到在散列表中的存储位置。这是一个普通散列表的实现,PHP散列表的实现整体也是这个思路,只是有几个特殊的地方,下面就是PHP中HashTable的数据结构:
//Bucket:散列表中存储的元素 typedef struct _Bucket { zval val; //存储的具体value,这里嵌入了一个zval,而不是一个指针 zend_ulong h; //key根据times 33计算得到的哈希值,或者是数值索引编号 zend_string *key; //存储元素的key } Bucket; //HashTable结构 typedef struct _zend_array HashTable; struct _zend_array { zend_refcounted_h gc; union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar flags, zend_uchar nApplyCount,