问题描述
技术上有两个 query_posts()
功能。一个 query_posts()
实际上是 WP_Query::query_posts()
,另一个在全球空间。
要求理智:
如果全球 query_posts()
是”evil” 为什么不被弃用?
或为什么没有标记为_doing_it_wong
。
最佳解决方案
我刚刚创建了一个新的 trac 门票,ticket #36874,提出了 query_posts()
的弃用。是否接受仍然是一个很好的问题。
query_posts()
的真正的大问题是,它仍然被插件和主题广泛使用,尽管在这个主题上,为什么你永远不会使用它是非常好的着作。我认为 WPSE 上最史诗的帖子如下:
deprecation!==删除,所以不赞成 query_posts()
不会停止使用质量差的开发人员和一般人谁不知道 WordPress 和谁使用质量差的教程作为指导。就像一些证明,我们还有几个问题在这里人们在 WP_Query
中使用 caller_get_posts
?它已经被淘汰多年了。
但是,随着核心开发人员可以随时删除已弃用的函数和参数,但这绝对不会发生在 query_posts()
上,因为这将破坏数百万个站点。所以是的,我们可能永远不会看到完全删除 query_posts()
– 这可能导致事实上它很可能永远不会被弃用。
这是一个起点,但是必须记住,弃用 WordPress 中的东西不会停止使用。
2016 年 5 月 19 日更新
我提出的机票现在已关闭,并标记为与 4 年票相同的复印件,该票已被关闭,并重新开放,仍然保持开放和尚未解决。
似乎核心的开发人员正在坚持这个老忠实的小恶魔。每个人都有兴趣,这里是重复的 4 年票
次佳解决方案
基本问题
让我们深入三人:::query_posts
,::get_posts
和 class WP_Query
更好地了解::query_posts
。
在 WordPress 中获取数据的基石是 WP_Query
类。两种方法::query_posts
和::get_posts
都使用该类。
Note that the class
WP_Query
also contains the methods with the same name:WP_Query::query_posts
andWP_Query::get_posts
, but we actually only consider the global methods, so don’t get confused.
了解 WP_Query
The class called
WP_Query
has been introduced back in 2004. All fields having the ☂ (umbrella) mark where present back in 2004. The additional fields were added later.
这是 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
This list from WordPress version 4.7 will certainly change in the future.
这将是从参数创建 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 can
WordPress 开发人员决定尽早获得所有可能的数据,因为这是 good for the performance 。这就是为什么默认情况下,该查询从数据库中获取 10 个帖子,它还将通过单独的查询获取这些帖子的条款和元数据。条款和元数据将被缓存 (预取) 。
Note the caching is just for the single request lifetime.
如果在设置 WP_Query
参数时将 update_post_meta_cache
和 update_post_term_cache
设置为 false
,则可以禁用缓存。当缓存被禁用时,数据库将仅在需要时从数据库中请求。
对于大多数 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
循环。
Note.
for
andwhile
loops are semantically equivalent.
WP_Query
一级和二级
在 WordPress 中,您有一个主要和零个或多个次要查询。
It is possible not to have the primary query, but this is beyond the scope of this article.
主查询称为主查询或常规查询。辅助查询也称为自定义查询。
WordPress 早期使用 WP_Rewrite
类来创建基于 URL 的查询参数。基于这些参数,它将两个相同的对象存储在全局空间中。这两个都将保持主要查询。
global $wp_query @since WordPress 1.5
global $wp_the_query @since WordPress 2.1
当我们说主查询时,我们想到这些变量。其他查询可以称为辅助或自定义。
It is completely legal to use either
global $wp_query
or$GLOBALS['wp_query']
, but using the second notation is much more notable, and saves typing an extra line inside the scope of the functions.
$GLOBALS['wp_query']
and$GLOBALS['wp_the_query']
are separate objects.$GLOBALS['wp_the_query']
should remain frozen.
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;
});
这个钩子很棒,它可以改变任何查询参数。
这是你可以 read:
Fires after the query variable object is created, but before the actual query is run.
所以这个钩子是参数管理器,但不能创建新的 WP_Query
对象。如果您有一个主要和一个辅助查询,pre_get_posts
无法创建第三个。或者如果你只有一个主要的,它不能创建次要的。
Note in case you need to alter the main query only you can use the
request
hook also.
WP_Query
支持嵌套循环
This scenario may happen if you use plugins, and you call plugin functions from the template.
这是展示示例 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;
输出将是这样,因为我安装了 theme unit test data:
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!
即使我在自定义 $查询中请求了 5 个帖子,它将返回给我六个,因为粘贴的帖子将会继续。如果在前面的例子中没有 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_Query
具有 wp_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);
The line numbers may change in the future.
它只是 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
Tip: At first in 2004 there was only
global $wp_query
. As of WordPress 2.1 version$wp_the_query
came. Tip:$GLOBALS['wp_query']
and$GLOBALS['wp_the_query']
are separate objects.
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 的 memory 。如果我们使用 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
中我们不是返回实际的对象,而是对对象的引用。
From php.net: A PHP reference is an alias, which allows two different variables to write to the same value. As of PHP 5, an object variable doesn’t contain the object itself as value anymore. It only contains an object identifier which allows object accessors to find the actual object. When an object is sent by argument, returned or assigned to another variable, the different variables are not aliases: they hold a copy of the identifier, which points to the same object.
Also in PHP5+ the assign (=) operator is smart. It will use shallow copy and not hard object copy. When we write like this
$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
only the data will be copied, not the whole object since these share the same object type.
这是一个例子
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
的技巧。
第三种解决方案
[有点] t]
这是现在的核心理念,没有什么是真正的弃用。弃用通知,虽然它是一个很好的,只是将被忽略,如果该功能实际上不会在某些时候被删除。有很多人不会用 WP_DEBUG
开发,如果没有实际的破损,也不会注意到通知。
OTOH 手,这个功能就像 goto
语句。我个人从来没有 (对于较小的定义,然后预期) 使用 goto
,但我可以理解指出一些情况下,它不是默认的邪恶的论据。与 query_posts
相同,它是一种简单的方法来设置所需的全局变量,以便进行简单循环,并且可以在 ajax 或 rest-api 上下文中使用。我也不会在这些上下文中使用它,但是我可以看到,在编码风格上更是一个问题,那么一个功能本身就是邪恶的。
更深一层,主要的问题是全球人需要设置。这是主要的问题,而不是帮助设置它们的一个功能。
参考文献
注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。