前言中提到变量的三个基本特性,其中的有一个特性为变量的类型,变量都有特定的类型,如:字符串、数组、对象等等。编程语言的类型系统可以分为强类型和弱类型两种:

强类型语言是一旦某个变量被申明为某个类型的变量,在程序运行过程中,就不能将该变量的类型以外的值赋予给它(当然并不完全如此,这可能会涉及到类型的转换,后面的小节会有相应介绍),C/C++/Java等语言就属于这类。

PHP及Ruby,JavaScript等脚本语言属于弱类型语言:一个变量可以表示任意的数据类型。

PHP之所以成为一个简单而强大的语言,很大一部分的原因是它拥有弱类型的变量。但是有些时候这也是一把双刃剑,使用不当也会带来一些问题。就像仪器一样,越是功能强大,出现错误的可能性也就越大。

在官方的PHP实现内部,所有变量使用同一种数据结构(zval)来保存,而这个结构同时表示PHP中的各种数据类型。它不仅仅包含变量的值,也包含变量的类型。这就是PHP弱类型的核心。

那zval结构具体是如何实现弱类型的呢,下面我们一起来揭开面纱。

## 一. PHP变量类型及存储结构

PHP在声明或使用变量的时候,并不需要显式指明其数据类型。

PHP是弱类型语言,这并不表示PHP没有类型,在PHP中,存在8种变量类型,可以分为三类

**标量类型**: _boolean_、_integer_、_float(double)_、_string_

**复合类型**: _array_、_object_

**特殊类型**: _resource_、_NULL_

官方PHP是用C实现的,而C是强类型的语言,那这是怎么实现PHP中的弱类型的呢?

### 1. 变量存储结构

变量的值存储到以下所示zval结构体中。zval结构体定义在Zend/zend.h文件,其结构如下:

typedef struct _zval_struct zval;

...

struct _zval_struct {

/* Variable information */

zvalue_value value; /* value */

zend_uint refcount__gc;

zend_uchar type; /* active type */

zend_uchar is_ref__gc;

};

PHP使用这个结构来存储变量的所有数据。和其他编译性静态语言不同,PHP在存储变量时将PHP用户空间。的变量类型也保存在同一个结构体中。这样我们就能通过这些信息获取到变量的类型。

zval结构体中有四个字段,其含义分别为:

| 属性名 | 含义 | 默认值 |

|-----|-----|-----|

| refcount__gc | 表示引用计数 | 1 |

| is_ref__gc | 表示是否为引用 | 0 |

| value | 存储变量的值 | |

| type | 变量具体的类型 | |

> 在PHP5.3之后,引入了新的垃圾收集机制,引用计数和引用的字段名改为refcount__gc和is_ref__gc。在此之前为refcount和is__ref。

而变量的值则存储在另外一个结构体zvalue_value中。值存储见下面的介绍。

> PHP用户空间指的在PHP语言这一层面,而本书中大部分地方都在探讨PHP的实现。 这些实现可以理解为内核空间。由于PHP使用C实现,而这个空间的范畴就会限制在C语言。 而PHP用户空间则会受限于PHP语法及功能提供的范畴之内。

例如有些PHP扩展会提供一些PHP函数或者类,这就是向PHP用户空间导出了方法或类。

### 2.变量类型

zval结构体的type字段就是实现弱类型最关键的字段了,type的值可以为:IS_NULL、IS_BOOL、IS_LONG、IS_DOUBLE、IS_STRING、IS_ARRAY、IS_OBJECT和IS_RESOURCE 之一。从字面上就很好理解,他们只是类型的唯一标示,根据类型的不同将不同的值存储到value字段。除此之外,和他们定义在一起的类型还有IS_CONSTANT和IS_CONSTANT_ARRAY。

这和我们设计数据库时的做法类似,为了避免重复设计类似的表,使用一个标示字段来记录不同类型的数据。

## 二.变量的值存储

前面提到变量的值存储在zvalue_value联合体中,结构体定义如下:

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;

} zvalue_value;

> 这里使用联合体而不是用结构体是出于空间利用率的考虑,因为一个变量同时只能属于一种类型。 如果使用结构体的话将会不必要的浪费空间,而PHP中的所有逻辑都围绕变量来进行的,这样的话, 内存浪费将是十分大的。这种做法成本小但收益非常大。

各种类型的数据会使用不同的方法来进行变量值的存储,其对应赋值方式如下:

