详解 WordPress `wp_list_pluck()` 函数的源码:如何高效地从对象数组中提取指定字段。

各位观众老爷们,晚上好!我是你们的老朋友,今天咱们来聊聊 WordPress 里的一个“小而美”的函数:wp_list_pluck()。 别看它名字长,功能可一点都不复杂,简单来说,它就是个从一堆对象或者数组里,批量提取某个字段的“搬运工”。

开场白:为什么我们需要这个搬运工?

想象一下,你从数据库里查了一大堆文章数据,每篇文章都是一个对象或者数组,包含了标题、内容、作者、发布时间等等信息。但是你现在只想拿到所有的文章标题,怎么办?难道要用循环一个个遍历,手动提取?

$articles = [
    (object) ['title' => 'WordPress 教程', 'author' => '张三'],
    (object) ['title' => 'PHP 编程', 'author' => '李四'],
    (object) ['title' => 'JavaScript 入门', 'author' => '王五'],
];

$titles = [];
foreach ($articles as $article) {
    $titles[] = $article->title;
}

print_r($titles); // 输出: Array ( [0] => WordPress 教程 [1] => PHP 编程 [2] => JavaScript 入门 )

这样做当然可以,但是代码略显冗长,而且效率也不高。这时候,wp_list_pluck() 就派上用场了,它可以一行代码搞定:

$titles = wp_list_pluck( $articles, 'title' );
print_r($titles); // 输出: Array ( [0] => WordPress 教程 [1] => PHP 编程 [2] => JavaScript 入门 )

是不是简洁多了? 接下来,咱们就深入扒一扒 wp_list_pluck() 的源码,看看它到底是怎么工作的。

源码解析:wp_list_pluck() 的内心世界

wp_list_pluck() 函数的源码位于 wp-includes/functions.php 文件中。 我们一起把它拉出来溜溜:

/**
 * Retrieves a list of values from an array of objects or arrays.
 *
 * @since 4.7.0
 *
 * @param array       $list      An array of objects or arrays to pluck from.
 * @param string|int  $field     Field from the object or array.
 * @param string|int  $index_key Optional. Field from the object or array to use as index.
 *                               Default null.
 * @return array Array of values.
 */
function wp_list_pluck( $list, $field, $index_key = null ) {
    if ( ! is_array( $list ) ) {
        return array();
    }

    $new_list = array();

    if ( null === $index_key ) {
        foreach ( $list as $key => $value ) {
            if ( is_object( $value ) ) {
                if ( isset( $value->$field ) ) {
                    $new_list[ $key ] = $value->$field;
                } else {
                    $new_list[ $key ] = null; // Or handle the missing field as you prefer
                }
            } else {
                if ( isset( $value[ $field ] ) ) {
                    $new_list[ $key ] = $value[ $field ];
                } else {
                    $new_list[ $key ] = null; // Or handle the missing field as you prefer
                }
            }
        }
    } else {
        foreach ( $list as $value ) {
            if ( is_object( $value ) ) {
                if ( isset( $value->$index_key ) ) {
                    $new_list[ $value->$index_key ] = $value->$field;
                }
            } else {
                if ( isset( $value[ $index_key ] ) ) {
                    $new_list[ $value[ $index_key ] ] = $value->$field;
                }
            }
        }
    }

    return $new_list;
}

