Почему query_posts () не помечен как устаревший?

15

query_posts()Технически говоря, есть две функции. Один query_posts()на самом деле, WP_Query::query_posts()а другой в глобальном пространстве.

Просить от здравомыслия:

Если глобально query_posts()это "зло", почему не считается устаревшим?

Или почему не помечено как _doing_it_wong.

прости
источник
2
Это отличный вопрос! Для тех, кто сталкивается с этим, кто не знает, почему вы не должны использовать query_posts (), здесь и здесь есть несколько хороших вопросов и ответов для начинающих.
Тим Мэлоун

Ответы:

11

Существенный вопрос

Давайте копаться в трио ::query_posts, ::get_postsи лучше class WP_Queryпонять ::query_posts.

Краеугольным камнем для получения данных в WordPress является WP_Queryкласс. Оба метода ::query_postsи ::get_postsиспользуют этот класс.

Обратите внимание, что класс WP_Queryтакже содержит методы с одинаковыми именами: WP_Query::query_postsи WP_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:

  • это то, что вы можете контролировать с помощью аргументов, которые вы передаете
  • это жадный по умолчанию
  • он содержит вещество для цикла
  • он сохраняется в глобальном пространстве х2
  • это может быть первичным или вторичным
  • он использует вспомогательные классы
  • у него есть удобный 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 canразработчики WordPress решили получить все возможные данные на ранней стадии, поскольку это хорошо для производительности . Вот почему по умолчанию, когда запрос получает 10 сообщений из базы данных, он также получает термины и метаданные для этих сообщений через отдельные запросы. Термины и метаданные будут кэшироваться (предварительно выбираться).

Обратите внимание, что кэширование только для времени жизни одного запроса.

Вы можете отключить кэширование , если вы установите update_post_meta_cacheи update_post_term_cacheв falseпроцессе настройки 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цикл.

Заметка. forи whileпетли семантически эквивалентны.

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!

Несмотря на то, что я запросил 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);

Номера строк могут измениться в будущем.

Это просто обертка вокруг того, WP_Queryчто возвращает на посты объекта запроса.

Значение ignore_sticky_poststrue означает, что прикрепленные сообщения могут появляться только в естественном положении. Там не будет никаких липких сообщений в передней части. Другое no_found_rowsзначение true означает, что API базы данных WordPress не будет использоваться 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-запросом, если SQL_CALC_FOUND_ROWSтаковой существует.

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

Допустим, каждый из этих трех занимает 1М памяти. Всего будет 3М памяти. Если мы используем 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в общей сложности потребляется 2М памяти, в то время как get_postsпотребляется 3М памяти.

Обратите внимание, что 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

Я только что создал новый билет trac, билет # 36874 , для предложения об устаревании query_posts(). Вопрос о том, будет ли он принят, остается хорошим вопросом.

Реальная большая проблема в query_posts()том, что он по-прежнему широко используется плагинами и темами, даже несмотря на то, что были действительно хорошие статьи на тему, почему вы НИКОГДА не должны его использовать. Я думаю, что самый эпический пост здесь на WPSE следующий:

deprecation! == удаление , поэтому устаревание query_posts()не остановит его использование разработчиками низкого качества и людьми в целом, которые не знают WordPress и которые используют руководства низкого качества в качестве руководства. Так же , как некоторые доказательства, сколько вопросов мы до сих пор получаем здесь , где люди используют caller_get_postsв WP_Query? Это осуждается уже много лет.

Однако устаревшие функции и аргументы могут быть удалены в любое время, когда разработчики ядра сочтут это целесообразным, но, скорее всего, этого никогда не произойдет, так query_posts()как это сломает миллионы сайтов. Так что, да, мы, вероятно, никогда не увидим полного удаления query_posts()- что может привести к тому, что оно, скорее всего, никогда не станет устаревшим.

Это только отправная точка, но нужно помнить, что устаревшее в WordPress не останавливает его использование.

ОБНОВЛЕНИЕ 19 мая 2016

Билет, который я поднял, теперь закрыт и помечен как дубликат для 4-летнего билета, который был закрыт как wontfix и был вновь открыт и все еще остается открытым и неразрешенным.

Кажется, разработчики ядра держатся за это старое верное маленькое зло. Всем интересно, вот дубликат 4-летнего билета

Питер Гусен
источник
Почему они закрыли тикет core.trac.wordpress.org/ticket/36874 ? Пожалуйста, @PieterGoosen. Можете ли вы включить ссылку на эту ветку в свой тикет core.trac.wordpress.org/ticket/36874, поскольку этот вопрос относится к тикету 1: 1
prosti
@ prosti Похоже, что он был помечен как дубликат, поскольку этот вопрос уже был поднят ... 4 года назад найден здесь .
Howdy_McGee
3

[несколько разглагольствует]

На данный момент основной философией является то, что ничто не является действительно устаревшим. Уведомление об устаревании, хотя это и неплохо, просто будет проигнорировано, если функция в какой-то момент не будет удалена. Есть много людей, которые не развиваются с WP_DEBUGи не заметят уведомления, если не будет фактической поломки.

OTOH стороны, эта функция похожа на gotoутверждение. Лично я никогда (для меньшего определения, чем ожидалось) не использовалgoto но я могу понять аргументы, указывающие на некоторую ситуацию, в которой это не зло по умолчанию. То же самое query_posts, это простой способ настроить все глобальные переменные, необходимые для создания простого цикла, и может быть полезен в контексте ajax или rest-api. Я бы никогда не использовал его и в этом контексте, но я вижу, что там больше проблема стиля кодирования, чем функция, которая сама по себе является злой.

Если пойти немного глубже, главная проблема заключается в том, что глобальные переменные должны быть установлены вообще. Это главная проблема, а не единственная функция, которая помогает их устанавливать.

Марк Каплун
источник
И для сравнения, это действительно query_postsмедленнее, чем вторичный запрос (читай: не основной запрос).
Прости
@prosti, поскольку он просто устанавливает и запускает wp_query, насколько медленнее это может быть? конечно, есть некоторые накладные расходы, но мы, вероятно, говорим здесь миллисекунды. Это, конечно, предполагает, что вы используете его там, где WP не предоставляет запрос по умолчанию. В местах, где это происходит, это плохо, но не query_postsсам запрос, а бесполезный запрос, который был сделан при загрузке WP
Марк Каплун