A daily php function - array_change_key_case

Keywords: PHP C

Because there are already documents, some people may think that I am a little redundant to write this. But not every PHPer reads the document well, and naturally some functions may not have been heard of (unfortunately I am one of them). I also hope that by writing these articles, I can complete the document, and at the same time, I can give other PHP a reference, "Ah, there was this function" feeling. At the same time, I also hope that I can read the C language of each function by writing these articles. Self-motivated learning is also realized.

Function prototype

array array_change_key_case ( array $array [, int $case = CASE_LOWER ] )

The function converts all English letters in an array to upper or lower case.

As you can see, this function takes two parameters and returns an array. The first parameter array does not use a reference method, so that the function does not change the original array, it will generate a new array as a return value. The second parameter is optional, which controls whether the function is converted to uppercase or lowercase. The default is to convert to lowercase.

Function usage

Second parameter

The second parameter of the function passes in a predefined constant, CASE_LOWER and CASE_UPPER, which converts the key to lowercase and is the default value of the function, and CASE_UPPER, which converts the key to uppercase.

Use

$arr = [
    'loWer' => 1,
];
 
$toLower = array_change_key_case($arr, CASE_LOWER);
// I think we have to write this second parameter, regardless of its default value. Our code is written for people to see, not for machines to see.
// So our code should contain as much semantics as possible.

$toUpper = array_change_key_case($arr, CASE_UPPER);

var_dump($toLower);

var_dump($toUpper);

pit

The use of this function has a pit, which is, after conversion, if there are two identical key s in the result, the last one will be retained. For instance.

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

$toLower = array_change_key_case($arr, CASE_UPPER);

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

In this example, we find that when the transformation is performed, the three keys become the same, in which case only the last element is retained as the key. The resulting array is ['key'=> 3].

Kernel Implementation

The source code of this function is in php-src/ext/standard/array.c.

Source code

Let's first look at the source code.

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();
}

About PHP_FUNCTION macro

Students familiar with PHP extension development should know that PHP_FUNCTION macro is used to define a PHP function, and the parameter is the function name of the PHP function. For this macro, it's interesting to look at the source code. It actually replaces PHP_FUNCTION(array_change_key_case) with void zif_array_change_key_case (zend_execute_data*execute_data, zval*return_value), a function definition. Notice the return_value variable inside, which will be used later.

Logic code

In fact, the real logic code is between ZEND_HASH_FOREACH_KEY_VAL macro and ZEND_HASH_FOREACH_END. The above macros are designed to check and obtain variables that are passed into PHP functions. We can see zend_long change_to_upper=0; this is used to determine whether it is capitalized or lowercase. The default value defined here is 0, so the default for this function is lowercase. The core code of the whole function is php_string_toupper and php_string_tolower.

This is one of the codes.

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 = '\0';
            return res;
        }
        c++;
    }
    return zend_string_copy(s);
}

The students who have written c to convert the case of strings know that, in fact, our own ideas are basically the same. Only a few macros were used. c is the first address of the string, e is the address of the string'\ 0'. Loop from c to e, and then convert the case to character for each address.

The islower, isupper, tolower and toupper used here are all functions provided in ANSI C.

epilogue

Documentation in PHP is actually a good learning material, but many PHPer haven't really looked at the documentation (including me). As I said before, the purpose of my writing this is to scan the document comprehensively in this way. At the same time, from each function, we can see the implementation of the PHP kernel step by step. For the latter C's text, I can only do my best to explain, there are different places that you can include, after all, I am also a learner.

Posted by Dark_AngeL on Fri, 21 Dec 2018 12:57:05 -0800