分析 WordPress `wp_list_pluck()` 函数源码:高效提取对象数组属性的原理。

各位观众老爷,晚上好! 今天咱们聊聊 WordPress 里面一个其貌不扬,但效率奇高的函数:wp_list_pluck()。 别看它名字土气,功能却相当实用,就像你家楼下不起眼的小饭馆,味道却让你欲罢不能。 咱们今天就来扒一扒它的底裤,看看它到底是怎么高效地从一堆对象数组里,把我们想要的属性给揪出来的。

一、 啥是 wp_list_pluck()?它能干啥?

简单来说,wp_list_pluck() 的作用就是从一个对象数组或者关联数组里,提取指定字段的值,并返回一个新的、只包含这些值的数组。 举个例子,假设我们有一堆用户数据,每个用户都是一个对象,包含姓名、年龄、邮箱等信息。 如果我们只想拿到所有用户的邮箱地址,那就可以用 wp_list_pluck() 来轻松搞定。

二、 源码解析: 庖丁解牛式分析

咱们直接上代码,看看 wp_list_pluck() 的庐山真面目:

/**
 * Retrieves a list of values from a list of objects, based on a property name.
 *
 * @since 4.7.0
 *
 * @param array        $list     A list of objects or arrays.
 * @param string       $field    Field from the object to place into the new array.
 * @param string|null  $index_key Optional. Field from the object to use as keys for the new array.
 *                                Default null.
 * @return array A list 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 ) && isset( $value->$field ) ) {
                $new_list[ $key ] = $value->$field;
            } elseif ( is_array( $value ) && isset( $value[ $field ] ) ) {
                $new_list[ $key ] = $value[ $field ];
            }
        }
    } else {
        foreach ( $list as $value ) {
            if ( is_object( $value ) && isset( $value->$index_key ) ) {
                $new_list[ $value->$index_key ] = ( isset( $value->$field ) ) ? $value->$field : null;
            } elseif ( is_array( $value ) && isset( $value[ $index_key ] ) ) {
                $new_list[ $value[ $index_key ] ] = ( isset( $value[ $field ] ) ) ? $value[ $field ] : null;
            }
        }
    }

    return $new_list;
}

别慌,咱们一步步来:

  1. 参数说明:

    • $list: 这是我们要处理的数据,必须是一个数组,数组的每个元素要么是一个对象,要么是一个关联数组。
    • $field: 这是我们要提取的字段名,也就是对象属性名或者数组的键名。
    • $index_key: 这是一个可选参数,用于指定用哪个字段的值作为新数组的键名。 如果不传,新数组的键名就是原来的数组的键名。
  2. 类型检查:

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

    上来先判断传入的 $list 是不是数组,如果不是,直接返回一个空数组,省的后面出错。 就像你去饭馆点菜,结果发现老板今天没进货,直接告诉你没得吃。

  3. 初始化结果数组:

    $new_list = array();

    创建一个空数组 $new_list,用于存放提取出来的值。 就像准备好一个盘子,用来装一会儿要“pluck”出来的果实。

  4. 处理逻辑:

    这里是整个函数的核心,也是区分两种不同使用场景的地方:

    • $index_keynull 的情况: 这意味着我们不需要指定新数组的键名,直接用原数组的键名。

      if ( null === $index_key ) {
          foreach ( $list as $key => $value ) {
              if ( is_object( $value ) && isset( $value->$field ) ) {
                  $new_list[ $key ] = $value->$field;
              } elseif ( is_array( $value ) && isset( $value[ $field ] ) ) {
                  $new_list[ $key ] = $value[ $field ];
              }
          }
      }

      这段代码遍历 $list 数组,对每个元素进行判断:

      • 判断元素是不是对象: is_object( $value ),如果是对象,就用 -> 访问属性;
      • 判断元素是不是数组: is_array( $value ),如果是数组,就用 [] 访问键值;
      • isset() 的作用: 在访问属性或者键值之前,先用 isset() 判断一下这个属性或键值是否存在,避免出现 "Undefined property" 或者 "Undefined index" 的错误。 这就像你开门之前,先看看门把手是不是还在,免得一用力把门给拽掉了。
    • $index_key 不为 null 的情况: 这意味着我们需要指定新数组的键名,键名的值从 $list 数组的每个元素中 $index_key 对应的属性或键值获取。

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

      这段代码和上面的逻辑类似,只是在赋值的时候,把 $value->$index_key (或 $value[ $index_key ]) 的值作为新数组的键名。 注意这里用了一个三元运算符 ( isset( $value->$field ) ) ? $value->$field : null,如果 $field 对应的属性或键值不存在,就赋值为 null,避免出错。 相当于你问老板有没有红烧肉,老板说没了,那就给你来个白菜吧(null)。

  5. 返回结果:

    return $new_list;

    最后,把提取出来的 $new_list 数组返回。 就像老板把做好的菜端给你,你可以开吃了。

三、 实例演示: 理论结合实践

光说不练假把式,咱们来几个例子:

例子 1:提取用户邮箱

假设我们有以下用户数据:

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

$emails = wp_list_pluck( $users, 'email' );

print_r( $emails );

输出结果:

Array
(
    [0] => [email protected]
    [1] => [email protected]
    [2] => [email protected]
)

我们成功地提取了所有用户的邮箱地址,并放在一个新的数组里,键名保持不变。

例子 2:提取用户邮箱,并以用户 ID 作为键名

$users = array(
    (object) array( 'id' => 1, 'name' => '张三', 'email' => '[email protected]' ),
    (object) array( 'id' => 2, 'name' => '李四', 'email' => '[email protected]' ),
    (object) array( '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]
)

这次我们指定了 $index_key 为 ‘id’,所以新数组的键名变成了用户 ID。 这样我们就可以通过用户 ID 快速找到对应的邮箱地址了。

例子 3:处理关联数组

$users = array(
    array( 'id' => 1, 'name' => '张三', 'email' => '[email protected]' ),
    array( 'id' => 2, 'name' => '李四', 'email' => '[email protected]' ),
    array( '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]
)

wp_list_pluck() 同样可以处理关联数组,只需要把访问对象属性的 -> 换成访问数组键值的 [] 即可。

四、 效率分析: 为什么 wp_list_pluck() 如此高效?

wp_list_pluck() 的效率主要体现在以下几个方面:

  • 简洁的循环: 它只用了一个 foreach 循环遍历数组,避免了嵌套循环带来的性能损耗。
  • 直接访问属性/键值: 它直接通过 ->[] 访问对象的属性或数组的键值,避免了使用复杂的函数调用。
  • 类型判断: 它会判断数组元素是对象还是数组,并根据不同的类型进行处理,保证了代码的健壮性。
  • isset() 的使用: 避免了访问不存在的属性或键值导致的错误,提高了代码的稳定性。

当然,wp_list_pluck() 也有一些局限性:

  • 只能提取一个字段: 如果需要提取多个字段,需要多次调用 wp_list_pluck()
  • 只能处理简单的对象/数组: 如果数组元素是复杂的对象或者嵌套数组,可能需要更复杂的处理逻辑。

五、 替代方案: 条条大路通罗马

除了 wp_list_pluck(),我们还可以使用其他方法来实现类似的功能:

  1. array_map() 这是 PHP 内置的函数,可以将数组中的每个元素应用一个回调函数,并返回一个新的数组。

    $emails = array_map( function( $user ) {
        return $user->email;
    }, $users );

    array_map() 的灵活性更高,可以处理更复杂的逻辑,但代码相对来说也更冗长。

  2. foreach 循环: 自己写 foreach 循环也可以实现相同的功能,但代码可读性可能不如 wp_list_pluck()

    $emails = array();
    foreach ( $users as $user ) {
        $emails[] = $user->email;
    }

    自己写循环的优点是可以完全掌控代码逻辑,但需要更多的代码量。

  3. PHP 8 的 array_column() 函数: 如果你的 PHP 版本是 5.5+,也可以使用 array_column() 函数,它专门用于从多维数组或对象数组中返回单列的值。

    $emails = array_column( $users, 'email', 'id' ); // 键名可选

array_column() 效率很高,而且代码简洁,推荐使用。

各种方法的对比:

方法 优点 缺点 适用场景
wp_list_pluck() 代码简洁,可读性高,针对 WordPress 对象数组优化 只能提取一个字段,无法处理复杂的逻辑 提取 WordPress 对象数组的单个字段,需要兼容旧版本 PHP
array_map() 灵活性高,可以处理复杂的逻辑 代码相对冗长 需要处理复杂的提取逻辑
foreach 循环 可以完全掌控代码逻辑 代码可读性可能较差,代码量较多 需要高度定制的提取逻辑
array_column() 代码简洁,效率高,PHP 5.5+ 版本可用, 可以指定键名 只能提取一个字段,无法处理复杂的逻辑, 只能用于数组,对于对象数组需要 (array)$object强制转换 提取数组或对象数组的单个字段,且不需要兼容旧版本 PHP (PHP 5.5+)

六、 总结: wp_list_pluck() 的价值

wp_list_pluck() 就像一把瑞士军刀,虽然功能不多,但在特定场景下非常实用。 它可以帮助我们快速、高效地从对象数组或关联数组中提取指定字段的值,简化代码,提高开发效率。 在 WordPress 开发中,经常会遇到需要处理大量数据的情况,wp_list_pluck() 可以帮助我们轻松应对这些挑战。

记住,选择合适的工具,才能事半功倍! 下次再遇到需要提取数组字段的情况,不妨试试 wp_list_pluck(), 相信它会给你带来惊喜。

好了,今天的讲座就到这里,感谢各位的观看! 祝大家编码愉快,bug 远离!

发表回复

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