Wie oft wird dieser Code ausgeführt? (Oder wie reich ist Oma?)

20

Hypothetisches Beispiel, aber reale Anwendbarkeit (für jemanden, der lernt, wie ich).

Gegeben diesen 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, jetzt rufe ich meine WP-Site auf und melde mich an. Ich überquere einige Seiten in Admin. Die Aktion 'init' wird insgesamt 100 mal ausgelöst, bevor der Akku meines Laptops leer ist.

Erste Fragen: Wie viel Geld haben wir an Oma geschickt? Ist es 1 $, 2 $, 100 $ oder 200 $ (oder etwas anderes?)

Wenn Sie auch Ihre Antwort erklären könnten, wäre das fantastisch.

Zweite Frage: Wenn wir sicherstellen möchten, dass wir nur Oma $ 1 schicken, wie geht das am besten? Globale Variable (Semaphor), die beim ersten Senden von $ 1 auf 'true' gesetzt wird? Oder gibt es einen anderen Test, um festzustellen, ob eine Aktion bereits stattgefunden hat und um zu verhindern, dass sie mehrmals ausgelöst wird?

Dritte Frage: Ist das etwas, worüber sich Plugin-Entwickler Sorgen machen? Mir ist klar, dass mein Beispiel albern ist, aber ich habe sowohl an Leistungsprobleme als auch an andere unerwartete Nebenwirkungen gedacht (z. B. wenn die Funktion in der Datenbank aktualisiert / eingefügt wird).

CC
quelle
2
Zugegeben, dies ist eine der besten Fragen seit langem ;-)
Pieter Goosen

Antworten:

21

Hier sind einige zufällige Gedanken dazu:

Frage 1

Wie viel Geld haben wir an Oma geschickt?

Für 100 Seiten haben wir ihr 100 x $ 1 = $ 100 geschickt.

Hier meinen wir eigentlich 100 x do_action( 'init' )Anrufe.

Es war egal, dass wir es zweimal hinzugefügt haben mit:

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

weil die Rückrufe und Prioritäten (Standard 10) identisch sind .

Wir können überprüfen, wie das add_actionnur ein Wrapper für add_filterdas globale $wp_filterArray ist:

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

Wenn wir jedoch die Priorität geändert haben:

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

Dann würden wir ihr 2 x 1 USD pro Seite oder 200 USD für 100 Seiten senden.

Dasselbe gilt, wenn die Rückrufe unterschiedlich waren:

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

Frage 2

Wenn wir sicherstellen wollen, dass wir nur Oma $ 1 schicken

Wenn wir es nur einmal pro Laden der Seite senden möchten, sollte dies folgendermaßen aussehen:

add_action( 'init','send_money_to_grandma' );

weil der inithaken nur einmal abgefeuert wird. Möglicherweise gibt es andere Hooks, die pro Seitenladevorgang mehrmals ausgelöst werden.

Lass uns anrufen:

add_action( 'someaction ','send_money_to_grandma' );

aber was passiert, wenn someaction10 mal pro Seite gezündet wird?

Wir könnten die send_money_to_grandma()Funktion mit anpassen

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

oder verwenden Sie eine statische Variable als Zähler:

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

Wenn wir es nur einmal (je!) Ausführen möchten, registrieren wir möglicherweise eine Option in der wp_optionsTabelle über die Options-API :

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

Wenn wir ihr einmal täglich Geld senden möchten, können wir die Transient-API verwenden

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

oder auch den wp-cron benutzen.

Beachten Sie, dass Sie möglicherweise Ajax-Aufrufe haben. auch.

Es gibt Möglichkeiten, diese zu überprüfen, z. B. mit DOING_AJAX

Es kann auch Umleitungen geben, die den Fluss unterbrechen können.

Dann könnten wir nur an das Backend zu beschränken, is_admin()oder nicht: ! is_admin().

Frage 3

Ist das etwas, worüber sich Plugin-Entwickler Sorgen machen?

ja das ist wichtig

Wenn wir unsere Oma sehr glücklich machen wollen, machen wir:

add_action( 'all','send_money_to_grandma' );

aber das wäre sehr schlecht für die leistung ... und unsere brieftasche ;-)

