PHP语言有一个特点就是资源共享,这一点在PHP的变量存储方式上也有体现。PHP的变量是一种弱类型,PHP是如何去实现变量的存储呢?
引用计数器
变量的C语言实现有两个重要的元素,一个是zvalue_value联合体,另一个是zval结构体
zvalue_value联合体的定义
typedef union _zvalue_value {
long lval; // 对应PHP中的integer boolean long resource
double dval; // 对应PHP中的float
// 对应PHP中的string
struct {
char *val;
int len;
} str;
HashTable *ht; // 对应PHP中的array
zend_object_value obj; // 对应PHP中的object
} zvalue_value;
PHP中的变量类型都被对应到联合体中某个变量,值得一提的是resource类型,被对应到long类型的原因是,php会有一个资源表,long里面存的是一个偏移量。
zval结构体的定义
struct _zval_struct {
// 变量实际的值
zvalue_value value;
// 变量被引用的次数
zend_uint refcount__gc;
// 变量的类型
zend_uchar type;
// 变量是否被引用,&$a
zend_uchar is_ref__gc;
};
每个php变量都存在"zval"的变量容器中。如上所示,zval变量容器包含了变量的类型和值,另外还有两个字节的额外信息。第一个是"is_ref",是个bool值,用来标识这个变量是否是属于引用集合(reference set)。通过这个字节,php引擎才能把普通变量和引用变量区分开来,由于php允许用户通过使用&来使用自定义引用,zval变量容器中还有一个内部引用计数机制,来优化内存使用。第二个额外字节是"refcount",用以表示指向这个zval变量容器的变量(也称符号即symbol)个数。所有的符号存在一个符号表中,其中每个符号都有作用域(scope),那些主脚本(比如:通过浏览器请求的的脚本)和每个函数或者方法也都有作用域。
例如编写以下的代码
<?php
$a = "new string" ;
xdebug_debug_zval ( 'a' );
$b = $a ;
xdebug_debug_zval ( 'a' );
$c = $a ;
xdebug_debug_zval ( 'a' );
unset ( $b , $c );
xdebug_debug_zval ( 'a' );
$d = & $a ;
xdebug_debug_zval ( 'a' );
?>
打印的结果应该为
a: (refcount=1, is_ref=0)='new string'
a: (refcount=2, is_ref=0)='new string'
a: (refcount=3, is_ref=0)='new string'
a: (refcount=1, is_ref=0)='new string'
a: (refcount=1, is_ref=1)='new string'
ref:http://www.php.net/manual/zh/features.gc.refcounting-basics.php