每天一个PHP语法六数组函数array_keys、reset的使用及实现

2022-10-11 20:59:05 155 0
魁首哥

说明

这里基于php7.2.5进行测试,php7之后内部结构变化应该不是太大,但与php5.X有差别。

今天我们来学习下数组内置函数,这里用两个函数来作为例子进行。先看语法

 array_keys ( array $array [, mixed $search_value = null [, bool $strict = false ]] ) : array
array_keys() 返回 input 数组中的数字或者字符串的键名。如果指定了可选参数 search_value,则只返回该值的键名。
否则 input 数组中的所有键名都会被返回。$strict是否严格模式


reset ( array &$array ) : mixed
reset() 将 array 的内部指针倒回到第一个单元并返回第一个数组单元的值。  

 $params = [
    'name' => '愤怒的鸟',
    'total_mount' => 100,
    'remain_amount' => 8
];
print_r(array_keys($params));
print_r(array_keys($params, 100));
print_r(reset($params));

/*
Array
(
    [0] => name
    [1] => total_mount
    [2] => remain_amount
)
Array
(
    [0] => total_mount
)
愤怒的鸟
*/  

我们之前说过函数分为 用户自定义函数 与内置函数,

来看array_keys的实现

 PHP_FUNCTION(array_keys)
{
  // 第一个参数数组变量
	zval *input,				/* Input array */
    // 第二个可选参数值
	     *search_value = NULL,	/* Value to search for */
	     *entry,				/* An entry in the input array */
	       new_val;				/* New value */
	zend_bool strict = 0;		/* do strict comparison */
	zend_ulong num_idx;
	zend_string *str_idx;
	zend_array *arrval;
	zend_ulong elem_count;

	ZEND_PARSE_PARAMETERS_START(1, 3)
		Z_PARAM_ARRAY(input)
		Z_PARAM_OPTIONAL
		Z_PARAM_ZVAL(search_value)
		Z_PARAM_BOOL(strict)
	ZEND_PARSE_PARAMETERS_END();
  // arrval返回 HashTable  Z_ARRVAL_P返回input指向的zend_value.arr
	arrval = Z_ARRVAL_P(input);
  // elem_count是数组元素个数  下面有解释
	elem_count = zend_hash_num_elements(arrval);

	/* Base case: empty input */
  // 元素个数为0 直接返回空数组
	if (!elem_count) {
		RETURN_ZVAL(input, 1, 0)
	}

	/* Initialize return array */
  // 如果没有第二个可选参数
	if (search_value != NULL) {
		array_init(return_value);

    // 是否是严格模式
		if (strict) {
			ZEND_HASH_FOREACH_KEY_VAL_IND(arrval, num_idx, str_idx, entry) {
				ZVAL_DEREF(entry);
				if (fast_is_identical_function(search_value, entry)) {
					if (str_idx) {
						ZVAL_STR_COPY(&new_val, str_idx);
					} else {
						ZVAL_LONG(&new_val, num_idx);
					}
					zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
				}
			} ZEND_HASH_FOREACH_END();
		} else {
			ZEND_HASH_FOREACH_KEY_VAL_IND(arrval, num_idx, str_idx, entry) {
				if (fast_equal_check_function(search_value, entry)) {
					if (str_idx) {
						ZVAL_STR_COPY(&new_val, str_idx);
					} else {
						ZVAL_LONG(&new_val, num_idx);
					}
					zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &new_val);
				}
			} ZEND_HASH_FOREACH_END();
		}
	} else {
    // 获取整个数组的keys
    // 复制元素个数
		array_init_size(return_value, elem_count);
		zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
		ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) 
    
			if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
				/* Optimistic case: range(0..n-1) for vector-like packed array */
				ZVAL_LONG(&new_val, 0);
				for (; Z_LVAL(new_val) < elem_count; ++Z_LVAL(new_val)) {
					ZEND_HASH_FILL_ADD(&new_val);
				}
			} else {
				/* Go through input array and add keys to the return array */
				ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
					if (str_idx) {
						ZVAL_STR_COPY(&new_val, str_idx);
					} else {
						ZVAL_LONG(&new_val, num_idx);
					}
					ZEND_HASH_FILL_ADD(&new_val);
				} ZEND_HASH_FOREACH_END();
			}
		} ZEND_HASH_FILL_END();
	}
}




#define Z_ARR(zval)					(zval).value.arr
#define Z_ARR_P(zval_p)				Z_ARR(*(zval_p))

#define Z_ARRVAL(zval)				Z_ARR(zval)
#define Z_ARRVAL_P(zval_p)			Z_ARRVAL(*(zval_p))



// 返回数组元素个数
#define zend_hash_num_elements(ht) \
	(ht)->nNumOfElements  

小结:array_keys获取到数组的key,原理都是进行循环获取数组元素的索引值。

来看reset的实现

 PHP_FUNCTION(reset)
{
	HashTable *array;
	zval *entry;

	ZEND_PARSE_PARAMETERS_START(1, 1)
		Z_PARAM_ARRAY_OR_OBJECT_HT_EX(array, 0, 1)
	ZEND_PARSE_PARAMETERS_END();

  // 操作在这里
	zend_hash_internal_pointer_reset(array);

	if (USED_RET()) {
		if ((entry = zend_hash_get_current_data(array)) == NULL) {
			RETURN_FALSE;
		}

		if (Z_TYPE_P(entry) == IS_INDIRECT) {
			entry = Z_INDIRECT_P(entry);
		}

		ZVAL_DEREF(entry);
		ZVAL_COPY(return_value, entry);
	}
}


ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos)
{
    uint32_t idx;

	IS_CONSISTENT(ht);
	HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);

  // 循环这个数组,直到第一个有效的元素,返回这个元素
	for (idx = 0; idx < ht->nNumUsed; idx++) {
		if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) {
			*pos = idx;
			return;
		}
	}
	*pos = HT_INVALID_IDX;
}  

小结:reset会循环元素直到返回有效的元素值停止。

参考资料:《PHP7内核剖析》

收藏
分享
海报
0 条评论
155
上一篇:派克服能改大小吗(派克服大一码好还是小一码好) 下一篇:Jenkins发布PHP项目之一自动化部署

本站已关闭游客评论,请登录或者注册后再评论吧~

忘记密码?

图形验证码