Berapa kali kode ini dijalankan? (Atau, seberapa kayakah nenek?)

20

Contoh hipotetis tetapi penerapan dunia nyata (untuk seseorang yang belajar, seperti saya).

Diberikan kode ini:

<?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, sekarang saya membuka situs WP saya dan masuk. Saya melintasi beberapa halaman di Admin. Aksi 'init' menyala total 100 kali sebelum baterai laptop saya mati.

Pertanyaan pertama: Berapa banyak uang yang kami kirim ke nenek? Apakah $ 1, $ 2, $ 100 atau $ 200 (atau yang lainnya?)

Jika Anda juga bisa menjelaskan jawaban Anda, itu akan luar biasa.

Pertanyaan kedua: Jika kita ingin memastikan bahwa kita hanya mengirim nenek $ 1, apa cara terbaik untuk melakukannya? Variabel global (semaphore) yang disetel 'benar' saat pertama kali kami mengirim $ 1? Atau adakah tes lain untuk melihat apakah suatu tindakan sudah terjadi dan mencegahnya menembak berulang kali?

Pertanyaan ketiga: Apakah ini sesuatu yang dikhawatirkan pengembang plugin? Saya menyadari bahwa contoh saya konyol tetapi saya memikirkan masalah kinerja dan efek samping lain yang tidak terduga (misalnya jika fungsi memperbarui / memasukkan ke basis data).

CC
sumber
2
Harus mengakui, ini adalah salah satu pertanyaan terbaik dalam waktu yang lama ;-)
Pieter Goosen

Jawaban:

21

Berikut adalah beberapa pemikiran acak tentang ini:

Pertanyaan 1

Berapa banyak uang yang kami kirim ke nenek?

Untuk 100 halaman memuat, kami mengiriminya 100 x $ 1 = $ 100.

Di sini yang kita maksud sebenarnya adalah 100 x do_action( 'init' )panggilan.

Tidak masalah bahwa kami menambahkannya dua kali dengan:

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

karena callback dan prioritas (default 10) identik .

Kita dapat memeriksa bagaimana add_actionini hanyalah pembungkus untuk add_filteryang membangun $wp_filterarray 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;
}

Namun, jika kami mengubah prioritas:

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

maka kami akan mengirimnya 2 x $ 1 per halaman memuat atau $ 200 untuk 100 halaman memuat.

Sama jika panggilan baliknya berbeda:

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

Pertanyaan # 2

Jika kami ingin memastikan kami hanya mengirim nenek $ 1

Jika kami hanya ingin mengirimnya sekali per pemuatan halaman , maka ini harus dilakukan:

add_action( 'init','send_money_to_grandma' );

karena initkailnya hanya sekali ditembakkan. Kami mungkin memiliki kait lain yang menyala berkali-kali per pemuatan halaman.

Mari kita panggil:

add_action( 'someaction ','send_money_to_grandma' );

tetapi apa yang terjadi jika someactionkebakaran 10 kali per memuat halaman?

Kita dapat menyesuaikan send_money_to_grandma()fungsi dengan

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

atau gunakan variabel statis sebagai penghitung:

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

Jika kami hanya ingin menjalankannya sekali saja!, Maka kami mungkin mendaftarkan opsi di wp_optionstabel melalui API Opsi :

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

Jika kami ingin mengirim uang kepadanya sekali setiap hari, maka kami dapat menggunakan API Sementara

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

atau bahkan menggunakan wp-cron.

Perhatikan bahwa Anda mungkin memiliki panggilan ajax. demikian juga.

Ada beberapa cara untuk memeriksanya, misalnya dengan DOING_AJAX

Mungkin juga ada pengalihan, yang dapat mengganggu aliran.

Maka kita mungkin ingin membatasi untuk backend saja, is_admin()atau tidak: ! is_admin().

Pertanyaan # 3

Apakah ini sesuatu yang dikhawatirkan pengembang plugin?

ya ini penting.

Jika kita ingin membuat nenek kita sangat bahagia, kita akan melakukannya:

add_action( 'all','send_money_to_grandma' );

