`
student_lp
  • 浏览: 438917 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

为什么要了解php内部结构HashTable

阅读更多

一、认识HASHTable

1、hashtable的定义

     哈希表是将键名key按指定的散列函数HASH经过HASH(key)计算后映射到表中一个记录,而这个数组就是哈希表。其中这里的HASH指任意的函数,例如:MD5、CRC32、SHA1或自定义的函数。

2、hashtable的性能

     hashtable是一种查找性能极高的数据结构,在很多语言内部都实现了HashTable。理想情况下HashTable的性能是O(1)的,性能消耗主要集中在散列函数HASH(key),通过HASH(key)直接定位到表中的记录。而在实际情况下经常会发生key1!=key2,但HASH(key1)=HASH(key2),这种情况即HASH碰撞问题,碰撞的概率越低HashTable的性能越好。当然Hash算法太过复杂也会影响Hashtable的性能。

3、HashTable的应用

     在php内核也同样实现了HashTable并广泛应用,包括线程安全、变量存储、资源管理等基本上所有的地方都能看到他的身影。不仅如此,在php脚本中数组、类也是被广泛使用的。下面就着重介绍一下HashTable在数组、变量、函数、类这几个方面的应用。

二、HashTable在数组上的应用

    PHP大部分功能都是通过HashTable来实现,其中就包括数组。HashTable即具有双向链表的优点,同时具有能与数据匹配的操作性能。PHP中的定义的变量保存在一个符号表里,而这个符号表其实就是一个HashTable,它的每一个元素都是一个zval*类型的变量。不仅如此,保存用户定义的函数、类、资源等的容器都是以HashTable的形式在内核中实现的。

    下面是PHP中定义的数组:

$array = array();
$array["key"] = "value";

    在内核中使用宏来实现:

zval* array;
array_init(array);
add_assoc_string(array,"key","value",1);

    将上述代码中的宏展开:

zval* array;  
ALLOC_INIT_ZVAL(array);  
Z_TYPE_P(array) = IS_ARRAY;  
  
HashTable *h;  
ALLOC_HASHTABLE(h);  
Z_ARRVAL_P(array)=h;  
zend_hash_init(h, 50, NULL,ZVAL_PTR_DTOR, 0);  
  
zval* barZval;  
MAKE_STD_ZVAL(barZval);  
ZVAL_STRING(barZval, "value", 0);  
zend_hash_add(h, "key", 4, &barZval, sizeof(zval*), NULL);  

    通过上面的代码,我们就发现了HashTable在array中的应用。实际上在PHP内核中数组正是通过HashTable实现的。将数组初始化后,接下来就要向其添加元素了。因为PHP语言中有多种类型的变量,所以也对应的有多种类型的add_assoc_*()、add_index_*、add_next_index_*()函数,这三个函数分别对应着我们在php编程中为数组添加元素的方式,其中:add_assoc_*()是添加指定key->value形式的数组元素;add_index_*()是添加key为数字类型的元素;add_next_index_*()是不指定key添加元素。数组中允许添加资源、对象、数组等复合类型的PHP变量。下面让我们通过一个例子来演示下它们的用法:

ZEND_FUNCTION(sample_array)
{
	zval *subarray;

	array_init(return_value);
	
	/* Add some scalars */
	add_assoc_long(return_value, "life", 42);
	add_index_bool(return_value, 123, 1);
	add_next_index_double(return_value, 3.1415926535);
	
	/* Toss in a static string, dup'd by PHP */
	add_next_index_string(return_value, "Foo", 1);
	
	/* Now a manually dup'd string */
	add_next_index_string(return_value, estrdup("Bar"), 0);

	/* Create a subarray */
	MAKE_STD_ZVAL(subarray);
	array_init(subarray);
	
	/* Populate it with some numbers */
	add_next_index_long(subarray, 1);
	add_next_index_long(subarray, 20);
	add_next_index_long(subarray, 300);
	
	/* Place the subarray in the parent */
	add_index_zval(return_value, 444, subarray);
}

    这时如果我们用户端var_dump这个函数的返回值便会得到:

<?php
var_dump(sample_array());
?>
//输出
array(6)
{
	["life"]=> int(42)
	[123]=> bool(true)
	[124]=> float(3.1415926535)
	[125]=> string(3) "Foo"
	[126]=> string(3) "Bar"
	[444]=> array(3)
	{
		[0]=> int(1)
		[1]=> int(20)
		[2]=> int(300)
	}
}

三、变量的符号表(变量方面的应用)

     在上一章节中讲述了HashTable在数组中的应用,下面我们来看看HashTable在变量中是如何应用的。在这里我们需要了解两方面的问题:一个是变量都是变量名和变量值对应出现的,那他们是如何存储的呢?另一个是变量都有对应的生命周期,这个是如何实现的呢?

    在任一时刻PHP代码都可以看见两个变量符号表——symbol_table和active_symbol_table——前者用于存储全局变量,称为全局符号表;后者是个指针,指向当前活动的变量符号表,通常情况下就是全局符号表。但是,当每次进入一个PHP函数时(此处指的是用户使用PHP代码创建的函数),Zend都会创建函数局部的变量符号表,并将active_symbol_table指向局部符号表。Zend总是使用active_symbol_table来访问变量,这样就实现了局部变量的作用域控制。

    但如果在函数局部访问标记为global的变量,Zend会进行特殊处理——在active_symbol_table中创建symbol_table中同名变量的引用,如果symbol_table中没有同名变量则会先创建。
struct _zend_executor_globals {  
    //略  
    HashTable symbol_table;//全局变量的符号表  
    HashTable *active_symbol_table;//局部变量的符号表  
    //略  
};  

   可以通过EG宏来访问变量符号表,EG(symbol_table)访问全局作用域的变量符号表,EG(active_symbol_table)访问当前作用域的变量符号表。

<?php  
$foo='bar';  
?> 

   上面这段代码很简单,创建变量foo,并赋值bar。之后的PHP代码中就可以调用$foo变量了。现在看看PHP中定义的变量,内核中是如何实现的。伪代码:

zval* foo;  
MAKE_STD_ZVAL(foo);  
ZVAL_STRING(foo, "bar", 1);  
ZEND_SET_SYMBOL( EG(active_symbol_table), "foo", foo);  

第1步:创建一个zval结构,并设置类型。

第2步:赋值为bar。

第3步:将其加入当前作用域符号表,只有这样用户才能在PHP里使用这个变量。

备注:大家都知道PHP脚本在执行的时候用户全局变量(在用户空间显式定义的变量)会保存在一个HashTable数据类型的符号表(symbol_table)中, 在PHP中有一些比较特殊的全局变量例如: $_GET,$_POST,$_SERVER等变量,我们并没有在程序中定义这些变量,并且这些变量也同样保存在符号表中, 从这些表象我们不难得出结论:PHP是在脚本运行之前就将这些特殊的变量加入到了符号表中了。

四、HashTable在类上的应用

     类和函数类似,PHP内置及PHP扩展均可以实现自己的内部类,也可以由用户使用PHP代码进行定义。 当然我们在编写代码时通常是自己定义。

     使用上,我们使用class关键字进行定义,后面接类名,类名可以是任何非PHP保留字的名字。 在类名后面紧跟着一对花括号,里面是类的实体,包括类所具有的属性,这些属性是对象的状态的抽象, 其表现为PHP中支持的数据类型,也可以包括对象本身,通常我们称其为成员变量。 除了类的属性, 类的实体中也包括类所具有的操作,这些操作是对象的行为的抽象,其表现为用操作名和实现该操作的方法, 通常我们称其为成员方法或成员函数。看类示例的代码:

class ParentClass {
}
 
interface Ifce {
        public function iMethod();
}
 
final class Tipi extends ParentClass implements Ifce {
        public static $sa = 'aaa';
        const CA = 'bbb';
 
        public function __constrct() {
        }
 
        public function iMethod() {
        }
 
        private function _access() {
        }
 
        public static function access() {
        }
}

    这里定义了一个父类ParentClass,一个接口Ifce,一个子类Tipi。子类继承父类ParentClass, 实现接口Ifce,并且有一个静态变量$sa,一个类常量 CA,一个公用方法,一个私有方法和一个公用静态方法。 这些结构在Zend引擎内部是如何实现的?我们看看类的内部存储结构:

struct _zend_class_entry {
    char type;     // 类型:ZEND_INTERNAL_CLASS / ZEND_USER_CLASS
    char *name;// 类名称
    zend_uint name_length;                  // 即sizeof(name) - 1
    struct _zend_class_entry *parent; // 继承的父类
    int refcount;  // 引用数
    zend_bool constants_updated;
 
    zend_uint ce_flags; // ZEND_ACC_IMPLICIT_ABSTRACT_CLASS: 类存在abstract方法
    // ZEND_ACC_EXPLICIT_ABSTRACT_CLASS: 在类名称前加了abstract关键字
    // ZEND_ACC_FINAL_CLASS
    // ZEND_ACC_INTERFACE
    HashTable function_table;      // 方法
    HashTable default_properties;          // 默认属性
    HashTable properties_info;     // 属性信息
    HashTable default_static_members;// 类本身所具有的静态变量
    HashTable *static_members; // type == ZEND_USER_CLASS时,取&default_static_members;
    // type == ZEND_INTERAL_CLASS时,设为NULL
    HashTable constants_table;     // 常量
    struct _zend_function_entry *builtin_functions;// 方法定义入口
 
 
    union _zend_function *constructor;
    union _zend_function *destructor;
    union _zend_function *clone;
 
 
    /* 魔术方法 */
    union _zend_function *__get;
    union _zend_function *__set;
    union _zend_function *__unset;
    union _zend_function *__isset;
    union _zend_function *__call;
    union _zend_function *__tostring;
    union _zend_function *serialize_func;
    union _zend_function *unserialize_func;
    zend_class_iterator_funcs iterator_funcs;// 迭代
 
    /* 类句柄 */
    zend_object_value (*create_object)(zend_class_entry *class_type TSRMLS_DC);
    zend_object_iterator *(*get_iterator)(zend_class_entry *ce, zval *object,
        intby_ref TSRMLS_DC);
 
    /* 类声明的接口 */
    int(*interface_gets_implemented)(zend_class_entry *iface,
            zend_class_entry *class_type TSRMLS_DC);
 
 
    /* 序列化回调函数指针 */
    int(*serialize)(zval *object, unsignedchar**buffer, zend_uint *buf_len,
             zend_serialize_data *data TSRMLS_DC);
    int(*unserialize)(zval **object, zend_class_entry *ce, constunsignedchar*buf,
            zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC);
 
 
    zend_class_entry **interfaces;  //  类实现的接口
    zend_uint num_interfaces;   //  类实现的接口数
 
 
    char *filename; //  类的存放文件地址 绝对地址
    zend_uint line_start;   //  类定义的开始行
    zend_uint line_end; //  类定义的结束行
    char *doc_comment;
    zend_uint doc_comment_len;
 
 
    struct _zend_module_entry *module; // 类所在的模块入口:EG(current_module)
};

    我们可以看到,在类的实现上,大量使用了hashTable来存储一些类的相关信息,类的属性和方法这些关键信息都是由hashTable存储记录的。

    上面我们列举了hashTable在php应用的几个方面,可以看到hashTable在php内核代码中应用非常广泛,所以有必要深入了解一下hashTable是如何实现的,这对我们深入理解php有很大的帮助。

分享到:
评论

相关推荐

    深入PHP中的HashTable结构详解

    在PHP编程语言中,HashTable是核心的内部数据结构,用于存储和管理键值对。它在 Zend 引擎中扮演着至关重要的角色,被广泛应用于数组、对象、类属性等多种场景。深入理解HashTable的内部机制有助于提升PHP程序的性能...

    php中hashtable实现示例分享

    这些API函数是PHP内部处理数组操作、变量存储等任务的关键,它们封装了HashTable的基本操作,使得开发者可以不必直接与复杂的内部结构打交道。 需要强调的是,尽管我们可以从源码中了解HashTable的实现原理,但绝大...

    变量在 PHP7 内部的实现(二)

    在上一篇文章中,我们初步了解了PHP7变量内部实现的基础概念。在本文中,我们将深入探究PHP7变量实现的更多细节,以及它与PHP5之间的关键区别。理解这些知识对于深入掌握PHP7的工作原理非常重要。 PHP7中的变量通过...

    变量在 PHP7 内部的实现(一)

    在讨论PHP7内部变量实现之前,我们需要了解PHP5中变量的内部结构。PHP的变量存储在一个称为zval的数据结构中,其中包含了值(value)、类型(type)、引用计数(refcount)以及一个标志位(is_ref)来指示变量是否通过引用...

    php扩展开发与内核应用

    对于那些希望通过PHP扩展来提高应用程序性能和功能的开发人员来说,这门课程提供了一条深入探索PHP内部实现的道路。通过这个项目的迁移和共享,学习者可以加入项目,贡献自己的力量,并且在实践中掌握PHP扩展开发的...

    php_extension_writing

    `HashTable`是PHP内部用来实现数组和对象属性的主要数据结构。理解如何在C代码中操作`HashTable`对于高效地编写PHP扩展至关重要。 ### 结论 通过深入了解`zval`结构、PHP数据处理方式、创建扩展的基本流程以及如何...

    PHP内核介绍及扩展开发指南—基础知识.pdf

    zval是PHP内部用于存储变量的所有信息的数据结构。每一个变量在PHP内部都对应一个zval,它包含变量的值以及变量的一些附加信息,如引用计数和变量类型。zval结构体定义如下: ```c typedef struct _zval_struct { ...

    PHP7源码分享

    HashTable根据内部结构的不同可以分为packed array和hash array两种类型。 - **packed array**: - 当HashTable中元素较少时,会采用这种形式,以节省空间。 - **hash array**: - 当HashTable中元素较多或者存在...

    PHP内核介绍及扩展开发指南.pdf

    总的来说,《PHP内核介绍及扩展开发指南》是一本深入的技术书籍,它不仅涵盖了PHP的基本数据结构和内存管理,还详细讲解了如何编写PHP扩展,对于想要提升PHP开发技能的程序员来说,是一份宝贵的参考资料。...

    PHP的性能测试全过程分享.doc

    PHP的弱类型特性导致所有变量在内部以Zval结构存储。为了优化内存使用,PHP引入了Reference Counting(引用计数)和Copy On Write(写时复制)机制,减少了不必要的内存拷贝。此外,数组通过高效的hashtable实现,...

    php-5.5.3 源码

    本文将深入探讨PHP 5.5.3版本的源码,带你走进PHP的世界,了解其内部工作原理和关键组件。 1. **源码结构** PHP 5.5.3源码主要分为几个核心部分: - **Zend Engine**:这是PHP的执行引擎,负责解析、编译和执行...

    PHP 源代码分析 Zend HashTable详解第1/3页

    【PHP 源代码分析:深入理解 Zend HashTable】 在 PHP 的核心引擎 Zend Engine 中,HashTable 是一个至关重要的...通过熟练掌握 HashTable,开发者能够更好地理解和利用 PHP 内部的数据处理机制,提升程序的运行效率。

    php内核探索

    php内核探索,详细介绍php的变量、内部结构、以及hashtable的细节。

    php扩展编写(PHP_Extension_Writing-phpquebec_2009.pdf)

    了解`zval`的内部结构对于理解PHP扩展开发至关重要。 #### `zval`结构 ```c typedef struct _zval_struct { zvalue_value value; zend_uint refcount; zend_uchar type; zend_uchar is_ref; } zval; ``` - **...

    php-底层原理

    - **扩展开发**:通过了解PHP的核心结构,开发者可以开发自定义的扩展,实现更多的功能。 #### 二、PHP的设计理念及特点 PHP的设计理念和技术特点如下: - **多进程模型**:PHP最初采用了多进程模型,这意味着每...

    php中实现application对象的扩展源代码

    在PHP中,通过编写C语言的模块可以访问PHP引擎的内部结构,增加新的函数、常量或者类。`my_module.c`很可能包含了以下内容:模块的初始化和清理函数、注册自定义函数或类、以及其他必要的功能实现。 - `BEGIN_...

    PHP内核介绍及扩展开发指南

    `HashTable`是PHP中数组和对象的核心数据结构,它提供了一种高效的方式来存储和检索数据。`HashTable`使用哈希算法来存储键值对,从而在大多数情况下提供了O(1)的时间复杂度。这种结构不仅支持整数键,还支持字符串...

Global site tag (gtag.js) - Google Analytics