首页 > 开发 > Php > 正文

变量在 PHP7 内部的实现(二)

2020-02-18 22:58:13
字体:
来源:转载
供稿:网友

在上篇文章给大家介绍了变量在 PHP7 内部的实现(一),本篇继续给大家介绍php7内部实现相关知识,感兴趣的朋友通过本篇文章一起学习吧。

本文第一部分和第二均翻译自Nikita Popov(nikic,PHP 官方开发组成员,柏林科技大学的学生) 的 博客 。为了更符合汉语的阅读习惯,文中并不会逐字逐句的翻译。

要理解本文,你应该对 PHP5 中变量的实现有了一些了解,本文重点在于解释 PHP7 中 zval 的变化。

第一部分讲了 PHP5 和 PHP7 中关于变量最基础的实现和变化。这里再重复一下,主要的变化就是 zval 不再单独分配内存,不自己存储引用计数。整型浮点型等简单类型直接存储在 zval 中。复杂类型则通过指针指向一个独立的结构体。

复杂的 zval 数据值有一个共同的头,其结构由 zend_refcounted 定义:

struct _zend_refcounted { uint32_t refcount; union {  struct {   ZEND_ENDIAN_LOHI_3(    zend_uchar type,    zend_uchar flags,    uint16_t  gc_info)  } v;  uint32_t type_info; } u;};

这个头存储有 refcount (引用计数),值的类型 type 和循环回收的相关信息 gc_info 以及类型标志位 flags 。

接下来会对每种复杂类型的实现单独进行分析并和 PHP5 的实现进行比较。引用虽然也属于复杂类型,但是上一部分已经介绍过了,这里就不再赘述。另外这里也不会讲到资源类型(因为作者觉得资源类型没什么好讲的)。

字符串

PHP7 中定义了一个新的结构体 zend_string 用于存储字符串变量:

struct _zend_string { zend_refcounted gc; zend_ulong  h;  /* hash value */ size_t   len; char    val[1];};

除了引用计数的头以外,字符串还包含哈希缓存 h ,字符串长度 len 以及字符串的值 val 。哈希缓存的存在是为了防止使用字符串做为 hashtable 的 key 在查找时需要重复计算其哈希值,所以这个在使用之前就对其进行初始化。

如果你对 C 语言了解的不是很深入的话,可能会觉得 val 的定义有些奇怪:这个声明只有一个元素,但是显然我们想存储的字符串偿付肯定大于一个字符的长度。这里其实使用的是结构体的一个『黑』方法:在声明数组时只定义一个元素,但是实际创建 zend_string 时再分配足够的内存来存储整个字符串。这样我们还是可以通过 val 访问完整的字符串。

当然这属于非常规的实现手段,因为我们实际的读和写的内容都超过了单字符数组的边界。但是 C 语言编译器却不知道你是这么做的。虽然 C99 也曾明确规定过支持『柔性数组』,但是感谢我们的好朋友微软,没人能在不同的平台上保证 C99 的一致性(所以这种手段是为了解决 Windows 平台下柔性数组的支持问题)。

新的字符串类型的结构比原生的 C 字符串更方便使用:第一是因为直接存储了字符串的长度,这样就不用每次使用时都去计算。第二是字符串也有引用计数的头,这样也就可以在不同的地方共享字符串本身而无需使用 zval。一个经常使用的地方就是共享 hashtable 的 key。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表