web-dev-qa-db-fra.com

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

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é à 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 d’envoyer seulement 1 $ à grand-mère, 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 existe-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: Est-ce quelque chose qui inquiète les développeurs de plugins? Je réalise que mon exemple est ridicule, 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).

20
C C

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

Question 1

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

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

_ {Nous entendons ici les appels 100 x do_action( 'init' ).

Peu importait que nous l'ajoutions deux fois avec:

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

parce que les rappels _ et priorités (par défaut 10) sont identiques .

Nous pouvons vérifier comment le 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 toutefois nous changions la priorité:

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

nous lui envoyions ensuite 2 x 1 $ par page ou 200 $ pour 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 2

Si nous voulons nous assurer que nous envoyons seulement grand-mère 1 $

Si nous voulons seulement l'envoyer une fois par chargement de page, alors ceci devrait le faire:

add_action( 'init','send_money_to_grandma' );

parce que le hook init n'est déclenché qu'une 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 passe-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 l'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. ainsi que.

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

Il pourrait également y avoir des redirections qui pourraient interrompre le flux.

Ensuite, nous pourrions vouloir limiter au backend uniquement, is_admin() ou non: ! is_admin().

Question 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 porte-monnaie ;-)

21
birgire

Ceci est plus un commentaire sur le très bon la réponse de Birgire que une réponse complète, mais devoir écrire du code, les commentaires ne correspondent pas.

D'après la réponse, il peut sembler que la seule raison pour laquelle l'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 _wp_filter_build_unique_id() call function, qui crée un identifiant unique par callback .

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

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

  • noms de fonction
  • 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'identifiant est toujours le même et vous pouvez être sûr que le rappel est ajouté une fois si la priorité est la même.

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

Prenons 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 pour $sender1 et $sender2 sont différents.

La même chose arrive 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 à l'intérieur des fermetures, et même si le code est identique, les 2 fermetures sont 2 instances différentes d'objet \Closure, donc WP créera 2 identifiants différents, ce qui entraînera l'ajout de l'action deux fois. , même si la priorité est la même.

8
gmazzap

Vous ne pouvez pas ajouter le même action au même action, crochet , avec le même priorité .

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 $ dans sa poche (1, 2, 3 et le défaut: 10).

4
Andrei Gheorghiu