记得网上流传甚广的段子“PHP是世界上最好的语言”,暂且不去讨论是否言过其实,但至少PHP确实有独特优势的,比如它的弱类型,即只需要$符号即可声明变量,使得PHP入手门槛极低,成为大家所青睐的Web服务端语言。那么它的变量是如何实现的呢?我们今天就来学习一下PHP的基本变量。

一、引言

PHP的变量存储在zval结构体中,在执行阶段中编译为op_array时就能看到zval的身影。结构体定义在Zend/zend_types.h中,定义内容如下所示:

struct _zval_struct {
zend_value value; /* value */
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar type, /* active type */
zend_uchar type_flags,
zend_uchar const_flags,
zend_uchar reserved) /* 保留字段 */
} v;
uint32_t type_info;
} u1;
union {
uint32_t var_flags;
uint32_t next; /* hash collision chain */
uint32_t cache_slot; /* literal cache slot */
uint32_t lineno; /* line number (for ast nodes) */
uint32_t num_args; /* arguments number for EX(This) */
uint32_t fe_pos; /* foreach position */
uint32_t fe_iter_idx; /* foreach iterator index */
} u2;
};

二、结构体剖析

2.1、zend_value

结构体的第一个变量是zend_value,顾名思义,它其实也是一个结构体,用于存放变量的值,比如整型、浮点型、引用计数、字符串、数组、对象、资源等。zend_value定义了众多类型的指针,但这些类型并不都是变量的类型,有些是给内核自己使用的,比如指针ast、zv、ptr。

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.2、u1

u1是一个联合体,它联合了结构体v和整型type_info。下面我们先来看一下结构体v的构成。

union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar type, /* active type */
zend_uchar type_flags,
zend_uchar const_flags,
zend_uchar reserved) /* call info for EX(This) */
} v;
uint32_t type_info;
} u1;

2.2.1、type

type是指变量的类型,刚在2.1中讲到了zend_value是用来存储变量的值,所以也应该有地方存储变量的类型,而这就是type的职责。以下是PHP定义的所有变量类型,有我们熟知的布尔、NULL、浮点、数组、字符串等类型。也有陌生的undef、indirect、ptr类型,变量类型在下一章中详解,这里不再赘述。

/* regular data types */
#define IS_UNDEF 0
#define IS_NULL 1
#define IS_FALSE 2
#define IS_TRUE 3
#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 /* constant expressions */
#define IS_CONSTANT 11
#define IS_CONSTANT_AST 12 /* fake types */
#define _IS_BOOL 13
#define IS_CALLABLE 14 /* internal types */
#define IS_INDIRECT 15
#define IS_PTR 17

2.2.2、type_flags

可以把它理解为子类型,上面提到了变量的类型,这个是针对不同类型的子类型或标记,type_flags一共有以下6种。

/* zval.u1.v.type_flags */
#define IS_TYPE_CONSTANT (1<<0) /* 常量 */
#define IS_TYPE_IMMUTABLE (1<<1) /* 不可变的类型 */
#define IS_TYPE_REFCOUNTED (1<<2) /* 需要引用计数的类型 */
#define IS_TYPE_COLLECTABLE (1<<3) /* 可能包含循环引用的类型 */
#define IS_TYPE_COPYABLE (1<<4) /* 可被复制的类型 */
#define IS_TYPE_SYMBOLTABLE (1<<5) /* 符号表类型 */

2.2.3、const_flags

常量类型的标记,对应的属性为:

/* zval.u1.v.const_flags */
#define IS_CONSTANT_UNQUALIFIED 0x010
#define IS_LEXICAL_VAR 0x020
#define IS_LEXICAL_REF 0x040
#define IS_CONSTANT_CLASS 0x080 /* __CLASS__ in trait */
#define IS_CONSTANT_IN_NAMESPACE 0x100 /* used only in opline->extended_value */

2.2.4、type_info

type_info与结构体v共用内存,修改type_info等同于修改结构体v的值,所以type_info是v中四个char的组合。

2.3、u2

本来使用u1和zend_value就可以表示变量的,没有必要定义u2,但是我们来看一下,如果没有u2,在内存对齐的情况下zval内存大小为16个字节,当联合了u2后依然是占用16个字节。既然有或没有占用内存大小相同,不如用它来记录一些附属信息。下面我们来看下u2都存储了哪些内容。

2.3.1、next

用来解决哈希冲突问题,记录冲突的下一个元素位置。

2.3.2、cache_slot

运行时缓存,在执行函数时回去缓存中查找,若缓存中没有则到全局function表中查找。

2.3.3、lineno

文件执行的行号,应用在AST节点上。Zend引擎在词法和语法解析时会把当前执行的文件行号记录下来,记录在zend_ast中的lineno中。

2.3.4、num_args

函数调用时传入函数的参数个数。

2.3.5、fe_pos

用于遍历数组时记录当前遍历的位置,比如每次执行foreach时fe_pos都会加一,当再次调用foreach进行遍历时,fe_post会进行重置。

2.3.6、fe_iter_idx

这个与fe_pos类似,只不过它是针对对象的。对象的属性也是HashTable,传入的参数是对象时,会获取对象的属性,所以遍历对象就是在变量对象的属性。

