このコードは何回実行されますか?(または、おばあちゃんの豊かさは?)


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

OK、WPサイトを立ち上げてログインします。Adminでいくつかのページを走査します。「init」アクションは、ラップトップのバッテリーが切れる前に合計100回発生します。

最初の質問: おばあちゃんにいくら送金しましたか?$ 1、$ 2、$ 100、または$ 200(または他の何か?)

あなたの答えを説明することができたら、それは素晴らしいでしょう。

2番目の質問: おばあちゃんに$ 1だけを送信するようにしたい場合、これを行う最善の方法は何ですか?$ 1を初めて送信したときに「true」に設定されるグローバル変数(セマフォ)または、アクションが既に発生したかどうかを確認し、それが複数回発動しないようにする他のテストがありますか?

3番目の質問: これはプラグイン開発者が心配するものですか?私の例は馬鹿げていると思いますが、パフォーマンスの問題と他の予期しない副作用の両方を考えていました(たとえば、関数がデータベースに更新/挿入する場合)。


2
認めなければならない、これは長い間最高の質問の1つです;-)
ピーターグーセン

回答:


21

これに関するいくつかのランダムな考えを以下に示します。

質問1

おばあちゃんにいくら送金しましたか?

100ページの読み込みに対して、100 x $ 1 = $ 100を送信しました。

ここで実際に100 x do_action( 'init' )呼び出しを意味します。

次のように2回追加してもかまいません。

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ドル、100ページの読み込みごとに200ドルを送ります。

コールバックが異なる場合も同じです:

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

質問2

おばあちゃんに$ 1だけを送信するようにしたい場合

ページの読み込みごとに1回だけ送信する場合は、次のようにします。

add_action( 'init','send_money_to_grandma' );

なぜならinitフックは1回のみ起動されます。ページの読み込みごとに何度も起動する他のフックがあるかもしれません。

呼び出しましょう:

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_optionsOptions APIを使用してテーブルにオプションを登録します。

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

毎日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

これはプラグイン開発者が心配するものですか?

はい、これは重要です。

おばあちゃんをとても幸せにしたい場合は、次のようにします。

add_action( 'all','send_money_to_grandma' );

しかし、これはパフォーマンスにとって非常に悪いでしょう...そして私たちの財布;-)


すごい-このような徹底的な対応に感謝します。これは非常に役立ちます!
CC

1
どういたしまして-あなたの質問の言い回しが本当に楽しかったです;-) @CC
birgire

2
一日の終わりには、財布とおばあちゃんを幸せに保ちたいので、完璧な調和/バランスを見つけることがすべてです;
Pieter Goosen

非常に良い答え+1ですが、アクションが追加される回数はコールバックIDにも依存することを言う価値があります。オブジェクトを扱う場合、物事はもう少し複雑になります。 ...答えを書くでしょう
gmazzap

感謝の@gmazzap -私は3番目のキーの$ idxのと_wp_filter_build_unique_idを()カバーしていないことから、素晴らしいことだそうで、それ;-)表示
birgire

8

これは、完全な回答というよりも非常に優れたビルジールの回答に対するコメントですが、コードを作成する必要があるため、コメントは適合しません。

回答から、OPサンプルコードにアクションがadd_action()2回呼び出されたとしても、アクションが1回追加される唯一の理由は、同じ優先度が使用されているという事実のように思われるかもしれません。それは真実ではない。

add_filter重要な部分のコードには、コールバック_wp_filter_build_unique_id()ごとに一意のIDを作成する関数呼び出しがあります

関数名を保持する文字列などの単純な変数を使用する場合"send_money_to_grandma"、IDは文字列自体と等しくなるため、優先度が同じでIDも同じ場合、コールバックは1回追加されます。

しかし、物事は常にそのように単純ではありません。コールバックはcallable、PHPにあるものであれば何でもかまいません。

  • 関数名
  • 静的クラスメソッド
  • 動的クラスメソッド
  • 呼び出し可能なオブジェクト
  • クロージャ(匿名関数)

最初の2つはそれぞれ文字列と2つの文字列の配列('send_money_to_grandma'およびarray('MoneySender', 'send_to_grandma'))で表されるため、IDは常に同じであり、優先度が同じ場合は必ずコールバックが1回追加されます。

他の3つの場合はすべて、idはオブジェクトインスタンスに依存するため(匿名関数はPHPのオブジェクトです)、オブジェクトが同じインスタンスである場合にのみコールバックが1回追加され、同じインスタンス同じクラスに注意することが重要です2つの異なるものです。

次の例をご覧ください。

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が生成するID $sender1$sender2異なるためです。

この場合も同じことが起こります。

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

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

上記ではsent_to_grandmaクロージャー内で関数を使用しました。コードが同一であっても、2つのクロージャーは\Closureオブジェクトの2つの異なるインスタンスであるため、WPは2つの異なるIDを作成し、優先度が同じであってもアクションが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)で死亡しています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.