* Every user visible function must have anentry in ini_read_functions[].
PHP_FE(helloworld, NULL) /* Fortesting, remove later. */
unfold functions in source code. See the corresponding marks just before
follow this convention for the convenience of others editing your code.
* vim
*/
PHP_FUNCTION(helloworld)
{
RETURN_STRING(INI_STR("ini_read.helloworld"),1);
}
5.4 结果验证
上面步骤执行完了后,根据之前安装扩张的方式安装一下我们的扩展吧。
如果你安装完了扩展,那么运行下面的命令您就可以看到输出了
php -r "echohelloworld();" 此时的输出应该为hellohellohello
6 全局变量
6.1 抛砖引玉
将这一节的目的是想交给大家,如和声明一个变量。使得这个变量针对每次请求独立,也就是说,同一次请求我们访问的变量是同一个,不同的请求我们使用的变量不是同一个。
说道这里我先抛出一个问题:既然要实现上面的要求,那么我们该怎么办呢?我应该在哪里声明我的全局变量呢?
还记得SAPI简介那一张吗?SAPI的实现有三种方式,单进程,多进程,多线程,但是对于每一次而言,都必须执行的几个过程为RINIT RSHUTDOWN….说道这里你意识到了吗。我们是不是在RINIT过程的时候初始化我们的全局常量,这样每次请求的时候这个变量的值都会变成默认值。还没有看明白?看看下面的流程你也许就懂了。
1. 首先在.h文件中声明全局变量
2. 在RINIT过程时初始化这个变量
3. 调用变量
6.2 实现方式
我们还是直接上源代码:我把一些无用的注释去掉了,希望大家别介意
头文件php-iamnew.h
#ifndef PHP_IAMNEW_H
#define PHP_IAMNEW_H
extern zend_module_entry iamnew_module_entry;
#define phpext_iamnew_ptr &iamnew_module_entry
#ifdef PHP_WIN32
# definePHP_IAMNEW_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
# definePHP_IAMNEW_API __attribute__ ((visibility("default")))
#else
# definePHP_IAMNEW_API
#endif
#ifdef ZTS
#include "TSRM.h"
#endif
PHP_MINIT_FUNCTION(iamnew);
PHP_MSHUTDOWN_FUNCTION(iamnew);
PHP_RINIT_FUNCTION(iamnew);
PHP_RSHUTDOWN_FUNCTION(iamnew);
PHP_MINFO_FUNCTION(iamnew);
PHP_FUNCTION(confirm_iamnew_compiled); /* For testing, remove later. */
ZEND_BEGIN_MODULE_GLOBALS(iamnew)
long counter;
ZEND_END_MODULE_GLOBALS(iamnew)
#ifdef ZTS
#define IAMNEW_G(v) TSRMG(iamnew_globals_id,zend_iamnew_globals *, v)
#else
#define IAMNEW_G(v) (iamnew_globals.v)
#endif
#endif
PHP_FUNCTION(test_global_value);
源文件iamnew.c
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_iamnew.h"
ZEND_DECLARE_MODULE_GLOBALS(iamnew) //声明全局变量
static int le_iamnew;
const zend_function_entry iamnew_functions[]= {
PHP_FE(test_global_value,NULL)
{NULL,NULL,NULL} //此处修改以后不再解释
};
zend_module_entry iamnew_module_entry ={
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
"iamnew",
iamnew_functions,
PHP_MINIT(iamnew),
PHP_MSHUTDOWN(iamnew),
PHP_RINIT(iamnew),
PHP_RSHUTDOWN(iamnew),
PHP_MINFO(iamnew),
#if ZEND_MODULE_API_NO >= 20010901
"0.1",
#endif
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_IAMNEW
ZEND_GET_MODULE(iamnew)
#endif
// 这个函数之前是被注释的,去掉注释,并且函数内容为空即可
static void php_iamnew_init_globals(zend_iamnew_globals*iamnew_globals)
{
}
PHP_MINIT_FUNCTION(iamnew)
{
ZEND_INIT_MODULE_GLOBALS(iamnew, php_iamnew_init_globals,NULL);
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(iamnew)
{
return SUCCESS;
}
PHP_RINIT_FUNCTION(iamnew)
{
IAMNEW_G(counter)= 0; //初始化
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(iamnew)
{
return SUCCESS;
}
PHP_MINFO_FUNCTION(iamnew)
{
php_info_print_table_start();
php_info_print_table_header(2,"iamnew support","enabled");
php_info_print_table_end();
}
// 增加测试函数
PHP_FUNCTION(test_global_value)
{
IAMNEW_G(counter)++;
RETURN_LONG(IAMNEW_G(counter));
}
6.3 结果验证
修改完成后,编译安装我们的扩展,执行下面的命令进行测试
php -r "echotest_global_value(); test_global_value();"
以后我们可能不在对这些简单的结果进行验证了,但是为了学习效果,建议大家自己验证一下。
6.4 实现原理
6.4.1 知识点1
我们先看一下头文件中声明全局变量的两个宏
ZEND_BEGIN_MODULE_GLOBALS
ZEND_END_MODULE_GLOBALS
我们看一下这两个宏的展开内容:
#define ZEND_BEGIN_MODULE_GLOBALS(module_name) \
typedef struct _zend_##module_name##_globals {
#define ZEND_END_MODULE_GLOBALS(module_name) \
}zend_##module_name##_globals;
从展开信息中我们可以看到,这两个宏仅仅是定了一个叫做zend_##module##_globals的结构体,而ZEND_BEGIN_MODULE_GLOBALS和ZEND_END_MODULE_GLOBALS就是结构体zend_##module##_globals的成员变量。
6.4.2 知识点2
我们再来看一下ZEND_DECLARE_MODULE_GLOBALS这句话是
#define ZEND_DECLARE_MODULE_GLOBALS(module_name) \
ts_rsrc_idmodule_name##_globals_id;
其实ts_rsrc_id就是int类型,查看ts_rsrc_id的定义就可以知道:typedef int ts_rsrc_id;
6.4.3 知识点3
在一个宏声明ZEND_INIT_MODULE_GLOBALS,查看一下这个宏的定义
#define ZEND_INIT_MODULE_GLOBALS(module_name, globals_ctor,globals_dtor) \
ts_allocate_id(&module_name##_globals_id,sizeof(zend_##module_name##_globals), (ts_allocate_ctor) globals_ctor,(ts_allocate_dtor) globals_dtor);
从定义来看,ZEND_INIT_MODULE_GLOBALS给变量module_name##_globals_id分配了一个id,这个id是一个线程安全的资源id。而module_name##_globals_id不就是ZEND_DECLARE_MODULE_GLOBALS分配的变量吗!
globals_ctor是一个回调函数指针,这里不再多说。
6.4.4 知识点
我们再来看一下IAMNEW_G这个宏的定义
#ifdef ZTS //是否线程安全
#defineIAMNEW_G(v) TSRMG(iamnew_globals_id,zend_iamnew_globals *, v)
#else
#defineIAMNEW_G(v) (iamnew_globals.v)
#endif
----------------------------------------------------
TSRMG的定义为:
#define TSRMG(id, type, element) (((type) (*((void ***)tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
tsrm_ls的定义为:
void ***tsrm_ls;
从宏整体的定义可以看出IAMNEW_G是取得变量的值。
其实IAMNEW_G就是取的全局变量的值。
如果你对TSRMG非常感兴趣的话,也可以看看这个宏具体的实现方式,不多对于新手来说的确有点困难。
7 使用php.ini里面的配置初始化全局变量
前面我们讲到了如何读取php.ini里面的配置,也讲到了如何初始化全局变量,这一节的任务也很明了,就是如何使用php.ini里面的配置初始化全局变量。这一节我们作为课堂任务来进行处理,不在单独解释。
8 参数接收
前面我们讲到了php扩展开发的大体架构,本节我们介绍扩展如何接收php脚本中传入的参数。
8.1 普通参数接收
任务:写一个扩展,输出php脚本传入的参数。例如php –r “echo hello(‘param test.’);” 将要输出param test.
首先我们建立一个paramtest的扩展,建议、编译、安装、测试过程不再赘述。
首先,我们需要建立一个函数叫做hello,建立的过程和之前是一样的,也不多说。
我们具体看一下,hello函数的实现,paramtest.c中hello函数的实现如下:
PHP_FUNCTION(hello)
{
char* str_hello;
int int_hello_str_length;
if(zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC,"s",
&str_hello,&int_hello_str_length)==FAILURE)
{
RETURN_NULL();
}
php_printf("%s",str_hello);
RETURN_TRUE;
}
我们先看一下zend_parse_parameters的函数定义:ZEND_NUM_ARGS() TSRMLS_CC其实是zend_parse_parameters的两个参数,具体可以查找这两个宏的定义,这两个宏主要是传入参数信息和保证线程安全。参数”s”其实是格式化字符串,也就是说这个参数告诉ZEND编译器,我可以接收的参数类型是什么样子的。Zend_parse_parameters的其他参数负责具体接收php脚本中函数变量的值。
请注意:我们在php脚本的函数中传入了一个参数,但是在zend_parse_parameters中却需要两个参数进行接收,给大家解释一下原因:我们在php脚本中传入的参数是字符串,对于C语言来说,PHP脚本传入字符串的长度是无法直接用函数strlen来进行获取的。原因是因为,我们在php脚本中可以传入\0,但是\0在C语言中是字符串结尾的意思。
在zend_parse_parameters中,格式化字符创(本例中是”s”)有很多,如果你想要接受多个参数的话,只要在格式化参数中加入相应的类型标示符,在参数中添加接受变量就可以了。例如,如果你的php函数需要传入两个变量,第一个变量为问候字符串,第二个参数为bool,表示是否输出,那么zend_parse_parameters函数可以这样写:zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC,"sb", &str_hello, &len,&is_output)。
其中,格式化字符串可以列表如下:
PHP变量类型
代码
C扩展变量类型
boolean
b
zend_bool
long
l
long
double
d
double
string
s
char*, int
resource
r
zval*
array
a
zval*
object
o
zval*
zval
z
zval*
上表中有很多zval类型,这个类型我们下一节进行单独介绍。
另外一个需要注意的地方是,我们使用php_printf在C函数中进行输出,你能想到原因吗?前面我们已经讲到php既可以作为脚本在命令行运行,也可以通过web服务器,以单进程、多进程、多线程方式运行,如果我们在web服务器中将我们的信息输出到stdout中,会导致信息无法输出或者输出错误。给大家举个例子,我们可以再apache+php环境中写一个脚本,通过浏览器访问这个脚本,如果你使用printf输出字符串,那么浏览器将无法看到你的输出信息。但是如果你使用php_printf输出的话,浏览器就能显示你输出的信息。
8.2 可选参数接收
我们已经学会了如何接收普通参数,大家都知道php中还有可选参数,那么我们改怎样接收可选参数呢?(如果你不知道php可以使用可选参数的话,建议你先温习一下php的基础知识。)
这个很简单,我们直接上代码:
PHP_FUNCTION(hello)
{
char*str_hello;
int int_hello_str_length;
zend_bool is_output =0;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"s|b",&str_hello,
&int_hello_str_length,&is_output)== FAILURE)
{
RETURN_NULL();
}
if(is_output)
{
php_printf("%s", str_hello);
}
RETURN_TRUE;
}
很简单吧,如果你有可选参数,那么在格式化字符串中加入|就可以了,|后面的就是可选参数,其他的和普通变量的接收是一样的。
9 zval结构分析
9.1 初识zval
我们先来看一下zval的定义:
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;
struct _zval_struct {
/* Variable information*/
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /*active type */
zend_uchar is_ref__gc;
};
typedef struct_zval_struct zval;
9.2 zval的创建和使用
我们先来看一段代码:
PHP_FUNCTION(hello)
{
zval* t;
ALLOC_INIT_ZVAL(t);
Z_TYPE_P(t)= IS_LONG;
Z_LVAL_P(t)= 1234567890;
zval_ptr_dtor(&t);
}
ALLOC_INIT_ZVAL宏用来给t分配内存,并且将t初始化为一个空变量,Z_TYPE_P用来给zval变量指定变量的类型,Z_LVAL_P用来给变量赋值,zval_ptr_dtor用来清理变量空间。
我们可以使用上面的代码使用变量外,我们还可以使用宏ZVAL_LONG来快速的定义变量和给变量赋值。也就是说上面的代码我们可以使用下面的代码来代替。
PHP_FUNCTION(hello)
{
zval* t;
ZVAL_LONG(t, 1234567890);
}
我们可以使用下面的宏来快速的定义和使用zval变量:
ZVAL_RESOURCE、 ZVAL_BOOL、ZVAL_NULL、ZVAL_LONG、ZVAL_DOUBLE
ZVAL_STRING、ZVAL_STRINGL、ZVAL_EMPTY_STRING、ZVAL_ZVAL
上面的宏很简单,如果大家有什么不明白的地方,大家可以去看看源代码。
10 函数返回值
终于讲完zval了,前面我们讲到了函数的定义和使用,但是我们没有讲函数的返回值。因为C扩展中,函数的返回值类型为zval的,所以我们把这一节放在了这里进行讲解。
本节任务,我们写一个简单的计算器,完成加减乘除运算,要求:编写函数calculate(num1, num2, opt),我们希望完成num1 opt num2= ?的运算。我们看一下完成上面任务的代码:
PHP_FUNCTION(calculate)
{
int num1;
int num2;
char* opt;
int opt_len;
if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"lls",
&num1,&num2,&opt,&opt_len)==FAILURE)
{
php_printf("param error. example: calculate(123, 456, '+')\n");
RETURN_NULL();
}
if(1!= opt_len)
{
php_printf("param error. example: calculate(123, 456, '+')\n");
RETURN_NULL();
}
switch (opt[0])
{
case '+':
return_value->type= IS_LONG;
return_value->value.lval= num1 + num2;
break;
case '-':
return_value->type= IS_LONG;
return_value->value.lval= num1 - num2;
break;
case '*':
return_value->type= IS_LONG;
return_value->value.lval= num1 * num2;
break;
case '/':
return_value->type= IS_DOUBLE;
return_value->value.lval= num1 *1.0 / num2;
break;
default:
return_value->type= IS_LONG;
return_value->value.lval= 0;
break;
}
}
看到上面代码,不知道大家有没有疑惑,return_value是怎么来的?
return_value是你宏PHP_FUNCTION宏中定义的,PHP_FUNCTION会声明这个变量,并且将这个变量赋值为NULL,我们来看一下宏定义:
#define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS)
#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define INTERNAL_FUNCTION_PARAMETERSint ht, zval *return_value, \
zval **return_value_ptr, zval *this_ptr, intreturn_value_used TSRMLS_DC
从上面的宏定义来看,return_value就是zval的一个指针,php用这个变量来指向函数的返回值。其实我们也有一个宏可以进行简单的返回,宏定义如下,RETVAL_*(v),*表示的就是各种变量类型,v表示变量的值,例如RETVAL_LONG(34),将返回一个long类型的数值,其值为34。
00:14啦,说点题外话,小时候非常喜欢过年,但是长大后发现自己害怕了。不知道你有没有同感?
不说啦,大家有问题给我发邮件niujiaming0819@163.com,或者联系我qq947847775.
现在太晚啦,大家晚安。
11 数组使用以及HashTable介绍
本节我们讲一下php的数组,在php中,数组使用HashTable实现的。本节中我们先详细的介绍一下HashTable,然后再讲讲如何使用HastTable
11.1 变长结构体
所谓的变长结构体,其实是我们C语言结构体的一种特殊用法,并没有什么新奇之处。我们先来看一下变长结构体的一种通用定义方法。
typedef struct bucket {
int n;
char key[30];
char value[1];
}Bucket;
我们定义了一个结构体Bucket,我们希望用这个结构体存放学生的个人简介。其中key用来存在学生的姓名,value用来存放学生的简介。大家可能很好奇,我们的value声明了长度为1. 1个char能存多少信息呀?
其实,对于变长结构体,我们在使用的使用不能直接定义变量,例如:Bucket bucket; 您要是这样使用,value肯定存储不了多少信息。对于变长结构体,我们在使用的时候需要先声明一个变长结构体的指针,然后通过malloc函数分配函数空间,我们需要用到的空间长度是多少,我们就可以malloc多少。通用的使用方法如下:
Bucket*pBucket;
pBucket =malloc(sizeof(Bucket)+ n *sizeof(char));
其中n就是你要使用value的长度。如果这样使用的话,value指向的字符串不久变长了吗!
11.2 Hashtable简介
我们先看一下HashTable的定义
struct _hashtable;
typedef struct bucket {
ulong h;//当元素是数字索引时使用
uint nKeyLength;//当使用字符串索引时,这个变量表示索引的长度,索引(字符串)保存在最后一个元素aKey
void *pData;//用来指向保存的数据,如果保存的数据是指针的话,pDataPtr就指向这个数据,pData指向pDataPtr
void *pDataPtr;
struct bucket *pListNext;//上一个元素
struct bucket *pListLast;//下一个元素
struct bucket *pNext;//指向下一个bucket的指针
struct bucket *pLast;//指向上一个bucket的指针
char arKey[1];//必须放在最后,主要是为了实现变长结构体
}Bucket;
typedef struct _hashtable {
uint nTableSize; //哈希表的大小
uint nTableMask; //数值上等于nTableSize - 1
uint nNumOfElements; //记录了当前HashTable中保存的记录数
ulongnNextFreeElement; //指向下一个空闲的Bucket
Bucket *pInternalPointer; //这个变量用于数组反转
Bucket *pListHead; //指向Bucket的头
Bucket *pListTail; //指向Bucket的尾
Bucket **arBuckets;
dtor_func_tpDestructor; //函数指针,数组增删改查时自动调用,用于某些清理操作
zend_bool persistent; //是否持久
unsigned char nApplyCount;
zend_boolbApplyProtection; //和nApplyCount一起起作用,防止数组遍历时无限递归
#if ZEND_DEBUG
int inconsistent;
#endif
}HashTable;
希望大家能好好看看上面的定义,有些东西我将出来反而会说不明白,不如大家看看代码浅显明了。PHP的数组,其实是一个带有头结点的双向链表,其中HashTable是头,Bucket存储具体的结点信息。
11.3 HashTable内部函数分析
11.3.1 宏HASH_PROTECT_RECURSION
#defineHASH_PROTECT_RECURSION(ht) \
if ((ht)->bApplyProtection) { \
if ((ht)->nApplyCount++ >= 3){ \
zend_error(E_ERROR, "Nestinglevel too deep - recursive dependency?"); \
} \
}
这个宏主要用来防止循环引用。
11.3.2 宏ZEND_HASH_IF_FULL_DO_RESIZE
#defineZEND_HASH_IF_FULL_DO_RESIZE(ht) \
if ((ht)->nNumOfElements >(ht)->nTableSize) { \
zend_hash_do_resize(ht); \
}
这个宏的作用是检查目前HashTable中的元素个数是否大于了总的HashTable的大小,如果个数大于了HashTable的大小,那么我们就重新分配空间。我们看一下zend_hash_do_resize
static intzend_hash_do_resize(HashTable*ht)
{
Bucket **t;
IS_CONSISTENT(ht);
if ((ht->nTableSize0) { /* Let's double the table size */
t = (Bucket**) perealloc_recoverable(ht->arBuckets,
(ht->nTableSizepersistent);
if (t){
HANDLE_BLOCK_INTERRUPTIONS();
ht->arBuckets = t;
ht->nTableSize = (ht->nTableSize
ht->nTableMask = ht->nTableSize- 1;
zend_hash_rehash(ht);
HANDLE_UNBLOCK_INTERRUPTIONS();
return SUCCESS;
}
return FAILURE;
}
return SUCCESS;
}
从上面的代码中我们可以看出,HashTable在分配空间的时候,新分配的空间等于原有空间的2倍。
11.3.3 函数 _zend_hash_init
这个函数是用来初始化HashTable的,我们先看一下代码:
ZEND_API int_zend_hash_init(HashTable*ht, uint nSize,hash_func_t pHashFunction,dtor_func_t pDestructor,zend_bool persistent ZEND_FILE_LINE_DC)
{
uint i = 3; //HashTable的大小默认无2的3次方
Bucket **tmp;
SET_INCONSISTENT(HT_OK);
if (nSize>= 0x80000000){
ht->nTableSize = 0x80000000;
} else{
while ((1U
i++;
}
ht->nTableSize = 1
}
ht->nTableMask = ht->nTableSize-1;
ht->pDestructor = pDestructor;
ht->arBuckets =NULL;
ht->pListHead =NULL;
ht->pListTail =NULL;
ht->nNumOfElements = 0;
ht->nNextFreeElement = 0;
ht->pInternalPointer = NULL;
ht->persistent = persistent;
ht->nApplyCount =0;
ht->bApplyProtection = 1;
/* Uses ecalloc() so that Bucket* == NULL */
if (persistent){
tmp = (Bucket **)calloc(ht->nTableSize,sizeof(Bucket*));
if (!tmp){
return FAILURE;
}
ht->arBuckets = tmp;
} else{
tmp = (Bucket **)ecalloc_rel(ht->nTableSize,sizeof(Bucket*));
if (tmp){
ht->arBuckets = tmp;
}
}
return SUCCESS;
}
可以看出,HashTable的大小被初始化为2的n次方,另外我们看到有两种内存方式,一种是calloc,一种是ecalloc_rel,这两中内存分配方式我们细讲了,有兴趣的话大家可以自己查一查。
11.3.4 函数_zend_hash_add_or_update
这个函数向HashTable中添加或者修改元素信息
ZEND_API int_zend_hash_add_or_update(HashTable*ht,const char *arKey, uintnKeyLength,void *pData, uintnDataSize,void **pDest,int flag ZEND_FILE_LINE_DC)
{
ulong h;
uint nIndex;
Bucket *p;
IS_CONSISTENT(ht);
if (nKeyLength
#if ZEND_DEBUG
ZEND_PUTS("zend_hash_update: Can't put inempty key\n");
#endif
return FAILURE;
}
h = zend_inline_hash_func(arKey, nKeyLength);
nIndex = h & ht->nTableMask;
p = ht->arBuckets[nIndex];
while (p!= NULL){
if ((p->h== h)&& (p->nKeyLength==nKeyLength)){
if (!memcmp(p->arKey, arKey,nKeyLength)){
if (flag &HASH_ADD){
return FAILURE;
}
HANDLE_BLOCK_INTERRUPTIONS();
#if ZEND_DEBUG
if (p->pData == pData){
ZEND_PUTS("Fatalerror in zend_hash_update: p->pData == pData\n");
HANDLE_UNBLOCK_INTERRUPTIONS();
return FAILURE;
}
#endif
if (ht->pDestructor){
ht->pDestructor(p->pData);
}
UPDATE_DATA(ht, p, pData,nDataSize);
if (pDest) {
*pDest = p->pData;
}
HANDLE_UNBLOCK_INTERRUPTIONS();
return SUCCESS;
}
}
p = p->pNext;
}
p = (Bucket*)pemalloc(sizeof(Bucket)- 1 +nKeyLength, ht->persistent);
if (!p){
return FAILURE;
}
memcpy(p->arKey, arKey, nKeyLength);
p->nKeyLength = nKeyLength;
INIT_DATA(ht, p, pData, nDataSize);
p->h = h;
CONNECT_TO_BUCKET_DLLIST(p, ht->arBuckets[nIndex]);
if (pDest){
*pDest = p->pData;
}
HANDLE_BLOCK_INTERRUPTIONS();
CONNECT_TO_GLOBAL_DLLIST(p, ht);
ht->arBuckets[nIndex]= p;
HANDLE_UNBLOCK_INTERRUPTIONS();
ht->nNumOfElements++;
ZEND_HASH_IF_FULL_DO_RESIZE(ht); /* Ifthe Hash table is full, resize it */
return SUCCESS;
}
11.3.5 宏CONNECT_TO_BUCKET_DLLIST
#define CONNECT_TO_BUCKET_DLLIST(element, list_head) \
(element)->pNext= (list_head); \
(element)->pLast= NULL; \
if((element)->pNext) { \
(element)->pNext->pLast= (element); \
}
这个宏是将bucket加入到bucket链表中
11.3.6 其他函数或者宏定义
我们只是简单的介绍一下HashTable,如果你想细致的了解HashTable的话,建议您看看php的源代码,关于HashTable的代码在Zend/zend_hash.h 和Zend/zend_hash.c中。
zend_hash_add_empty_element 给函数增加一个空元素
zend_hash_del_key_or_index 根据索引删除元素
zend_hash_reverse_apply 反向遍历HashTable
zend_hash_copy 拷贝
_zend_hash_merge 合并
zend_hash_find 字符串索引方式查找
zend_hash_index_find 数值索引方法查找
zend_hash_quick_find 上面两个函数的封装
zend_hash_exists 是否存在索引
zend_hash_index_exists 是否存在索引
zend_hash_quick_exists 上面两个方法的封装
http://www.bkjia.com/PHPjc/477765.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/477765.htmlTechArticle1 前言 这篇文章是我个人的学习笔记,我把这篇文章送给所有喜欢PHP语言,喜欢PHP扩展开发的同行、同学们。 2 前期准备 闲话不和大家撤了...