web-dev-qa-db-fra.com

Pourquoi query_posts () n'est pas marqué comme obsolète?

Techniquement, il existe deux fonctions query_posts(). Une query_posts() est en réalité WP_Query::query_posts() et l'autre est dans l'espace global.

Demander à la santé mentale:

Si query_posts() global est ce "mal", pourquoi n'est-il pas obsolète?

Ou pourquoi n'est pas marqué comme _doing_it_wong.

14
prosti

Question essentielle

Fouillons dans le trio: ::query_posts, ::get_posts et class WP_Query pour mieux comprendre ::query_posts.

La pierre angulaire pour obtenir les données dans WordPress est la classe WP_Query. Les deux méthodes ::query_posts et ::get_posts utilisent cette classe.

Notez que la classe WP_Query contient également les méthodes du même nom: WP_Query::query_posts et WP_Query::get_posts, mais nous ne considérons en fait que les méthodes globales, ne vous trompez donc pas.

 enter image description here 

Comprendre le WP_Query

La classe appelée WP_Query a été introduite en 2004. Tous les champs portant la marque (parapluie) étaient présents en 2004. Les champs supplémentaires ont été ajoutés ultérieurement.

Voici la structure 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 est le couteau suisse.

Quelques points sur WP_Query:

  • c'est quelque chose que vous pouvez contrôler via les arguments que vous passez
  • c'est gourmand par défaut
  • il contient la substance pour la boucle
  • il est enregistré dans l'espace global x2
  • il peut être primaire ou secondaire
  • il utilise des classes d'assistance
  • il a un crochet pratique pre_get_posts
  • il a même un support pour les boucles imbriquées
  • il contient la chaîne de requête SQL
  • il contient le nombre de résultats
  • il détient les résultats
  • il contient la liste de tous les arguments de requête possibles
  • il contient les drapeaux de modèle
  • ...

Je ne peux pas expliquer tout cela, mais certains d'entre eux sont difficiles, donnons-en de brefs conseils.

WP_Query est quelque chose que vous pouvez contrôler via les arguments que vous passez

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

Cette liste de WordPress version 4.7 changera certainement dans le futur.

Ce serait l'exemple minimal créant l'objet WP_Query à partir des arguments:

// 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 est gourmand

Créé sur l’idée get all you can, les développeurs WordPress ont décidé d’obtenir rapidement toutes les données possibles car c’est bon pour la performance . C'est pourquoi, par défaut, lorsque la requête prélève 10 publications dans la base de données, les termes et les métadonnées de ces publications sont également collectés via des requêtes distinctes. Les termes et métadonnées seront mis en cache (pré-extraits).

Notez que la mise en cache ne concerne que la durée de vie d'une seule demande.

Vous pouvez désactiver la mise en cache si vous définissez update_post_meta_cache et update_post_term_cache sur false tout en définissant les arguments WP_Query. Lorsque la mise en cache est désactivée, les données ne seront demandées à la base de données que sur demande.

Pour la majorité des blogs WordPress, la mise en cache fonctionne bien, mais vous pouvez parfois désactiver la mise en cache.

WP_Query utilise des classes auxiliaires

Si vous avez coché les champs WP_Query, vous avez ces trois:

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

Vous pouvez imaginer ajouter de nouveaux dans le futur.

 enter image description here 

WP_Query contient la substance pour la mise en boucle

Dans ce code:

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

vous remarquerez peut-être que le WP_Query contient la substance que vous pouvez itérer. Les méthodes d'assistance sont là aussi. Vous venez de définir la boucle while.

Remarque. Les boucles for et while sont sémantiquement équivalentes.

WP_Query primaire et secondaire

Dans WordPress, vous avez une primaire et zéro ou plus secondaire requêtes.

Il est possible de ne pas avoir la requête principale, mais cela sort du cadre de cet article.

Requête primaire connue sous le nom de requête principale ou la requête standard. Requête secondaire également appelée requête personnalisée.

WordPress utilise la classe WP_Rewrite à un stade précoce pour créer les arguments de requête basés sur l'URL. Sur la base de ces arguments, il stocke les deux objets identiques dans l'espace global. Les deux détiendront la requête principale.

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

Quand nous disons requête principale}, nous pensons à ces variables. D'autres requêtes peuvent être appelées secondaires ou personnalisées.