代码虽然不长,但是信息量还是蛮大的,我们逐行拆解:

  1. 函数定义和文档注释:

    /**
     * Retrieves a list of values from an array of objects or arrays.
     *
     * @since 4.7.0
     *
     * @param array       $list      An array of objects or arrays to pluck from.
     * @param string|int  $field     Field from the object or array.
     * @param string|int  $index_key Optional. Field from the object or array to use as index.
     *                               Default null.
     * @return array Array of values.
     */
    function wp_list_pluck( $list, $field, $index_key = null ) {

    这部分是函数的定义和文档注释,告诉我们这个函数的作用、参数以及返回值。其中:

    • $list: 要处理的数组,里面的元素可以是对象或者数组。
    • $field: 要提取的字段名。
    • $index_key: 可选参数,指定用哪个字段的值作为结果数组的键名。 默认是 null,也就是使用默认的数字索引。
  2. 参数校验:

    if ( ! is_array( $list ) ) {
        return array();
    }

    首先,判断传入的 $list 是否是一个数组。如果不是,就直接返回一个空数组,避免出现错误。

  3. 初始化结果数组:

    $new_list = array();

    创建一个新的数组 $new_list,用于存储提取出来的字段值。

  4. 判断是否指定了 index_key:

    if ( null === $index_key ) {
        // 没有指定 index_key 的情况
        // ...
    } else {
        // 指定了 index_key 的情况
        // ...
    }

    这里是整个函数的关键分支,根据是否指定了 $index_key,采取不同的处理方式。

  5. 没有指定 index_key 的情况:

    foreach ( $list as $key => $value ) {
        if ( is_object( $value ) ) {
            if ( isset( $value->$field ) ) {
                $new_list[ $key ] = $value->$field;
            } else {
                $new_list[ $key ] = null; // Or handle the missing field as you prefer
            }
        } else {
            if ( isset( $value[ $field ] ) ) {
                $new_list[ $key ] = $value[ $field ];
            } else {
                $new_list[ $key ] = null; // Or handle the missing field as you prefer
            }
        }
    }
    • 循环遍历 $list 数组。
    • 对于每个元素 $value,判断它是对象还是数组。
    • 如果是对象,使用 isset( $value->$field ) 检查对象是否存在 $field 属性,如果存在,则将该属性的值赋给 $new_list[ $key ]。 如果不存在,赋值为 null (你可以根据需要修改)。
    • 如果是数组,使用 isset( $value[ $field ] ) 检查数组是否存在 $field 键,如果存在,则将该键的值赋给 $new_list[ $key ]。 如果不存在,赋值为 null (你可以根据需要修改)。
    • 注意:这里使用 $key 作为 $new_list 的键名,也就是保留了原始数组的索引。
  6. 指定了 index_key 的情况:

    foreach ( $list as $value ) {
        if ( is_object( $value ) ) {
            if ( isset( $value->$index_key ) ) {
                $new_list[ $value->$index_key ] = $value->$field;
            }
        } else {
            if ( isset( $value[ $index_key ] ) ) {
                $new_list[ $value[ $index_key ] ] = $value->$field;
            }
        }
    }
    • 循环遍历 $list 数组。
    • 对于每个元素 $value,判断它是对象还是数组。
    • 如果是对象,使用 isset( $value->$index_key ) 检查对象是否存在 $index_key 属性,如果存在,则使用该属性的值作为 $new_list 的键名,并将 $field 属性的值赋给 $new_list[ $value->$index_key ]
    • 如果是数组,使用 isset( $value[ $index_key ] ) 检查数组是否存在 $index_key 键,如果存在,则使用该键的值作为 $new_list 的键名,并将 $field 键的值赋给 $new_list[ $value[ $index_key ] ]
    • 注意: 如果 $index_key 对应的值重复了,那么后面的值会覆盖前面的值。
  7. 返回结果数组:

    return $new_list;

    最后,返回提取出来的 $new_list 数组。

使用场景举例:

光说不练假把式,接下来我们用几个例子来演示 wp_list_pluck() 的用法。

例子 1:提取文章标题

$articles = [
    (object) ['ID' => 1, 'title' => 'WordPress 教程', 'author' => '张三'],
    (object) ['ID' => 2, 'title' => 'PHP 编程', 'author' => '李四'],
    (object) ['ID' => 3, 'title' => 'JavaScript 入门', 'author' => '王五'],
];

$titles = wp_list_pluck( $articles, 'title' );
print_r($titles);
// 输出: Array ( [0] => WordPress 教程 [1] => PHP 编程 [2] => JavaScript 入门 )

这个例子我们前面已经用过了,作用是从 $articles 数组中提取出所有的 title 字段的值,并返回一个新的数组 $titles

例子 2:提取文章标题,并使用文章 ID 作为键名

$articles = [
    (object) ['ID' => 1, 'title' => 'WordPress 教程', 'author' => '张三'],
    (object) ['ID' => 2, 'title' => 'PHP 编程', 'author' => '李四'],
    (object) ['ID' => 3, 'title' => 'JavaScript 入门', 'author' => '王五'],
];

$titles = wp_list_pluck( $articles, 'title', 'ID' );
print_r($titles);
// 输出: Array ( [1] => WordPress 教程 [2] => PHP 编程 [3] => JavaScript 入门 )

这个例子和上一个例子类似,但是我们指定了 $index_keyID,这意味着返回的数组 $titles 的键名将会是文章的 ID。

例子 3:处理数组

$users = [
    ['id' => 1, 'name' => '张三', 'email' => '[email protected]'],
    ['id' => 2, 'name' => '李四', 'email' => '[email protected]'],
    ['id' => 3, 'name' => '王五', 'email' => '[email protected]'],
];

$emails = wp_list_pluck( $users, 'email', 'id' );
print_r($emails);
// 输出: Array ( [1] => [email protected] [2] => [email protected] [3] => [email protected] )

这个例子处理的是一个包含数组的数组, 作用是从 $users 数组中提取出所有的 email 字段的值,并使用 id 字段的值作为键名。

例子 4:处理字段不存在的情况

$products = [
    (object) ['id' => 1, 'name' => '苹果', 'price' => 5],
    (object) ['id' => 2, 'name' => '香蕉'], // 缺少 price 字段
    (object) ['id' => 3, 'name' => '橙子', 'price' => 4],
];

$prices = wp_list_pluck( $products, 'price', 'id' );
print_r($prices);
// 输出: Array ( [1] => 5 [2] =>  [3] => 4 )  (WordPress 6.4.3 版本输出的是空字符串)

// 如果想将不存在的字段设置为 null
function my_wp_list_pluck( $list, $field, $index_key = null ) {
    if ( ! is_array( $list ) ) {
        return array();
    }

    $new_list = array();

    if ( null === $index_key ) {
        foreach ( $list as $key => $value ) {
            if ( is_object( $value ) ) {
                $new_list[ $key ] = isset( $value->$field ) ? $value->$field : null;
            } else {
                $new_list[ $key ] = isset( $value[ $field ] ) ? $value[ $field ] : null;
            }
        }
    } else {
        foreach ( $list as $value ) {
            if ( is_object( $value ) ) {
                if ( isset( $value->$index_key ) ) {
                    $new_list[ $value->$index_key ] = isset( $value->$field ) ? $value->$field : null;
                }
            } else {
                if ( isset( $value[ $index_key ] ) ) {
                    $new_list[ $value[ $index_key ] ] = isset( $value[ $field ] ) ? $value[ $field] : null;
                }
            }
        }
    }

    return $new_list;
}

$prices = my_wp_list_pluck( $products, 'price', 'id' );
print_r($prices);
// 输出: Array ( [1] => 5 [2] => [3] => 4 )

这个例子演示了如果某个元素缺少指定的字段,wp_list_pluck() 会如何处理。 在 WordPress 6.4.3 版本中,如果字段不存在,会赋值为空字符串。 你可以根据自己的需求,修改源码,例如将不存在的字段赋值为 null 或者其他默认值。 上面的代码演示了如何通过自定义函数 my_wp_list_pluck 来实现这个功能。

性能分析:wp_list_pluck() 效率如何?

wp_list_pluck() 的时间复杂度是 O(n),其中 n 是 $list 数组的元素个数。 因为它需要遍历整个数组来提取字段值。 对于小型数据集来说,wp_list_pluck() 的效率完全可以接受。 但是对于大型数据集,例如包含几千甚至几万个元素的数组,wp_list_pluck() 的性能可能会成为瓶颈。

如果需要处理大型数据集,可以考虑以下优化方案:

  • 使用数据库查询优化: 尽量在数据库查询时就只获取需要的字段,避免将大量无关数据加载到内存中。
  • 使用缓存: 如果数据不经常变化,可以使用缓存来避免重复计算。
  • 使用更高效的算法: 对于非常特殊的需求,可以考虑使用更高效的算法来提取字段值,例如使用 array_map 函数结合自定义的回调函数。

和其他方法的比较:

方法 优点 缺点 适用场景
wp_list_pluck() 简洁易用,可指定索引键,内置于 WordPress,无需额外引入。 时间复杂度 O(n),对于大型数据集性能可能受限;字段不存在时默认返回空字符串,需要修改源码才能自定义行为。 中小型数据集,需要从对象/数组数组中提取指定字段,并且可能需要使用指定字段作为索引。
array_map() + 回调函数 灵活性高,可以自定义提取逻辑,处理各种复杂情况。 代码相对冗长,需要编写额外的回调函数;不如 wp_list_pluck() 简洁。 需要进行复杂的数据转换和处理,或者需要自定义字段不存在时的行为。
循环遍历 简单直接,易于理解。 代码冗长,效率较低。 数据量很小,或者只需要处理少量元素。
数据库查询优化 性能最佳,直接从数据库中获取需要的字段,避免加载大量无关数据。 需要修改数据库查询语句,可能增加代码复杂度。 数据量非常大,并且对性能要求很高。

总结:

wp_list_pluck() 是一个非常实用的函数,可以方便地从对象或数组数组中提取指定字段。 它的源码虽然简单,但是包含了对象和数组的处理逻辑,以及 index_key 的处理。 在实际开发中,我们可以根据需要灵活使用 wp_list_pluck(),提高代码效率。 但是需要注意,对于大型数据集,wp_list_pluck() 的性能可能会成为瓶颈,需要考虑使用其他优化方案。

希望今天的分享对大家有所帮助! 记住,熟练掌握这些“小而美”的函数,可以让我们在 WordPress 开发的道路上走得更远! 下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注