Сколько раз будет выполняться этот код? (или, насколько богата бабушка?)

Гипотетический пример, но реальная применимость в мире (для кого-то, кто учится, как и я).

Учитывая этот код:

<?php function send_money_to_grandma() { internetofThings("send grandma","$1"); } add_action('init','send_money_to_grandma'); add_action('init','send_money_to_grandma'); 

хорошо, теперь я поднимаю свой сайт WP и регистрируюсь. Я просматриваю несколько страниц в Admin. Действие «init» срабатывает в общей сложности 100 раз, прежде чем батарея ноутбука умрет.

Первые вопросы: Сколько денег мы отправили бабушке? Это $ 1, $ 2, $ 100 или $ 200 (или что-то еще?)

Если бы вы могли объяснить свой ответ, который был бы потрясающим.

Второй вопрос: если мы хотим удостовериться, что мы отправляем только бабушку 1 доллар, как лучше всего это сделать? Глобальная переменная (семафор), которая получает значение «true» при первом отправке $ 1? Или есть какой-то другой тест, чтобы увидеть, произошло ли уже действие и не позволяет ему стрелять несколько раз?

Третий вопрос: о чем беспокоиться разработчики плагинов? Я понимаю, что мой пример глупо, но я думал о проблемах с производительностью и других неожиданных побочных эффектах (например, если функция обновляет / вставляет в базу данных).

Solutions Collecting From Web of "Сколько раз будет выполняться этот код? (или, насколько богата бабушка?)"

Вот некоторые случайные мысли по этому поводу:

Вопрос 1

Сколько денег мы отправили бабушке?

Для 100-страничных загрузок мы отправили ей 100 x $ 1 = $ 100.

Здесь мы фактически имеем в виду 100 x do_action( 'init' ) .

Не важно, что мы добавили его дважды:

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

потому что обратные вызовы и приоритеты (по умолчанию 10) идентичны .

Мы можем проверить, как add_action является просто оболочкой для add_filter которая строит глобальный массив $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; } 

Однако, если мы изменили приоритет:

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

то мы отправим ей 2 x $ 1 за загрузку страницы или 200 долларов за 100 загрузок страниц.

То же самое, если обратные вызовы, где разные:

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

Вопрос 2

Если мы хотим удостовериться, что мы отправляем только бабушку $ 1

Если мы хотим отправить его только один раз на загрузку страницы , тогда это должно сделать это:

 add_action( 'init','send_money_to_grandma' ); 

потому что крючок init запускается только один раз. У нас могут быть другие крючки, которые срабатывают много раз на загрузку страницы.

Давай позвоним:

 add_action( 'someaction ','send_money_to_grandma' ); 

но что произойдет, если someaction то someaction срабатывает 10 раз на загрузку страницы?

Мы могли бы настроить send_money_to_grandma() с помощью

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

или использовать статическую переменную в качестве счетчика:

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

Если мы хотим запустить его только один раз (когда-либо!), Тогда мы можем зарегистрировать опцию в таблице wp_options помощью 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" ); } } 

Если мы хотим отправлять деньги каждый день, мы можем использовать 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 ); } } 

или даже использовать wp-cron.

Обратите внимание, что у вас могут быть вызовы ajax. также.

Есть способы проверить их, например, с DOING_AJAX

Также могут быть перенаправления, которые могут прерывать поток.

Тогда мы можем ограничить только бэкэнд, is_admin() или нет ! is_admin() ! is_admin() .

Вопрос № 3

Это то, о чем волнуются разработчики плагинов?

да, это важно.

Если мы хотим, чтобы наша бабушка была очень счастлива, мы бы сделали:

 add_action( 'all','send_money_to_grandma' ); 

но это было бы очень плохо для производительности … и нашего кошелька 😉

Это скорее комментарий к очень хорошему ответу Бирги, чем полный ответ, но для написания кода комментарии не подходят.

Из ответа может показаться, что единственная причина, по которой действие добавляется один раз в примерный код OP, даже если add_action() вызывается дважды, является тот факт, что используется тот же приоритет. Это не правда.

В коде add_filter важной частью является _wp_filter_build_unique_id() функции _wp_filter_build_unique_id() , который создает уникальный идентификатор для обратного вызова .

Если вы используете простую переменную, такую ​​как строка, содержащая имя функции, например "send_money_to_grandma" , тогда идентификатор будет равен самой строке, поэтому, если приоритет один и тот же, то же самое, что и id, обратный вызов один раз.

Однако все не так просто. Обратные callable могут быть любыми, callable в PHP:

  • имена функций
  • методы статического класса
  • методы динамического класса
  • invokable objects
  • закрытия (анонимные функции)

Первые два представлены соответственно строкой и массивом из двух строк ( 'send_money_to_grandma' и array('MoneySender', 'send_to_grandma') ), поэтому идентификатор всегда один и тот же, и вы можете быть уверены, что обратный вызов добавлен один раз, если приоритет тот же.

Во всех остальных 3 случаях идентификатор зависит от экземпляров объекта (анонимная функция является объектом в PHP), поэтому обратный вызов добавляется один раз, только если объект является одним и тем же экземпляром , и важно отметить тот же экземпляр и тот же класс это две разные вещи.

Возьмем следующий пример:

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

Сколько долларов мы отправляем на загрузку страницы?

Ответ равен 2, потому что id WordPress генерирует для $sender1 и $sender2 разные.

То же самое происходит в этом случае:

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

Выше я использовал функцию sent_to_grandma внутри закрытий, и даже если код идентичен, 2 закрытия – это 2 разных экземпляра объекта \Closure , поэтому WP создаст 2 разных идентификатора, что приведет к добавлению действия дважды, даже если приоритет тоже самое.

Вы не можете добавить одно и то же действие к одному и тому же ключу действия с тем же приоритетом .

Это делается для предотвращения повторения нескольких плагинов, связанных с действием сторонних плагинов (подумайте о woocommerce и всех его сторонних плагинах, таких как интеграция платежей со шлюзами и т. Д.). Таким образом, без указания приоритета бабушка остается бедной:

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

Однако, если вы придаете приоритет этим действиям:

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

Бабушка теперь умирает с 4 долларами в кармане (1, 2, 3 и по умолчанию: 10).