Por que query_posts () não está marcado como obsoleto?

15

Existem duas query_posts()funções tecnicamente falando. Um query_posts()é realmenteWP_Query::query_posts() e o outro está no espaço global.

Pedindo sanidade:

Se global query_posts() é esse "mal", por que não é preterido?

Ou por que não está marcado como _doing_it_wong.

prosti
fonte
2
Essa é uma ótima pergunta! Para outros que se deparam com isso e que não sabem por que você não deve usar query_posts (), aqui e aqui estão algumas boas perguntas e respostas sobre ele.
Tim Malone

Respostas:

11

Questão essencial

Vamos cavar o trio: ::query_posts, ::get_postse class WP_Querypara entender ::query_postsmelhor.

A pedra angular para obter os dados no WordPress é a WP_Queryclasse. Ambos os métodos ::query_postse ::get_postsusam essa classe.

Observe que a classe WP_Querytambém contém os métodos com o mesmo nome: WP_Query::query_postse WP_Query::get_posts, mas na verdade consideramos apenas os métodos globais, portanto, não fique confuso.

insira a descrição da imagem aqui

Compreendendo o WP_Query

A classe chamada WP_Queryfoi introduzida em 2004. Todos os campos com a marca umbrella (guarda-chuva) estavam presentes em 2004. Os campos adicionais foram adicionados posteriormente.

Aqui está a WP_Queryestrutura:

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 é o canivete suíço.

Algumas coisas sobre WP_Query:

  • é algo que você pode controlar através de argumentos que você passa
  • é ganancioso por padrão
  • contém a substância para loop
  • ele é salvo no espaço global x2
  • Pode ser primário ou secundário
  • usa classes auxiliares
  • tem um pre_get_postsgancho à mão
  • ele ainda tem suporte para loops aninhados
  • ele contém a string de consulta SQL
  • mantém o número dos resultados
  • mantém os resultados
  • mantém a lista de todos os argumentos de consulta possíveis
  • mantém as bandeiras do modelo
  • ...

Não sei explicar tudo isso, mas alguns são complicados, então vamos fornecer dicas curtas.

WP_Query é algo que você pode controlar através de argumentos que você passa

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

Esta lista do WordPress versão 4.7 certamente mudará no futuro.

Este seria o exemplo mínimo da criação do WP_Queryobjeto a partir dos argumentos:

// 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 é ganancioso

Criado com a idéia, os get all you candesenvolvedores do WordPress decidiram obter todos os dados possíveis o quanto antes, pois isso é bom para o desempenho . É por isso que, por padrão, quando a consulta recebe 10 postagens do banco de dados, ela também obtém os termos e os metadados para essas postagens por meio de consultas separadas. Os termos e metadados serão armazenados em cache (pré-buscados).

Observe que o armazenamento em cache é apenas para a vida útil da solicitação única.

Você pode desativar o cache de se definir update_post_meta_cachee update_post_term_cachepara falseao definir aWP_Query argumentos. Quando o armazenamento em cache está desativado, os dados serão solicitados no banco de dados somente sob demanda.

Para a maioria dos blogs do WordPress, o cache funciona bem, mas há algumas ocasiões em que você pode desativar o cache.

WP_Query usa classes auxiliares

Se você verificou os WP_Querycampos, você tem estes três:

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

Você pode imaginar adicionando novas no futuro.

insira a descrição da imagem aqui

WP_Query contém a substância para loop

Neste código:

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

você pode notar que WP_Querypossui a substância que pode iterar. Os métodos auxiliares também estão lá. Você acabou de definir o whileloop.

Nota. fore whileloops são semanticamente equivalentes.

WP_Query primário e secundário

No WordPress, você tem uma consulta primária e zero ou mais consultas secundárias .

É possível não ter a consulta principal, mas isso está além do escopo deste artigo.

Consulta primária conhecida como consulta principal ou consulta regular . Consulta secundária também chamada de consulta personalizada .

O WordPress usa a WP_Rewriteclasse mais cedo para criar os argumentos de consulta com base na URL. Com base nesses argumentos, ele armazena os dois objetos idênticos no espaço global. Ambos conterão a consulta principal.

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

Quando dizemos a consulta principal , pensamos nessas variáveis. Outras consultas podem ser chamadas de secundárias ou personalizadas.

É totalmente legal usar um global $wp_queryou outro $GLOBALS['wp_query'], mas o uso da segunda notação é muito mais notável e economiza a digitação de uma linha extra dentro do escopo das funções.

