剖析 `get_posts()` 函数如何通过内部实例化 `WP_Query` 来获取文章列表。

各位观众老爷,大家好!我是你们的老朋友,今天咱们来聊聊 WordPress 里那个神秘又实用的 get_posts() 函数。 别看它名字简单,但它背后的小秘密可不少,今天咱们就来把它扒个精光,看看它是如何偷偷摸摸地实例化 WP_Query 来获取文章列表的。

第一幕:get_posts() 的华丽登场

get_posts() 函数,顾名思义,就是用来获取文章列表的。它比直接使用 WP_Query 更加方便快捷,因为它已经帮你封装好了一些常用的参数。

<?php
$args = array(
    'numberposts' => 5,
    'offset' => 0,
    'category' => 0,
    'orderby' => 'post_date',
    'order' => 'DESC',
    'include' => array(),
    'exclude' => array(),
    'meta_key' => '',
    'meta_value' =>'',
    'post_type' => 'post',
    'suppress_filters' => true,
);

$recent_posts = get_posts( $args );
foreach ( $recent_posts as $post ) :
    setup_postdata( $post );
    echo '<h3><a href="' . get_permalink( $post->ID ) . '">' . get_the_title( $post->ID ) . '</a></h3>';
    echo '<p>' . get_the_excerpt() . '</p>';
endforeach;
wp_reset_postdata();
?>

上面的代码就是一个简单的例子,它获取了最新的 5 篇文章,并循环输出了标题和摘要。 看起来很简单对不对?但魔鬼就藏在细节里。

第二幕:深入虎穴,探寻源码

想要知道 get_posts() 是如何工作的,最好的办法就是去看它的源码。 打开 wp-includes/post.php 文件,找到 get_posts() 函数的定义。你会发现,它其实是一个非常简洁的函数:

function get_posts( $args = null ) {
    $defaults = array(
        'numberposts' => 5,
        'category' => 0, 'orderby' => 'post_date',
        'order' => 'DESC', 'include' => array(),
        'exclude' => array(), 'meta_key' => '',
        'meta_value' =>'', 'post_type' => 'post',
        'suppress_filters' => true,
    );

    $r = wp_parse_args( $args, $defaults );
    if ( empty( $r['numberposts'] ) )
        $r['numberposts'] = 5;

    $r = apply_filters( 'get_posts_args', $r );
    $posts = get_posts_by_author_url( $r );

    return apply_filters( 'get_posts', $posts, $r );
}

仔细观察,你会发现一个关键的函数调用:get_posts_by_author_url( $r )。 别急,咱们继续追踪下去。

第三幕:get_posts_by_author_url() 的真实面目

get_posts_by_author_url() 函数的代码稍微复杂一些,但它才是真正实例化 WP_Query 的地方。 它的代码如下

function get_posts_by_author_url( $r ) {
    $key = md5( serialize( $r ) );
    $last_changed = wp_cache_get( 'last_changed', 'posts' );
    if ( ! $last_changed ) {
        $last_changed = time();
        wp_cache_set( 'last_changed', $last_changed, 'posts' );
    }

    $cache_key = "get_posts:$key:$last_changed";
    if ( false !== ( $posts = wp_cache_get( $cache_key, 'posts' ) ) ) {
        return $posts;
    }

    $qv = apply_filters( 'posts_args', $r );
    $query = new WP_Query( $qv );

    $posts = $query->posts;

    wp_cache_set( $cache_key, $posts, 'posts', 3600 );

    return $posts;
}

看到没? $query = new WP_Query( $qv ); 这一行代码就是真相! get_posts_by_author_url() 函数接收参数,然后默默地创建了一个 WP_Query 对象,并把参数传递给它。 然后,它从 WP_Query 对象的 $posts 属性中获取文章列表,并返回。

第四幕:WP_Query 的参数传递

那么,get_posts() 接收到的参数是如何传递给 WP_Query 的呢?

  • wp_parse_args() 函数: 这个函数负责将用户传递的参数和默认参数合并成一个完整的参数数组。这样,即使你只传递了几个参数,get_posts() 也能正常工作。

  • apply_filters( 'get_posts_args', $r ) 这个过滤器允许你修改传递给 WP_Query 的参数。 这为我们提供了很大的灵活性,可以根据自己的需求来定制查询。

  • apply_filters( 'posts_args', $r ) 这是另一个过滤器,也允许你修改传递给 WP_Query 的参数。 区别在于,这个过滤器是在 get_posts_by_author_url() 函数中调用的,更加接近 WP_Query 的实例化。

这些参数最终会被传递给 WP_Query 类的构造函数,用于构建 SQL 查询语句,并从数据库中获取文章列表。

第五幕:WP_Query 的幕后英雄

WP_Query 类才是真正干活的。它负责:

  1. 解析参数: 将传递给它的参数解析成 SQL 查询语句。

  2. 执行查询: 执行 SQL 查询语句,从数据库中获取文章数据。

  3. 填充文章对象: 将获取到的文章数据填充到 WP_Post 对象中。

  4. 返回文章列表:WP_Post 对象组成的数组返回给 get_posts() 函数。

