Ile razy ten kod będzie działał? (lub jak bogata jest babcia?)

20

Hipotetyczny przykład, ale zastosowanie w prawdziwym świecie (dla kogoś uczącego się, takiego jak ja).

Biorąc pod uwagę ten kod:

<?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, teraz otwieram swoją stronę WP i loguję się. Przechodzę przez kilka stron w Administratorze. Akcja „init” strzela łącznie 100 razy, zanim wyczerpie się bateria mojego laptopa.

Pierwsze pytania: ile pieniędzy wysłaliśmy babci? Czy to 1 USD, 2 USD, 100 USD lub 200 USD (czy coś innego?)

Gdybyś mógł także wyjaśnić swoją odpowiedź, byłoby wspaniale.

Drugie pytania: jeśli chcemy mieć pewność, że wysyłamy babci tylko 1 USD, jaki jest najlepszy sposób? Zmienna globalna (semafor), która zostaje ustawiona na „prawda” przy pierwszym wysłaniu 1 $? A może jest jakiś inny test, aby sprawdzić, czy akcja już się wydarzyła i zapobiec wielokrotnemu uruchomieniu?

Trzecie pytanie: czy jest to coś, o co martwią się twórcy wtyczek? Zdaję sobie sprawę, że mój przykład jest głupi, ale myślałem o problemach z wydajnością i innych nieoczekiwanych skutkach ubocznych (np. Czy funkcja aktualizuje / wstawia do bazy danych).

CC
źródło
2
Trzeba przyznać, że to jedno z najlepszych pytań od dłuższego czasu ;-)
Pieter Goosen

Odpowiedzi:

21

Oto kilka przypadkowych przemyśleń na ten temat:

Pytanie 1

Ile pieniędzy wysłaliśmy babci?

Przy 100 ładowaniach stron wysłaliśmy jej 100 x 1 USD = 100 USD.

Tutaj mamy na myśli 100 x do_action( 'init' )połączenia.

Nie miało znaczenia, że ​​dodaliśmy go dwa razy z:

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

ponieważ wywołania zwrotne i priorytety (domyślnie 10) są identyczne .

Możemy sprawdzić, w jaki sposób add_actionjest to tylko opakowanie, add_filterktóre tworzy globalną $wp_filtertablicę:

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

Gdybyśmy jednak zmienili priorytet:

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

wtedy wysyłalibyśmy jej 2 x 1 USD za ładowanie strony lub 200 USD za 100 ładowań strony.

To samo, jeśli wywołania zwrotne są różne:

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

Pytanie 2

Jeśli chcemy mieć pewność, że wysyłamy babci tylko 1 USD

Jeśli chcemy wysłać go tylko raz przy ładowaniu strony , należy to zrobić:

add_action( 'init','send_money_to_grandma' );

ponieważ inithak wystrzeliwuje się tylko raz. Możemy mieć inne zaczepy, które uruchamiają się wielokrotnie podczas ładowania strony.

Zadzwońmy:

add_action( 'someaction ','send_money_to_grandma' );

ale co się stanie, jeśli someactionuruchomi się 10 razy na ładowanie strony?

Możemy dostosować send_money_to_grandma()funkcję za pomocą

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

lub użyj zmiennej statycznej jako licznika:

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

Jeśli chcemy uruchomić go tylko raz (kiedykolwiek!), Możemy zarejestrować opcję w wp_optionstabeli za pośrednictwem interfejsu API opcji :

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

Jeśli chcemy wysyłać jej pieniądze raz dziennie, możemy skorzystać z Transient API

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

lub nawet użyj wp-cron.

Pamiętaj, że możesz mieć wywołania ajax. także.

Istnieją sposoby, aby je sprawdzić, np. Za pomocą DOING_AJAX

Mogą również istnieć przekierowania, które mogą zakłócić przepływ.

Wtedy może chcemy ograniczać tylko do backend is_admin()lub nie: ! is_admin().

Pytanie 3

Czy jest to coś, o co martwią się twórcy wtyczek?

tak, to ważne.

Jeśli chcemy uszczęśliwić naszą babcię, zrobilibyśmy:

add_action( 'all','send_money_to_grandma' );

ale byłoby to bardzo złe dla wydajności ... i naszego portfela ;-)

