记得网上流传甚广的段子“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. 《PHP内核剖析 - 变量/内存管理》

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

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

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

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

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

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

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

  5. PHP7内核(七):常见变量类型的基本结构

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

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

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

  7. PHP7内核(八):深入理解字符串的实现

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

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

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

  9. 深入理解PHP内核(五)变量及数据类型-变量的结构和类型

    原文链接:http://www.orlion.ga/238/ 编程语言的类型可以分为强类型和弱类型两种,PHP是弱类型语言,但是C语言是强类型语言.在官网PHP实现内部,所有变量使用同一种数据结构(z ...

随机推荐

  1. C++扬帆远航——16(猜数字)

    /* * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:guessnum.cpp * 作者:常轩 * 微信公众号:Wor ...

  2. MongoDB监控及报警

    转载请注明出处:https://www.cnblogs.com/shining5/p/11142357.html MongoDB监控及报警 Prometheus是由SoundCloud开发的开源监控报 ...

  3. nvm安装以及注意事项

    nvm初衷:由于以后的开发工作可能会在多个Node版本中测试,而且Node的版本也比较多,所以需要这么款工具来管理 1. 下载:[nvm-windows](https://github.com/cor ...

  4. Safari配置WebApp----添加启动图和桌面图标让你的WebApp在ios设备上体验如原生一样

    现在很多开发者的Web应用程序的设计样式和交互类似本机应用程序,例如,它的缩放比例适合iOS上的整个屏幕.当用户将其添加到主屏幕时,您可以通过使其看起来像本机应用程序一样,在iOS上为您的Web应用程 ...

  5. 前端性能优化之 Composite

    摘要: 一个 Web 页面的展示,简单来说可以认为经历了 JavaScript/Style/Layout/Paint/Composite 几个步骤.本文主要深入 Composite 部分,从渲染原理. ...

  6. 使用node打造自己的命令行

    一.实现一个简单的功能 二.环境 1.系统: window 10 2.编辑器: vscode 3.node版本: 8.7.0 三.开始玩 1.打开命令行,新建一个pa'ckage.json npm i ...

  7. 微信WXSS样式文件

    目录 WXSS官方文档 1. WXSS 1.1. 尺寸单位 1.2. 样式导入 1.3. 内联样式 1.4. 选择器 1.5. 全局样式与局部样式 WXSS官方文档 https://developer ...

  8. nes 红白机模拟器 第1篇

    对比了很多种,开源的 NES 模拟器 VirtuaNES , nestopia , FakeNES , FCEUX , InfoNES , LiteNES 最后决定使用 LiteNES 进行移值,它是 ...

  9. Vue2.0 【第二季】第1节 Vue.directive自定义指令

    目录 Vue2.0 [第二季]第1节 Vue.directive自定义指令 一.什么是全局API? 二. Vue.directive自定义指令 三.自定义指令中传递的三个参数 四.自定义指令的生命周期 ...

  10. 一口气说出 6种,@Transactional注解的失效场景

    整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 一口气说出 9种 分布式ID生成方式,面试官有点懵了 面试总被问 ...