問題描述
技術上有兩個 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_Queryalso contains the methods with the same name:WP_Query::query_postsandWP_Query::get_posts, but we actually only consider the global methods, so don’t get confused.
瞭解 WP_Query
The class called
WP_Queryhas 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.
forandwhileloops 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_queryor$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
requesthook 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_querycame. 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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。

