PHP源码分析-变量
1. 变量的三要素
变量名称,变量类型,变量值
那么在PHP用户态下变量类型都有哪些,如下:
// Zend/zend.h
#define IS_NULL 0
#define IS_LONG 1
#define IS_DOUBLE 2
#define IS_BOOL 3
#define IS_ARRAY 4
#define IS_OBJECT 5
#define IS_STRING 6
#define IS_RESOURCE 7
#define IS_CONSTANT 8
#define IS_CONSTANT_AST 9
#define IS_CALLABLE 10
2. 变量值和变量类型的存储
变量的类型和值被存储在结构体zval中,如下:
/ Zend/zend_types.h
typedef struct _zval_struct zval; // Zend/zend.h
typedef union _zvalue_value {
long lval; /* long value */
double dval; /* double value */
struct {
char *val;
int len;
} str;
HashTable *ht; /* hash table value */
zend_object_value obj;
zend_ast *ast;
} zvalue_value; struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /* active type */
zend_uchar is_ref__gc;
};
先看结构体_zval_struct,它的成员中value存储变量的值,type存储变量的类型,refcount__gc和is_ref__gc是变量引用相关的标记,先忽略;
而具体存储变量值的结构体_zvalue_value如何存储一个变量的值呢?根据变量的类型进行不同方式的存储,如下:
a. 变量类型为boolean(ZVAL_BOOL), integer(ZVAL_LONG), resource(ZVAL_RESOURCE)
zval中的type存储变量类型(IS_BOOL, IS_LONG, IS_RESOURCE),zvalue_value中的lval存储变量值
b. 变量类型为float(ZVAL_DOUBLE)
zval中的type存储变量类型(IS_DOUBLE),zvalue_value中的dval存储变量值
c. 变量类型为null(ZVAL_NULL)
zval中的type存储变量类型(IS_NULL),不需要存储值
d. 变量类型为字符串
zval中的type存储变量类型(IS_STRING),zvalue_value中的结构体str存储字符串值和字符串长度
e. 变量类型为数组
zval中的type存储变量类型(IS_ARRAY),zvalue_value中的*ht将指向一个哈希表,而这个哈希表里则存储数组的值
f. 变量类型为对象
zval中的type存储变量类型(IS_OBJECT),zvalue_value中的obj用于存储其值
以上对于变量类型为数组时,数组值是使用内核态的哈希表存储的,那么PHP内核态的哈希表到底是个什么东东?
3. PHP内核态的哈希表
/ Zend/zend_hash.h
typedef struct bucket {
ulong h; /* Used for numeric indexing */
uint nKeyLength;
void *pData;
void *pDataPtr;
struct bucket *pListNext;
struct bucket *pListLast;
struct bucket *pNext;
struct bucket *pLast;
const char *arKey;
} Bucket; typedef struct _hashtable {
uint nTableSize;
uint nTableMask;
uint nNumOfElements;
ulong nNextFreeElement;
Bucket *pInternalPointer; /* Used for element traversal */
Bucket *pListHead;
Bucket *pListTail;
Bucket **arBuckets;
dtor_func_t pDestructor;
zend_bool persistent;
unsigned char nApplyCount;
zend_bool bApplyProtection;
#if ZEND_DEBUG
int inconsistent;
#endif
} HashTable;
首先在理解哈希表时,先不要想它和PHP数组,变量符号表等的关系。先集中精神理解哈希表本身。上面代码里的HashTable和bucket实际上是为了表示多个有关联的元素,
其中一个bucket代表一个元素,它的*pListNext和*pListLast分别指向它的下一个和上一个bucket元素,所以这些bucket实际上是一个双向链表。而bucket元素具体的值存放在了由*pData指向的一块内存中,元素名称在保存在*arKey中;
HashTable中的*pListHead和*pListTail分别指向bucket元素构成的双向链表的头和尾,arBuckets则是一个数组,数组的key是一个哈希值(bucket元素名称的hash值),数组的value是双向链表中一个bucket元素的地址指针,arBuckets保存了所有的双向链表中元素的地址指针。
因为有了arBuckets,便能够快速的根据一个元素的名称检索到对应的bucket元素。
以上为PHP内核态哈希表的简单介绍,也是核心功能介绍。
4. 用户态的数组在内核态由哈希表存储
一个数组是由多个key->value的元素构成,而哈希表正可以表示这个元素构成。比如,要想一个数组push一个元素,PHP内核先为此数组元素申请一块bucket内存,将数组元素的key进行哈希计算后得到一个哈希值,再操作HashTable中的*pListHead,和Bucket中的pListNext,pListLast。
5. 哈希表是PHP内核的核心
哈希表作用巨大,比如:
a. 数组由哈希表实现
b. 用户态的php脚本里出现的所有全局变量和所有局部变量在内核态也是使用哈希表来组织到一起
当内核检索某个变量时,通过对变量名的哈希值到哈希表的arBuckets去找到对应的Bucket。
//Zend/zend_globals.h
struct _zend_executor_globals {
...
HashTable *active_symbol_table;
HashTable symbol_table; /* main symbol table */
...
}
active_symbol_table指向当前局部变量的哈希表;而symbol_table则是全局变量的哈希表
PHP源码分析-变量的更多相关文章
- HashTable原理与源码分析
本文版权归 远方的风lyh和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作,如有错误之处忘不吝批评指正! HashTable内部存储结构 HashTable内部存储结构为数组+单向链 ...
- Set存储元素为啥是唯一的(以HashSet为例源码分析)
本文版权归 远方的风lyh和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作,如有错误之处忘不吝批评指正! 说些废话 以前面试的时候会遇到有人问Set 和list的区别 这个很好答,但 ...
- Spark大师之路:广播变量(Broadcast)源码分析
概述 最近工作上忙死了……广播变量这一块其实早就看过了,一直没有贴出来. 本文基于Spark 1.0源码分析,主要探讨广播变量的初始化.创建.读取以及清除. 类关系 BroadcastManager类 ...
- HashMap与TreeMap源码分析
1. 引言 在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...
- ABP源码分析十:Unit Of Work
ABP以AOP的方式实现UnitOfWork功能.通过UnitOfWorkRegistrar将UnitOfWorkInterceptor在某个类被注册到IOCContainner的时候,一并添加到该类 ...
- ABP源码分析二十七:ABP.Entity Framework
IRepository:接口定义了Repository常见的方法 AbpRepositoryBase:实现了IRepository接口的常见方法 EfRepositoryBase:实现了AbpRepo ...
- u-boot源码分析之C语言段
题外话: 最近一直在学习u-boot的源代码,从代码量到代码风格,都让我认识到什么才是真正的程序.以往我所学到的C语言知识和u-boot的源代码相比,实在不值一提.说到底,机器都是0和1控制的.感觉这 ...
- LinqToDB 源码分析——生成表达式树
当我们知道了Linq查询要用到的数据库信息之后.接下就是生成对应的表达式树.在前面的章节里面笔者就已经介绍过.生成表达式树是事实离不开IQueryable<T>接口.而处理表达式树离不开I ...
- MyCat源码分析系列之——结果合并
更多MyCat源码分析,请戳MyCat源码分析系列 结果合并 在SQL下发流程和前后端验证流程中介绍过,通过用户验证的后端连接绑定的NIOHandler是MySQLConnectionHandler实 ...
随机推荐
- .Net多线程编程—并发集合
并发集合 1 为什么使用并发集合? 原因主要有以下几点: System.Collections和System.Collections.Generic名称空间中所提供的经典列表.集合和数组都不是线程安全 ...
- bootstrap-datetimepicker 进一步跟进~~~开始时间和结束时间的样式显示
上次简单介绍了一下:05.LoT.UI 前后台通用框架分解系列之——漂亮的时间选择器(http://www.cnblogs.com/dunitian/p/5524019.html) 这次深入再介绍一下 ...
- HTML 事件(三) 事件流与事件委托
本篇主要介绍HTML DOM中的事件流和事件委托. 其他事件文章 1. HTML 事件(一) 事件的介绍 2. HTML 事件(二) 事件的注册与注销 3. HTML 事件(三) 事件流与事件委托 4 ...
- hash表长度优化证明
hash表冲突的解决方法一般有两个方向: 一个是倾向于空间换时间,使用向量加链表可以最大程度的在节省空间的前提下解决冲突. 另外一个倾向于时间换空间,下面是关于这种思路的一种合适表长度的证明过程: 这 ...
- [APUE]UNIX进程的环境(上)
一. 前言 本章将学习:当执行程序时,其main函数是如何被调用的,命令行参数是如何传送给执行程序的,典型的存储器布局是什么样式,如何分配另外的存储空间,进程如何使用环境变量,进程终止的不同方式等.另 ...
- 【初学python】使用python连接mysql数据查询结果并显示
因为测试工作经常需要与后台数据库进行数据比较和统计,所以采用python编写连接数据库脚本方便测试,提高工作效率,脚本如下(python连接mysql需要引入第三方库MySQLdb,百度下载安装) # ...
- Javascript实用方法二
承接上一篇, Object keys object的keys方法能够获取一个给定对象的所有键(key/属性名)并以数组的形式返回.这个方法可以用于键的筛选.匹配等. var basket = { st ...
- ActionContext.getContext().getSession()
ActionContext.getContext().getSession() 获取的是session,然后用put存入相应的值,只要在session有效状态下,这个值一直可用 ActionConte ...
- 浅谈SQL注入风险 - 一个Login拿下Server
前两天,带着学生们学习了简单的ASP.NET MVC,通过ADO.NET方式连接数据库,实现增删改查. 可能有一部分学生提前预习过,在我写登录SQL的时候,他们鄙视我说:“老师你这SQL有注入,随便都 ...
- Linux学习笔记(一):常用命令
经过统计Linux中能够识别的命令超过3000种,当然常用的命令就远远没有这么多了,按照我的习惯,我把已经学过的Linux常用命令做了以下几个方面的分割: 1.文件处理命令 2.文件搜索命令 3.帮助 ...