阐述 `get_posts()` 函数的源码,它是如何将参数传递给 `WP_Query` 类并返回文章数组的?

大家好,我是老码,今天咱们来聊聊 WordPress 里的 get_posts() 这个函数,这玩意儿看起来简单,但背后调用了强大的 WP_Query 类,把各种参数玩得溜溜的,最后吐出一堆文章。咱们今天就扒开它的源码,看看它到底是怎么运作的。

开场:get_posts() 是个啥?

get_posts() 函数是 WordPress 提供的一个便捷函数,用于获取文章列表。 简单来说,它就是 WP_Query 的一个简化版封装,让咱们不用每次都写一大坨 WP_Query 的代码。

第一幕:get_posts() 源码亮相

我们先来看看 get_posts() 的源码,它藏身在 wp-includes/post.php 文件里。

function get_posts( $args = null ) {
    $defaults = array(
        'numberposts' => 5,
        'orderby'     => 'date',
        'order'       => 'DESC',
        'post_type'   => 'post',
        'suppress_filters' => true,
    );

    $r = wp_parse_args( $args, $defaults );

    if ( isset( $r['post_status'] ) && 'any' === $r['post_status'] ) {
        unset( $r['post_status'] );
    }

    if ( ! empty( $r['numberposts'] ) && (int) $r['numberposts'] < 0 ) {
        $r['numberposts'] = 5;
    }

    if ( isset( $r['caller_get_posts'] ) ) {
        _deprecated_argument( __FUNCTION__, '3.1',
            sprintf(
                /* translators: %s: caller_get_posts */
                __( 'The %s argument is deprecated since version 3.1! Use ignore_sticky_posts instead.' ),
                '<code>caller_get_posts</code>'
            )
        );
        if ( ! isset( $r['ignore_sticky_posts'] ) ) {
            $r['ignore_sticky_posts'] = $r['caller_get_posts'];
        }
        unset( $r['caller_get_posts'] );
    }

    $r['ignore_sticky_posts'] = (bool) $r['ignore_sticky_posts'];
    $r['suppress_filters']    = (bool) $r['suppress_filters'];

    $get_posts = new WP_Query( $r );

    return $get_posts->posts;
}

第二幕:源码解读,庖丁解牛

我们一行一行地分析一下这段代码,就像拆解一个精密的机械钟表一样。

  1. 默认参数 ($defaults):

    $defaults = array(
        'numberposts' => 5,
        'orderby'     => 'date',
        'order'       => 'DESC',
        'post_type'   => 'post',
        'suppress_filters' => true,
    );

    这里定义了一组默认参数。 如果没有传递任何参数给 get_posts(),它就会使用这些默认值。

    • numberposts: 默认返回 5 篇文章。
    • orderby: 默认按照日期排序。
    • order: 默认降序排列 (最新的文章在前)。
    • post_type: 默认获取 post (文章) 类型的文章。
    • suppress_filters: 默认禁止应用任何过滤器,提高性能。
  2. 参数合并 (wp_parse_args):

    $r = wp_parse_args( $args, $defaults );

    wp_parse_args() 函数是 WordPress 提供的一个非常实用的函数,用于合并用户传递的参数 ($args) 和默认参数 ($defaults)。 如果用户传递了参数,它们会覆盖默认参数。 如果没有传递,就使用默认值。 这保证了所有需要的参数都存在,而且具有合理的默认值。

    例如,如果用户传递了 $args = array( 'numberposts' => 10, 'category_name' => 'news' ),那么 $r 就会变成:

    array(
        'numberposts' => 10,
        'orderby'     => 'date',
        'order'       => 'DESC',
        'post_type'   => 'post',
        'suppress_filters' => true,
        'category_name' => 'news',
    )
  3. 处理 post_status = 'any':

    if ( isset( $r['post_status'] ) && 'any' === $r['post_status'] ) {
        unset( $r['post_status'] );
    }

    如果用户将 post_status 设置为 'any',则将其从参数中删除。 WP_Query 在内部处理 post_status = 'any',因此不需要显式传递它。

  4. 修正 numberposts:

    if ( ! empty( $r['numberposts'] ) && (int) $r['numberposts'] < 0 ) {
        $r['numberposts'] = 5;
    }

    如果 numberposts 是一个负数,就将其重置为默认值 5。 这防止了意外行为,确保始终返回一个非负数数量的文章。

  5. 处理 caller_get_posts (已弃用):

    if ( isset( $r['caller_get_posts'] ) ) {
        _deprecated_argument( __FUNCTION__, '3.1',
            sprintf(
                /* translators: %s: caller_get_posts */
                __( 'The %s argument is deprecated since version 3.1! Use ignore_sticky_posts instead.' ),
                '<code>caller_get_posts</code>'
            )
        );
        if ( ! isset( $r['ignore_sticky_posts'] ) ) {
            $r['ignore_sticky_posts'] = $r['caller_get_posts'];
        }
        unset( $r['caller_get_posts'] );
    }

    caller_get_posts 参数在 WordPress 3.1 中已弃用,现在应该使用 ignore_sticky_posts。 这段代码是为了向后兼容,如果用户仍然使用 caller_get_posts,它会将它的值复制到 ignore_sticky_posts,并发出一个弃用警告。

  6. 强制转换为布尔值:

    $r['ignore_sticky_posts'] = (bool) $r['ignore_sticky_posts'];
    $r['suppress_filters']    = (bool) $r['suppress_filters'];

    确保 ignore_sticky_postssuppress_filters 都是布尔值。 这防止了类型错误,并确保这些参数按预期工作。

  7. 实例化 WP_Query:

    $get_posts = new WP_Query( $r );

    这是最关键的一步。 使用合并后的参数 $r 实例化 WP_Query 类。 WP_Query 类会根据这些参数构建 SQL 查询,从数据库中检索文章。

  8. 返回文章数组:

    return $get_posts->posts;

    WP_Query 类有一个名为 posts 的属性,它是一个包含所有检索到的文章对象的数组。 get_posts() 函数返回这个数组。

