上篇文章讲述了变量的存储结构zval,今天我们就来学习一下几个常见变量类型的基本结构。

一、类型一览

zval中的u1.v.type用来存储变量的类型,而zval.value存储的是不同类型对应的值,所以type决定value取值的地方,以下是PHP7所定义的所有类型。

#define IS_UNDEF		0   /* 标记未使用类型 */
#define IS_NULL 1 /* NULL */
#define IS_FALSE 2 /* 布尔类型false */
#define IS_TRUE 3 /* 布尔类型true */
#define IS_LONG 4 /* 长整型 */
#define IS_DOUBLE 5 /* 浮点型 */
#define IS_STRING 6 /* 字符串 */
#define IS_ARRAY 7 /* 数组 */
#define IS_OBJECT 8 /* 对象 */
#define IS_RESOURCE 9 /* 资源 */
#define IS_REFERENCE 10 /* 引用 */ /* 常量相关类型 */
#define IS_CONSTANT 11 /* 常量 */
#define IS_CONSTANT_AST 12 /* 常量抽象语法树 */ /* 伪类型 */
#define _IS_BOOL 13
#define IS_CALLABLE 14 /* 内部类型 */
#define IS_INDIRECT 15 /* 间接类型 */
#define IS_PTR 17 /* 指针类型 */
  • IS_UNDEF:标记未定义,表示数据可以被覆盖或删除。
  • IS_TRUE/IS_FALSE:本来在PHP5中统一用IS_BOOL来代替,这里分成两个可以避免一次类型的检查。
  • IS_REFERRENCE:引用类型,用于处理PHP脚本中的符号&
  • IS_PTR:用来解析value.ptr,通常用在函数类型上,比如声明一个函数或方法。
  • IS_INDIRECT:用于解决在全局符号表访问CV变量的问题。

二、不同类型的结构

刚才聊到zval.u1.v.type决定了zval.value,下面来看一下zend_value结构体的定义。

typedef union _zend_value {
zend_long lval; /* 整型 */
double dval; /* 浮点型 */
zend_refcounted *counted; /* 引用计数 */
zend_string *str; /* 字符串 */
zend_array *arr; /* 数组 */
zend_object *obj; /* 对象 */
zend_resource *res; /* 资源 */
zend_reference *ref; /* 引用 */
zend_ast_ref *ast; /* 抽象语法树 */
zval *zv; /* zval类型 */
void *ptr; /* 指针类型 */
zend_class_entry *ce; /* class类型 */
zend_function *func; /* function类型 */
struct {
uint32_t w1;
uint32_t w2;
} ww;
} zend_value;

基本可以看出该结构体的变量和上文定义的类型是一一对应的,我们抽取几个常用的类型讲述一下。

2.1、字符串

字符串str对应的结构体是zend_string,它有四个成员,定义如下。

struct _zend_string {
zend_refcounted_h gc;
zend_ulong h; /* hash value */
size_t len;
char val[1];
};
  • gc:变量的引用计数信息,用于内存管理。
  • h:字符串通过Time33算法计算的到的Hash值,避免了在数组操作中hash值的重复计算,据说提高了PHP7百分之5的性能。
  • len:字符串的长度。
  • val:字符串的内容,val[1]并不表示只能存储1个字节,在字符串分配时实际上是操作了malloc(sizeof(zend_string)+字符串你长度),也就是会多分配一些内存,而多出来的内存起始位置就是val,这样就可以将字符串直接存储到val,并通过val进行读取,这种采用了柔性数组的方式,读写效率更高。

2.2、数组

成员变量arr对应的结构体是zend_array,它就是你可能有所耳闻的HashTable,zend_array结构体定义如下。

struct _zend_array {
zend_refcounted_h gc;
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar flags,
zend_uchar nApplyCount,
zend_uchar nIteratorsCount,
zend_uchar reserve)
} v;
uint32_t flags;
} u;
uint32_t nTableMask;
Bucket *arData;
uint32_t nNumUsed;
uint32_t nNumOfElements;
uint32_t nTableSize;
uint32_t nInternalPointer;
zend_long nNextFreeElement;
dtor_func_t pDestructor;
};
  • nTableMask:根据key的hash code映射元素存储位置时有用到,它的值是nTableSize的负数,nTableMask=-nTableSize。
  • arData:数组的每一个元素都保存在这里,默认指向第一个元素。
  • nNumUsed:当前使用的Bucket数,但不都是有效的,因为有的Bucket虽然被unset了但是没有马上被删除,而是做了IS_UNDEF标记。
  • nNumOfElements:有效的Bucket数,这个就与上面不同了,这里记录的是真实有效的Bucket数量。
  • nTableSize:数组的总容量。
  • nIternalPointer:当前遍历的指针。
  • nNextFreeElement:下一个索引的值,比如每次给数组新增数据时,该值就会加一,$a[] = 1
  • pDestructor:析构函数,在删除或覆盖某个元素时,调用该函数,可以对旧元素进行清理。
  • u:这里的u主要还是起到辅助作用,比如flags用来设置散列表的一些属性是否持久化、是否已经初始化等。

2.3、对象

struct _zend_object {
zend_refcounted_h gc;
uint32_t handle;
zend_class_entry *ce;
const zend_object_handlers *handlers;
HashTable *properties;
zval properties_table[1];
};
  • gc:引用计数。
  • handle:一次请求期间对象的编号,每一个对象都有一个唯一的编号,与创建的先后顺序有关,主要是在垃圾回收的时候使用。
  • ce:该对象所属的类。
  • handlers:对象操作的处理函数,比如成员属性的读写、成员方法的获取、对象的销毁克隆等。
  • properties:普通成员属性的哈希表,初始化对象时该值为NULL。
  • properties_table:用来存储普通成员的属性值,对象对非静态成员属性的操作就是通过这个数组。

