web-dev-qa-db-fra.com

Comment identifier uniquement les requêtes?

Aujourd'hui, je réfléchissais à la façon de différencier les requêtes, principalement pour créer un identifiant unique. Dans le processus, j'ai joué un peu, voir le code ci-dessous.

L'idée principale est de le faire en fonction de la propriété $query ou $query_vars de WP_Query. Il existe en fait déjà une propriété query_vars_hash, mais elle est créée assez tôt avec un sous-ensemble de vars et ne permet pas de distinguer les requêtes qui se produisent sur la même requête.

Ce qui suit est ce que j'ai jusqu'à présent:

add_action( 'pre_get_posts', 'unique_query' );
/**
 * unique_query
 *
 * Create unique identifier
 *
 */
function unique_query( $query ) {
    global $wp_query;
    $query_hashes = array();

    if ( is_admin() ) return;

    /**
     * Skip additional cases
     */
    /**
    if ( ! isset( $query->query['post_type'] )
        || empty( $query->query['post_type'] )
        || $query->query['post_type'] == 'nav_menu_item'
        || $query->is_main_query()
    ) {
        return;
    }
    /**/

    /**
     * Debug stuff
     */
    /**
    echo '<pre>';
        print_r( $query );
        print_r( $wp_query );
        print_r( $query->query );
        print_r( $wp_query->query );
        print_r( $query->query_vars );
        print_r( $wp_query->query_vars );
    echo '</pre>';
    /**/

    /**
     * Setup query hashes array
     */
    $query_hashes[ '$wp_query->query_vars_hash' ]  = $wp_query->query_vars_hash;
    $query_hashes[ 'uhash_$wp_query->query_vars' ] = md5( serialize( $wp_query->query_vars ) );
    $query_hashes[ '$query->query_vars_hash' ]     = $query->query_vars_hash;
    $query_hashes[ 'uhash_$query->query_vars' ]    = md5( serialize( $query->query_vars ) );
    $query_hashes[ 'uhash_$query->query' ]         = md5( serialize( $query->query ) );
    $query_hashes[ 'uhash_$wp_query->query' ]      = md5( serialize( $wp_query->query ) );

    /**
     * Print query hashes array
     */
    echo '<pre>';
        print_r( $query_hashes );
    echo '</pre>';

}


Ce qui produit la sortie exemplaire suivante:

/* Main query */
[$wp_query->query_vars_hash]  => ffb3c63f9b858ab4882d8e9ed20fce67
[uhash_$wp_query->query_vars] => ffb3c63f9b858ab4882d8e9ed20fce67
[$query->query_vars_hash]     => ffb3c63f9b858ab4882d8e9ed20fce67
[uhash_$query->query_vars]    => ffb3c63f9b858ab4882d8e9ed20fce67
[uhash_$query->query]         => 40cd750bba9870f18aada2478b24840a
[uhash_$wp_query->query]      => 40cd750bba9870f18aada2478b24840a

/* Query of wp_nav_menu */
[$wp_query->query_vars_hash]  => ffb3c63f9b858ab4882d8e9ed20fce67
[uhash_$wp_query->query_vars] => 59e1dad0ba690eed5e758fbf15cda702
[$query->query_vars_hash]     => c5a049c558df9cf4cfd14dad179a97e8
[uhash_$query->query_vars]    => c5a049c558df9cf4cfd14dad179a97e8
[uhash_$query->query]         => d4226d11be5a53e8b44d5b85db43e8b0
[uhash_$wp_query->query]      => 40cd750bba9870f18aada2478b24840a

/* Front/home query */
[$wp_query->query_vars_hash]  => ffb3c63f9b858ab4882d8e9ed20fce67
[uhash_$wp_query->query_vars] => 59e1dad0ba690eed5e758fbf15cda702
[$query->query_vars_hash]     => 712b303f79eef3c8b5928d4ed8acfe2f
[uhash_$query->query_vars]    => 712b303f79eef3c8b5928d4ed8acfe2f
[uhash_$query->query]         => b7ac5bdfe084209f0142d7ab3aa0683e
[uhash_$wp_query->query]      => 40cd750bba9870f18aada2478b24840a


Comme vous pouvez le voir, les seuls candidats à une détermination distincte sont $query->query_vars_hash/uhash_$query->query_vars ou uhash_$query->query. Bien sûr, dans cet exemple, le premier et le second sont les mêmes, mais à l'avenir, il pourrait être nécessaire de faire des choses qui ne sont pas couvertes par la propriété query_vars_hash existante. De plus, vous devez le faire vous-même pour en être sûr.

La question serait donc tout d’abord, s’agit-il d’une approche raisonnable pour identifier les requêtes de manière unique? Ou est-ce que quelque chose me manque, en particulier, y en a-t-il peut-être un meilleur, alors, ensuite, si quelqu'un a son mot à dire, cela m'intéresse.


