为什么没有将query_posts()标记为已弃用?


15

query_posts()技术上讲,有两个功能。一个query_posts()实际上是WP_Query::query_posts(),另一个在全球空间。

从理智上问:

如果query_posts()说这个“邪恶”是全球性的,那为什么不弃用呢?

或为什么未标记为_doing_it_wong


2
这是一个很大的问题!对于不知道为什么不应该使用query_posts()的其他人,这里这里有一些很好的入门问答。
蒂姆·马隆

Answers:


11

基本问题

让我们深入到三重奏:::query_posts::get_postsclass WP_Query理解::query_posts好。

WP_Query该类是在WordPress中获取数据的基石。两种方法::query_posts::get_posts使用该类。

请注意,该类WP_Query还包含具有相同名称的方法:WP_Query::query_postsWP_Query::get_posts,但实际上我们仅考虑全局方法,因此请不要感到困惑。

在此处输入图片说明

了解 WP_Query

该类称为 WP_Query早在2004年就引入了被。所有带有☂(雨伞)标记的字段都在2004年出现。后来又添加了其他字段。

这里是 WP_Query结构:

class WP_Query (as in WordPress v4.7) 
    public $query; 
    public $query_vars = array(); 
    public $tax_query;
    public $meta_query = false;
    public $date_query = false;
    public $queried_object; 
    public $queried_object_id; 
    public $request;
    public $posts; 
    public $post_count = 0; 
    public $current_post = -1; 
    public $in_the_loop = false;
    public $post; 
    public $comments;
    public $comment_count = 0;
    public $current_comment = -1;
    public $comment;
    public $found_posts = 0;
    public $max_num_pages = 0;
    public $max_num_comment_pages = 0;
    public $is_single = false; 
    public $is_preview = false; 
    public $is_page = false; 
    public $is_archive = false; 
    public $is_date = false; 
    public $is_year = false; 
    public $is_month = false; 
    public $is_day = false; 
    public $is_time = false; 
    public $is_author = false; 
    public $is_category = false; 
    public $is_tag = false;
    public $is_tax = false;
    public $is_search = false; 
    public $is_feed = false; 
    public $is_comment_feed = false;
    public $is_trackback = false; 
    public $is_home = false; 
    public $is_404 = false; 
    public $is_embed = false;
    public $is_paged = false;
    public $is_admin = false; 
    public $is_attachment = false;
    public $is_singular = false;
    public $is_robots = false;
    public $is_posts_page = false;
    public $is_post_type_archive = false;
    private $query_vars_hash = false;
    private $query_vars_changed = true;
    public $thumbnails_cached = false;
    private $stopwords;
    private $compat_fields = array('query_vars_hash', 'query_vars_changed');
    private $compat_methods = array('init_query_flags', 'parse_tax_query');
    private function init_query_flags()

WP_Query 是瑞士军刀。

关于的一些事情 WP_Query

  • 您可以通过传递的参数来控制它
  • 默认情况下是贪婪的
  • 它含有循环的物质
  • 它保存在全局空间x2中
  • 它可以是主要或次要的
  • 它使用辅助类
  • 它有一个方便 pre_get_posts钩子
  • 它甚至支持嵌套循环
  • 它包含SQL查询字符串
  • 它保存结果数
  • 它保存结果
  • 它包含所有可能的查询参数的列表
  • 它包含模板标志
  • ...

我无法解释所有这些问题,但是其中一些是棘手的,因此让我们提供一些简短提示。

WP_Query 您可以通过传递的参数来控制

The list of the arguments
---
 attachment
 attachment_id
 author
 author__in
 author__not_in
 author_name
 cache_results
 cat
 category__and
 category__in
 category__not_in
 category_name
 comments_per_page
 day
 embed
 error
 feed
 fields
 hour
 ignore_sticky_posts
 lazy_load_term_meta
 m
 menu_order
 meta_key
 meta_value
 minute
 monthnum
 name
 no_found_rows
 nopaging
 order
 p
 page_id
 paged
 pagename
 post__in
 post__not_in
 post_name__in
 post_parent
 post_parent__in
 post_parent__not_in
 post_type
 posts_per_page
 preview
 s
 second
 sentence
 static
 subpost
 subpost_id
 suppress_filters
 tag
 tag__and
 tag__in
 tag__not_in
 tag_id
 tag_slug__and
 tag_slug__in
 tb
 title
 update_post_meta_cache
 update_post_term_cache
 w
 year