birgire
quelle
wow - danke für eine so gründliche Antwort; das hilft immens!
CC
1
Gern
geschehen
2
Letztendlich wollen wir unsere Brieftaschen und Oma bei Laune halten, also geht es darum, die perfekte Harmonie / Balance zu finden ;-)
Pieter Goosen
sehr nette Antwort +1, aber es lohnt sich zu erwähnen, dass es auch von der Callback-ID abhängt, wie oft eine Aktion hinzugefügt wird, und wenn es um Objekte geht, sind die Dinge etwas komplexer. Schreibe eine Antwort ...
gmazzap
danke @gmazzap - ja das wäre toll, da ich den dritten Schlüssel $ idx und _wp_filter_build_unique_id () nicht behandelt habe, sondern nur angezeigt habe ;-)
birgire
8

Dies ist eher ein Kommentar zur Antwort der sehr guten Birgire als eine vollständige Antwort, aber da sie Code schreiben muss, passen Kommentare nicht.

Aus der Antwort geht hervor, dass der einzige Grund, warum im OP-Beispielcode eine Aktion einmal hinzugefügt wird, selbst wenn add_action()sie zweimal aufgerufen wird, die Tatsache ist, dass dieselbe Priorität verwendet wird. Das ist nicht wahr.

Im Code add_filtereines wichtigen Teils befindet sich ein _wp_filter_build_unique_id()Funktionsaufruf, der pro Rückruf eine eindeutige ID erzeugt .

Wenn Sie eine einfache Variable verwenden, z. B. eine Zeichenfolge, die einen Funktionsnamen enthält, entspricht "send_money_to_grandma"die ID der Zeichenfolge selbst. Wenn also die Priorität identisch ist und die ID identisch ist, wird der Rückruf einmal hinzugefügt.

Es ist jedoch nicht immer so einfach. Callbacks können alles sein, was callablein PHP enthalten ist:

  • Funktionsnamen
  • statische Klassenmethoden
  • dynamische Klassenmethoden
  • aufrufbare Objekte
  • Verschlüsse (anonyme Funktionen)

Die ersten beiden werden jeweils durch eine Zeichenfolge und ein Array mit 2 Zeichenfolgen ( 'send_money_to_grandma'und array('MoneySender', 'send_to_grandma')) dargestellt, sodass die ID immer dieselbe ist und Sie sicher sein können, dass der Rückruf einmal hinzugefügt wird, wenn die Priorität dieselbe ist.

In allen anderen 3 Fällen hängt die ID von Objektinstanzen ab (eine anonyme Funktion ist ein Objekt in PHP), sodass der Rückruf nur einmal hinzugefügt wird, wenn das Objekt dieselbe Instanz ist , und es wichtig ist, dieselbe Instanz und dieselbe Klasse zu notieren sind zwei verschiedene Dinge.

Nehmen Sie dieses Beispiel:

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

Wie viele Dollar senden wir pro Seite laden?

Antwort 2, weil die ID Wordpress erzeugt für $sender1und $sender2sind unterschiedlich.

Das gleiche passiert in diesem Fall:

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

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

Oben habe ich die Funktion sent_to_grandmainnerhalb von Closures verwendet, und selbst wenn der Code identisch ist, sind die beiden Closures zwei unterschiedliche \ClosureObjektinstanzen. WP erstellt also zwei unterschiedliche IDs, wodurch die Aktion zweimal hinzugefügt wird, auch wenn die Priorität gleich ist.

gmazzap
quelle
4

Sie können nicht das Hinzufügen gleiche Aktion auf die gleiche Aktion Haken , mit der gleichen Priorität .

Dies wird getan, um zu verhindern, dass mehrere Plugins, die sich auf die Aktion eines Drittanbieter-Plugins stützen, mehrmals ausgeführt werden (denken Sie an woocommerce und alle Plugins von Drittanbietern, wie Gateway-Zahlungsintegrationen usw.). Ohne Angabe der Priorität bleibt Oma also arm:

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

Wenn Sie diesen Aktionen jedoch Priorität hinzufügen, gilt Folgendes:

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

Oma stirbt jetzt mit 4 $ in der Tasche (1, 2, 3 und der Standard: 10).

tao
quelle