- 一般类型

变量类型
boolean ZVAL_BOOL 布尔型/整型的变量值存储于(zval).value.lval中,其类型也会以相应的IS_*进行存储。

 Z_TYPE_P(z)=IS_BOOL/LONG;  Z_LVAL_P(z)=((b)!=0); 
integer ZVAL_LONG
float ZVAL_DOUBLE
null ZVAL_NULL NULL值的变量值不需要存储,只需要把(zval).type标为IS_NULL。

 Z_TYPE_P(z)=IS_NULL; 
resource ZVAL_RESOURCE 资源类型的存储与其他一般变量无异,但其初始化及存取实现则不同。

 Z_TYPE_P(z) = IS_RESOURCE;  Z_LVAL_P(z) = l; 

- 字符串String

字符串的类型标示和其他数据类型一样,不过在存储字符串时多了一个字符串长度的字段。

struct {

char *val;

int len;

} str;

> C中字符串是以\0结尾的字符数组,这里多存储了字符串的长度,这和我们在设计数据库时增加的冗余字段异曲同工。 因为要实时获取到字符串的长度的时间复杂度是O(n),而字符串的操作在PHP中是非常频繁的,这样能避免重复计算字符串的长度, 这能节省大量的时间,是空间换时间的做法。

这么看在PHP中strlen()函数可以在常数时间内获取到字符串的长度。 计算机语言中字符串的操作都非常之多,所以大部分高级语言中都会存储字符串的长度。

- 数组Array

数组是PHP中最常用,也是最强大变量类型,它可以存储其他类型的数据,而且提供各种内置操作函数。数组的存储相对于其他变量要复杂一些,数组的值存储在zvalue_value.ht字段中,它是一个HashTable类型的数据。PHP的数组使用哈希表来存储关联数据。哈希表是一种高效的键值对存储结构。PHP的哈希表实现中使用了两个数据结构HashTable和Bucket。PHP所有的工作都由哈希表实现,在下节HashTable中将进行哈希表基本概念的介绍以及PHP的哈希表实现。

- 对象Object

在面向对象语言中,我们能自己定义自己需要的数据类型,包括类的属性,方法等数据。而对象则是类的一个具体实现。对象有自身的状态和所能完成的操作。

PHP的对象是一种复合型的数据,使用一种zend_object_value的结构体来存放。其定义如下:

typedef struct _zend_object_value {

zend_object_handle handle; // unsigned int类型,EG(objects_store).object_buckets的索引

zend_object_handlers *handlers;

} zend_object_value;

PHP的对象只有在运行时才会被创建,前面的章节介绍了EG宏,这是一个全局结构体用于保存在运行时的数据。其中就包括了用来保存所有被创建的对象的对象池,EG(objects_store),而object对象值内容的zend_object_handle域就是当前对象在对象池中所在的索引,handlers字段则是将对象进行操作时的处理函数保存起来。这个结构体及对象相关的类的结构_zend_class_entry,将在第五章作详细介绍。

PHP的弱变量容器的实现方式是兼容并包的形式体现,针对每种类型的变量都有其对应的标记和存储空间。使用强类型的语言在效率上通常会比弱类型高,因为很多信息能在运行之前就能确定,这也能帮助排除程序错误。而这带来的问题是编写代码相对会受制约。

PHP主要的用途是作为Web开发语言,在普通的Web应用中瓶颈通常在业务和数据访问这一层。不过在大型应用下语言也会是一个关键因素。facebook因此就使用了自己的php实现。将PHP编译为C++代码来提高性能。不过facebook的hiphop并不是完整的php实现,由于它是直接将php编译为C++,有一些PHP的动态特性比如eval结构就无法实现。当然非要实现也是有方法的,hiphop不实现应该也是做了一个权衡。