参考文献

  • 《PHP7内核剖析》
  • 《PHP7底层设计与源码实现》

PHP7内核(七):常见变量类型的基本结构的更多相关文章

  1. 跟厂长学PHP7内核(七):常见变量类型的基本结构

    上篇文章讲述了变量的存储结构zval,今天我们就来学习一下几个常见变量类型的基本结构. 一.类型一览 zval中的u1.v.type用来存储变量的类型,而zval.value存储的是不同类型对应的值, ...

  2. 跟厂长学PHP7内核(六):变量之zval

    记得网上流传甚广的段子"PHP是世界上最好的语言",暂且不去讨论是否言过其实,但至少PHP确实有独特优势的,比如它的弱类型,即只需要$符号即可声明变量,使得PHP入手门槛极低,成为 ...

  3. 深入剖析PHP7内核源码(二)- PHP变量容器

    简介 PHP的变量使用起来非常方便,其基本结构是底层实现的zval,PHP7采用了全新的zval,由此带来了非常大的性能提升,本文重点分析PHP7的zval的改变. PHP5时代的ZVAL typed ...

  4. PHP7内核(六):变量之zval

    记得网上流传甚广的段子"PHP是世界上最好的语言",暂且不去讨论是否言过其实,但至少PHP确实有独特优势的,比如它的弱类型,即只需要$符号即可声明变量,使得PHP入手门槛极低,成为 ...

  5. 深入理解PHP内核(七)变量及数据类型-常量

    原文链接:http://www.orlion.ga/246/ 在PHP中,常量的名字是一个简单值的标识符,在脚本执行期间该值不能改变.和变量一样,常量默认为大小写敏感,但是通常是大写的. 常量是在变量 ...

  6. (七)Java 变量类型

    Java 变量类型 在Java语言中,所有的变量在使用前必须声明.声明变量的基本格式如下: type identifier [ = value][, identifier [= value] ...] ...

  7. PHP内核探索之变量(7)- 不平凡的字符串

    切,一个字符串有什么好研究的. 别这么说,看过<平凡的世界>么,平凡的字符串也可以有不平凡的故事.试看: (1)       在C语言中,strlen计算字符串的时间复杂度是?PHP中呢? ...

  8. PHP内核探索之变量(4)- 数组操作

    上一节(PHP内核探索之变量(3)- hash table),我们已经知道,数组在PHP的底层实际上是HashTable(链接法解决冲突),本文将对最常用的函数系列-数组操作的相关函数做进一步的跟踪. ...

  9. PHP内核探索之变量(1)Zval

    作为数据的容器,我们常常需要跟变量打交道,不管这个变量是数字.数组.字符串.对象还是其他,因而可以说变量是构成语言的不可或缺的基础.本文是PHP内核探索之变量的第一篇,主要介绍zval的基本知识,包括 ...

随机推荐

  1. 将mysql数据库集成到idea中

    将mysql数据库集成到idea中

  2. win10 pycharm快捷键

    编辑: 1.复制(行) ctrl + D (光标与需要复制的代码同行) 2.删除(行) ctrl + Y(光标与需要删除的代码同行) 3.光标换行 Shift + Enter(不管光标在该行的哪个位置 ...

  3. CVE-2020-7245 CTFd v2.0.0 – v2.2.2漏洞分析复现

    CVE-2020-7245 CTFd v2.0.0 – v2.2.2漏洞分析复现 一.漏洞介绍 ​ 在 CTFd v2.0.0 - v2.2.2 的注册过程中,如果知道用户名并在 CTFd 实例上启用 ...

  4. 重置gitlab管理员密码

    gitlab web管理员登录密码忘记以后可以用如下方式修改密码(本演示系统为Linux-CentOS6.6): [root@localhost ~]# gitlab-rails console pr ...

  5. ES6 常用知识点总结

    ES6常用知识总结 之前总结了es5中js的一些知识点.这段时间看了石川blue老师讲解的es6课程,结合阮一峰老师的es6教程,随手做了一些笔记和总结分享给大家.内容还是es6主要的知识点,基本没有 ...

  6. MacOS麦克风输入监听的方法

    Windows上很多人都知道,然而实际上并没什么用——延迟太大,根本没法用. MacOS上有两种方法: QuickTime Player新建音频录制(不需要真的录音),如下图: 这个方法和Window ...

  7. 有关vue中用element ui 中的from表单提交json格式总是有冒号的问题解决办法

    因为后台要求要传递JSON格式的数据给他,然后我转了之后总是多了冒号,后来又看了自己的报错,原来是报了404错误,说明路径找不到, 数据格式 后来发现怎么都不行了,然后突然查看了报错报的是404,说明 ...

  8. 文本编辑器 - Sublime Text 3 换行无法自动缩进的解决方法

    一.换行无法自动缩进的问题,如图: 稍微查了一下网上的办法,是把汉化文件删除,但是会造成菜单栏混乱,简直无法忍受... 那么这里介绍的是另一种解决办法.在用户的热键配置文件(preferences-k ...

  9. mysql实现读写分离

    MySQL读写分离概述 1.读写分离介绍 对于目前单机运行MySQL服务.会导致MySQL连接数过多.最终导致mysql的宕机.因此可以使用多台MySQL服务器一起承担压力.考虑到项目中读写比例的不一 ...

  10. python3.6 单文件爬虫 断点续存 普通版 文件续存方式

    # 导入必备的包 # 本文爬取的是顶点小说中的完美世界为列.文中的aa.text,bb.text为自己创建的text文件 import requests from bs4 import Beautif ...