Modifier:

@ G.M. est à peu près correct pour tout ce qui était impliqué dans le problème soulevé. En fait, j'avais en tête d'ajouter cette information, je n'avais tout simplement pas assez de temps à ce moment-là. Maintenant, ce serait simplement redondant, mais je voulais confirmer le fait.

5
Nicolai

tl; dr : construire des identifiants uniques abordables pour les requêtes est possible, mais est plutôt inutile.


Si vous recherchez des identifiants uniques pour la requête objets, vous pouvez utiliser spl_object_hash , mais IMHO est plutôt inutile: il est non prévisible, et vous obtenez un identifiant différent même si la requête est différente. sont identiques, mais définies sur 2 objets de requête différents. On peut obtenir une statistique comme: "Au cours de cette requête, XX objets WP_Query différents ont été instanciés."

Si vous avez attiré votre attention sur la requête variables, c'est probablement parce que vous recherchez un identifiant pour identifier la requête résultats, c'est-à-dire que vous pouvez obtenir le même identifiant à partir de 2 objets de requête différents. être le même pour les deux requêtes.

Dans ce cas, j'ai une mauvaise surprise pour vous: cela ne se produira jamais, car quelle que soit la technique utilisée, si vous construisez votre ID de requête à partir de vars de requête, vous ne prenez pas en compte 'posts_*' filters ('posts_where', ' posts_join 'et ainsi de suite, ils sont 19 IIRC).

Preuve de concept:

$query1 = new WP_Query( 'post_type=post' );

add_filter( 'posts_where', function() { return ' AND 1 = 0'; } );

$query2 = new WP_Query( 'post_type=post' );

$query1 et $query2 sont identiques, donc un rappel virtuel renvoyant un identifiant unique basé sur des vars de requête retournera un identifiant identique pour les 2 requêtes, mais la première requête renvoie tous les articles publiés, la seconde requête ne renvoie rien.

C'est la raison pour laquelle les vars de requête ne constituent pas un bon point de départ pour créer un identifiant de requête unique.

Si ce qui compte, ce sont les résultats de requête, vous devez vous concentrer sur requête SQL: lorsque requête est identique, les résultats sont identiques, en outre, requête est une chaîne, donc facilement lavable.

Un bon crochet candidat pour le scope devrait en être un lorsque la requête est complètement construite, mais pas encore exécutée, 'split_the_query' hook devrait bien se passer.

// prepare our ids storage
add_action( 'init', function() {
   global $query_ids;
   $query_ids = array();
});

// use the filter to build and store the id
add_filter( 'split_the_query', function( $split_the_query, $query ) {
  $hash = md5( $query->request );
  $hash .= $split_the_query ? '_split' : '';
  global $query_ids;
  if ( ! in_array( $hash, $query_ids, TRUE ) ) {
    $query_ids[] = $hash; // store hash if not already stored
  }
  return $split_the_query; // return $split_the_query as is
}, PHP_INT_MAX, 2 );

Nous avons maintenant un moyen de créer des identifiants de requête uniques: lorsque nous pouvons obtenir le même identifiant à partir de 2 requêtes, les résultats des 2 requêtes sont identiques.

Mais que pouvons-nous faire avec ça? Peut-être une statistique comme:

add_action('shutdown', function() {
   global $query_ids;
   echo count( $query_ids ) . " different WP_Query requests were performed.";
});

Je ne trouve pas quelle est l'utilité d'une statistique de ce type, mais peut-être que quelqu'un le peut.

Je sais que vous envisagez d'utiliser l'identifiant de requête pour cache la requête, et ne déclenchez pas de requête de base de données lorsque id est identique, et c'est une bonne idée, mais ... vous ne pouvez pas .

Le problème est que lorsque WordPress a créé la demande, elle l'exécute immédiatement, sans aucune possibilité de la court-circuiter!

Ou mieux, une fois que WordPress utilise $wpdb->get_results pour exécuter la requête et que $wpdb est une variable globale, une chance est de remplacer cet objet par une classe wpdb modifiée pouvant être mise en cache, c’est une bonne idée, mais dans ce cas, inutile de construire un identifiant unique pour les requêtes une fois que tout le cache se produit sur $wpdb en fonction des requêtes SQL qui y ont été exécutées.

En gros, le problème est que WordPress souffre de 2 gros problèmes concernant la logique WP_Query (entre autres sur le code désordonné WP_Query):

  1. processus de construction la requête et processus de en cours d'exécution la requête ne sont pas séparables
  2. il n'y a aucun moyen de court-circuiter la requête: une fois que la méthode WP_Query::get_posts() a été appelée, il n'y a aucun moyen d'empêcher l'exécution de la requête SQL

En attendant que ces 2 problèmes restent là, IMHO créer un identifiant unique pour les requêtes est quelque chose qui ne peut être utilisé que pour les statistiques excentriques ...

4
gmazzap