php变量结构体的深入理解,第一节 变量的结构和类型相关推荐

  1. php变量结构体的深入理解,深入理解PHP内核之变量(zval)的结构

    第一节 变量的结构 每门计算机语言都需要一些容器来保存变量数据.在一些语言当中,变量都有特定的类型,如字符串,数组,对象等等.比如C和Pascal就属于这种.而php则没有这样的类型.在PHP中,一个 ...

  2. C语言定义了一个结构体怎么分配内存?C\C++中结构体变量与结构体指针内存分配问题?

    C语言定义了一个结构体怎么分配内存?C\C++中结构体变量与结构体指针内存分配问题? 问题1:结构体指针最开始怎么分配内存?用sizeof()是不能确定大小的. 问题2:给结构体变量分配之后,是否还要 ...

  3. 【精华文】C语言结构体特殊情况分析:结构体指针 / 基本数据类型指针,指向其他结构体

    参考链接:Structure pointer pointing to different structure instance 注:可以查看此篇的问题和唯一的回复,那是相对正确的,不要看comment ...

  4. c语言怎么判定结构体有无数据,C语言中什么是结构体,怎么定义结构体。

    为什么要用结构体? 没有结构体之前,C语言中,数据的组织依靠:变量+数组.最初最简单的时候,只需要使用基本数据类型来定义单个变量,需要几个就定义几个,后来有时候需要很多意义相关的变量这时候数组出现了. ...

  5. 中的数组怎么转成结构体_PLC知识,什么是数组和结构体?

    今天给大家介绍一下什么是数组,什么是结构体,关于数组我们会介绍数组的定义.它的使用方法,关于结构体我们会介绍结构体的使用.怎么去建立一个结构体等知识,之前也是有部分学员有问过这些问题的,那么都一块儿给 ...

  6. c语言 结构体 ppt,第8章C语言的结构体和共同体.ppt

    <第8章C语言的结构体和共同体.ppt>由会员分享,可在线阅读,更多相关<第8章C语言的结构体和共同体.ppt(41页珍藏版)>请在皮匠网上搜索. 1.1第八章结构体.共用体和 ...

  7. 结构体复数相乘c语言,复数乘法中的结构体赋值实现代码

    复数乘法中的结构体赋值实现代码 废话不多说,直接上代码 复制代码 代码如下: #include using namespace std; typedef struct { double real; d ...

  8. Go 学习笔记(14)— 结构体定义、实例化、初始化、匿名结构体、结构体访问、结构体作为形参、结构体指针

    Go 语言中没有 "类" 的概念,也不支持 "类" 的继承等面向对象的概念.Go 语言不仅认为结构体能拥有方法,且每种自定义类型也可以拥有自己的方法. 1. 结 ...

  9. PAT甲级1141 PAT Ranking of Institutions :[C++题解]结构体、排序、哈希表、结构体构造函数、结构体内写函数、排名

    文章目录 题目分析 题目来源 题目分析 来源:acwing 分析:和下面这题是一道题: PAT甲级1137 Final Grading:[C++题解]结构体.排序.哈希表.结构体构造函数.结构体内写函 ...

最新文章

  1. LeetCode Linked List Cycle II
  2. Py之lightgbm:lightgbm的简介、安装、使用方法之详细攻略
  3. EOJ_1064_树的层号表示法
  4. 如何定制化SAP Spartacus的页面布局
  5. java inputstream字节流_Java输入输出流ByteArrayInputStream和ByteArrayOutPutStream
  6. 惯性矩和偏心距描述器
  7. Markdown——编辑器语法——字体、字号与颜色
  8. mysql重复make_Mysql5.6 make 错误以及解决办法
  9. Zookeeper Watcher(事件监听器)?
  10. 使用libcurl进行HTTP PUT
  11. js 字符串转换成数字(转)
  12. 消费信贷 开源数据_消费或发布开源软件的6大动机
  13. 高级文本编辑器——Kate
  14. Marvell车载以太网交换机芯片88Q5050
  15. Poker2的Programmer Dvorak解决方案
  16. Unit firewalld.service could not be found
  17. excel中为什么不显示单引号
  18. 2022年5月26日:用C#生成.NET应用程序--创建新的.NET项目并使用依赖项(部分没看懂。)
  19. 长征二号超级计算机,厉害了,我们的计算机!
  20. IMX6ULL-IRQ中断之添加中断向量表

热门文章

  1. rman连接目标数据库的方式
  2. python中的注释有哪些符号_python表示注释的符号是什么
  3. java线程挂起唤醒_java线程技术6_线程的挂起和唤醒[转]
  4. 右键我的电脑后点击属性没有高级系统设置(桌面我的电脑等图标的恢复)
  5. 一副对联,16个外国典故
  6. android 平台 c 程序编译
  7. mysql数据库导入sql文件失败解决办法
  8. C语言编程规范 — 头文件、函数
  9. 自学html全栈开发,Html5全栈开发怎么学?
  10. css中的伪类选择器有哪些