三、参考文献

跟厂长学PHP7内核(六):变量之zval的更多相关文章

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

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

  2. 跟厂长学PHP7内核(八):深入理解字符串的实现

    在前面大致预览了常用变量的结构之后,我们今天来仔细的剖析一下字符串的具体实现. 一.字符串的结构 struct _zend_string { zend_refcounted_h gc; /* 字符串类 ...

  3. 跟厂长学PHP7内核(三):源码目录结构

    上篇文章我们已经介绍了源码分析工具的安装.配置以及调试方法,本文我们来讲述一下PHP源码的目录结构. 一.目录概览 以php-7.0.12为例,看过源码的同学们应该发现源码目录多达十多个,下面是每个目 ...

  4. 跟厂长学PHP7内核(四):生命周期之开始前的躁动

    上一章我们对PHP的源码目录结构有了初步了解,本章我们继续从生命周期的维度对PHP进行剖析. 一.概览 生命周期是什么呢?你可以把它看作执行过程,PHP的生命周期也就是它从开始执行到结束执行的过程. ...

  5. 跟厂长学PHP7内核(二):源码分析的环境与工具

    本文主要介绍分析源码的方式,其中包含环境的搭建.分析工具的安装以及源码调试的基本操作. 一.工具清单 PHP7.0.12 GDB CLion 二.源码下载及安装 $ wget http://php.n ...

  6. 跟厂长学PHP7内核(一):发展史

    PHP1 1994年,一位名叫Rasmus lerdorf的兄台为了在网上展示自己的履历和网页流量的统计,用Perl开发了一套脚本,后来因与日俱增的需求无法得到满足,lerdorf便使用c语言进行了重 ...

  7. 跟厂长学PHP7内核(五):系统分析生命周期

    上篇文章讲述了模块初始化阶段之前的准备工作,本篇我来详细介绍PHP生命周期的五个阶段. 一.模块初始化阶段 我们先来看一下该阶段的每个函数的作用. 1.1.sapi_initialize_reques ...

  8. 《PHP内核剖析 - 变量/内存管理》

    本文总结自: <PHP7 内核剖析 - 变量的内部实现> 一:变量的实现 - 变量是一个语言实现的基础. - 在PHP中,变量的组成部分为 变量名(zval) 变量值(zend_value ...

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

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

随机推荐

  1. MyBatis 源码分析——配置信息

    MyBatis框架的启动前期需要加载相关的XML配置信息.从官网上我们可以了解到他具有十几个节点.其中笔者认为比较重要的节点是settings节点.properties节点.environments节 ...

  2. Eclipse Neon 汉化

    官网下载的Eclipse是英文版的,对于初学者来说为了减小学习的难度,将英文版汉化是有必要的. 第一步:依次点击Eclipse菜单栏上的“Help"-->”About",查看 ...

  3. eclipse启动tomcat内存溢出的解决方式

    eclipse启动tomcat内存溢出的解决方式 ——IT唐伯虎 摘要:eclipse启动tomcat内存溢出的解决方式. 1.打开Run Configurations 2.在VM arguments ...

  4. bzoj千题计划222:bzoj2329: [HNOI2011]括号修复(fhq treap)

    http://www.lydsy.com/JudgeOnline/problem.php?id=2329 需要改变的括号序列一定长这样 :)))((( 最少改变次数= 多余的‘)’/2 [上取整] + ...

  5. 如何将U盘转化成NTFS格式

    拷贝数据到U盘,出现 出现错误0x80070052:无法创建目录或文件 然后发现应该是U盘为fat32格式的,感觉应该是这个问题 方法一:如果你是新买的U盘,或者U盘内数据已经备份到电脑,可以使用该条 ...

  6. HDU 4508 湫湫系列故事——减肥记I (完全背包)

    题意:有n种食物,每种食物可以给湫湫带来一个幸福感a,同时也会给她带来b的卡路里的摄入,然后规定她一天摄入的卡路里的量不能超过m,一共有n种食物,问可以得到的 最大的幸福感是多少? 解题报告:一开始以 ...

  7. 第12月第30天 love2d

    1. Linux On Linux, you can use one of these command lines: love /home/path/to/gamedir/ love /home/pa ...

  8. HTTP 错误 404.0 - Not Found

    当网上的那些修改程序池的方法,无法解决此问题时,可以尝试修改以下的参数: 1.控制面板-->程序-->启用或关闭Windows功能--> Internet Information S ...

  9. 《区块链100问》第13集:比特币和Q币有哪些不同?

    比特币是一种去中心化的数字资产,没有发行主体.Q币是由腾讯公司发行的电子货币,类似于电子积分,其实不是货币. Q币需要有中心化的发行机构,Q币因为腾讯公司的信用背书,才能被认可和使用.使用范围也局限在 ...

  10. 洛谷 P4609: [FJOI2016] 建筑师

    本省省选题是需要做的. 题目传送门:洛谷P4609. 题意简述: 求有多少个 \(1\) 到 \(N\) 的排列,满足比之前的所有数都大的数正好有 \(A\) 个,比之后的所有数都大的数正好有 \(B ...