PHP内核探索之变量


Posted in PHP onDecember 22, 2015

php变量组成部分:

变量名:php语言的变量名以$开头+英文/下划线,可以包含数字、下划线、字母,区分大小写。同时PHP也支持复合变量,形如$$A,增加了php的动态性。

类型:php属于弱类型语言,可以赋值任意类型的值。

内容:在同一时刻只能有一种值。

php语言中存在8中数据类型,分为三大类:

1. 标量类型:Boolean,integer,float,string;

2. 复合类型:object,array;

3. 特殊类型:NULL,resource;

php作为一种弱类型语言,在实现内部所有变量是通过结构zval来存储数据的,不仅包含变量的值,也包含变量的类型,是php弱类型的核心。

zval数据结构:

struct _zval_struct{
  zvalue_value value;    //存储变量的值
  zend_unint  refcount_gc; //引用计数
  zend_char  is_ref_gc;  // 是否为引用
  zend_char  type;     //存储变量的类型
}

其中zvalue_value并不是一个结构体,为了节省内存使用的union来实现的,因为在同一时刻变量只能表示一种类型。其原型:

typedef union _zvalue_value{
  long lval;         
  double dval;
  struct {
      char *val;
      int len;      //字符串的长度
    }str;
  HashTable *ht;       //保存数组
  zend_object_value obj;   //对象
}zvalue_value;

哈希表:

php内部很多实现基于哈希表:变量的作用域、函数表、类的属性、方法等,Zend引擎内部的很多数据都是保存在哈希表中的。

php数组使用哈希表来存储关联数据,哈希表实现使用两个数据结构HashTable和Bucket:

HashTable:

typedef struct _hashtable { 
  uint nTableSize;    // hash Bucket的大小,最小为8,以2x增长。
  uint nTableMask;    // nTableSize-1 , 索引取值的优化
  uint nNumOfElements;  // hash Bucket中当前存在的元素个数,count()函数会直接返回此值 
  ulong nNextFreeElement; // 下一个数字索引的位置
  Bucket *pInternalPointer;  // 当前遍历的指针(foreach比for快的原因之一)
  Bucket *pListHead;     // 存储数组头元素指针
  Bucket *pListTail;     // 存储数组尾元素指针
  Bucket **arBuckets;     // 存储hash数组
  dtor_func_t pDestructor;  // 在删除元素时执行的回调函数,用于资源的释放
  zend_bool persistent;    // 指出了Bucket内存分配的方式。如果persisient为TRUE,
                  则使用操作系统本身的内存分配函数为Bucket分配内存,否则使用
                  PHP的内存分配函数。
  unsigned char nApplyCount; // 标记当前hash Bucket被递归访问的次数(防止多次递归)
  zend_bool bApplyProtection;// 标记当前hash桶允许不允许多次访问,不允许时,最多只能递归3次
#if ZEND_DEBUG
  int inconsistent;
#endif
} HashTable;

在HashTable中容量的扩增,始终调整为接近初始大小的2的整数次方。因为:

在选槽时,这里使用&操作而不是使用取模,这是因为是相对来说取模操作的消耗和按位与的操作大很多。mask的作用就是将哈希值映射到槽位所能存储的索引范围内。 例如:某个key的索引值是21, 哈希表的大小为8,则mask为7,则求与时的二进制表示为: 10101 & 111 = 101 也就是十进制的5。 因为2的整数次方-1的二进制比较特殊:后面N位的值都是1,这样比较容易能将值进行映射, 如果是普通数字进行了二进制与之后会影响哈希值的结果。那么哈希函数计算的值的平均分布就可能出现影响。

bucket:

typedef struct bucket {
  ulong h;      // 对char *key进行hash后的值,或者是用户指定的数字索引值
  uint nKeyLength;  // hash关键字的长度,如果数组索引为数字,此值为0
  void *pData;    // 指向value,一般是用户数据的副本,如果是指针数据,则指向pDataPtr
  void *pDataPtr;   //如果是指针数据,此值会指向真正的value,同时上面pData会指向此值
  struct bucket *pListNext;  // 整个hash表的下一元素
  struct bucket *pListLast;  // 整个哈希表该元素的上一个元素
  struct bucket *pNext;    // 存放在同一个hash Bucket内的下一个元素
  struct bucket *pLast;    // 同一个哈希bucket的上一个元素
// 保存当前值所对于的key字符串,这个字段只能定义在最后,实现变长结构体
  char arKey[1];       
} Bucket;

在Bucket中存储的是哈希值而不是哈希的索引。

上面结构体的最后一个字段用来保存key的字符串,而这个字段却申明为只有一个字符的数组, 其实这里是一种长见的变长结构体,主要的目的是增加灵活性。 以下为哈希表插入新元素时申请空间的代码

