Combien de fois ce code sera-t-il exécuté? (ou quelle est la richesse de grand-mère?)

20

Exemple hypothétique mais applicabilité dans le monde réel (pour quelqu'un qui apprend comme moi).

Étant donné ce code:

<?php

function send_money_to_grandma() {
     internetofThings("send grandma","$1");
}

add_action('init','send_money_to_grandma');
add_action('init','send_money_to_grandma');

ok, maintenant je fais apparaître mon site WP et je me connecte. Je traverse quelques pages dans Admin. L'action "init" déclenche un total de 100 fois avant la mort de la batterie de mon ordinateur portable.

Premières questions: Combien d’argent avons-nous envoyés à grand-mère? Est-ce 1 $, 2 $, 100 $ ou 200 $ (ou autre chose?)

Si vous pouviez aussi expliquer votre réponse, ce serait génial.

Deuxième question: Si nous voulons nous assurer que nous n'envoyons que grand-mère à 1 $, quelle est la meilleure façon de le faire? Variable globale (sémaphore) à définir comme "vraie" la première fois que nous envoyons $ 1? Ou y a-t-il un autre test pour voir si une action s'est déjà produite et l'empêcher de se déclencher plusieurs fois?

Troisième question: les développeurs de plug-ins s’inquiètent-ils? Je réalise que mon exemple est idiot, mais je pensais à la fois aux problèmes de performances et à d’autres effets secondaires inattendus (par exemple, si la fonction est mise à jour / insérée dans la base de données).

    
posée C C 24.11.2015 - 22:11

3 réponses

21

Voici quelques réflexions aléatoires à ce sujet:

Question n ° 1

  

Combien d’argent avons-nous envoyé à grand-mère?

Pour les chargements de 100 pages, nous lui avons envoyé 100 x 1 $ = 100 $.

Nous entendons en réalité 100 x do_action( 'init' ) appels.

Peu importe que nous l'ayons ajouté deux fois avec:

add_action( 'init','send_money_to_grandma' );
add_action( 'init','send_money_to_grandma' );

car les rappels et les priorités (valeur par défaut 10) sont identiques .

Nous pouvons vérifier comment add_action est juste un wrapper pour add_filter qui construit le tableau global $wp_filter :

function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
        global $wp_filter, $merged_filters;

        $idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
        $wp_filter[$tag][$priority][$idx] = array(
            'function'      => $function_to_add, 
            'accepted_args' => $accepted_args
        );
        unset( $merged_filters[ $tag ] );
        return true;
}

Si nous avions toutefois changé la priorité:

add_action( 'init','send_money_to_grandma', 9 );
add_action( 'init','send_money_to_grandma', 10 );

alors nous lui enverrions 2 x 1 $ par chargement de page ou 200 $ pour un chargement de 100 pages.

Idem si les rappels étaient différents:

add_action( 'init','send_money_to_grandma_1_dollar' );
add_action( 'init','send_money_to_grandma_also_1_dollar' );

Question n ° 2

  

Si nous voulons nous assurer que nous n'envoyons que grand-mère à 1 $

Si nous voulons seulement l'envoyer une fois par chargement de page , vous devriez le faire:

add_action( 'init','send_money_to_grandma' );

car le crochet init n'est déclenché qu'une seule fois. Nous pourrions avoir d’autres crochets qui se déclenchent plusieurs fois par page chargée.

Appelons:

add_action( 'someaction ','send_money_to_grandma' );

mais que se passera-t-il si someaction se déclenche 10 fois par page chargée?

Nous pourrions ajuster la fonction send_money_to_grandma() avec

function send_money_to_grandma() 
{
    if( ! did_action( 'someaction' ) )
        internetofThings("send grandma","$1");
}

ou utilisez une variable statique comme compteur:

function send_money_to_grandma() 
{
    static $counter = 0;
    if( 0 === $counter++ )
        internetofThings("send grandma","$1");
}

Si nous ne voulons l'exécuter qu'une seule fois (jamais!), nous pourrions alors enregistrer une option dans la table wp_options via la API Options. :

function send_money_to_grandma() 
{
    if( 'no' === get_option( 'sent_grandma_money', 'no' ) )
    {
        update_option( 'sent_grandma_money', 'yes' );
        internetofThings( "send grandma","$1" );
    }
}

Si nous voulons lui envoyer de l'argent une fois par jour, nous pouvons utiliser l’ API Transient

.
function send_money_to_grandma() 
{
    if ( false === get_transient( 'sent_grandma_money' ) ) )
    {
        internetofThings( "send grandma","$1" );
        set_transient( 'sent_grandma_money', 'yes', DAY_IN_SECONDS );
    }
}