第三幕:WP_Query 的魔力

WP_Query 类才是真正的幕后英雄。 它接收 get_posts() 传递来的参数,然后:

  1. 构建 SQL 查询: 根据参数,WP_Query 会构建复杂的 SQL 查询语句,从数据库中检索符合条件的文章。 它可以处理各种复杂的查询条件,比如分类、标签、作者、自定义字段等等。

  2. 执行查询: WP_Query 执行构建好的 SQL 查询,从数据库中获取数据。

  3. 填充文章对象: WP_Query 将从数据库中检索到的数据填充到 WP_Post 对象中,并将这些对象存储在 $posts 属性中。

WP_Query 参数详解:$args 的无限可能

get_posts() 能够接受的参数实际上就是 WP_Query 能够接受的参数。 这些参数可以分为几大类:

  • 文章参数:

    • p: 文章 ID。
    • name: 文章别名 (slug)。
    • post_parent: 父文章 ID。
    • post_type: 文章类型 (post, page, custom post type)。
    • post_status: 文章状态 (publish, draft, pending, private, trash, any)。
  • 分类和标签参数:

    • cat: 分类 ID。
    • category_name: 分类别名。
    • tag: 标签 ID。
    • tag_id: 标签 ID。
    • tag_slug__and: 必须同时包含的标签别名 (数组)。
    • tag_slug__in: 包含的标签别名 (数组)。
    • tag__and: 必须同时包含的标签 ID (数组)。
    • tag__in: 包含的标签 ID (数组)。
    • category__and: 必须同时属于的分类 ID (数组)。
    • category__in: 属于的分类 ID (数组)。
    • category__not_in: 不属于的分类 ID (数组)。
  • 作者参数:

    • author: 作者 ID。
    • author_name: 作者登录名。
    • author__in: 作者 ID (数组)。
    • author__not_in: 作者 ID (数组)。
  • 分页参数:

    • posts_per_page (或 numberposts): 每页显示的文章数量。 -1 表示显示所有文章。
    • paged: 当前页码。
    • offset: 偏移量,跳过指定数量的文章。
  • 排序参数:

    • orderby: 排序字段 (date, title, rand, comment_count, menu_order, meta_value, meta_value_num)。
    • order: 排序方式 (ASC, DESC)。
    • meta_key: 自定义字段键名 (用于 meta_valuemeta_value_num 排序)。
  • 时间参数:

    • year: 年份。
    • monthnum: 月份。
    • w: 周数。
    • day: 天数.
    • hour: 小时.
    • minute: 分钟
    • second: 秒.
    • date_query: 更复杂的日期查询 (数组)。
  • 自定义字段参数:

    • meta_key: 自定义字段键名。
    • meta_value: 自定义字段值。
    • meta_value_num: 自定义字段数值。
    • meta_compare: 自定义字段比较运算符 (=, !=, >, >=, <, <=, LIKE, NOT LIKE, IN, NOT IN, BETWEEN, NOT BETWEEN)。
    • meta_query: 更复杂的自定义字段查询 (数组)。
  • 其他参数:

    • s: 搜索关键词。
    • ignore_sticky_posts: 是否忽略置顶文章 (true/false)。
    • suppress_filters: 是否禁止应用过滤器 (true/false)。 一般情况下设置为 true 以提高性能。
    • fields: 返回的字段 (all, ids, id=>parent)。 ids 只返回文章 ID 的数组,id=>parent 返回文章 ID 和父文章 ID 的关联数组。

