成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

每日一個(gè) php 函數(shù)——array_change_key_case

awkj / 3454人閱讀

摘要:關(guān)于宏熟悉擴(kuò)展開(kāi)發(fā)的同學(xué)應(yīng)該都知道,這個(gè)宏,是定義一個(gè)函數(shù)用的,參數(shù)就是函數(shù)的函數(shù)名。關(guān)于這個(gè)宏,有興趣的可以去看看源碼,它其實(shí)是將替換成了,這樣的一個(gè)函數(shù)定義。上面的幾個(gè)宏,是為了檢查并獲取傳參進(jìn)函數(shù)的變量。

因?yàn)橐呀?jīng)有文檔了,可能有些人覺(jué)得我寫這個(gè)有些多余了??墒遣⒉皇敲恳粋€(gè) PHPer 都會(huì)好好地去閱讀文檔,自然有一些函數(shù)可能都沒(méi)有聽(tīng)說(shuō)過(guò)(很不幸我也是這其中的一員)。我也希望能通過(guò)寫這些文章,能夠促使我完整地讀完文檔,同時(shí),能夠給其它的 PHPer 一個(gè)參考,“啊,原來(lái)還有這個(gè)函數(shù)” 的感覺(jué)。同時(shí),我也希望我能通過(guò)寫這些文章,去閱讀各個(gè)函數(shù)的 C 語(yǔ)言實(shí)現(xiàn)。也實(shí)現(xiàn)自我驅(qū)動(dòng)地學(xué)習(xí)。

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

該函數(shù)的具體作用是,將一個(gè)數(shù)組中的所有的英文字母轉(zhuǎn)換為大寫或小寫。

我們可以看到,這個(gè)函數(shù)接收兩個(gè)參數(shù),返回一個(gè)數(shù)組。第一個(gè)參數(shù)數(shù)組沒(méi)有使用引用的方式,那么說(shuō)明該函數(shù)并不會(huì)改變?cè)瓟?shù)組,它會(huì)生成新的數(shù)組作為返回值。而第二個(gè)參數(shù)是可選的,它控制著該函數(shù)是轉(zhuǎn)換成大寫還是小寫。默認(rèn)是轉(zhuǎn)化為小寫。

函數(shù)使用 第二個(gè)參數(shù)

函數(shù)的第二個(gè)參數(shù)傳入的是一個(gè)預(yù)定義常量,分別是 CASE_LOWER 和 CASE_UPPER,前者是將 key 轉(zhuǎn)換成小寫,也是函數(shù)的默認(rèn)值;后者是將 key 轉(zhuǎn)換成大寫。

使用
$arr = [
    "loWer" => 1,
];
 
$toLower = array_change_key_case($arr, CASE_LOWER);
// 我認(rèn)為,不管它的默認(rèn)值是什么,我們都要寫上這第二個(gè)參數(shù)。我們的代碼寫出來(lái),是給人看的,不是給機(jī)器看的。
// 所以我們的代碼應(yīng)當(dāng)盡量多的包含語(yǔ)義。

$toUpper = array_change_key_case($arr, CASE_UPPER);

var_dump($toLower);

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

var_dump($toUpper);

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

不過(guò),這個(gè)函數(shù)不是遞歸的。我們看一下下面這個(gè)例子。

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

var_dump($toLower);

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

這個(gè)函數(shù)的使用,是有個(gè)坑的,這個(gè)坑就是,當(dāng)轉(zhuǎn)換之后,如果結(jié)果中有兩個(gè)相同的 key,那么就會(huì)保留最后的那個(gè)。舉個(gè)例子。

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

$toLower = array_change_key_case($arr, CASE_UPPER);

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

在這個(gè)例子中,我們發(fā)現(xiàn),當(dāng)執(zhí)行轉(zhuǎn)換之后,三個(gè) key 變成相同的了,那么在這種情況下,只會(huì)保留最后一個(gè)元素作為 key。這里得到的數(shù)組是 ["key" => 3]

內(nèi)核實(shí)現(xiàn)

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

源碼

我們先來(lái)看一下源代碼。

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();
}
關(guān)于 PHP_FUNCTION 宏

熟悉 PHP 擴(kuò)展開(kāi)發(fā)的同學(xué)應(yīng)該都知道,PHP_FUNCTION 這個(gè)宏,是定義一個(gè) PHP 函數(shù)用的,參數(shù)就是 PHP 函數(shù)的函數(shù)名。關(guān)于這個(gè)宏,有興趣的可以去看看源碼,它其實(shí)是將 PHP_FUNCTION(array_change_key_case) 替換成了 void zif_array_change_key_case(zend_execute_data *execute_data, zval *return_value),這樣的一個(gè)函數(shù)定義。注意里面的 return_value 變量,后面會(huì)用到這個(gè)變量。

邏輯代碼

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

這是其中之一的代碼。

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 = "