每天一個 PHP 語法六陣列函式 array_keys、reset 的使用及實現

ifelse發表於2020-04-23

說明

這裡基於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 {
    // 獲取整個陣列的key
    // 複製元素個數
        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核心剖析》

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章