¿Cuántas veces se ejecutará este código? (o, ¿cuán rica es la abuela?)

20

Ejemplo hipotético pero aplicabilidad en el mundo real (para alguien que está aprendiendo, como yo).

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, ahora abro mi sitio WP e inicio sesión. Atravieso algunas páginas en Admin. La acción 'init' dispara un total de 100 veces antes de que se agote la batería de mi computadora portátil.

Primeras preguntas: ¿Cuánto dinero le enviamos a la abuela? ¿Es $ 1, $ 2, $ 100 o $ 200 (o algo más?)

Si también pudieras explicar tu respuesta, sería genial.

Segunda pregunta: si queremos asegurarnos de que solo enviemos a la abuela $ 1, ¿cuál es la mejor manera de hacerlo? ¿Variable global (semáforo) que se establece como "verdadera" la primera vez que enviamos $ 1? ¿O hay alguna otra prueba para ver si ya ocurrió una acción y evitar que se dispare varias veces?

Tercera pregunta: ¿Esto es algo que preocupa a los desarrolladores de complementos? Me doy cuenta de que mi ejemplo es tonto, pero estaba pensando tanto en problemas de rendimiento como en otros efectos secundarios inesperados (por ejemplo, si la función se actualiza / inserta en la base de datos).

    
pregunta C C 24.11.2015 - 22:11

3 respuestas

21

Aquí hay algunos pensamientos aleatorios sobre esto:

Pregunta # 1

  

¿Cuánto dinero le enviamos a la abuela?

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

Aquí nos referimos a 100 x do_action( 'init' ) calls.

No importó que lo añadiéramos dos veces con:

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

porque las devoluciones de llamada y prioridades (por defecto, 10) son idénticas .

Podemos comprobar cómo add_action es solo un contenedor para add_filter que construye la matriz 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;
}

Sin embargo, si cambiamos la prioridad:

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

entonces le enviaríamos 2 x $ 1 por carga de página o $ 200 por 100 cargas de página.

Igual si las devoluciones de llamada son diferentes:

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

Pregunta # 2

  

Si queremos asegurarnos de que solo enviemos a la abuela $ 1

Si solo queremos enviarlo una vez por carga de página , entonces debería hacerlo:

add_action( 'init','send_money_to_grandma' );

porque el gancho init solo se dispara una vez. Podríamos tener otros ganchos que se activan muchas veces por carga de página.

Llamemos:

add_action( 'someaction ','send_money_to_grandma' );

pero, ¿qué sucede si someaction se dispara 10 veces por carga de página?

Podríamos ajustar la función send_money_to_grandma() con

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

o utilice una variable static como contador:

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

Si solo queremos ejecutarlo una vez (¡alguna vez!), entonces podríamos registrar una opción en la tabla wp_options a través de la API de opciones :

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

Si queremos enviarle dinero una vez al día, podemos usar la API transitoria

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

o incluso utilizar el cron wp.

Tenga en cuenta que puede tener llamadas ajax. también.

Hay formas de verificarlos, por ejemplo, con DOING_AJAX

También puede haber redirecciones, que podrían interrumpir el flujo.

Entonces podríamos querer restringir solo al backend, is_admin() o no: ! is_admin() .

Pregunta # 3

  

¿Es esto algo de lo que se preocupan los desarrolladores de complementos?

sí, esto es importante.

Si queremos hacer muy feliz a nuestra abuela, lo haríamos:

add_action( 'all','send_money_to_grandma' );

pero esto sería muy malo para el rendimiento ... y nuestra billetera ;-)

    
respondido por el birgire 24.11.2015 - 23:31
8

Esto es más un comentario para la muy buena la respuesta de Birgire que una respuesta completa, pero tener que escribir el código , los comentarios no encajan.

De la respuesta puede parecer que la única razón por la que la acción se agrega una vez en el código de ejemplo OP, incluso si se llama dos veces a add_action() , es el hecho de que se usa la misma prioridad. Eso no es cierto.

En el código de add_filter , una parte importante es la llamada a la función _wp_filter_build_unique_id() , que crea una ID único por devolución de llamada .

Si usa una variable simple, como una cadena que contiene un nombre de función, por ejemplo. "send_money_to_grandma" , entonces la identificación será igual a la cadena en sí misma, por lo que si la prioridad es la misma, al ser la misma identificación, la devolución de llamada se agrega una vez.

Sin embargo, las cosas no siempre son así de simples. Las devoluciones de llamada pueden ser cualquier cosa que sea callable en PHP:

  • nombres de funciones
  • métodos de clase estáticos
  • métodos de clase dinámicos
  • objetos invocables
  • cierres (funciones anónimas)

Los dos primeros están representados, respectivamente, por una cadena y un conjunto de 2 cadenas ( 'send_money_to_grandma' y array('MoneySender', 'send_to_grandma') ), por lo que la identificación es siempre la misma, y puede estar seguro de que la devolución de llamada se agrega una vez si la prioridad es el mismo.

En los otros 3 casos, el ID depende de las instancias de objeto (una función anónima es un objeto en PHP), por lo que la devolución de llamada se agrega una sola vez si el objeto es la misma instancia , y Es importante tener en cuenta que misma instancia y misma clase son dos cosas diferentes.

Toma este ejemplo:

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

¿Cuántos dólares estamos enviando por carga de página?

La respuesta es 2, porque la identificación que WordPress genera para $sender1 y $sender2 son diferentes.

Lo mismo sucede en este caso:

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

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

Arriba, utilicé la función sent_to_grandma dentro de los cierres, e incluso si el código es idéntico, los 2 cierres son 2 instancias diferentes del objeto \Closure , por lo que WP creará 2 ID diferentes, lo que hará que se agregue la acción dos veces, incluso si la prioridad es la misma.

    
respondido por el gmazzap 25.11.2015 - 13:42
4

No puedes agregar misma acción al mismo gancho de acción , con la misma prioridad .

Esto se hace para evitar que los complementos múltiples que se basan en la acción de los complementos de terceros ocurran más de una vez (piense en woocommerce y en todos los complementos de terceros, como las integraciones de pago de pasarela, etc.). Así que sin especificar la prioridad, la abuela sigue siendo 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');
}

Sin embargo, si agrega prioridad a esas acciones:

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

La abuela ahora muere con $ 4 en el bolsillo (1, 2, 3 y el valor predeterminado: 10).

    
respondido por el Andrei Gheorghiu 24.11.2015 - 23:27

Lea otras preguntas en las etiquetas