tapi ini akan sangat buruk untuk kinerja ... dan dompet kami ;-)

birgire
sumber
wow - terima kasih atas tanggapan yang saksama; ini sangat membantu!
CC
1
Sama
sama
2
Pada akhir hari, kami ingin menjaga dompet dan nenek kami bahagia, jadi ini semua tentang menemukan harmoni / keseimbangan sempurna ;-)
Pieter Goosen
jawaban yang sangat bagus +1, tetapi layak untuk mengatakan bahwa berapa kali tindakan ditambahkan juga tergantung pada panggilan balik id, dan ketika berhadapan dengan benda-benda hal-hal sedikit lebih kompleks ... Sulit untuk lebih menjelaskan konsep di sini, saya ' Saya akan menulis jawaban ...
gmazzap
terima kasih @ gmazzap - ya itu akan menjadi hebat, karena saya tidak menutupi kunci ketiga $ idx dan _wp_filter_build_unique_id (), hanya menampilkannya ;-)
birgire
8

Ini lebih merupakan komentar untuk jawaban Birgire yang sangat bagus daripada jawaban yang lengkap, tetapi harus menulis kode, komentar tidak cocok.

Dari jawaban itu mungkin tampak bahwa satu-satunya alasan mengapa tindakan ditambahkan sekali dalam kode sampel OP, bahkan jika add_action()dipanggil dua kali, adalah kenyataan bahwa prioritas yang sama digunakan. Itu tidak benar.

Dalam kode add_filterbagian penting adalah _wp_filter_build_unique_id()panggilan fungsi, yang menciptakan id unik per panggilan balik .

Jika Anda menggunakan variabel sederhana, seperti string yang menyimpan nama fungsi, misalnya "send_money_to_grandma", maka id akan sama dengan string itu sendiri, jadi jika prioritasnya sama, menjadi id juga sama, panggilan balik ditambahkan satu kali.

Namun, hal-hal tidak selalu sederhana seperti itu. Callback bisa berupa apa saja yang ada callabledi PHP:

  • nama fungsi
  • metode kelas statis
  • metode kelas dinamis
  • benda yang tidak bisa dimasuki
  • closures (fungsi anonim)

Dua yang pertama diwakili, masing-masing, dengan string dan array 2 string ( 'send_money_to_grandma'dan array('MoneySender', 'send_to_grandma')) sehingga id selalu sama, dan Anda dapat yakin bahwa callback ditambahkan satu kali jika prioritasnya sama.

Dalam semua 3 kasus lainnya, id bergantung pada instance objek (fungsi anonim adalah objek dalam PHP) sehingga callback ditambahkan hanya sekali jika objek adalah instance yang sama , dan penting untuk mencatat instance yang sama dan kelas yang sama Ada dua hal yang berbeda.

Ambil contoh ini:

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

Berapa banyak dolar yang kami kirim per pemuatan halaman?

Jawabannya adalah 2, karena id yang dihasilkan WordPress untuk $sender1dan $sender2berbeda.

Hal yang sama terjadi dalam kasus ini:

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

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

Di atas saya menggunakan fungsi sent_to_grandmadi dalam closure, dan bahkan jika kodenya identik, 2 closure adalah 2 instance \Closureobjek yang berbeda , jadi WP akan membuat 2 id yang berbeda, yang akan menyebabkan action ditambahkan dua kali, walaupun prioritasnya sama.

gmazzap
sumber
4

Anda tidak dapat menambahkan tindakan yang sama ke kait tindakan yang sama , dengan prioritas yang sama .

Hal ini dilakukan untuk mencegah beberapa plugin mengandalkan tindakan pihak ketiga plugin terjadi lebih dari satu kali (pikirkan woocommerce dan semua plugin pihak ketiga, seperti integrasi pembayaran gateway, dll). Jadi tanpa menentukan prioritas, Nenek tetap miskin:

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

Namun, jika Anda menambahkan prioritas pada tindakan tersebut:

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

Nenek sekarang meninggal dengan $ 4 di sakunya (1, 2, 3 dan default: 10).

tao
sumber