PHP 7.3.0之前的版本獲取數組第一個和最後一個元素的key的幾種方法。
$a = [
'one' => 1,
'two' => 2,
'three' => 3
];
方法一
$first = reset($a);
$first_key = key($a);
$last = end($a);
$last_key = key($a);
方法二
$keys = array_keys($a);
$first = reset($keys);
$last = end($keys);
可以使用array_keys(),但是這樣做的效率可能相當低。
也可以使用reset()和key(),但是這可能會改變內部數組指針。
PHP 7.3.0 加入兩個函數解決這些問題了。
see https://www.php.net/manual/zh/function.array-key-last.php
- array_key_first
- array_key_last
看看它們是如果實現的。源碼加了一些中文註釋方便理解。
文件在 array.c : 3952
PHP_FUNCTION(array_key_first)
{
zval *stack; /* 傳入數組 */
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY(stack)
ZEND_PARSE_PARAMETERS_END();
HashTable *target_hash = Z_ARRVAL_P (stack);
HashPosition pos = 0; //位置0
zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos); // 根據位置取數組的key
}
ZEND_API void ZEND_FASTCALL zend_hash_get_current_key_zval_ex(const HashTable *ht, zval *key, HashPosition *pos)
{
uint32_t idx = *pos;
Bucket *p;
IS_CONSISTENT(ht);
if (idx >= ht->nNumUsed) { //檢查數組位置是否下超過數組實際使用最大元素的個數
ZVAL_NULL(key);
} else {
if (idx == 0) {
idx = _zend_hash_get_first_pos(ht); // 查找第個一有效元素的位置,因爲位置0的元素可能被刪除。
if (idx >= ht->nNumUsed) {
ZVAL_NULL(key);
return;
}
}
p = ht->arData + idx; // 根據位置找到第一個元素
if (p->key) { // 判斷是關鍵數組,還是索引數組
ZVAL_STR_COPY(key, p->key); //返回關鍵數組key
} else {
ZVAL_LONG(key, p->h); //返回索引數組key
}
}
}
PHP_FUNCTION(array_key_last)
{
zval *stack; /* 傳入數組 */
HashPosition pos; // 最後一個有效元素的位置
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ARRAY(stack)
ZEND_PARSE_PARAMETERS_END();
HashTable *target_hash = Z_ARRVAL_P (stack);
zend_hash_internal_pointer_end_ex(target_hash, &pos); //獲取最後一個有效元素的位置
zend_hash_get_current_key_zval_ex(target_hash, return_value, &pos); //根據位置取數組的key
}
ZEND_API void ZEND_FASTCALL zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos)
{
uint32_t idx;
IS_CONSISTENT(ht);
HT_ASSERT(ht, &ht->nInternalPointer != pos || GC_REFCOUNT(ht) == 1);
idx = ht->nNumUsed; // 位置等於數組實際使用最大元素的個數
while (idx > 0) {
idx--; // 從後住前遞減
if (Z_TYPE(ht->arData[idx].val) != IS_UNDEF) { // 查找元素是否有效
*pos = idx;
return;
}
}
*pos = ht->nNumUsed;
}