摘要:關于宏熟悉擴展開發的同學應該都知道,這個宏,是定義一個函數用的,參數就是函數的函數名。關于這個宏,有興趣的可以去看看源碼,它其實是將替換成了,這樣的一個函數定義。上面的幾個宏,是為了檢查并獲取傳參進函數的變量。
函數原型因為已經有文檔了,可能有些人覺得我寫這個有些多余了。可是并不是每一個 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_toupper 和 php_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 = "