国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

每日一個 php 函數——array_change_key_case

awkj / 3439人閱讀

摘要:關于宏熟悉擴展開發的同學應該都知道,這個宏,是定義一個函數用的,參數就是函數的函數名。關于這個宏,有興趣的可以去看看源碼,它其實是將替換成了,這樣的一個函數定義。上面的幾個宏,是為了檢查并獲取傳參進函數的變量。

因為已經有文檔了,可能有些人覺得我寫這個有些多余了。可是并不是每一個 PHPer 都會好好地去閱讀文檔,自然有一些函數可能都沒有聽說過(很不幸我也是這其中的一員)。我也希望能通過寫這些文章,能夠促使我完整地讀完文檔,同時,能夠給其它的 PHPer 一個參考,“啊,原來還有這個函數” 的感覺。同時,我也希望我能通過寫這些文章,去閱讀各個函數的 C 語言實現。也實現自我驅動地學習。

函數原型
array array_change_key_case ( array $array [, int $case = CASE_LOWER ] )

該函數的具體作用是,將一個數組中的所有的英文字母轉換為大寫或小寫。

我們可以看到,這個函數接收兩個參數,返回一個數組。第一個參數數組沒有使用引用的方式,那么說明該函數并不會改變原數組,它會生成新的數組作為返回值。而第二個參數是可選的,它控制著該函數是轉換成大寫還是小寫。默認是轉化為小寫。

函數使用 第二個參數

函數的第二個參數傳入的是一個預定義常量,分別是 CASE_LOWER 和 CASE_UPPER,前者是將 key 轉換成小寫,也是函數的默認值;后者是將 key 轉換成大寫。

使用
$arr = [
    "loWer" => 1,
];
 
$toLower = array_change_key_case($arr, CASE_LOWER);
// 我認為,不管它的默認值是什么,我們都要寫上這第二個參數。我們的代碼寫出來,是給人看的,不是給機器看的。
// 所以我們的代碼應當盡量多的包含語義。

$toUpper = array_change_key_case($arr, CASE_UPPER);

var_dump($toLower);

/**
[
    "lower" => 1
]
*/

var_dump($toUpper);

/**
[
    "LOWER" => 1
]
*/

不過,這個函數不是遞歸的。我們看一下下面這個例子。

$arr = [
    "loWer" => [
        "Lower" => 1,
    ],
];
 
$toLower = array_change_key_case($arr, CASE_LOWER);

var_dump($toLower);

/**
[
    "lower" => [
        "Lower" => 1,
    ],
]
*/

這個函數的使用,是有個坑的,這個坑就是,當轉換之后,如果結果中有兩個相同的 key,那么就會保留最后的那個。舉個例子。

$arr = [
    "key" => 1,
    "kEy" => 2,
    "keY" => 3,
];

$toLower = array_change_key_case($arr, CASE_UPPER);

var_dump($toLower); // ["key" => 3]

在這個例子中,我們發現,當執行轉換之后,三個 key 變成相同的了,那么在這種情況下,只會保留最后一個元素作為 key。這里得到的數組是 ["key" => 3]

內核實現

該函數的源代碼在 php-src/ext/standard/array.c 中。

源碼

我們先來看一下源代碼。

PHP_FUNCTION(array_change_key_case)
{
    zval *array, *entry;
    zend_string *string_key;
    zend_string *new_key;
    zend_ulong num_key;
    zend_long change_to_upper=0;

    ZEND_PARSE_PARAMETERS_START(1, 2)
        Z_PARAM_ARRAY(array)
        Z_PARAM_OPTIONAL
        Z_PARAM_LONG(change_to_upper)
    ZEND_PARSE_PARAMETERS_END();

    array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(array)));

    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_key, string_key, entry) {
        if (!string_key) {
            entry = zend_hash_index_update(Z_ARRVAL_P(return_value), num_key, entry);
        } else {
            if (change_to_upper) {
                new_key = php_string_toupper(string_key);
            } else {
                new_key = php_string_tolower(string_key);
            }
            entry = zend_hash_update(Z_ARRVAL_P(return_value), new_key, entry);
            zend_string_release(new_key);
        }

        zval_add_ref(entry);
    } ZEND_HASH_FOREACH_END();
}
關于 PHP_FUNCTION 宏

熟悉 PHP 擴展開發的同學應該都知道,PHP_FUNCTION 這個宏,是定義一個 PHP 函數用的,參數就是 PHP 函數的函數名。關于這個宏,有興趣的可以去看看源碼,它其實是將 PHP_FUNCTION(array_change_key_case) 替換成了 void zif_array_change_key_case(zend_execute_data *execute_data, zval *return_value),這樣的一個函數定義。注意里面的 return_value 變量,后面會用到這個變量。

邏輯代碼

其實,真正的邏輯代碼,是在 ZEND_HASH_FOREACH_KEY_VAL 宏和 ZEND_HASH_FOREACH_END 之間的。上面的幾個宏,是為了檢查并獲取傳參進 PHP 函數的變量。我們可以看到 zend_long change_to_upper=0; 這個是用來判斷,是大寫還是小寫的。這里定義的默認值是 0,所以這個函數的默認是小寫。而整個函數的最核心的代碼是 php_string_toupperphp_string_tolower 這兩個函數。

這是其中之一的代碼。

PHPAPI zend_string *php_string_toupper(zend_string *s)
{
    unsigned char *c, *e;

    c = (unsigned char *)ZSTR_VAL(s);
    e = c + ZSTR_LEN(s);

    while (c < e) {
        if (islower(*c)) {
            register unsigned char *r;
            zend_string *res = zend_string_alloc(ZSTR_LEN(s), 0);

            if (c != (unsigned char*)ZSTR_VAL(s)) {
                memcpy(ZSTR_VAL(res), ZSTR_VAL(s), c - (unsigned char*)ZSTR_VAL(s));
            }
            r = c + (ZSTR_VAL(res) - ZSTR_VAL(s));
            while (c < e) {
                *r = toupper(*c);
                r++;
                c++;
            }
            *r = "