Скільки разів цей код буде працювати? (чи, наскільки багата бабуся?)


20

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

Враховуючи цей код:

<?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-сайт і входжу в систему. Я переходжу кілька сторінок в адміністраторі. Дія "init" спрацьовує загалом у 100 разів, перш ніж мій акумулятор ноутбука вмирає.

Перші запитання: Скільки грошей ми надіслали бабусі? Це $ 1, $ 2, 100 або 200 $ (чи щось інше?)

Якби ви могли також пояснити свою відповідь, це було б приголомшливо.

Друге питання: Якщо ми хочемо переконатися, що ми надсилаємо бабусі лише $ 1, який найкращий спосіб це зробити? Глобальна змінна (семафор), яка встановлюється "справжньою" вперше, коли ми надсилаємо $ 1? Або є якийсь інший тест, щоб перевірити, чи дія вже відбулося, і запобігти її запуску кілька разів?

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


2
Потрібно визнати, це одне з найкращих запитань за довгий час ;-)
Пітер Гусен

Відповіді:


21

Ось кілька випадкових думок з цього приводу:

Питання 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 х 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пожежі 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 параметрів :

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

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

або навіть використовувати wp-cron.

Зауважте, що у вас можуть виникнути дзвінки Ajax. так само.

Є способи перевірити їх, наприклад, за допомогою DOING_AJAX

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

Тоді ми могли б обмежити тільки на внутрішній інтерфейс is_admin()чи ні: ! is_admin().

Питання №3

Це хвилює розробників плагінів?

так це важливо.

Якщо ми хочемо зробити нашу бабусю дуже щасливою, ми б зробили:

add_action( 'all','send_money_to_grandma' );

але це було б дуже погано для продуктивності ... і наш гаманець ;-)


вау - дякую за таку ретельну відповідь; це надзвичайно допомагає!
КК

1
Запрошуємо вас - мені дуже сподобалось, як ви сформулювали своє питання ;-) @CC
birgire

2
Зрештою, ми хочемо підтримувати наші гаманці та бабусю щасливими, тому справа в тому, щоб знайти ідеальну гармонію / рівновагу ;-)
Пітер Гусен

дуже приємна відповідь +1, але варто сказати, що те, наскільки може бути додано дію, також залежить від ідентифікатора зворотного виклику, а при роботі з об'єктами все є дещо складнішим ... Важко тут пояснити поняття, я '
Напишу

дякую @gmazzap - так, це було б чудово, оскільки я не прикривав третій ключ $ idx та _wp_filter_build_unique_id (), просто показав його ;-)
birgire

8

Це скоріше коментар до дуже хорошої відповіді Біргіре, ніж повна відповідь, але необхідність писати код, коментарі не підходять.

З відповіді може здатися, що єдиною причиною того, що дії додаються один раз у зразок коду OP, навіть якщо add_action()він викликається двічі, є той факт, що використовується однаковий пріоритет. Що це не так.

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

Якщо ви використовуєте просту змінну, наприклад, рядок, яка містить ім'я функції, наприклад "send_money_to_grandma", ідентифікатор буде дорівнює самому рядку, тому, якщо пріоритет однаковий, ідентичний ідентичний той самий, зворотний виклик додається один раз.

Однак речі не завжди є простими. Відкликання викликів може бути будь-що, що є callableв PHP:

  • назви функцій
  • статичні методи класу
  • методи динамічного класу
  • викликаються об'єкти
  • закриття (анонімні функції)

Перші два представлені відповідно рядком і масивом з 2 рядків ( 'send_money_to_grandma'і array('MoneySender', 'send_to_grandma')), тож id завжди однаковий, і ви можете бути впевнені, що зворотний виклик додається один раз, якщо пріоритет однаковий.

У всіх інших 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, тому що ідентифікатор 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 різних ідентифікатора, що спричинить додавання дії двічі, навіть якщо пріоритет однаковий.


4

Ви не можете додати ту саму дію до одного і того ж гака дій з однаковим пріоритетом .

Це робиться для того, щоб кілька плагінів, які покладаються на дії сторонніх плагінів, траплялися неодноразово (подумайте, що 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).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.