热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

php结构体_PHP底层原理知其然知其所以然

1、PHP变量的底层实现PHP代码执行图解1.1:变量在内存中的存储结构PHP变量是通过zval结构体来存储的,文件:Zendzend.h316行左右1.2:值的存储

1·、PHP变量的底层实现

PHP代码执行图解

7523a83e4b30db1a6b0897ef68759e8f.png

1.1:变量在内存中的存储结构

PHP变量是通过zval结构体来存储的,文件: Zend/zend.h 316行左右

38cbc8f8adb844974921edc21457bed5.png

1.2:值的存储

PHP变量的值是放在zval结构体中的value段中的,文件: Zend/zend.h

76fbd447d5f7e5ed56b62eaf77f2203e.png

1.3:结构体的字段解释

struct _zval_struct {

/* Variable information */

zvalue_value value; /*变量的值,是个联合体*/

zend_uint refcount__gc; /*指向次数*/

zend_uchar type; /* 变量类型 */

zend_uchar is_ref__gc; /*是否引用*/

};

type字段的值为以下常量

IS_NULL, IS_BOOL,IS_LONG,IS_DOUBLE

IS_STRING,IS_ARRAY,IS_OBJECT

IS_RESOURCE

1.4:联合体中的值

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;

联合中为什么只列出了5种值?

NULL不用,zval的type为IS_NULL即可

Bool以1,0存储在lval上

resource的type为resource,其resource的内容用long来标志(资源标记)

1.5:变量的结构图

5fb861d06c28579587a4a3fc10b84ef5.png

1.6:变量的创建

创建变量的步骤: $str = "hello";

1:创建zval结构,并设置其类型 IS_STRING

2:设置其值为 hello

3:讲其加入符号表 

{

    zval *fooval;

    MAKE_STD_ZVAL(fooval);

    ZVAL_STRING(fooval, "hello", 1);

    ZEND_SET_SYMBOL( EG(active_symbol_table) ,  "foo" , fooval);

}

1.7:符号表 symbol_table

符号表是什么?

符号表是一张哈希表,里面存储了变量名->变量的zval结构体的地址,

// zend/zend_globals.h 182行

struct _zend_executor_globals {

    ...

    ...

HashTable *active_symbol_table; /*活动符号表*/

HashTable symbol_table; /* 全局符号表 */

HashTable included_files; /* files already included */

1.8:符号表与函数

Zend/zend_compiles.h

struct _zend_execute_data {

    ...

    zend_op_array *op_array; //函数的执行步骤

    HashTable *symbol_table; // 此函数的符号表地址

    zend_class_entry *current_scope;

    zval *current_this;

    zval *current_object;

    ...

};

上面这个,是当前函数执行时的符号表

1.9:符号表与作用域

当执行到函数时,会生成函数的"执行环境结构体",包含函数名,参数,执行步骤,所在的类(如果是方法),以及为这个函数生成一个符号表.符号表统一放在栈上.并把active_symbol_table指向刚产生的符号表

bd77d0436d432d3e547b98ab4301333f.png

1.10:函数中静态变量的实现

a140fd30817cd380bd7e4a0f91a18bac.png

56e00644823102378a33762c9cc7fef2.png

2.0:常量-常量结构体

结构体 Zend/constants.h 33行

typedef struct _zend_constant {

zval value;  //变量结构体

int flags;    //标志,是否大小写敏感等

char *name; //常量名

uint name_len;

int module_number;//模块名

} zend_constant;

2.1:常量的生成

int zend_register_constant(zend_constant *c TSRMLS_DC) {

...

...

zend_hash_add(EG(zend_constants), name, c->name_len, (void *) c, sizeof(zend_constant), NULL)==FAILURE)

...

...

}

2.2:define函数的实现

define函数当然是调用zend_register_constant声明的常量 :) 

具体如下 Zend/zend_builtin_functions.c

关键代码:

c.value = *val;

zval_copy_ctor(&c.value);

if (val_free) {

zval_ptr_dtor(&val_free);

}

c.flags = case_sensitive; /* non persistent */

c.name = zend_strndup(name, name_len);

c.name_len = name_len+1;

c.module_number = PHP_USER_CONSTANT;

if (zend_register_constant(&c TSRMLS_CC) == SUCCESS) {

RETURN_TRUE;

} else {

RETURN_FALSE;

}

2.3:有趣的测试

1:常量并没有检测名字...

define('^_^',"laugh");

2:常量的第2个参数还可以是对象,与手册上介绍的不同

define('obj',new className());

(当然,对象要有toString方法等着)

2.4:常量为什么是全局有效的?

很简单,常量的哈希表只有一个

EG(zend_constants)

3.0:内存管理与垃圾回收

7406c538ebc4e93778e1b5ce9f19f444.png

PHP封装了对系统内存的请求,不要直接用malloc直接请求内存

3.1:PHP的hashtable太强大

6c1bd377bf08e4cebb8130818f76e26f.png

3.2:引用计数

$a = 1;

$b = $a;

?>

$a,$b的值及类型 都一样,有必要再申请一个zval结构吗?

1d57945ca799802ec39adc8a344031a8.png

3.3:引用计数解释

$a = 1;

$b = $a;

?>

当$a的值赋给$b时,并没有为$b生成一个新的zval结构体.而是$b与$a共享一个结构体.