表格总结:get_posts()WP_Query 的关系

特性 get_posts() WP_Query
本质 一个函数,WP_Query 的封装 一个类,用于执行灵活的数据库查询
易用性 简单易用,适用于简单的文章检索 更加灵活,适用于复杂的查询需求
代码量 代码量少 代码量多
性能 稍好 (因为封装了常用的参数) 灵活,但需要根据实际情况进行优化
适用场景 简单的文章列表,快速原型开发 需要高度定制化的查询,例如复杂的条件组合,自定义字段查询,分页等
直接SQL查询 不直接支持 不直接支持,但可以通过 posts_whereposts_join 等过滤器来修改 SQL 查询

举例说明:get_posts() 的实际应用

  1. 获取最新的 10 篇文章:

    $recent_posts = get_posts( array( 'numberposts' => 10 ) );
    foreach ( $recent_posts as $post ) {
        setup_postdata( $post );
        echo '<h3><a href="' . get_permalink( $post->ID ) . '">' . get_the_title( $post->ID ) . '</a></h3>';
        the_excerpt();
    }
    wp_reset_postdata();
  2. 获取特定分类下的文章:

    $category_posts = get_posts( array( 'category_name' => 'news', 'numberposts' => 5 ) );
    foreach ( $category_posts as $post ) {
        setup_postdata( $post );
        echo '<h4><a href="' . get_permalink( $post->ID ) . '">' . get_the_title( $post->ID ) . '</a></h4>';
    }
    wp_reset_postdata();
  3. 使用自定义字段进行查询:

    $custom_field_posts = get_posts( array(
        'meta_key'   => 'featured',
        'meta_value' => 'true',
        'numberposts' => 3
    ) );
    foreach ( $custom_field_posts as $post ) {
        setup_postdata( $post );
        echo '<p><a href="' . get_permalink( $post->ID ) . '">' . get_the_title( $post->ID ) . '</a></p>';
    }
    wp_reset_postdata();

高级用法:利用过滤器修改 WP_Query

虽然 get_posts() 已经很方便了,但有时候我们需要更高级的定制。 这时候,我们可以利用 WordPress 的过滤器来修改 WP_Query 的行为。

  • pre_get_posts: 在 WP_Query 执行之前修改查询参数。 这是一个非常强大的过滤器,可以完全控制查询的行为。
  • posts_where: 修改 SQL 查询的 WHERE 子句。
  • posts_join: 修改 SQL 查询的 JOIN 子句。
  • posts_orderby: 修改 SQL 查询的 ORDER BY 子句。

例如,我们可以使用 pre_get_posts 来修改主循环的查询:

function my_custom_query( $query ) {
    if ( is_home() && $query->is_main_query() ) {
        $query->set( 'category_name', 'featured' );
        $query->set( 'posts_per_page', 5 );
    }
}
add_action( 'pre_get_posts', 'my_custom_query' );

这段代码会将首页的主循环修改为只显示 featured 分类下的 5 篇文章。

总结:get_posts(),小身材,大能量

get_posts() 函数虽然看起来简单,但它背后隐藏着强大的 WP_Query 类。 它是一个非常实用的工具,可以帮助我们快速、方便地获取文章列表。 掌握了 get_posts() 的用法,就等于掌握了 WordPress 数据检索的一把钥匙。 同时,理解 WP_Query 的工作原理,可以让我们更加灵活地定制 WordPress 的行为。

希望今天的讲解能帮助大家更好地理解 get_posts() 函数,并在实际开发中灵活运用它。 记住,代码的世界充满了乐趣,不断探索才能发现更多的惊喜!

发表回复

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