ou même utiliser le wp-cron.

Notez que vous pourriez avoir des appels ajax. aussi bien.

Il existe des moyens de vérifier ces informations, par exemple avec DOING_AJAX

Il peut également y avoir des redirections pouvant interrompre le flux.

Ensuite, nous voudrons peut-être restreindre le backend uniquement, is_admin() ou non: ! is_admin() .

Question n ° 3

  

Est-ce quelque chose qui inquiète les développeurs de plugins?

oui c'est important.

Si nous voulons rendre notre grand-mère très heureuse, nous ferions:

add_action( 'all','send_money_to_grandma' );

mais ce serait très mauvais pour la performance ... et notre portefeuille; -)

    
réponse donnée birgire 24.11.2015 - 23:31
8

Il s’agit plus d’un commentaire sur le très bon réponse de Birgire d’une réponse complète, à condition d’écrire du code. , les commentaires ne correspondent pas.

D'après la réponse, il peut sembler que la seule raison pour laquelle une action est ajoutée une fois dans le code exemple OP, même si add_action() est appelé deux fois, est le fait que la même priorité est utilisée. Ce n'est pas vrai.

Dans le code de add_filter , une partie importante est l’appel de fonction _wp_filter_build_unique_id() , qui crée un identifiant unique par rappel .

Si vous utilisez une variable simple, telle qu'une chaîne contenant un nom de fonction, par exemple. "send_money_to_grandma" , alors l'id sera égal à la chaîne elle-même. Ainsi, si la priorité est identique, id étant identique, le rappel est ajouté une fois.

Cependant, les choses ne sont pas toujours simples comme ça. Les callbacks peuvent être n'importe quoi qui est callable en PHP:

  • noms de fonctions
  • méthodes de classe statique
  • méthodes de classe dynamiques
  • objets invocables
  • fermetures (fonctions anonymes)

Les deux premiers sont représentés respectivement par une chaîne et un tableau de 2 chaînes ( 'send_money_to_grandma' et array('MoneySender', 'send_to_grandma') ), de sorte que l'id est toujours identique et vous pouvez être sûr que le rappel est ajouté une fois si la priorité est le même.

Dans tous les 3 autres cas, l'id dépend des instances d'objet (une fonction anonyme est un objet en PHP), de sorte que le rappel n'est ajouté qu'une fois si l'objet est la même instance , et il Il est important de noter que même instance et même classe sont deux choses différentes.

Prenez cet exemple:

class MoneySender {

   public function sent_to_grandma( $amount = 1 ) {
     // things happen here
   }

}

$sender1 = new MoneySender();
$sender2 = new MoneySender();

add_action( 'init', array( $sender1, 'sent_to_grandma' ) );
add_action( 'init', array( $sender1, 'sent_to_grandma' ) );
add_action( 'init', array( $sender2, 'sent_to_grandma' ) );

Combien de dollars envoyons-nous par chargement de page?

La réponse est 2, car les identifiants générés par WordPress sont différents pour $sender1 et $sender2 .

La même chose se produit dans ce cas:

add_action( 'init', function() {
   sent_to_grandma();
} );

add_action( 'init', function() {
   sent_to_grandma();
} );

Ci-dessus, j'ai utilisé la fonction sent_to_grandma dans les fermetures. Même si le code est identique, les 2 fermetures sont deux instances différentes de \Closure object. WP crée donc deux identifiants différents, ce qui entraîne l'ajout de l'action. deux fois, même si la priorité est la même.

    
réponse donnée gmazzap 25.11.2015 - 13:42
4

Vous ne pouvez pas ajouter la même action au crochet d'action même , avec la priorité identique .

Ceci est fait pour éviter que plusieurs plugins s'appuyant sur l'action d'un plugin tiers ne se produisent plus d'une fois (pensez à woocommerce et à tous ses plugins tiers, comme les intégrations de paiement par passerelle, etc.). Donc, sans préciser la priorité, grand-mère reste pauvre:

add_action('init','print_a_buck');
add_action('init','print_a_buck');

function print_a_buck() {
    echo '$1</br>';
}
add_action('wp', 'die_hard');
function die_hard() {
    die('hard');
}

Toutefois, si vous ajoutez une priorité à ces actions:

add_action('init','print_a_buck', 1);
add_action('init','print_a_buck', 2);
add_action('init','print_a_buck', 3);

Grand-mère meurt maintenant avec 4 dollars dans sa poche (1, 2, 3 et 10 par défaut).

    
réponse donnée Andrei Gheorghiu 24.11.2015 - 23:27

Lire d'autres questions sur les étiquettes