PHP底层运行机制与原理( 二 )

4.2 PHP变量实现原理:
Zval是zend中另一个非常重要的数据结构,用来标识并实现PHP变量,其数据结构如下:

PHP底层运行机制与原理

文章插图
 
Zval结构体主要由三部分组成:
type:指定了变量所述的类型(整数、字符串、数组等)
refcount&is_ref:用来实现引用计数(后面具体介绍)
value:核心部分,存储了变量的实际数据
Zvalue是用来保存一个变量的实际数据 。因为要存储多种类型,所以zvalue是一个union,也由此实现了弱类型 。
PHP变量类型和其实际存储对应关系如下:
IS_LONG -> lvalueIS_DOUBLE -> dvalueIS_ARRAY -> htIS_STRING -> strIS_RESOURCE -> lvalue4.2.1 整数、浮点数变量
整数、浮点数是PHP中的基础类型之一,也是一个简单型变量 。对于整数和浮点数,在zvalue中直接存储对应的值 。其类型分别是long和double 。
从zvalue结构中可以看出,对于整数类型,和c等强类型语言不同,PHP是不区分int、unsigned int、long、long long等类型的,对它来说,整数只有一种类型也就是long 。由此,可以看出,在PHP里面,整数的取值范围是由编译器位数来决定而不是固定不变的 。
对于浮点数,类似整数,它也不区分float和double而是统一只有double一种类型 。
在PHP中,如果整数范围越界了怎么办?这种情况下会自动转换为double类型,这个一定要小心,很多trick都是由此产生 。
4.2.2 字符变量
和整数一样,字符变量也是PHP中的基础类型和简单型变量 。通过zvalue结构可以看出,在PHP中,字符串是由由指向实际数据的指针和长度结 构体组成,这点和c++中的string比较类似 。由于通过一个实际变量表示长度,和c不同,它的字符串可以是2进制数据(包含),同时在PHP中,求字符串长度strlen是O(1)操作 。
在新增、修改、追加字符串操作时,PHP都会重新分配内存生成新的字符串 。最后,出于安全考虑,PHP在生成一个字符串时末尾仍然会添加
常见的字符串拼接方式及速度比较:
假设有如下4个变量:strA=‘123’; strB = ‘456’; intA=123; intB=456;
现在对如下的几种字符串拼接方式做一个比较和说明:
1 res = strA.strB和res = “strAstrB”
这种情况下,zend会重新malloc一块内存并进行相应处理,其速度一般 。
2 strA = strA.strB
这种是速度最快的,zend会在当前strA基础上直接relloc,避免重复拷贝
3 res = intA.intB
这种速度较慢,因为需要做隐式的格式转换,实际编写程序中也应该注意尽量避免
4 strA = sprintf (“%s%s”,strA,strB);
这会是最慢的一种方式,因为sprintf在PHP中并不是一个语言结构,本身对于格式识别和处理就需要耗费比较多时间,另外本身机制也是malloc 。不过sprintf的方式最具可读性,实际中可以根据具体情况灵活选择 。
4.2.3 数组变量
PHP的数组通过Zend HashTable来天然实现 。
foreach操作如何实现?对一个数组的foreach就是通过遍历hashtable中的双向链表完成 。对于索引数组,通过foreach遍 历效率比for高很多,省去了key->value的查找 。count操作直接调用 HashTable->NumOfElements,O(1)操作 。对于’123’这样的字符串,zend会转换为其整数形 式 。arr[‘123’]和arr[123]是等价的
4.2.4 资源变量
资源类型变量是PHP中最复杂的一种变量,也是一种复合型结构 。
PHP的zval可以表示广泛的数据类型,但是对于自定义的数据类型却很难充分描述 。由于没有有效的方式描绘这些复合结构,因此也没有办法对它们使用传统的操作符 。要解决这个问题,只需要通过一个本质上任意的标识符(label)引用指针,这种方式被称为资源 。
在zval中,对于resource,lval作为指针来使用,直接指向资源所在的地址 。Resource可以是任意的复合结构,我们熟悉的MySQLi、fsock、memcached等都是资源 。
如何使用资源:
1 注册:对于一个自定义的数据类型,要想将它作为资源 。首先需要进行注册,zend会为它分配全局唯一标示 。
2 获取一个资源变量:对于资源,zend维护了一个id->实际数据的hash_tale 。对于一个resource,在zval中只记录了它的id 。fetch的时候通过id在hash_table中找到具体的值返回 。
3 资源销毁:资源的数据类型是多种多样的 。Zend本身没有办法销毁它 。因此需要用户在注册资源的时候提供销毁函数 。当unset资源时,zend调用相应的函数完成析构 。同时从全局资源表中删除它 。


推荐阅读