p = (Bucket *) pemalloc(sizeof(Bucket) - 1 + nKeyLength, ht->persistent);
if (!p) {
  return FAILURE;
}
memcpy(p->arKey, arKey, nKeyLength);

插入过程图

哈希算法

php中hash函数使用DJBX33A算法来实现。

对象:

php对象使用数据结构zend_object_value来存储;

PHP 相关文章推荐
第三节--定义一个类
Nov 16 PHP
Windows中安装Apache2和PHP4权威指南
Nov 18 PHP
PHP删除非空目录的函数代码小结
Feb 28 PHP
zf框架的数据库追踪器使用示例
Mar 13 PHP
PHP递归删除目录几个代码实例
Apr 21 PHP
php数组合并array_merge()函数使用注意事项
Jun 19 PHP
php提示Failed to write session data错误的解决方法
Dec 17 PHP
PHP并发多进程处理利器Gearman使用介绍
May 16 PHP
thinkPHP线上自动加载异常与修复方法实例分析
Dec 01 PHP
浅析PHP类的反射来实现依赖注入过程
Feb 06 PHP
PHP命名空间与自动加载类详解
Sep 04 PHP
PHP7原生MySQL数据库操作实现代码
Jul 03 PHP
yii2.0使用Plupload实现带缩放功能的多图上传
Dec 22 #PHP
PHP输入流php://input实例讲解
Dec 22 #PHP
服务器迁移php版本不同可能诱发的问题
Dec 22 #PHP
php上传图片并压缩的实现方法
Dec 22 #PHP
PHP实现图片上传并压缩
Dec 22 #PHP
Linux系统中设置多版本PHP共存配合Nginx服务器使用
Dec 21 #PHP
在Mac OS上搭建Nginx+PHP+MySQL开发环境的教程
Dec 21 #PHP
You might like
收音机的保养
2021/03/01 无线电
Php+SqlServer实现分页显示
2006/10/09 PHP
php文件怎么打开 如何执行php文件
2011/12/21 PHP
php删除左端与右端空格的方法
2014/11/29 PHP
PHP实现检测客户端是否使用代理服务器及其匿名级别
2015/01/07 PHP
浅谈PHP中单引号和双引号到底有啥区别呢?
2015/03/04 PHP
使用GD库生成带阴影文字的图片
2015/03/27 PHP
[原创]PHP正则匹配中英文、数字及下划线的方法【用户名验证】
2017/08/01 PHP
PHP读取目录树的实现方法分析
2019/03/22 PHP
一个简单的Ext.XTemplate的实例代码
2012/03/18 Javascript
JS实现选中当前菜单后高亮显示的导航条效果
2015/10/15 Javascript
jquery 实现滚动条下拉时无限加载的简单实例
2016/06/01 Javascript
浅谈html转义及防止javascript注入攻击的方法
2016/12/04 Javascript
AngularJS Toaster使用详解
2017/02/24 Javascript
JS简单获取日期相差天数的方法
2017/04/24 Javascript
详解使用mpvue开发github小程序总结
2018/07/25 Javascript
vue实现可视化可拖放的自定义表单的示例代码
2019/03/20 Javascript
[51:53]完美世界DOTA2联赛循环赛 LBZS vs DM BO2第二场 11.01
2020/11/02 DOTA
浅谈django中的认证与登录
2016/10/31 Python
python 信息同时输出到控制台与文件的实例讲解
2018/05/11 Python
python判断输入日期为第几天的实例
2018/11/13 Python
python读取指定字节长度的文本方法
2019/08/27 Python
python实现广度优先搜索过程解析
2019/10/19 Python
马来西亚网上美容店:Hermo.my
2017/11/25 全球购物
在线购买廉价折扣书籍和小说:BookOutlet.com
2018/02/19 全球购物
美国一家运动专业鞋类零售商:Warehouse Shoe Sale(WSS)
2018/03/28 全球购物
马来西亚最大的在线隐形眼镜商店:MrLens
2019/03/27 全球购物
Vita Fede官网:在意大利手工制作,在纽约市设计
2019/10/25 全球购物
亿阳信通股份有限公司笔试题(C#)
2016/03/04 面试题
党的群众路线教育实践活动公开承诺书
2014/03/28 职场文书
领导干部查摆“四风”问题自我剖析材料思想汇报
2014/10/05 职场文书
党的群众路线教育实践活动个人剖析材料
2014/10/07 职场文书
2015年政教主任工作总结
2015/07/23 职场文书
Docker 镜像介绍以及commit相关操作
2022/04/13 Servers
IDEA 2022 Translation 未知错误 翻译文档失败
2022/04/24 Java/Android
在windows server 2012 r2中安装mysql的详细步骤
2022/07/23 Servers