以下是“变量在 PHP7 内部的实现(二)”的完整攻略。
什么是变量
变量是一个可存储数据的容器,在 PHP 中我们必须先声明变量然后再给其赋值。变量名称由一个美元符号 "$" 开始,后面跟着变量的名称。
在 PHP7 中,变量的实现是通过结构体 zval
实现的。zval
(Zend Value)是 PHP 变量的内部表示,所有的 PHP 值都必须使用 zval
才能在 PHP 中表示和操作。
zval
结构体
下面是 zval
结构体定义:
typedef struct _zval_struct zval;
struct _zval_struct {
/* variable value */
union {
long lval; /* long 整型 */
double dval; /* 双精度浮点型 */
zend_refcounted *counted;
zend_string *str; /* 字符串 */
zend_array *arr; /* 数组 */
zend_object *obj; /* 对象 */
zend_resource *res; /* 资源 */
zend_reference *ref; /* 引用 */
void *ptr;
zend_ast *ast;
} value;
/* variable information */
unsigned int type:3; /* 值的类型 */
unsigned int count:30; /* 引用计数 */
union {
uint32_t flags; /* 引用计数相关的标记 */
uint32_t next; /* hash chained bucket */
uint32_t cache_slot; /* cache slot (for RECV_INIT) */
uint32_t opline_num; /* opcode number (for _RECV_VR) */
uint32_t num_args; /* arguments number for EX(This) */
uint32_t fe_pos; /* foreach position */
uint32_t fe_iter_idx; /* foreach iterator index */
} u1;
union {
uint32_t var_flags; /* flags for $... */
uint32_t next; /* hash chained bucket */
uint32_t cache_slot; /* cache slot (for RECV_INIT) */
uint32_t label; /* label position */
uint32_t opline_num; /* opline number (for _DECLARE_ANON_CLASS) */
uint32_t num_args; /* arguments number for EX(This) */
uint32_t fe_iter_idx; /* foreach iterator index */
} u2;
};
可以看到 zval
实际上是一个联合体,它可以代表 long, double, string, array, object, resource, reference 等多种类型。
zval
的引用计数
在 PHP 中,引用计数用于判断一个 zval
是否已经被垃圾回收,如果一个 zval
的引用计数为0,那么 PHP 核心将会回收它。因此,当我们使用一个已经被释放的 zval
时,可能会发生堆栈故障(segmentation fault)等问题。
zval
结构体中的 count
字段就是用来维护引用计数的,它的值就是指向该值的变量的数量即:可通过单一的变量声明n次来引用同一值。例如:
$a = 123;
$b = $a;
$c = &$b;
在上述 PHP 代码中,$a, $b, $c 分别是三个 zval
,它们的值都是 123。同时,$b 和 $c 都是 $a 的别名(即引用)。此时,三个 zval
的引用计数都是 1。
示例说明
下面通过两个示例,来说明 PHP7 中 zval
的使用:
示例一:变量的类型转换
$x = 123; // $x 是一个整型变量
$x = 123.0; // $x 变成了一个浮点型变量
当我们给 zval
赋值时,PHP7 会根据变量的值来选择合适的 zval
类型。当变量的值发生改变时,PHP7 会检查该变量的类型,然后尝试将其转换为新的类型。在上述代码中,当 $x
赋值为 123
时,PHP7 会创建一个整型的 zval
,当 $x
的值变为 123.0
时,在存储 $x
的 zval
中的 type
字段被改为了浮点型类型。
示例二:foreach 循环中的引用计数自增
$arr = [1, 2, 3];
foreach ($arr as &$data) {}
在上述代码中,我们使用 foreach
循环遍历数组 $arr
。由于 $data
是一个引用,因此 PHP7 内部会将 $data
的引用计数加 1。由于 $data
是 $arr
中的一个元素,因此 PHP7 也会将该元素的引用计数加 1。这种引用计数的自增策略,能够避免在循环结束后,$data
的引用计数被错误地减 1,同时也能够避免因为在循环中修改元素值过程中,无法实时的同步更新数组元素值的问题。
在上面的两个示例中,我们可以看到 PHP7 内部是如何使用 zval
来实现变量存储和操作的,并且通过对 zval
的引用计数的维护,能够使 PHP7 能够更加高效地管理内存。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:变量在 PHP7 内部的实现(二) - Python技术站