birgire
źródło
wow - dziękuję za tak dokładną odpowiedź; to ogromnie pomaga!
CC
1
Nie ma za co - bardzo podobało mi się to, jak sformułowałeś swoje pytanie ;-) @CC
birgire
2
Na koniec dnia chcemy, aby nasze portfele i babcia były szczęśliwe, dlatego chodzi o znalezienie idealnej harmonii / równowagi ;-)
Pieter Goosen
bardzo ładna odpowiedź +1, ale warto powiedzieć, że to, jak razy może być dodawana akcja, zależy również od identyfikatora wywołania zwrotnego, a gdy mamy do czynienia z obiektami, sprawy są nieco bardziej skomplikowane ... Trudno tu lepiej wyjaśnić pojęcie, „ Napiszę odpowiedź ...
gmazzap
dzięki @gmazzap - tak, byłoby świetnie, ponieważ nie omawiałem trzeciego klucza $ idx i _wp_filter_build_unique_id (), po prostu go wyświetliłem ;-)
birgire
8

Jest to bardziej komentarz do bardzo dobrej odpowiedzi Birgire niż pełna odpowiedź, ale ponieważ trzeba napisać kod, komentarze nie pasują.

Z odpowiedzi może się wydawać, że jedynym powodem, dla którego akcja jest dodawana raz w przykładowym kodzie OP, nawet jeśli add_action()jest wywoływana dwukrotnie, jest fakt, że zastosowano ten sam priorytet. To nieprawda.

W kodzie add_filterważnej części znajduje się _wp_filter_build_unique_id()wywołanie funkcji, które tworzy unikalny identyfikator dla każdego wywołania zwrotnego .

Jeśli użyjesz prostej zmiennej, takiej jak ciąg znaków, który zawiera nazwę funkcji, np "send_money_to_grandma". Identyfikator będzie równy samemu ciągowi, więc jeśli priorytet jest taki sam, a także identyczny, wywołanie zwrotne jest dodawane raz.

Jednak rzeczy nie zawsze są takie proste. Callbacki mogą być dowolnymi callableelementami PHP:

  • nazwy funkcji
  • metody klasy statycznej
  • metody klas dynamicznych
  • obiekty możliwe do wywołania
  • zamknięcia (funkcje anonimowe)

Pierwsze dwa są reprezentowane odpowiednio przez ciąg i tablicę 2 ciągów ( 'send_money_to_grandma'i array('MoneySender', 'send_to_grandma')), więc identyfikator jest zawsze taki sam i możesz być pewien, że wywołanie zwrotne zostanie dodane raz, jeśli priorytet jest taki sam.

We wszystkich pozostałych 3 przypadkach identyfikator zależy od instancji obiektu (anonimowa funkcja jest obiektem w PHP), więc wywołanie zwrotne jest dodawane tylko raz, jeśli obiekt jest tą samą instancją , i należy zauważyć, że ta sama instancja i ta sama klasa są dwie różne rzeczy.

Weź ten przykład:

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

Ile dolarów wysyłamy na ładowanie strony?

Odpowiedź to 2, ponieważ identyfikator WordPress generuje $sender1i $sender2jest inny.

To samo dzieje się w tym przypadku:

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

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

Powyżej użyłem funkcji sent_to_grandmawewnątrz zamknięć i nawet jeśli kod jest identyczny, 2 zamknięcia są 2 różnymi instancjami \Closureobiektu, więc WP utworzy 2 różne identyfikatory, co spowoduje, że akcja zostanie dodana dwukrotnie, nawet jeśli priorytet jest taki sam.

gmazzap
źródło
4

Nie możesz dodać tej samej akcji do tego samego haka akcji , z takim samym priorytetem .

Ma to na celu zapobieżenie wielokrotnemu wtyczkom polegającym na działaniu wtyczek innych firm więcej niż raz (pomyśl woocommerce i wszystkie wtyczki innych firm, takie jak integracja płatności z bramą itp.). Bez określenia priorytetu babcia pozostaje biedna:

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

Jeśli jednak dodasz priorytet do tych działań:

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

Babcia umiera teraz z 4 $ w kieszeni (1, 2, 3 i domyślnie: 10).

tao
źródło