一步一步开发PHP扩展(6):数组处理

php7扩展开发教程

在上一篇笔记中波波分享了PHP扩展开发中字符串的处理,本篇笔记继续分享PHP扩展开发中数组的处理。其实不管任何语言也不管任何程序,日常开发基本也就是字符串和数组的处理。首先我们看一段PHP代码。

  1. <?php
  2. function array_concat ($arr$prefix) {
  3.     foreach($arr as $key => $val) {
  4.         if (isset($prefix[$key])
  5.                 && is_string($val)
  6.                 && is_string($prefix[$key])) {
  7.             $arr[$key] = $prefix[$key].$val;
  8.         }
  9.     }
  10.     return $arr;
  11. }
  12. $arr = array(
  13.     0 => '0',
  14.     1 => '123',
  15.     'a' => 'abc',
  16. );
  17. $prefix = array(
  18.     1 => '456',
  19.     'a' => 'def',
  20. );
  21. var_dump(array_concat($arr$prefix));
  22. ?>

在上述代码片段中我们通过构造array_concat方法实现了把两个数组,相同key的字符串值拼接。接下来我们依然在say扩展中看下如何实现array_concat。

实现array_concat方法

  1. PHP_FUNCTION(array_concat)
  2. {
  3.     zval *arr, *prefix, *entry, *prefix_entry, value;
  4.     zend_string *string_key, *result;
  5.     zend_ulong num_key;
  6.     if (zend_parse_parameters(ZEND_NUM_ARGS(), "aa", &arr, &prefix) == FAILURE) {
  7.         return;
  8.     }
  9.     array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(arr)));
  10.     ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(arr), num_key, string_key, entry) {
  11.         if (string_key && zend_hash_exists(Z_ARRVAL_P(prefix), string_key)) {
  12.             prefix_entry = zend_hash_find(Z_ARRVAL_P(prefix), string_key);
  13.             if (Z_TYPE_P(entry) == IS_STRING && prefix_entry != NULL && Z_TYPE_P(prefix_entry) == IS_STRING) {
  14.                 result = strpprintf(0, "%s%s", Z_STRVAL_P(prefix_entry), Z_STRVAL_P(entry));
  15.                 ZVAL_STR(&value, result);
  16.                 zend_hash_update(Z_ARRVAL_P(return_value), string_key, &value);
  17.             }
  18.         } else if (string_key == NULL && zend_hash_index_exists(Z_ARRVAL_P(prefix), num_key)){
  19.             prefix_entry = zend_hash_index_find(Z_ARRVAL_P(prefix), num_key);
  20.             if (Z_TYPE_P(entry) == IS_STRING && prefix_entry != NULL && Z_TYPE_P(prefix_entry) == IS_STRING) {
  21.                 result = strpprintf(0, "%s%s", Z_STRVAL_P(prefix_entry), Z_STRVAL_P(entry));
  22.                 ZVAL_STR(&value, result);
  23.                 zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, &value);
  24.             }
  25.         } else if (string_key) {
  26.             zend_hash_update(Z_ARRVAL_P(return_value), string_key, entry);
  27.             zval_add_ref(entry);
  28.         } else  {
  29.             zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
  30.             zval_add_ref(entry);
  31.         }
  32.     }ZEND_HASH_FOREACH_END();
  33. }

代码解读:

PHP中的数组本质上就是一个哈希。对于哈希处理的方法主要集中在Zend/zend_hash.h中。

对于数组的操作方法主要集中在Zend/zend_API.h。数组的方法其实就是对哈希处理方法的一层包装。
数组操作的方法主要是以add_assoc_ 和 add_index_开头的一些列方法。

下面是代码中涉及的一些方法。

zend_hash_num_elements获取数组的元素个数。

array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(arr))); 初始化一个数组。在PHP扩展中,我们是通过return_value这个变量设置方法的返回值。因此,我们直接修改这个return_value变量即可。感兴趣的话,可以把宏方法PHP_FUNCTION展开看下。

PHP7提供了一套宏方法用于遍历哈希和对哈希进行操作。这些宏方法主要放在Zend/zend_hash.h文件中。如,代码中的ZEND_HASH_FOREACH_KEY_VAL就是一个变量哈希的宏。是不是和PHP代码中的foreach有点像?

在这里我们把代码中用到的哈希相关的方法做下整理说明:

ZEND_HASH_FOREACH_KEY_VAL 和 ZEND_HASH_FOREACH_END 配合使用,实现foreach的效果。

zend_hash_exists 检测指定的key在哈希中是否存在。key为字符串。

zend_hash_index_exists 检测指定的key在哈希中是否存在。key为数字。

zend_hash_find 根据key查找指定的值。key为字符串。

zend_hash_index_find 根据key查找指定的值。key为数字。

zend_hash_update更新指定key的值。key为字符串。

zend_hash_index_update 更新指定key的值。key为数字。

基本上有这些方法,你就可以对数组进行一些基本操作了。方法命名也很有规律,key为字符串和数字提供了两套。

zval_add_ref(entry); 给数组的值,增加一次引用计数。zend_hash_update方法只自动给string_key自动增加了一次引用计数。数组return_value共用数组arr的值。因此,我们需要手动增加一次引用计数。

PHP7哈希相关的文章大家可以看看

http://jpauli.github.io/2016/04/08/hashtables.html

 

你想把广告放到这里吗?

发表评论

您必须 登录 才能发表留言!