深入PHP内核之数组
定义:
PHP 中的数组实际上是一个有序映射。映射是一种把 values 关联到 keys 的类型。此类型在很多方面做了优化,因此可以把它当成真正的数组,或列表(向量),散列表(是映射的一种实现),字典,集合,栈,队列以及更多可能性。数组元素的值也可以是另一个数组。树形结构和多维数组也是允许的。
这是手册中对PHP数组的定义,本质上是一种键-值映射的关系,算是一种散列表(哈希表)。
到这里我不得不说,PHP内核中的神器了 HashTable
HashTable即具有双向链表的优点,PHP中的定义的变量保存在一个符号表里,而这个符号表其实就是一个HashTable,它的每一个元素都是一个zval*类型的变量。不仅如此,保存用户定义的函数、类、资源等的容器都是以HashTable的形式在内核中实现的。
因此,PHP的数组读写都可以在O(1)内完成,这是非常高效的,因此开销和C++、Java相比也就是hashtable的创建了
我们看一下PHP定义数组
<?php
$array = array();
$array["key"] = "value";
在内核中
zval* array;
array_init(array);
add_assoc_string(array, "key", "value", 1);
这样就可以了
看一下源码是怎么实现
在zend_API.h 文件中,这是一个宏定义
#define array_init(arg) _array_init((arg), 0 ZEND_FILE_LINE_CC)
接着看,在zend_API.c 文件中
/* Argument parsing API -- andrei */
ZEND_API int _array_init(zval *arg, uint size ZEND_FILE_LINE_DC) /* {{{ */
{
ALLOC_HASHTABLE_REL(Z_ARRVAL_P(arg)); _zend_hash_init(Z_ARRVAL_P(arg), size, NULL, ZVAL_PTR_DTOR, 0 ZEND_FILE_LINE_RELAY_CC);
Z_TYPE_P(arg) = IS_ARRAY;
return SUCCESS;
}
/* }}} */
再接着看,在zend_alloc.h文件中
#define ALLOC_HASHTABLE_REL(ht) \
(ht) = (HashTable *) emalloc_rel(sizeof(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;//初始化是为1<<3=8 SET_INCONSISTENT(HT_OK);
//最大个数为1>>31 = 2^31 =2147483648
if (nSize >= 0x80000000) {
/* prevent overflow */
ht->nTableSize = 0x80000000;
} else {
while ((1U << i) < nSize) {
i++;
}
ht->nTableSize = 1 << i;
} ht->nTableMask = 0; /* 0 means that ht->arBuckets is uninitialized */
ht->pDestructor = pDestructor; /* 元素的析构函数(指针) */
ht->arBuckets = (Bucket**)&uninitialized_bucket;
ht->pListHead = NULL; /* 头元素, 用于线性遍历 */
ht->pListTail = NULL; /* 尾元素, 用于线性遍历 */
ht->nNumOfElements = 0; /* HashTable中实际元素的个数 */
ht->nNextFreeElement = 0; /* 下个空闲可用位置的数字索引 */
ht->pInternalPointer = NULL; /* 内部位置指针, 会被reset, current这些遍历函数使用 */
ht->persistent = persistent;
ht->nApplyCount = 0;/* 循环遍历保护 */
ht->bApplyProtection = 1;
return SUCCESS;
}
以上是数组初始化可以参考 http://www.laruence.com/2009/08/23/1065.html
然后赋值的过程
zval* val;
MAKE_STD_ZVAL(val);
ZVAL_STRING(val, "value", 0);
zend_hash_add(h, "key", 4, &val, sizeof(zval*), NULL);
内核为我们提供了方便的宏来管理数组
ZEND_API int add_index_long(zval *arg, ulong idx, long n);
ZEND_API int add_index_null(zval *arg, ulong idx);
ZEND_API int add_index_bool(zval *arg, ulong idx, int b);
ZEND_API int add_index_resource(zval *arg, ulong idx, int r);
ZEND_API int add_index_double(zval *arg, ulong idx, double d);
ZEND_API int add_index_string(zval *arg, ulong idx, const char *str, int duplicate);
ZEND_API int add_index_stringl(zval *arg, ulong idx, const char *str, uint length, int duplicate);
ZEND_API int add_index_zval(zval *arg, ulong index, zval *value); ZEND_API int add_next_index_long(zval *arg, long n);
ZEND_API int add_next_index_null(zval *arg);
ZEND_API int add_next_index_bool(zval *arg, int b);
ZEND_API int add_next_index_resource(zval *arg, int r);
ZEND_API int add_next_index_double(zval *arg, double d);
ZEND_API int add_next_index_string(zval *arg, const char *str, int duplicate);
ZEND_API int add_next_index_stringl(zval *arg, const char *str, uint length, int duplicate); ZEND_API int add_assoc_long_ex(zval *arg, const char *key, uint key_len, long n);
ZEND_API int add_assoc_null_ex(zval *arg, const char *key, uint key_len);
ZEND_API int add_assoc_bool_ex(zval *arg, const char *key, uint key_len, int b);
ZEND_API int add_assoc_resource_ex(zval *arg, const char *key, uint key_len, int r);
ZEND_API int add_assoc_double_ex(zval *arg, const char *key, uint key_len, double d);
ZEND_API int add_assoc_string_ex(zval *arg, const char *key, uint key_len, char *str, int duplicate);
ZEND_API int add_assoc_stringl_ex(zval *arg, const char *key, uint key_len, char *str, uint length, int duplicate);
ZEND_API int add_assoc_zval_ex(zval *arg, const char *key, uint key_len, zval *value); #define add_assoc_long(__arg, __key, __n) add_assoc_long_ex(__arg, __key, strlen(__key)+1, __n)
#define add_assoc_null(__arg, __key) add_assoc_null_ex(__arg, __key, strlen(__key) + 1)
#define add_assoc_bool(__arg, __key, __b) add_assoc_bool_ex(__arg, __key, strlen(__key)+1, __b)
#define add_assoc_resource(__arg, __key, __r) add_assoc_resource_ex(__arg, __key, strlen(__key)+1, __r)
#define add_assoc_double(__arg, __key, __d) add_assoc_double_ex(__arg, __key, strlen(__key)+1, __d)
#define add_assoc_string(__arg, __key, __str, __duplicate) add_assoc_string_ex(__arg, __key, strlen(__key)+1, __str, __duplicate)
#define add_assoc_stringl(__arg, __key, __str, __length, __duplicate) add_assoc_stringl_ex(__arg, __key, strlen(__key)+1, __str, __len
gth, __duplicate)
#define add_assoc_zval(__arg, __key, __value) add_assoc_zval_ex(__arg, __key, strlen(__key)+1, __value)
具体实现
$arr[] = NULL; add_next_index_null(arr);
$arr[] = 42; add_next_index_long(arr, 42);
$arr[] = true; add_next_index_bool(arr, 1);
$arr[] = 3.14; add_next_index_double(arr, 3.14);
$arr[] = 'foo'; add_next_index_string(arr, "foo");
$arr[] = $var; add_next_index_zval(arr, zval); $arr[0] = NULL; add_index_null(arr, 0);
$arr[1] = 42; add_index_long(arr, 1, 42);
$arr[2] = true; add_index_bool(arr, 2, 1);
$arr[3] = 3.14; add_index_double(arr, 3, 3.14);
$arr[4] = 'foo'; add_index_string(arr, 4, "foo", 1);
$arr[5] = $var; add_index_zval(arr, 5, zval); $arr["abc"] = NULL; add_assoc_null(arr, "abc");
$arr["def"] = 42; add_assoc_long(arr, "def", 42);
$arr["ghi"] = true; add_assoc_bool(arr, "ghi", 1);
$arr["jkl"] = 3.14 add_assoc_double(arr, "jkl", 3.14);
$arr["mno"]="foo" add_assoc_string(arr, "mno", "foo", 1");
$arr["pqr"] = $var; add_assoc_zval(arr, "pqr", zval);
扩展中测试
PHP_FUNCTION(array_test1)
{
zval *value;
zval *element;
char *s="This is a test";
char *key="a";
MAKE_STD_ZVAL(element);
MAKE_STD_ZVAL(value);
array_init(value);
ZVAL_STRING(element,s,strlen(s));
zend_hash_update(value->value.ht,key,strlen(key)+1,(void*)&element,sizeof(zval*),NULL);
ZEND_SET_SYMBOL(EG(active_symbol_table),"siren",value);
} PHP_FUNCTION(array_test2){
char* str;
zval* subarray;
array_init(return_value);
array_init(subarray);
add_next_index_string(return_value, "test", 1);
str = estrdup("This is a test");
add_next_index_string(return_value, str, 0);
add_assoc_double(return_value, "double", 3.14);
add_next_index_string(subarray, "hello", 1);
add_assoc_zval(return_value, "xsubarray", subarray);
}
深入PHP内核之数组的更多相关文章
- php内核之数组
1.zend_hash_num_elements 获取数组元素个数.宏定义如下: #define zend_hash_num_elements(ht) \ (ht)->nNumOfElement ...
- [翻译] Linux 内核中的位数组和位操作
目录 Linux 内核里的数据结构 原文链接与说明 Linux 内核中的位数组和位操作 位数组声明 体系结构特定的位操作 通用位操作 链接 Linux 内核里的数据结构 原文链接与说明 https:/ ...
- Netlink 内核实现分析 2
netlink 应用层如何创建socket 应用层通过socket()系统调用创建Netlink套接字,socket系统调用的第一个参数可以是AF_NETLINK或PF_NETLINK(在Linux系 ...
- kernel编程规范
1. 制表符8个空格2. 每行最长80字符3. 代码块的{放在首行,但是函数的{放在次行4. 只有一行的if块,不加{}5. 不在()前后加空格6. 正常关键字后加一个空格,if, switch, c ...
- select、poll、epoll三组IO复用
int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,struct timeval* timeout)//其中n ...
- Linux中的IO复用接口简介(文件监视?)
I/O复用是Linux中的I/O模型之一.所谓I/O复用,指的是进程预先告诉内核,使得内核一旦发现进程指定的一个或多个I/O条件就绪,就通知进程进行处理,从而不会在单个I/O上导致阻塞. 在Linux ...
- Linux Platform驱动模型(二) _驱动方法
在Linux设备树语法详解和Linux Platform驱动模型(一) _设备信息中我们讨论了设备信息的写法,本文主要讨论平台总线中另外一部分-驱动方法,将试图回答下面几个问题: 如何填充platfo ...
- Linux Platform驱动模型(二) _驱动方法【转】
转自:http://www.cnblogs.com/xiaojiang1025/archive/2017/02/06/6367910.html 在Linux设备树语法详解和Linux Platform ...
- 嵌入式:UCOSIII的使用(17.01.24补充)
0.一些移植.系统相关 OS_CFG_APP.H /* --------------------- MISCELLANEOUS ------------------ */ #define OS_CFG ...
随机推荐
- Ubuntu双系统安装
原文链接: http://www.jianshu.com/p/2eebd6ad284d 作者 Volcanoo 2016.01.31 00:07 字数 1737 阅读 141859评论 161喜欢 ...
- java操作Hbase实例
所用HBase版本为1.1.2,hadoop版本为2.4 /* * 创建一个students表,并进行相关操作 */ import java.io.IOException; import java.u ...
- [转]一键安装藏隐患,phpStudy批量入侵的分析与溯源
一.前言 近日,腾讯安全云鼎实验室监测到大量主机被入侵并添加了一个名为“vusr_dx$”的隐藏帐号:同时,云鼎实验室还监测到此类帐号被大量创建的同时存在对应帐号异地登录的情况. Windows 的帐 ...
- C语言数字与字符串转换 atoi()函数、itoa()函数、sprintf()函数
在编程中经常需要用到数字与字符串的转换,下面就总结一下. 1.atoi() C/C++标准库函数,用于字符串到整数的转换. 函数原型:int atoi (const char * str); #inc ...
- 如何将thick provision lazy zeroed的VMDK文件转换为thick provision eager zeroed?
详细步骤在此: Enabling clustering features for an existing virtual disk by converting in place(1035823) ht ...
- 直播【95秀】JNI 基本实现 简洁
2017-2-8 基本架构 1.使用SurfaceView在UI层面展示视频 2.通过JNI调用C代码控制视频的播放.停止 基本功能 1.从服务器获取正在直播的主播的列表信息 2.进入直播界面 3.可 ...
- 关于ListView中getView被重复调用的问题
我用ListView显示数据时,自定义了一个适配器(extends ArrayAdapter),然后重写了getView方法,现在出现一个问题,就是这个getView()方法被重复调用了,比如我的_d ...
- Ubuntu14.04下Neo4j图数据库官网安装部署步骤(图文详解)(博主推荐)
不多说,直接上干货! 说在前面的话 首先,查看下你的操作系统的版本. root@zhouls-virtual-machine:~# cat /etc/issue Ubuntu 14.04.4 LTS ...
- HDU 3535 AreYouBusy(混合背包)
HDU3535 AreYouBusy(混合背包) http://acm.hdu.edu.cn/showproblem.php?pid=3535 题意: 给你n个工作集合,给你T的时间去做它们.给你m和 ...
- C++学习笔记12-模板1
1. 函数模板 函数模板是一个独立于类型的函数,可作为一种方式.产生函数的特定类型版本号. // implement strcmp-like generic compare function // ...