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源码分析-变量的更多相关文章

  1. HashTable原理与源码分析

    本文版权归 远方的风lyh和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作,如有错误之处忘不吝批评指正! HashTable内部存储结构 HashTable内部存储结构为数组+单向链 ...

  2. Set存储元素为啥是唯一的(以HashSet为例源码分析)

    本文版权归 远方的风lyh和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作,如有错误之处忘不吝批评指正! 说些废话 以前面试的时候会遇到有人问Set 和list的区别 这个很好答,但 ...

  3. Spark大师之路:广播变量(Broadcast)源码分析

    概述 最近工作上忙死了……广播变量这一块其实早就看过了,一直没有贴出来. 本文基于Spark 1.0源码分析,主要探讨广播变量的初始化.创建.读取以及清除. 类关系 BroadcastManager类 ...

  4. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

  5. ABP源码分析十:Unit Of Work

    ABP以AOP的方式实现UnitOfWork功能.通过UnitOfWorkRegistrar将UnitOfWorkInterceptor在某个类被注册到IOCContainner的时候,一并添加到该类 ...

  6. ABP源码分析二十七:ABP.Entity Framework

    IRepository:接口定义了Repository常见的方法 AbpRepositoryBase:实现了IRepository接口的常见方法 EfRepositoryBase:实现了AbpRepo ...

  7. u-boot源码分析之C语言段

    题外话: 最近一直在学习u-boot的源代码,从代码量到代码风格,都让我认识到什么才是真正的程序.以往我所学到的C语言知识和u-boot的源代码相比,实在不值一提.说到底,机器都是0和1控制的.感觉这 ...

  8. LinqToDB 源码分析——生成表达式树

    当我们知道了Linq查询要用到的数据库信息之后.接下就是生成对应的表达式树.在前面的章节里面笔者就已经介绍过.生成表达式树是事实离不开IQueryable<T>接口.而处理表达式树离不开I ...

  9. MyCat源码分析系列之——结果合并

    更多MyCat源码分析,请戳MyCat源码分析系列 结果合并 在SQL下发流程和前后端验证流程中介绍过,通过用户验证的后端连接绑定的NIOHandler是MySQLConnectionHandler实 ...

随机推荐

  1. solr服务中集成IKAnalyzer中文分词器、集成dataimportHandler插件

    昨天已经在Tomcat容器中成功的部署了solr全文检索引擎系统的服务:今天来分享一下solr服务在海量数据的网站中是如何实现数据的检索. 在solr服务中集成IKAnalyzer中文分词器的步骤: ...

  2. 探索ASP.NET MVC5系列之~~~5.缓存篇(页面缓存+二级缓存)

    其实任何资料里面的任何知识点都无所谓,都是不重要的,重要的是学习方法,自行摸索的过程(不妥之处欢迎指正) 汇总:http://www.cnblogs.com/dunitian/p/4822808.ht ...

  3. 第一个移动前端开源项目-dailog

    你还在为手机上没有忙碌光标而发愁吗?你还在抱怨弹出框组件要依赖zepto/jqery吗?你还在纠结是否要自己写一套还是去网上寻找成现成的UI组件吗?YouA为你轻松解决所有烦恼.YouA是我为移动前端 ...

  4. CRL快速开发框架系列教程六(分布式缓存解决方案)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  5. Oracle安装部署,版本升级,应用补丁快速参考

    一.Oracle安装部署 1.1 单机环境 1.2 Oracle RAC环境 1.3 Oracle DataGuard环境 1.4 主机双机 1.5 客户端部署 二.Oracle版本升级 2.1 单机 ...

  6. (翻译)FIFO In Hardware

    翻译一些自己觉得有价值的材料,工作中碰到英语大多数是读,基本没有写或者翻的,翻得不好不到位的敬请指摘. 同时也附原文以供参考. http://electronics.stackexchange.com ...

  7. Linux字符设备驱动框架

    字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l ...

  8. web.xml中welcome-file-list的作用

    今天尝试使用struts2+ urlrewrite+sitemesh部署项目,结果发现welcome-file-list中定义的欢迎页不起作用: <welcome-file-list> & ...

  9. js中的null 和undefined

    参考链接:http://blog.csdn.net/qq_26676207/article/details/53100912 http://www.ruanyifeng.com/blog/2014/0 ...

  10. ubuntu+mono+jexus 搭建.net的web平台 实现.net跨平台

    准备工作: vmware 用来安装 ubuntu 下载地址:VMware-workstation-9.0.1-894247.exe.tar 注册码: 1A4P8-DMK0N-FZ431-7K8NH-2 ...