Discuz教程网

PHP源代码数组统计count分析

[复制链接]
authicon dly 发表于 2011-9-5 19:55:21 | 显示全部楼层 |阅读模式
zend给php的所有变量都用结构的方式去保存,而字符串的保存和数组的保存也是不同的,数组采用的是hash表的方式去保存(大家知道hash保存的地址有效的减少冲突-hash散列表的概念你懂的),而在php中的结构体上表现如下:
代码如下:

  1. //文件1:zend/zend.h
  2. /*
  3. * zval
  4. */
  5. typedef struct _zval_struct zval;
  6. ...
  7. typedef union _zvalue_value {
  8. long lval; /* long value */
  9. double dval; /* double value */
  10. struct {
  11. char *val;
  12. int len;
  13. } str;
  14. HashTable *ht; /* hash table value */
  15. zend_object_value obj;
  16. } zvalue_value;

  17. struct _zval_struct {
  18. /* Variable information */
  19. zvalue_value value; /* value */
  20. zend_uint refcount__gc;
  21. zend_uchar type; /* active type */
  22. zend_uchar is_ref__gc;
  23. };
  24. //hash表的结构如下
  25. //文件2:zend/zend_hash.h
  26. typedef struct _hashtable {
  27. uint nTableSize;
  28. uint nTableMask;
  29. uint nNumOfElements;
  30. ulong nNextFreeElement;
  31. Bucket *pInternalPointer; /* Used for element traversal */
  32. Bucket *pListHead;
  33. Bucket *pListTail;
  34. Bucket **arBuckets;
  35. dtor_func_t pDestructor;
  36. zend_bool persistent;
  37. unsigned char nApplyCount;
  38. zend_bool bApplyProtection;
  39. #if ZEND_DEBUG
  40. int inconsistent;
  41. #endif
  42. }
  43. HashTable;
复制代码




一般的变量(字符串)在使用strlen获取长度的时候,其实获取的就是zvalue_value.str这个结构中的len属性,效率上O(1)次,特别说明的一点是:strlen在php中并没有核心的实现,而是在使用了zend中的宏定义来获取:

代码如下:

  1. //文件3:zend/zend_operators.php
  2. #define Z_STRLEN(zval) (zval).value.str.len
  3. ...
  4. #define Z_STRLEN_P(zval_p) Z_STRLEN(*zval_p)
  5. ...
  6. #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代码如下:
代码如下:

  1. //文件4:ext/standard/array.c
  2. PHP_FUNCTION(count)
  3. {
  4. zval *array;
  5. long mode = COUNT_NORMAL;

  6. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &array, &mode) == FAILURE) {
  7. return;
  8. }

  9. switch (Z_TYPE_P(array)) {
  10. case IS_NULL:
  11. RETURN_LONG(0);
  12. break;
  13. case IS_ARRAY:
  14. RETURN_LONG (php_count_recursive (array, mode TSRMLS_CC));
  15. break;
  16. .....

  17. //php_count_recursive的实现
  18. static int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */
  19. {
  20. long cnt = 0;
  21. zval **element;

  22. if (Z_TYPE_P(array) == IS_ARRAY) {
  23. //错误处理
  24. if (Z_ARRVAL_P(array)->nApplyCount > 1) {
  25. php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
  26. return 0;
  27. }
  28. //通过zend_hash_num_elements直接获得长度
  29. cnt = zend_hash_num_elements(Z_ARRVAL_P(array));

  30. //如果指定了需要重新统计,则会进入一次循环统计
  31. if (mode == COUNT_RECURSIVE) {
  32. HashPosition pos;

  33. for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
  34. zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **) &element, &pos) == SUCCESS;
  35. zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)
  36. ) {
  37. Z_ARRVAL_P(array)->nApplyCount++;
  38. cnt += php_count_recursive(*element, COUNT_RECURSIVE TSRMLS_CC);
  39. Z_ARRVAL_P(array)->nApplyCount--;
  40. }
  41. }
  42. }

  43. return cnt;
  44. }

  45. //文件5:zend/zend_hash.c
  46. //zend_hash_num_elements的实现
  47. ZEND_API int zend_hash_num_elements(const HashTable *ht)
  48. {
  49. IS_CONSISTENT(ht);

  50. return ht->nNumOfElements;
  51. }
复制代码







上一篇:分享PHP笔记和应用开发中涉及到的css
下一篇:linux下为PHP添加curl扩展的方法
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1314学习网 ( 浙ICP备10214163号 )

GMT+8, 2025-5-2 20:12

Powered by Discuz! X3.4

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表