WP_Query 类是一个非常强大的类,它提供了很多参数来控制查询的行为。 常用的参数包括:

参数 描述
post_type 文章类型,例如 postpagecustom_post_type
post_status 文章状态,例如 publishdraftpending
category_name 分类目录别名,用于获取指定分类目录下的文章。
tag 标签别名,用于获取指定标签下的文章。
posts_per_page 每页显示的文章数量。
orderby 排序方式,例如 datetitlerand
order 排序顺序,例如 ASC(升序)、DESC(降序)。
s 搜索关键词,用于搜索文章内容。
author_name 作者别名,用于获取指定作者的文章。
meta_key 自定义字段键名,用于根据自定义字段的值来过滤文章。
meta_value 自定义字段键值,与 meta_key 配合使用。

第六幕:缓存的奥秘

细心的你可能已经发现了,get_posts_by_author_url() 函数使用了 WordPress 的对象缓存 API:

    $key = md5( serialize( $r ) );
    $last_changed = wp_cache_get( 'last_changed', 'posts' );
    if ( ! $last_changed ) {
        $last_changed = time();
        wp_cache_set( 'last_changed', $last_changed, 'posts' );
    }

    $cache_key = "get_posts:$key:$last_changed";
    if ( false !== ( $posts = wp_cache_get( $cache_key, 'posts' ) ) ) {
        return $posts;
    }

这段代码的作用是:

  1. 生成缓存键: 根据参数生成一个唯一的缓存键。

  2. 检查缓存: 检查缓存中是否存在该缓存键对应的数据。

  3. 如果缓存命中: 直接从缓存中返回文章列表,避免重复查询数据库。

  4. 如果缓存未命中: 执行查询,并将结果缓存起来,以便下次使用。

使用缓存可以大大提高网站的性能,减少数据库的负载。

第七幕:setup_postdata()wp_reset_postdata() 的作用

在循环输出文章列表时,你可能会看到 setup_postdata()wp_reset_postdata() 这两个函数:

foreach ( $recent_posts as $post ) :
    setup_postdata( $post );
    echo '<h3><a href="' . get_permalink( $post->ID ) . '">' . get_the_title( $post->ID ) . '</a></h3>';
    echo '<p>' . get_the_excerpt() . '</p>';
endforeach;
wp_reset_postdata();
  • setup_postdata() 这个函数的作用是将当前的文章设置为全局文章对象 $post。 这样,你就可以在循环中使用 the_title()the_content() 等模板标签来获取文章的信息。

  • wp_reset_postdata() 这个函数的作用是重置全局文章对象 $post 为主循环中的文章。 如果不调用这个函数,可能会导致在其他地方使用模板标签时出现错误。

第八幕:总结

get_posts() 函数虽然简单,但它背后隐藏着很多细节。 通过分析它的源码,我们了解了它是如何通过实例化 WP_Query 来获取文章列表的,以及参数是如何传递的,缓存是如何工作的。

现在,让我们用一张图来总结一下 get_posts() 的工作流程:

graph LR
A[get_posts()] --> B(wp_parse_args());
B --> C{apply_filters('get_posts_args')};
C --> D(get_posts_by_author_url());
D --> E{apply_filters('posts_args')};
E --> F(new WP_Query());
F --> G(查询数据库);
G --> H(填充 WP_Post 对象);
H --> I(返回文章列表);
I --> J{apply_filters('get_posts')};
J --> K[返回文章列表];

额外赠送:如何定制 get_posts() 的行为

了解了 get_posts() 的工作原理,你就可以根据自己的需求来定制它的行为。

  • 使用过滤器: 你可以使用 get_posts_argsposts_args 过滤器来修改传递给 WP_Query 的参数。
add_filter( 'get_posts_args', 'my_custom_get_posts_args' );
function my_custom_get_posts_args( $args ) {
    $args['category_name'] = 'featured'; // 只获取 "featured" 分类目录下的文章
    return $args;
}
  • 直接使用 WP_Query 如果你需要更精细的控制,可以直接使用 WP_Query 类。
$args = array(
    'post_type' => 'product',
    'posts_per_page' => 10,
    'meta_query' => array(
        array(
            'key' => 'price',
            'value' => 100,
            'compare' => '>',
            'type' => 'NUMERIC'
        )
    )
);
$query = new WP_Query( $args );

if ( $query->have_posts() ) {
    while ( $query->have_posts() ) {
        $query->the_post();
        echo '<h3><a href="' . get_permalink() . '">' . get_the_title() . '</a></h3>';
        echo '<p>Price: ' . get_post_meta( get_the_ID(), 'price', true ) . '</p>';
    }
    wp_reset_postdata();
} else {
    echo 'No products found.';
}

好了,今天的讲座就到这里。希望大家通过今天的学习,对 get_posts() 函数有了更深入的了解。 记住,理解源码才是王道! 下次再见!

发表回复

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