搜索
首页后端开发php教程PHP内核-简单讲解PHP灵魂HashTble

一。前言

HashTable是PHP的灵魂,因为在Zend引擎中 大量的使用了HashTable,如变量表,常量表,函数表等,这些都是 适应HashTable保存的,另外,PHP的数组也是通过使用HashTble实现的,所以,了解PHP的HashTable才能真正了解PHP


为了方便阅读,这里列举一下HashTable实现中出现的基本概念。 哈希表是一种通过哈希函数,将特定的键映射到特定值的一种数据结构,它维护键和值之间一一对应关系。

  • 键(key):用于操作数据的标示,例如PHP数组中的索引,或者字符串键等等。

  • 槽(slot/bucket):哈希表中用于保存数据的一个单元,也就是数据真正存放的容器。

  • 哈希函数(hash function):将key映射(map)到数据应该存放的slot所在位置的函数。

  • 哈希冲突(hash collision):哈希函数将两个不同的key映射到同一个索引的情况。

PHP中的哈希表实现在Zend/zend_hash.h中,先看看PHP实现中的数据结构, PHP使用如下两个数据结构来实现哈希表,HashTable结构体用于保存整个哈希表需要的基本信息, 而Bucket结构体用于保存具体的数据内容,(具体源码见最后)

二。举例

那么,以创建一个变量为例,在底层到底发生了什么呢?

创建变量的步骤: $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);
}

前两步在上一篇的变量结构中有提到过,详见PHP内核的存储机制(分离/改变)

符号表是什么?

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

// zend/zend_globals.h 161行 符号表

struct _zend_executor_globals { 
      ...   
      ...	
HashTable *active_symbol_table; /*活动符号表*/	
HashTable symbol_table;		/* 全局符号表 */	
HashTable included_files;	/* files already included */
     ...
}

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


Zend/zend_compiles.h 384行,执行环境结构体:

struct _zend_execute_data {
	struct _zend_op *opline;
	zend_function_state function_state;
	zend_op_array *op_array;//函数编译后的执行逻辑,编译后的opcode二进制代码,称为op_array
	zval *object;
	HashTable *symbol_table;//此函数的符号表地址
	struct _zend_execute_data *prev_execute_data;
	zval *old_error_reporting;
	zend_bool nested;
	zval **original_return_value;
	zend_class_entry *current_scope;
	zend_class_entry *current_called_scope;
	zval *current_this;  
	struct _zend_op *fast_ret; /* used by FAST_CALL/FAST_RET (finally keyword) */
	call_slot *call_slots;
	call_slot *call;
};

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

通过下边例子,来描述下函数在执行中,PHP对各个存储空间的分配,以及解释了为什么PHP的静态变量可以共享。

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


解释:

1.执行t1时,形成t1的环境结构体,t1调入到执行栈,t1也有自己的符号表,符号表里边存储的变量对应这个t1环境(局部变量嘛)

2.执行t1到第三行,执行了t2,形成t2的环境结构体,t2入栈,t2也有自己的变量自己的符号表,与t1互不影响。

3.假使t1函数内部出现了递归调用t1,此时会生成第二个t1环境结构体,和【1】中是两个结构体,互不影响


函数执行时的栈变化

当函数调用时,为此函数生成了一个”执行环境变量”的结构体,里面存储了当前函数的名称,参数,对应的类....等等信息.称为_zend_execute_data {}结构体

struct _zend_execute_data {
	struct _zend_op *opline;
	zend_function_state function_state;
	zend_op_array *op_array;//函数编译后的执行逻辑,编译后的opcode二进制代码,称为op_array
	zval *object;
	HashTable *symbol_table;//此函数的符号表地址
	struct _zend_execute_data *prev_execute_data;
	zval *old_error_reporting;
	zend_bool nested;
	zval **original_return_value;
	zend_class_entry *current_scope;
	zend_class_entry *current_called_scope;
	zval *current_this;  
	struct _zend_op *fast_ret; /* used by FAST_CALL/FAST_RET (finally keyword) */
	call_slot *call_slots;
	call_slot *call;
};

这个结构体中,有2个重要的信息需要注意!:

{

*op_array ------>是函数的执行步骤,公用(静态变量字段存储于此!所以改一次依赖于此逻辑的函数全修改!)

*hash_table---->symbol_table 这个函数对应的符号表

}

 

思考一下: 1个函数,递归调用自己3次, 如t1

问:在栈上,肯定要有3个 execute_data生成.但是,这3个execute_data--->对应几个*op_array;

答:函数编译完了,生成一份*op_array,因为函数的执行逻辑是固定的.

 

问:生成了几个 symbol_table?

答:生成3个符号表.


结论:

1.每一个函数调用是都会生成自己的环境栈和符号表栈,不同的环境栈对应了自己的符号表栈,所以每个函数中的变量常量等,他们是有对应函数内的作用域限制

2.虽然每次会生成不同的环境栈与作用域,但是如果调用的是同一个函数,其 *op_array;是公用1份的,换句话说,t1递归调用自己,每次都会开辟一个环境栈区分独立,但是他们是同一个函数逻辑,所以op_array是一样的,而


三。其他