3.4:copy-on-write 写时复制

$a = 1;

$b = $a;

$b=6

?>

6dc9e9318785f6c13aac4233fdeb7936.png

3.5:引用传值发生了什么?

$a = 1;

$b = &$a;

?>

2d2fc8c5bd9fa02f0915b20656c0a859.png

3.6:引用传值为什么影响了2个值

$a = 1;

$b = &$a;

$b = 6;

?>

内核修改zval的值时,发现is_ref_gc为1,则直接修改该value,而不是复制一份.如下图:

752f51b36114ada3df92c522df2b742b.png



推荐阅读
  • PHP预处理常量详解:如何定义与使用常量 ... [详细]
  • 在使用 Qt 进行 YUV420 图像渲染时,由于 Qt 本身不支持直接绘制 YUV 数据,因此需要借助 QOpenGLWidget 和 OpenGL 技术来实现。通过继承 QOpenGLWidget 类并重写其绘图方法,可以利用 GPU 的高效渲染能力,实现高质量的 YUV420 图像显示。此外,这种方法还能显著提高图像处理的性能和流畅性。 ... [详细]
  • 在尝试对 QQmlPropertyMap 类进行测试驱动开发时,发现其派生类中无法正常调用槽函数或 Q_INVOKABLE 方法。这可能是由于 QQmlPropertyMap 的内部实现机制导致的,需要进一步研究以找到解决方案。 ... [详细]
  • Unity与MySQL连接过程中出现的新挑战及解决方案探析 ... [详细]
  • Android 构建基础流程详解
    Android 构建基础流程详解 ... [详细]
  • 本文详细解析了使用C++实现的键盘输入记录程序的源代码,该程序在Windows应用程序开发中具有很高的实用价值。键盘记录功能不仅在远程控制软件中广泛应用,还为开发者提供了强大的调试和监控工具。通过具体实例,本文深入探讨了C++键盘记录程序的设计与实现,适合需要相关技术的开发者参考。 ... [详细]
  • 2.2 组件间父子通信机制详解
    2.2 组件间父子通信机制详解 ... [详细]
  • 基于Net Core 3.0与Web API的前后端分离开发:Vue.js在前端的应用
    本文介绍了如何使用Net Core 3.0和Web API进行前后端分离开发,并重点探讨了Vue.js在前端的应用。后端采用MySQL数据库和EF Core框架进行数据操作,开发环境为Windows 10和Visual Studio 2019,MySQL服务器版本为8.0.16。文章详细描述了API项目的创建过程、启动步骤以及必要的插件安装,为开发者提供了一套完整的开发指南。 ... [详细]
  • 在C#中,一旦对象被实例化后,直接重新调用构造函数是不可行的。与C++不同,C#不支持在对象实例化后强制调用构造函数。为了实现类似的功能,可以通过定义一个重置方法或使用工厂模式来重新初始化对象的状态。例如,可以创建一个 `Reset` 方法,在该方法中重新设置对象的属性和状态,从而达到类似于重新调用构造函数的效果。这样不仅保持了代码的清晰性和可维护性,还避免了潜在的副作用。 ... [详细]
  • 本文详细解析了 Android 系统启动过程中的核心文件 `init.c`,探讨了其在系统初始化阶段的关键作用。通过对 `init.c` 的源代码进行深入分析,揭示了其如何管理进程、解析配置文件以及执行系统启动脚本。此外,文章还介绍了 `init` 进程的生命周期及其与内核的交互方式,为开发者提供了深入了解 Android 启动机制的宝贵资料。 ... [详细]
  • 在C++程序中,文档A的每一行包含一个结构体数据,其中某些字段可能包含不同数量的数字。需要将这些结构体数据逐行读取并存储到向量中,随后不仅在控制台上显示,还要输出到新创建的文档B中。希望得到指导,感谢! ... [详细]
  • 本指南介绍了如何在ASP.NET Web应用程序中利用C#和JavaScript实现基于指纹识别的登录系统。通过集成指纹识别技术,用户无需输入传统的登录ID即可完成身份验证,从而提升用户体验和安全性。我们将详细探讨如何配置和部署这一功能,确保系统的稳定性和可靠性。 ... [详细]
  • 在Android平台中,播放音频的采样率通常固定为44.1kHz,而录音的采样率则固定为8kHz。为了确保音频设备的正常工作,底层驱动必须预先设定这些固定的采样率。当上层应用提供的采样率与这些预设值不匹配时,需要通过重采样(resample)技术来调整采样率,以保证音频数据的正确处理和传输。本文将详细探讨FFMpeg在音频处理中的基础理论及重采样技术的应用。 ... [详细]
  • 题目要求维护一个数列,并支持两种操作:一是查询操作,语法为QL,用于查询数列末尾L个数中的最大值;二是更新操作,用于修改数列中的某个元素。本文通过ST表(Sparse Table)优化查询效率,确保在O(1)时间内完成查询,同时保持较低的预处理时间复杂度。 ... [详细]
  • 如何撰写适应变化的高效代码:策略与实践
    编写高质量且适应变化的代码是每位程序员的追求。优质代码的关键在于其可维护性和可扩展性。本文将从面向对象编程的角度出发,探讨实现这一目标的具体策略与实践方法,帮助开发者提升代码效率和灵活性。 ... [详细]
author-avatar
幸福guaiguai乐园
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有