$GLOBALS['wp_query']e $GLOBALS['wp_the_query']são objetos separados. $GLOBALS['wp_the_query']deve permanecer congelado.

WP_Query tem a mão pre_get_posts gancho .

Este é o gancho de ação. Será aplicado a qualquer WP_Query instância. Você chama assim:

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

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

 return $query;  
});

Esse gancho é ótimo e pode alterar qualquer argumento de consulta.

Aqui está o que você pode ler :

Dispara após a criação do objeto variável de consulta, mas antes da execução da consulta real.

Portanto, este gancho é gerenciador de argumentos, mas não pode criar novos WP_Queryobjetos. Se você teve uma consulta primária e uma secundária, pre_get_postsnão pode criar a terceira. Ou, se você tivesse apenas um primário, ele não poderá criar o secundário.

Observe que, caso você precise alterar a consulta principal, você também pode usar o requestgancho.

WP_Query suporta loops aninhados

Esse cenário pode ocorrer se você usar plug-ins e chamar funções de plug-in do modelo.

Aqui está o exemplo de exemplo do WordPress com funções auxiliares, mesmo para os loops aninhados:

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;

A saída será assim desde que instalei os dados do teste da unidade temática :

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!

Mesmo que eu tenha solicitado 5 postagens na consulta $ personalizada, ele retornará seis, porque a postagem adesiva continuará. Se não houver wp_reset_postdatano exemplo anterior, a saída será assim, porque o valor $GLOBALS['post']será inválido.

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 tem wp_reset_query função

É como um botão de reset. $GLOBALS['wp_the_query']deve ser congelado o tempo todo, e plugins ou temas nunca devem alterá-lo.

Aqui está o que wp_reset_queryfazer:

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

Observações sobre get_posts

get_posts parece

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);

Os números das linhas podem mudar no futuro.

É apenas um invólucro em torno de WP_Queryque os retornos das mensagens objeto de consulta.

A ignore_sticky_postsconfiguração como true significa que as postagens adesivas podem aparecer apenas em uma posição natural. Não haverá postagens adesivas na frente. O outro no_found_rowsconjunto como true significa que a API do banco de dados WordPress não será usada SQL_CALC_FOUND_ROWSpara implementar a paginação, reduzindo a carga no banco de dados para executar linhas encontradas contagem de .

Isso é útil quando você não precisa de paginação. Entendemos agora que podemos imitar esta função com esta consulta:

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

Aqui está a solicitação SQL correspondente:

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

Compare o que temos agora com a solicitação SQL anterior, onde SQL_CALC_FOUND_ROWSexiste.

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

O pedido sem SQL_CALC_FOUND_ROWS será mais rápida.

Observações sobre query_posts

Dica: No início de 2004, havia apenas global $wp_query. A partir da versão 2.1 do WordPress $wp_the_querychegou. Dica: $GLOBALS['wp_query']e $GLOBALS['wp_the_query']são objetos separados.

query_posts()é WP_Queryinvólucro. Retorna a referência ao WP_Queryobjeto principal e, ao mesmo tempo, define o global $wp_query.

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

No PHP4, tudo, incluindo objetos, foi passado por valor. query_postsfoi assim:

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);
}

Observe no cenário típico com uma consulta primária e uma secundária que temos essas três variáveis:

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

Digamos que cada um desses três consuma 1 milhão de memória. Total seria 3M de memória. Se usarmos query_posts, $GLOBALS['wp_query']será desativado e criado novamente.

O PHP5 + deve ser inteligente esvaziando o $GLOBALS['wp_query']objeto, assim como no PHP4, fizemos com ounset($GLOBALS['wp_query']);

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

Como resultado, query_postsconsome 2M de memória no total, enquanto get_postsconsome 3M de memória.

Observe query_postsque não estamos retornando o objeto real, mas uma referência ao objeto.

Do php.net : Uma referência PHP é um alias, que permite que duas variáveis ​​diferentes gravem no mesmo valor. A partir do PHP 5, uma variável de objeto não contém mais o próprio objeto como valor. Ele contém apenas um identificador de objeto que permite que seus acessadores encontrem o objeto real. Quando um objeto é enviado por argumento, retornado ou atribuído a outra variável, as diferentes variáveis ​​não são alias: elas mantêm uma cópia do identificador, que aponta para o mesmo objeto.