Il est tout à fait légal d’utiliser soit global $wp_query, soit $GLOBALS['wp_query'], mais l’utilisation de la deuxième notation est beaucoup plus notable et permet d’enregistrer une ligne supplémentaire à l’intérieur de la portée des fonctions.

$GLOBALS['wp_query'] et $GLOBALS['wp_the_query'] sont des objets distincts. $GLOBALS['wp_the_query'] devrait rester figé.

WP_Query a le crochet pratique pre_get_posts.

C'est le crochet d'action. Cela s'appliquera à toute instance WP_Query. Vous l'appelez comme:

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

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

 return $query;  
});

Ce hook est génial et peut altérer tous les arguments de la requête.

Voici ce que vous pouvez lisez :

Se déclenche après la création de l'objet de variable de requête, mais avant l'exécution de la requête réelle.

Ce hook est donc un gestionnaire d'arguments mais ne peut pas créer de nouveaux objets WP_Query. Si vous avez une requête principale et une requête secondaire, pre_get_posts ne peut pas créer la troisième. Ou si vous avez juste un primaire, il ne peut pas créer le secondaire.

Remarque Si vous devez modifier la requête principale uniquement, vous pouvez également utiliser le hook request.

WP_Query supporte les boucles imbriquées

Ce scénario peut se produire si vous utilisez des plug-ins et que vous appelez des fonctions de plug-in à partir du modèle.

Voici l'exemple de démonstration WordPress possède des fonctions d'assistance, même pour les boucles imbriquées:

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;

La sortie sera comme ça depuis que j'ai installé données de test d'unité de thème :

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!

Même si j'ai demandé 5 messages dans la requête $ $ custom, il m'en retournera six, car le message persistant suivra. S'il n'y a pas wp_reset_postdata dans l'exemple précédent, la sortie sera comme ceci, car le $GLOBALS['post'] sera invalide.

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 a la fonction wp_reset_query

C'est comme un bouton de réinitialisation. $GLOBALS['wp_the_query'] devrait être gelé tout le temps, et les plugins ou les thèmes ne devraient jamais le modifier.

Voici ce que wp_reset_query fait:

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

Remarques sur get_posts

get_posts ressemble à

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

Les numéros de ligne peuvent changer dans le futur.

C'est juste un wrapper autour de WP_Query que renvoie l'objet de la requête est publié.

Le ignore_sticky_posts défini sur true signifie que les posts collants ne peuvent apparaître que dans une position naturelle. Il n'y aura pas de poteaux collants à l'avant. L'autre no_found_rows défini sur true signifie que l'API de base de données WordPress n'utilisera pas SQL_CALC_FOUND_ROWS afin d'implémenter la pagination, réduisant ainsi la charge sur la base de données pour exécuter lignes trouvées décompte.

C'est pratique lorsque vous n'avez pas besoin de pagination. Nous comprenons maintenant que nous pouvons imiter cette fonction avec cette requête:

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

Voici la requête SQL correspondante:

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

Comparez ce que nous avons maintenant avec la requête SQL précédente où SQL_CALC_FOUND_ROWS existe.

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

La demande sans SQL_CALC_FOUND_ROWS sera plus rapide.

Remarques sur query_posts

Conseil: en 2004, il n'y avait que global $wp_query. À partir de WordPress 2.1, la version $wp_the_query est arrivée. Conseil: $GLOBALS['wp_query'] et $GLOBALS['wp_the_query'] sont des objets distincts.

query_posts() est WP_Query wrapper. Il renvoie la référence à l'objet WP_Query principal et définit en même temps le global $wp_query.

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

En PHP4, tout, y compris les objets, était passé par valeur. query_posts était comme ceci:

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

Veuillez noter que dans un scénario typique avec une requête principale et une requête secondaire, nous avons ces trois variables:

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

Disons que chacun de ces trois prend 1M de mémoire. Total serait 3M de mémoire. Si nous utilisons query_posts, $GLOBALS['wp_query'] sera supprimé et créé à nouveau.

PHP5 + devrait être intelligent en vidant l'objet $GLOBALS['wp_query'], tout comme en PHP4 nous l'avons fait avec la unset($GLOBALS['wp_query']);

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

En conséquence, query_posts consomme 2 M de mémoire au total, alors que get_posts consomme 3 M de mémoire.

Remarque Dans query_posts, nous ne renvoyons pas l'objet réel, mais une référence à l'objet.

