問題描述
假設的例子,但真實世界的適用性 (對某人學習,像我一樣) 。
給出這個程式碼:
<?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 美元,那最好的辦法是什麼?全域性變數 (訊號量) 在第一次傳送 $ 1 時設定’true’?或者還有其他一些測試,看看是否已經發生了一個動作,並阻止它多次發射?
第三個問題:這是外掛開發人員擔心的事情嗎?我意識到我的例子是愚蠢的,但我正在考慮效能問題和其他意想不到的副作用 (例如,如果函式更新/插入資料庫) 。
最佳解決方案
這裡有一些隨機的想法:
問題#1
How much money did we send to grandma?
對於 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 是如何構建全域性 $wp_filter 陣列的 add_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 載入或 100 頁的載入 $ 200 。
如果回撥不同,則相同:
add_action( 'init','send_money_to_grandma_1_dollar' );
add_action( 'init','send_money_to_grandma_also_1_dollar' );
問題#2
If we want to make sure we only send grandma $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");
}
如果我們只想執行一次 (永遠!),那麼我們可以透過 Options API 在 wp_options 表中註冊一個選項:
function send_money_to_grandma()
{
if( 'no' === get_option( 'sent_grandma_money', 'no' ) )
{
update_option( 'sent_grandma_money', 'yes' );
internetofThings( "send grandma","$1" );
}
}
如果我們每天要送她一次錢,那麼我們可以使用 Transient 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
Is this something that plugin developers worry about?
是的,這很重要。
如果我們想讓我們的奶奶很開心,我們會做:
add_action( 'all','send_money_to_grandma' );
但這對於表演… 和我們的錢包來說會非常糟糕;-)
次佳解決方案
這是對一個非常好的 Birgire’s answer 的評論比一個完整的答案,但不得不編寫程式碼,評論不適合。
從答案可以看出,在 OP 示例程式碼中新增一次操作的唯一原因,即使 add_action()被呼叫兩次,這是事實上使用相同的優先順序。這不是真的。
在 add_filter 的程式碼中,重要的一個部分是_wp_filter_build_unique_id()函式呼叫,每個回撥建立一個唯一的 id 。
如果你使用一個簡單的變數,像一個儲存一個函式名的字串,例如"send_money_to_grandma",那麼 id 將等於字串本身,所以如果優先順序相同,id 也是一樣的,回撥將被新增一次。
然而,事情並不總是那麼簡單。回撥可能是 PHP 中的 callable:
-
函式名
-
靜態類方法
-
動態類方法
-
可呼叫物件
-
關閉 (匿名函式)
前兩個分別由一個字串和一個 2 字串陣列 ('send_money_to_grandma'和 array('MoneySender', 'send_to_grandma')) 表示,所以 id 總是相同的,您可以確保如果優先順序相同,則回撥將被新增一次。
在所有其他 3 種情況中,id 取決於物件例項 (匿名函式是 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 個閉包是 Closure 物件的兩個不同的例項,所以 WP 將建立 2 個不同的 id,即使優先順序是相同。
第三種解決方案
您不能將相同的操作新增到相同的操作鉤子,具有相同的優先順序。
這樣做是為了防止多個外掛依賴於第三方外掛的動作發生不止一次 (認為 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) 。
參考文獻
注:本文內容整合自 Google/Baidu/Bing 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。