通过一个哈希算法,它总有碰撞的时候吧。PHP中的哈希表是使用拉链法来解决冲突 (具体点讲就是使用链表来存储哈希到同一个槽位的数据,Zend为了保存数据之间的关系使用了双向链表来链接元素)。

对于HashTable的初始化_zend_hash_init,

插入_zend_hash_add_or_update,

元素访问_zend_hash_add_or_find等操作,源码中有就不再这里叙述。


这样回头一想,变量表,常量表,函数表等,他们在PHP中都是靠HashTable来实现的,如[二]中叙述,hashtable是不是很强大呢?


Zend引擎哈希表结构和关系:


Zend/zend_hash.h 55行

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;        // 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;


Zend/zend_compiles.h 261行,op_array结构代码

struct _zend_op_array {
	/* Common elements */
	zend_uchar type;
	const char *function_name;		
	zend_class_entry *scope;
	zend_uint fn_flags;
	union _zend_function *prototype;
	zend_uint num_args;
	zend_uint required_num_args;
	zend_arg_info *arg_info;
	/* END of common elements */

	zend_uint *refcount;

	zend_op *opcodes;
	zend_uint last;

	zend_compiled_variable *vars;
	int last_var;

	zend_uint T;

	zend_uint nested_calls;
	zend_uint used_stack;

	zend_brk_cont_element *brk_cont_array;
	int last_brk_cont;

	zend_try_catch_element *try_catch_array;
	int last_try_catch;
	zend_bool has_finally_block;

	/* static variables support */
	HashTable *static_variables;

	zend_uint this_var;

	const char *filename;
	zend_uint line_start;
	zend_uint line_end;
	const char *doc_comment;
	zend_uint doc_comment_len;
	zend_uint early_binding; /* the linked list of delayed declarations */

	zend_literal *literals;
	int last_literal;

	void **run_time_cache;
	int  last_cache_slot;

	void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};



以上是PHP内核-简单讲解PHP灵魂HashTble的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
高流量网站的PHP性能调整高流量网站的PHP性能调整May 14, 2025 am 12:13 AM

TheSecretTokeEpingAphp-PowerEdwebSiterUnningSmoothlyShyunderHeavyLoadInVolvOLVOLVOLDEVERSALKEYSTRATICES:1)emplactopCodeCachingWithOpcachingWithOpCacheToreCescriptexecution Time,2)使用atabasequercachingCachingCachingWithRedataBasEndataBaseLeSendataBaseLoad,3)

PHP中的依赖注入:初学者的代码示例PHP中的依赖注入:初学者的代码示例May 14, 2025 am 12:08 AM

你应该关心DependencyInjection(DI),因为它能让你的代码更清晰、更易维护。1)DI通过解耦类,使其更模块化,2)提高了测试的便捷性和代码的灵活性,3)使用DI容器可以管理复杂的依赖关系,但要注意性能影响和循环依赖问题,4)最佳实践是依赖于抽象接口,实现松散耦合。

PHP性能:是否可以优化应用程序?PHP性能:是否可以优化应用程序?May 14, 2025 am 12:04 AM

是的,优化papplicationispossibleandessential.1)empartcachingingcachingusedapcutorediucedsatabaseload.2)优化的atabaseswithexing,高效Quereteries,and ConconnectionPooling.3)EnhanceCodeWithBuilt-unctions,避免使用,避免使用ingglobalalairaiables,并避免使用

PHP性能优化:最终指南PHP性能优化:最终指南May 14, 2025 am 12:02 AM

theKeyStrategiestosiminificallyBoostphpapplicationPermenCeare:1)useOpCodeCachingLikeLikeLikeLikeLikeCacheToreDuceExecutiontime,2)优化AtabaseInteractionswithPreparedStateTemtStatementStatementSandProperIndexing,3)配置

PHP依赖注入容器:快速启动PHP依赖注入容器:快速启动May 13, 2025 am 12:11 AM

aphpdepentioncontiveContainerIsatoolThatManagesClassDeptions,增强codemodocultion,可验证性和Maintainability.itactsasaceCentralHubForeatingingIndections,因此reducingTightCightTightCoupOulplingIndeSingantInting。

PHP中的依赖注入与服务定位器PHP中的依赖注入与服务定位器May 13, 2025 am 12:10 AM

选择DependencyInjection(DI)用于大型应用,ServiceLocator适合小型项目或原型。1)DI通过构造函数注入依赖,提高代码的测试性和模块化。2)ServiceLocator通过中心注册获取服务,方便但可能导致代码耦合度增加。

PHP性能优化策略。PHP性能优化策略。May 13, 2025 am 12:06 AM

phpapplicationscanbeoptimizedForsPeedAndeffificeby:1)启用cacheInphp.ini,2)使用preparedStatatementSwithPdoforDatabasequesies,3)3)替换loopswitharray_filtaray_filteraray_maparray_mapfordataprocrocessing,4)conformentnginxasaseproxy,5)

PHP电子邮件验证:确保正确发送电子邮件PHP电子邮件验证:确保正确发送电子邮件May 13, 2025 am 12:06 AM

phpemailvalidation invoLvesthreesteps:1)格式化进行regulareXpressecthemailFormat; 2)dnsvalidationtoshethedomainhasavalidmxrecord; 3)

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中