Também no PHP5 +, o operador assign (=) é inteligente. Ele usará cópia rasa e não cópia de objeto físico. Quando escrevermos assim, $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];apenas os dados serão copiados, e não o objeto inteiro, pois eles compartilham o mesmo tipo de objeto.

Aqui está um exemplo

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'] ) ) );

Resultará:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
d6db1c6bfddac328442e91b6059210b5

Tente redefinir a consulta:

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'] ) ) );

Resultará:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef

Você pode criar problemas mesmo se usar 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'] ) ) );

Obviamente, a solução seria usar a wp_reset_queryfunção novamente.

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'] ) ) );

É por isso que acho que query_postspode ser melhor do ponto de vista da memória. Mas você deve sempre fazer wp_reset_querytruques.

prosti
fonte
10

Acabei de criar um novo ticket trac, ticket # 36874 , para propor a descontinuação query_posts(). Se será ou não aceito, continua sendo uma boa pergunta.

O grande problema query_posts()é que ele ainda é amplamente usado por plugins e temas, mesmo que tenha havido realmente bons escritos sobre o assunto de por que você NUNCA deve usá-lo. Eu acho que o post mais épico aqui no WPSE é o seguinte:

remoção de deprecation! == , portanto, a depreciação query_posts()não interromperá seu uso por desenvolvedores de baixa qualidade e por pessoas em geral que não conhecem o WordPress e que usam tutoriais de baixa qualidade como diretrizes. Assim como alguma prova, quantas perguntas que nós ainda chegar aqui onde as pessoas usam caller_get_postsem WP_Query? Está obsoleto há muitos anos.

No entanto, funções e argumentos obsoletos podem ser removidos a qualquer momento que os desenvolvedores principais entenderem, mas isso provavelmente nunca acontecerá, query_posts()pois isso quebrará milhões de sites. Portanto, sim, provavelmente nunca veremos a remoção total query_posts()- o que pode levar ao fato de que provavelmente nunca será reprovado.

Este é um ponto de partida, mas é preciso lembrar que a depreciação de algo no WordPress não interrompe seu uso.

ATUALIZAÇÃO 19 de maio de 2016

O ingresso que levantei agora está fechado e marcado como duplicado para um ingresso de 4 anos , que foi fechado como wontfix e foi reaberto e ainda permanece aberto e sem solução.

Parece que os principais desenvolvedores estão se apegando a esse velho e pequeno mal fiel. Todos os interessados, aqui está o bilhete duplicado de 4 anos

Pieter Goosen
fonte
Por que eles fecharam o ticket core.trac.wordpress.org/ticket/36874 ? Por favor @PieterGoosen você pode incluir o link para esta discussão no seu bilhete core.trac.wordpress.org/ticket/36874 uma vez que esta questão diz respeito à passagem 1: 1
prosti
@prosti Parece que foi marcado como duplicado, pois esse problema já foi mencionado ... há 4 anos encontrado aqui .
Howdy_McGee
3

[um tanto retórico]

É a filosofia central permanente que nada é verdadeiramente depreciado. O aviso de descontinuação, embora seja bom ter, só será ignorado se a função não for realmente descartada em algum momento. Existem muitas pessoas que não desenvolvem com WP_DEBUGe não notarão o aviso se não houver uma ruptura real.

OTOH mão, esta função é como gotodeclaração. Pessoalmente, nunca usei (por definição menor do que o esperado), gotomas posso entender os argumentos que apontam para alguma situação em que não é ruim por padrão. O mesmo acontece com query_posts, é uma maneira simples de configurar todos os globais necessários para fazer um loop simples e pode ser útil no contexto ajax ou rest-api. Eu nunca o usaria também nesses contextos, mas posso ver que, ali, é mais uma questão de estilo de codificação do que uma função que é má por si só.

Aprofundando um pouco, o principal problema é que os globais precisam ser definidos. Esse é o principal problema, não a única função que ajuda a defini-los.

Mark Kaplun
fonte
E para a comparação, é realmente query_postsmais lento que uma consulta secundária (leia-se: não a consulta principal).
Prosti
@prosti, como ele define e executa um wp_query, quanto mais lento pode ser? certeza de que há alguma sobrecarga, mas provavelmente estamos falando de milissegundos aqui. Obviamente, isso pressupõe que você o use em locais onde o WP não fornece uma consulta por padrão. Em lugares onde ele faz isso é ruim, não a query_postssi mesmo, mas a consulta inútil que foi feito quando WP foi de carregamento
Mark Kaplun