资讯专栏INFORMATION COLUMN

每日一个 php 函数——array_change_key_case

awkj / 3407人阅读

摘要:关于宏熟悉扩展开发的同学应该都知道,这个宏,是定义一个函数用的,参数就是函数的函数名。关于这个宏,有兴趣的可以去看看源码,它其实是将替换成了,这样的一个函数定义。上面的几个宏,是为了检查并获取传参进函数的变量。

因为已经有文档了,可能有些人觉得我写这个有些多余了。可是并不是每一个 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 = "";
            return res;
        }
        c++;
    }
    return zend_string_copy(s);
}

用 C 写过转换字符串大小写的同学都知道,其实和我们自己实现的思路基本都差不多。只是用了几个宏。c 就是字符串的首地址,e 是字符串 "" 的地址。从 ce 循环,然后来对每一个地址的字符转换大小写。

这里用到的 islowerisuppertolowertoupper 都是 ANSI C 中提供的函数。

结语

PHP 的文档其实是很好的学习资料,但是很多 PHPer 都没有真正好好看过文档(包括我)。前面也说了,我写这个的目的,就是希望能够通过这种方式,来对文档进行全面的扫描。同时,也从每一个函数切入,逐步地去看 PHP 内核的实现。对于后面 C 的文字,我只能尽我自己所能来解释了,有不到的地方大家多包涵,毕竟我也是一个学习者。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/25554.html

相关文章

  • PHP 每日函数】第 01 周期

    摘要:说明将所有键名改为全大写或者小写张三男张三男说明将一个数组分割成多个数组张三男张三男说明返回数组中指定的一列男男女男男女说明将一个数组作键值,另一个数组作值产生一个新的数组说明数组中的键是中的值,数组中的值是的值出现的次数使用键名比 2019 - 02 - 20 array_change_key_case() array_change_key_case(array $arr, [, i...

    seanHai 评论0 收藏0
  • php 内核探秘之 PHP_FUNCTION 宏

    摘要:语言中的宏,我认为,可以理解为一种简单的封装。通过宏定义,可以对开发者隐去一些细节,让开发者在使用简单的语法来完成重复的复杂的编码。当然,宏定义还有其它的用途,但是,我们在涉及到的就是这个作用。我们看到,在宏定义中,使用了另外的宏。 本人也只是个初入门的菜鸟,因对技术有着向往,故在无趣的工作之余,尽自己所能提升自己。由于我的 C 语言功底也有限,故本文的深度也有限,如有幸得大牛阅读,还...

    Rindia 评论0 收藏0
  • thinkphp源码分析(五)—配置篇

    摘要:对于这两种不同形式的参数,处理方式也不一样,为字符串形式字符串则表示单个配置设置二维数组判断字符串中是否带没有直接把的小写形式作为,作为值设置到配置中如果带,只处理前面两项,即把字符串通过分割成数组,取数组的前面两项,把设置到配置中。 源码分析---入口篇 源码分析 全局配置加载类 全局配置类的主要代码如下: class Config { /** * @var ar...

    HitenDev 评论0 收藏0
  • PHP 数组函数分类和整理

    摘要:获取元素数量获取数组元素总数获取所有的键获取数组所有键组成的数组。遍历数组元素获取数组当前元素。数组指针前移一步。数组指针指向最后一个元素。其他数组操作反转数组。随机取出数组元素。对数组的所有值求和。 这几天工作之余整理和分类了PHP 中常用的数组相关的函数。如有错误和遗漏,请留言指正! 数组函数整理 创建数组函数 array array ([ mixed $... ] ):创建数组...

    LiuZh 评论0 收藏0
  • PHP 每日函数】第 03 周期

    摘要:说明获取数组中部分的或者所有的键名说明为数组的每个元素应用回调函数说明合并一个或者多个数组说明对多个数组或者多维数组进行排序具体可查看官网说明弹出数组中最后一个单元说明计算数组中所有值的乘积之前因为一些事,就没有每天都更,以后会坚持保持 2019-02-27 array_keys() array_keys ( array $array [, mixed $search_value = ...

    骞讳护 评论0 收藏0

发表评论

0条评论

awkj

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<