De php.net : Une référence PHP est un alias, qui permet à deux variables différentes d'écrire à la même valeur. A partir de PHP 5, une variable d'objet ne contient plus l'objet lui-même en tant que valeur. Il ne contient qu'un identifiant d'objet qui permet aux accesseurs d'objet de trouver l'objet réel. Lorsqu'un objet est envoyé par argument, renvoyé ou affecté à une autre variable, les différentes variables ne sont pas des alias: elles contiennent une copie de l'identifiant qui pointe vers le même objet.

Également en PHP5 +, l'opérateur assign (=) est intelligent. Il utilisera shallow copy et non une copie d'objet dur. Lorsque nous écrivons comme ceci $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];, seules les données seront copiées, pas l'objet entier, car elles partagent le même type d'objet.

Voici un exemple

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

Résultera:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
d6db1c6bfddac328442e91b6059210b5

Essayez de réinitialiser la requête:

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

Résultera:

f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef
f14153cab65abf1ea23224a1068563ef

Vous pouvez créer des problèmes même si vous utilisez 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'] ) ) );

Bien sûr, la solution serait d’utiliser à nouveau la fonction 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'] ) ) );

C'est pourquoi je pense que query_posts pourrait être meilleur du point de vue de la mémoire. Mais vous devriez toujours faire wp_reset_query astuce.

10
prosti

Je viens de créer un nouveau ticket trac, ticket # 36874 , pour proposer la désapprobation de query_posts(). Que cela soit accepté ou non reste une bonne question.

Le gros problème avec query_posts() est qu’il est toujours largement utilisé par les plugins et les thèmes, même s’il existe de très bons écrits sur les raisons pour lesquelles vous devriez JAMAIS JAMAIS l’utiliser. Je pense que le post le plus épique sur WPSE est le suivant:

déprécie! == suppression}, déprécier query_posts() n'arrêtera donc pas son utilisation par les développeurs de mauvaise qualité et les personnes en général qui ne connaissent pas WordPress et qui utilisent des tutoriels de mauvaise qualité comme directives. Pour preuve, combien de questions avons-nous encore ici où les gens utilisent caller_get_posts dans WP_Query? Il est obsolète depuis de nombreuses années maintenant.

Les fonctions et les arguments obsolètes peuvent toutefois être supprimés à tout moment si les développeurs le jugent bon, mais cela n'arrivera probablement jamais avec query_posts() car cela endommagerait des millions de sites. Donc oui, nous ne verrons probablement jamais la suppression totale de query_posts() - ce qui pourrait expliquer le fait qu'il ne sera probablement jamais obsolète.

Ceci est un point de départ cependant, mais il faut se rappeler, désapprouver quelque chose dans WordPress n'arrête pas son utilisation.

MISE À JOUR 19 mai 2016

Le ticket que j'ai généré est maintenant fermé et marqué en double par unâgé de 4 ansticket, qui a été fermé en tant que wontfix et a été rouvert mais reste ouvert et non résolu. .

Il semble que les principaux développeurs s’accrochent à ce vieux petit fidèle fidèle. Tout le monde est intéressé, voici le ticket dupliqué de 4 ans

10
Pieter Goosen

[un peu rant]

À ce stade, la philosophie de base est que rien n’est vraiment déprécié. La notification de dépréciation, bien que ce soit une bonne chose à faire, va simplement être ignorée si la fonction ne sera pas supprimée à un moment donné. Il y a beaucoup de gens qui ne développent pas avec WP_DEBUG et ne remarqueront pas l'avis s'il n'y a pas de casse réelle.

OTOH hand, cette fonction est comme une instruction goto. Personnellement, je n’ai jamais (pour une définition plus petite qu’attendue) utilisé gotomais je peux comprendre les arguments qui indiquent une situation dans laquelle ce n’est pas mauvais par défaut. Même chose avec query_posts, c'est un moyen simple de configurer tous les globaux nécessaires à la création d'une boucle simple, et peut être utile dans un contexte ajax ou rest-api. Je ne l'utiliserais jamais non plus dans ces contextes, mais je peux voir que là-bas, il s'agit davantage d'un problème de style de codage qu'une fonction maléfique en soi.

Pour aller un peu plus loin, le principal problème est que les globals doivent être définis. C’est le principal problème et non la seule fonction qui facilite leur réglage.

3
Mark Kaplun