WordPress 4.7中的此列表将来肯定会更改。

这是WP_Query从参数创建对象的最小示例:

// WP_Query arguments
$args = array ( /* arguments*/ );
// creating the WP_Query object
$query = new WP_Query( $args );
// print full list of arguments WP_Query can take
print ( $query->query_vars );

WP_Query 贪婪

get all you canWordPress开发人员基于这个想法,决定尽早获取所有可能的数据,因为这对性能有好处。这就是为什么默认情况下,当查询从数据库中获取10个帖子时,它还将通过单独的查询获取这些帖子的术语和元数据。术语和元数据将被缓存(预取)。

请注意,缓存仅用于单个请求生存期。

您可以禁用缓存,如果你设置update_post_meta_cacheupdate_post_term_cachefalse在设置WP_Query参数。禁用缓存时,仅根据需要从数据库请求数据。

对于大多数WordPress博客而言,缓存效果很好,但是在某些情况下,您可能会禁用缓存。

WP_Query 使用助手类

如果您WP_Query在此处检查字段,则有以下三个:

public $tax_query;
public $meta_query;
public $date_query;

您可以想象在将来添加新的东西。

在此处输入图片说明

WP_Query 含有要循环的物质

在此代码中:

$query = new WP_Query( $args )
if ( $query->have_posts() ) {
        while ( $query->have_posts() ) {
            $query->the_post();

您可能会注意到WP_Query具有可以迭代的内容。辅助方法也在那里。您只需设置while循环。

注意。forwhile循环在语义上是等效的。

WP_Query 小学和中学

在WordPress中,您有一个查询和零个或多个辅助查询。

可能没有主查询,但这超出了本文的范围。

主查询称为主查询常规查询辅助查询也称为自定义查询

WordPress WP_Rewrite早期使用类来基于URL创建查询参数。基于这些参数,它将两个相同的对象存储在全局空间中。这两个都将保存主查询。

global $wp_query   @since WordPress 1.5
global $wp_the_query @since WordPress 2.1

当我们说主要查询时,我们会想到这些变量。其他查询可以称为辅助查询或自定义查询。

使用global $wp_query或 完全合法$GLOBALS['wp_query'],但是使用第二种表示法更为明显,并且省去了在函数范围内键入额外的行的麻烦。

$GLOBALS['wp_query']$GLOBALS['wp_the_query']是单独的对象。$GLOBALS['wp_the_query']应该保持冻结状态。

WP_Query有方便的pre_get_posts钩子。

这是动作挂钩。它将适用于任何 WP_Query实例。您这样称呼它:

add_action( 'pre_get_posts', function($query){

 if ( is_category() && $query->is_main_query() ) {
    // set your improved arguments
    $query->set( ... );  
    ...  
 }

 return $query;  
});

这个钩子很棒,它可以更改任何查询参数。

这是您可以阅读的内容

在创建查询变量对象之后但在运行实际查询之前触发。

因此,此挂钩是参数管理器,但无法创建新WP_Query对象。如果您有一个主查询和一个辅助查询,pre_get_posts则不能创建第三个查询。或者,如果您只有一个主服务器,则无法创建辅助服务器。

请注意,如果仅需要更改主查询,则也可以使用该request挂钩。

WP_Query 支持嵌套循环

如果您使用插件,并且从模板调用插件函数,则可能会发生这种情况。

这是展示示例,WordPress甚至对嵌套循环也具有辅助功能:

global $id;
while ( have_posts() ) : the_post(); 

    // the custom $query
    $query = new WP_Query( array(   'posts_per_page' => 5   ) );    
    if ( $query->have_posts() ) {

        while ( $query->have_posts() ) : $query->the_post();            
            echo '<li>Custom ' . $id . '. ' . get_the_title() . '</li>';
        endwhile;       
    }   

    wp_reset_postdata();
    echo '<li>Main Query ' . $id . '. ' . get_the_title() . '</li>';

endwhile;

自从我安装了主题单元测试数据以来,输出将是这样的:

Custom 100. Template: Sticky
Custom 1. Hello world!
Custom 10. Markup: HTML Tags and Formatting
Custom 11. Markup: Image Alignment
Custom 12. Markup: Text Alignment
Custom 13. Markup: Title With Special Characters
Main Query 1. Hello world!

即使我在自定义$ query中请求了5个帖子,它也会返回6个帖子,因为粘性帖子会继续发送。如果wp_reset_postdata在前面的示例中没有,则输出将像这样,因为$GLOBALS['post']将会无效。

Custom 1001. Template: Sticky
Custom 1. Hello world!
Custom 10. Markup: HTML Tags and Formatting
Custom 11. Markup: Image Alignment
Custom 12. Markup: Text Alignment
Custom 13. Markup: Title With Special Characters
Main Query 13. Markup: Title With Special Characters

WP_Querywp_reset_query功能

这就像一个重置按钮。$GLOBALS['wp_the_query']应该一直被冻结,并且插件或主题永远都不能改变它。

这是做wp_reset_query什么的:

function wp_reset_query() {
    $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
    wp_reset_postdata();
}

备注 get_posts

get_posts 好像

File: /wp-includes/post.php
1661: function get_posts( $args = null ) {
1662:   $defaults = array(
1663:       'numberposts' => 5,
1664:       'category' => 0, 'orderby' => 'date',
1665:       'order' => 'DESC', 'include' => array(),
1666:       'exclude' => array(), 'meta_key' => '',
1667:       'meta_value' =>'', 'post_type' => 'post',
1668:       'suppress_filters' => true
1669:   );
... // do some argument parsing
1685:   $r['ignore_sticky_posts'] = true;
1686:   $r['no_found_rows'] = true;
1687: 
1688:   $get_posts = new WP_Query;
1689:   return $get_posts->query($r);

行号将来可能会更改。

这仅仅是一个包装周围WP_Query返回查询对象的职位。

ignore_sticky_posts设置为true手段置顶文章可能只在自然的位置显示出来。前面不会有任何粘性帖子。另一个no_found_rows设置为true表示WordPress数据库API将不使用SQL_CALC_FOUND_ROWS它来实现分页,从而减少了数据库上执行找到的行数的负担。

当您不需要分页时,这很方便。现在我们知道可以使用以下查询来模仿此功能:

$args = array ( 'ignore_sticky_posts' => true, 'no_found_rows' => true);
$query = new WP_Query( $args );
print( $query->request );

这是相应的SQL请求:

SELECT wp_posts.ID FROM wp_posts WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') ORDER BY wp_posts.post_date DESC LIMIT 0, 10

将我们现在所拥有的与以前SQL_CALC_FOUND_ROWS存在的SQL请求进行比较。

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')  ORDER BY wp_posts.post_date DESC LIMIT 0, 10

没有的请求SQL_CALC_FOUND_ROWS将会更快。

备注 query_posts

提示:最初在2004年只有global $wp_query。从WordPress 2.1版本$wp_the_query开始。提示:$GLOBALS['wp_query']$GLOBALS['wp_the_query']是单独的对象。

query_posts()WP_Query包装纸。它将对主WP_Query对象的引用返回,同时将设置global $wp_query

File: /wp-includes/query.php
function query_posts($args) {
    $GLOBALS['wp_query'] = new WP_Query();
    return $GLOBALS['wp_query']->query($args);
}

在PHP4中,包括对象在内的所有东西都是按值传递的。query_posts就像这样:

File: /wp-includes/query.php (WordPress 3.1)
function &query_posts($args) {
    unset($GLOBALS['wp_query']);
    $GLOBALS['wp_query'] =& new WP_Query();
    return $GLOBALS['wp_query']->query($args);
}

请注意,在具有一个主查询和一个辅助查询的典型方案中,我们具有以下三个变量:

$GLOBALS['wp_the_query'] 
$GLOBALS['wp_query'] // should be the copy of first one
$custom_query // secondary

假设这三个都占用1M的内存。总计将是3M的内存。如果使用query_posts$GLOBALS['wp_query']将被取消设置并重新创建。

PHP5 +应该聪明地清空$GLOBALS['wp_query']对象,就像在PHP4中我们用unset($GLOBALS['wp_query']);

function query_posts($args) {
    $GLOBALS['wp_query'] = new WP_Query();
    return $GLOBALS['wp_query']->query($args);
}

结果query_posts,总共消耗2M的内存,而get_posts消耗3M的内存。

请注意,query_posts我们并不是返回实际的对象,而是返回对该对象的引用。

来自php.net:PHP引用是一个别名,它允许两个不同的变量写入相同的值。从PHP 5开始,对象变量不再包含对象本身作为值。它仅包含一个对象标识符,该标识符使对象访问者可以找到实际的对象。当对象通过参数发送,返回或分配给另一个变量时,不同的变量不是别名:它们持有标识符的副本,该副本指向相同的对象。

同样在PHP5 +中,assign(=)运算符很聪明。它将使用浅表副本而不是硬对象副本。当我们这样写时$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];,将仅复制数据,而不是整个对象,因为它们共享相同的对象类型。

这是一个例子

print( md5(serialize($GLOBALS['wp_the_query']) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
query_posts( '' );
print( md5(serialize($GLOBALS['wp_the_query']) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );

将导致:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
d6db1c6bfddac328442e91b6059210b5

尝试重置查询:

print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
query_posts( '' );
wp_reset_query();
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );

将导致:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef

即使您使用也会造成问题 WP_Query

print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
global $wp_query;
$wp_query = new WP_Query( array( 'post_type' => 'post' ) );   
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );

当然,解决方案将是wp_reset_query再次使用功能。

print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );
global $wp_query;
$wp_query = new WP_Query( array( 'post_type' => 'post' ) );
wp_reset_query();
print( md5(serialize($GLOBALS['wp_the_query'] ) ) );
print( md5(serialize($GLOBALS['wp_query'] ) ) );

这就是为什么我认为query_posts从内存角度来看可能会更好。但是你应该总是wp_reset_query骗人。


10

我刚刚创建了一个新的跟踪票,票号#36874,以提议弃用query_posts()。是否接受将是一个很好的问题。

真正的大问题query_posts()是,尽管有很多关于为什么要这么做的文章,但它仍被插件和主题广泛使用。永远不要使用它。我认为WPSE上最史诗般的帖子如下:

弃用!==移除,因此弃用query_posts()不会被质量差的开发人员和不了解WordPress且通常将质量差的教程用作指导原则的人们停止使用。正如一些证据,多少问题我们仍然来到这里,人们使用caller_get_postsWP_Query?现在已经不推荐使用了很多年。

但是,在核心开发人员认为合适的任何时候,都可以删除不推荐使用的函数和参数,但这很可能永远不会发生。 query_posts()因为这将破坏数百万个站点。因此,是的,我们可能永远不会看到完全删除query_posts()-可能导致这样的事实,即它很可能永远不会被弃用。

虽然这是一个起点,但必须记住,在WordPress中弃用某些内容不会停止其使用。

更新2016年5月19日

我所筹集的票现已关闭,并标记为与4岁的票重复的票,该票已作为wontfix关闭已重新打开,仍未解决。

似乎核心开发人员正在抓住这个古老的忠实小邪恶。每个人都有兴趣,这是重复的4岁门票


为什么他们关闭了票务core.trac.wordpress.org/ticket/36874?请@PieterGoosen可以在票证core.trac.wordpress.org/ticket/36874中包含指向该线程的链接,因为该问题与票证有关1:1
prosti

@prosti似乎已被标记为重复,因为此问题已被提出... 4年前 在此处找到
Howdy_McGee

3

[有点rant]

在这一点上,始终存在的核心哲学是什么都不是真正不推荐使用的东西。弃用通知虽然很不错,但是如果在某个时候实际上不会删除该函数,则将被忽略。有很多人不会继续发展WP_DEBUG下去,如果没有实际的破坏,他们不会注意到通知。

OTOH手,此功能类似于goto声明。就我个人而言,我从未使用过(对于较小的定义,然后是预期的),goto但是我可以理解指向默认情况下并不邪恶的某些情况的论点。一样query_posts道理,它是设置进行简单循环所需的所有全局变量的简单方法,并且在ajax或rest-api上下文中很有用。我也永远不会在那些上下文中使用它,但是我可以看到,它更多的是编码风格的问题,而不是函数本身就是邪恶的。

更深入一点,主要问题是根本需要设置全局变量。这是主要问题,而不是帮助设置它们的一个功能。


为了进行比较,它确实query_posts比辅助查询要慢(阅读:不是主查询)。
prosti

@prosti,因为它只是设置并运行wp_query,它可以慢多少?当然会有一些开销,但是我们这里可能要讲几毫秒。当然,这假定您在WP默认不提供查询的地方使用它。在这样做的地方,它不是很不好,不是很糟糕,query_posts而是在WP加载时进行的无用查询
Mark Kaplun,2016年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.