php内核中的变量

php是弱类型语言,它可以保存任何的数据类型。但是php是使用c语言编写的,而c语言是强类型语言。每个变量都有固定的类型,不能随意改变变量的类型。

在zend/zend.h中,查看结构体:

zval结构体就是通常用到的php变量在内核总的表示形式,在zval结构体中,可以看到四个成员变量,分别是:

zvalue_value value:变量的值,php变量的值就保存在这里。

zend_uint refcount:变量引用数,变量引用计算器。

zend_uchar type:变量的类型。

zend_uchar  is_ref:变量是否被引用。

zval结构体的value成员变量是zvalue_value联合体,php能够保持任何的结构类型就因为这个联合体。从zvalue_value联合体的成员变量中可以看到,不同的类型会保存到不同的成员变量中,这样就实现了php变量可以存储任何数据类型。例如,当变量是整数类型时,会保存到value的lval成员变量中,而当变量的类型是字符串时,又回保存到value的str成员变量中。

以上是解决了php变量可以保存任意类型的问题,但是zend引擎是怎么知道这个变量保存的是什么类型呢,在zval结构中有个type成员变量,这个成员变量就是保存一个php变量的类型。

我们都知道,php是不支持指针的,但是如果希望两个变量同事指向同一块内存怎么办?为了解决这个问题,php内核中使用了引用计数器。

zval结构中有两个成员变量用于引用计数器:

is_ref:bool值,标识变量是否是引用集合

refcount:计算指向引用集合的变量个数

<?php

$a = "this is leju";

?>

一个zval结构的实体称为zval容器,在php语言层创建一个变量就会相应的在php内核中创建一个zval容器。因为上面的代码创建了一个变量$a,所以在PHP内核中会创建一个zval容器。又因为这个变量不是一个引用,所以zval容器的is_ref等于false,并且refcount等于1.

<?php

$a = "this is leju";

$b = $a;

?>

上面这段代码中创建了两个变量  $a和$b,所以php内核中会创建两个zval容器来保存它们,变量b被赋予变量a的值,由于变量b并不是引用a,所以变量a的is_ref变量的值是false,但是使用xdebug打印变量a的话,会发现refcount等于2,这是为啥呢?

首先来了解下php写时复制(copy on write)机制。

写时复制是一个解决内存复用的方法,例如上面的代码,如果简单的把a的值赋值给b,那么就又两个 this is leju 字符串的复制,这样不利于内存的复用,因为完全可以使用一个 this is leju的字符串的复制完成工作,所以简单的赋值是非常耗内存的,写时复制就是为了解决这种问题而创造的,那什么是写时复制呢,就是当变量的值改变时才进行的内存的复制。

当将变量a的值赋值给变量b时,变量a的refcount增加1,所以这时候变量a和变量b是指向同一内存块的,当改变变量a的值时,发现refcount的值变回1,所以这个时候变量a和变量b指向不同的内存块,这就是写时复制机制,就是两个指向同一内存块的变量,当其中一个变量的值发生变化,才会另外创建一个内存块去保存新的值,其实写时复制也是一种引用,只不过这种引用会受变量值的改变而破坏罢了。

如果显示的引用变量,即$b = &a;变量的is_ref字段会设置1,表示此变量被引用,另外引用计数器(refcount)也相应的加1,在php内核中通过以下代码判断是否复制变量:

if ((*varval)->is_ref || (*varval)->refcount < 2){

   return *varval;

}

推荐一本书,《PHP核心技术与最佳实践》 非常好看~

原文地址:https://www.cnblogs.com/weiluoyan/p/6940327.html