Quantas vezes esse código será executado? (ou quão rica é a avó?)

20

Exemplo hipotético, mas aplicabilidade no mundo real (para alguém que está aprendendo, como eu).

Dado este código:

<?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, agora eu trago o meu site WP e entro. Atravesso algumas páginas no Admin. A ação 'init' dispara um total de 100 vezes antes que a bateria do meu laptop acabe.

Primeiras perguntas: quanto dinheiro enviamos para a avó? É $ 1, $ 2, $ 100 ou $ 200 (ou algo mais?)

Se você também pudesse explicar sua resposta, isso seria incrível.

Segunda pergunta: se queremos ter certeza de enviar apenas US $ 1 para a avó, qual é a melhor maneira de fazer isso? Variável global (semáforo) que é definida como 'true' na primeira vez que enviamos $ 1? Ou existe algum outro teste para ver se uma ação já aconteceu e impedir que ela seja disparada várias vezes?

Terceira pergunta: Isso é algo com que os desenvolvedores de plugins se preocupam? Percebo que meu exemplo é bobo, mas eu estava pensando em problemas de desempenho e outros efeitos colaterais inesperados (por exemplo, se a função for atualizada / inserida no banco de dados).

CC
fonte
2
Devo admitir, esta é uma das melhores perguntas em um longo tempo ;-)
Pieter Goosen

Respostas:

21

Aqui estão alguns pensamentos aleatórios sobre isso:

Questão 1

Quanto dinheiro enviamos para a avó?

Para 100 carregamentos de página, enviamos 100 x $ 1 = $ 100.

Aqui, na verdade, queremos dizer 100 x do_action( 'init' )chamadas.

Não importava que o adicionássemos duas vezes com:

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

porque os retornos de chamada e as prioridades (padrão 10) são idênticos .

Podemos verificar como o add_actioné apenas um invólucro add_filterque constrói a $wp_filtermatriz global :

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

Se, no entanto, alterássemos a prioridade:

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

enviaríamos a ela 2 x US $ 1 por carregamento de página ou US $ 200 por 100 carregamentos de página.

O mesmo se os retornos de chamada forem diferentes:

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

Questão 2

Se quisermos ter certeza de enviar apenas à avó US $ 1

Se quisermos enviá-lo apenas uma vez por carregamento de página , faça o seguinte:

add_action( 'init','send_money_to_grandma' );

porque o initgancho é acionado apenas uma vez. Podemos ter outros ganchos que são acionados muitas vezes por carregamento de página.

Vamos ligar:

add_action( 'someaction ','send_money_to_grandma' );

mas o que acontece se someactiondisparar 10 vezes por carregamento de página?

Nós poderíamos ajustar a send_money_to_grandma()função com

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

ou use uma variável estática como um contador:

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

Se quisermos executá-lo apenas uma vez (sempre!), Poderemos registrar uma opção na wp_optionstabela através da API de opções :

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

Se quisermos enviar dinheiro a ela uma vez por dia, podemos usar a API transitória

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 até mesmo usar o wp-cron.

Observe que você pode ter chamadas ajax. também.

Existem maneiras de verificar isso, por exemplo, com DOING_AJAX

Também pode haver redirecionamentos, que podem interromper o fluxo.

Então nós pode querer restringir somente, para o servidor is_admin()ou não: ! is_admin().

Questão 3

Isso é algo com que os desenvolvedores de plugins se preocupam?

sim, isso é importante.

Se queremos deixar nossa avó muito feliz, faremos:

add_action( 'all','send_money_to_grandma' );

mas isso seria muito ruim para o desempenho ... e nossa carteira ;-)

Birgire
fonte
uau - obrigado por uma resposta tão completa; isso ajuda imensamente!
CC
11
Você é bem-vindo - Eu realmente gostei de como você formulou sua pergunta ;-) @CC
birgire
2
No final do dia, nós queremos manter nossas carteiras e feliz avó, por isso, é toda sobre encontrar a perfeita harmonia / equilíbrio ;-)
Pieter Goosen
resposta muito boa +1, mas vale a pena dizer que o tempo de adição de uma ação também depende do ID de retorno de chamada e, ao lidar com objetos, as coisas são um pouco mais complexas ... É difícil explicar melhor o conceito aqui. vou escrever uma resposta ...
gmazzap
graças @gmazzap - sim que seria ótimo, desde que eu não cobrir a terceira chave $ idx e _wp_filter_build_unique_id (), basta exibido-lo ;-)
birgire
8

Isso é mais um comentário para a resposta muito boa de Birgire do que uma resposta completa, mas, tendo que escrever código, os comentários não se encaixam.

A partir da resposta, pode parecer que a única razão pela qual a ação é adicionada uma vez no código de amostra OP, mesmo que add_action()seja chamada duas vezes, é o fato de a mesma prioridade ser usada. Isso não é verdade.

No código de add_filteruma parte importante está a _wp_filter_build_unique_id()chamada de função, que cria um ID exclusivo por retorno de chamada .

Se você usar uma variável simples, como uma string que contém um nome de função, por exemplo "send_money_to_grandma", o id será igual à string em si, portanto, se a prioridade for a mesma, sendo a id a mesma, o retorno de chamada será adicionado uma vez.

No entanto, as coisas nem sempre são simples assim. Os retornos de chamada podem ser qualquer coisa que esteja callableno PHP:

  • nomes de funções
  • métodos de classe estática
  • métodos de classe dinâmica
  • objetos invocáveis
  • fechamentos (funções anônimas)

As duas primeiras são representadas, respectivamente, por uma sequência e uma matriz de 2 sequências ( 'send_money_to_grandma'e array('MoneySender', 'send_to_grandma')), para que o ID seja sempre o mesmo e você pode ter certeza de que o retorno de chamada será adicionado uma vez se a prioridade for a mesma.

Nos outros 3 casos, o id depende das instâncias do objeto (uma função anônima é um objeto no PHP); portanto, o retorno de chamada é adicionado apenas uma vez se o objeto for a mesma instância e é importante observar que a mesma instância e a mesma classe são duas coisas diferentes.

Veja este exemplo:

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

Quantos dólares estamos enviando por carregamento de página?

A resposta é 2, porque o id WordPress gera $sender1e $sender2é diferente.

O mesmo acontece neste caso:

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

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

Acima, usei a função sent_to_grandmadentro de closures e, mesmo que o código seja idêntico, os 2 closures são 2 instâncias diferentes de \Closureobjeto, portanto o WP criará 2 ids diferentes, o que fará com que a ação seja adicionada duas vezes, mesmo se a prioridade for a mesma.

gmazzap
fonte
4

Você não pode adicionar a mesma ação ao mesmo gancho de ação , com a mesma prioridade .

Isso é feito para impedir que vários plug-ins que dependem da ação de plug-ins de terceiros aconteçam mais de uma vez (pense em woocommerce e todos os plug-ins de terceiros, como integrações de pagamento de gateway, etc.). Portanto, sem especificar a prioridade, a vovó permanece pobre:

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

No entanto, se você adicionar prioridade a essas ações:

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

A avó agora morre com US $ 4 no bolso (1, 2, 3 e o padrão: 10).

tao
fonte