zend给php的所有变量都用结构的方式去保存,而字符串的保存和数组的保存也是不同的,数组采用的是hash表的方式去保存(大家知道hash保存的地址有效的减少冲突-hash散列表的概念你懂的),而在php中的结构体上表现如下:
代码如下:
- //文件1:zend/zend.h
- /*
- * zval
- */
- typedef struct _zval_struct 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;
- };
- //hash表的结构如下
- //文件2:zend/zend_hash.h
- typedef struct _hashtable {
- uint nTableSize;
- uint nTableMask;
- uint nNumOfElements;
- ulong nNextFreeElement;
- Bucket *pInternalPointer; /* Used for element traversal */
- Bucket *pListHead;
- Bucket *pListTail;
- Bucket **arBuckets;
- dtor_func_t pDestructor;
- zend_bool persistent;
- unsigned char nApplyCount;
- zend_bool bApplyProtection;
- #if ZEND_DEBUG
- int inconsistent;
- #endif
- }
- HashTable;
复制代码
一般的变量(字符串)在使用strlen获取长度的时候,其实获取的就是zvalue_value.str这个结构中的len属性,效率上O(1)次,特别说明的一点是:strlen在php中并没有核心的实现,而是在使用了zend中的宏定义来获取:
代码如下:
- //文件3:zend/zend_operators.php
- #define Z_STRLEN(zval) (zval).value.str.len
- ...
- #define Z_STRLEN_P(zval_p) Z_STRLEN(*zval_p)
- ...
- #define Z_STRLEN_PP(zval_pp) Z_STRLEN(**zval_pp)
复制代码
而对于数组的count操作,其实有两种结果,在count 的api中也提到了第二个参数mode《http://www.php.net/manual/en/function.count.php》,这个mode参数指明了,是否需要重新统计,而它的重新统计将会遍历一次数组,效率上是O(N)[N:长度],默认情况下是不重新统计,那这个时候将会直接输出hashtable中的nNumOfElements,此时的效率也是O(1)次:count代码如下:
代码如下:
- //文件4:ext/standard/array.c
- PHP_FUNCTION(count)
- {
- zval *array;
- long mode = COUNT_NORMAL;
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &mode) == FAILURE) {
- return;
- }
- switch (Z_TYPE_P(array)) {
- case IS_NULL:
- RETURN_LONG(0);
- break;
- case IS_ARRAY:
- RETURN_LONG (php_count_recursive (array, mode TSRMLS_CC));
- break;
- .....
- //php_count_recursive的实现
- static int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */
- {
- long cnt = 0;
- zval **element;
- if (Z_TYPE_P(array) == IS_ARRAY) {
- //错误处理
- if (Z_ARRVAL_P(array)->nApplyCount > 1) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
- return 0;
- }
- //通过zend_hash_num_elements直接获得长度
- cnt = zend_hash_num_elements(Z_ARRVAL_P(array));
- //如果指定了需要重新统计,则会进入一次循环统计
- if (mode == COUNT_RECURSIVE) {
- HashPosition pos;
- for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
- zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **) &element, &pos) == SUCCESS;
- zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)
- ) {
- Z_ARRVAL_P(array)->nApplyCount++;
- cnt += php_count_recursive(*element, COUNT_RECURSIVE TSRMLS_CC);
- Z_ARRVAL_P(array)->nApplyCount--;
- }
- }
- }
- return cnt;
- }
- //文件5:zend/zend_hash.c
- //zend_hash_num_elements的实现
- ZEND_API int zend_hash_num_elements(const HashTable *ht)
- {
- IS_CONSISTENT(ht);
- return ht->nNumOfElements;
- }
复制代码
|