web-dev-qa-db-ja.com

このコードは何回実行されますか? (または、おばあちゃんはどれだけ金持ちですか?)

仮定的な例だが現実の世界への適用性(私のように、学習している人には)。

このコードを考えます:

<?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(または他の何か)ですか?

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

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

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

20
C C

これに関するランダムな考えは次のとおりです。

質問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 );

その後、1ページあたり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ページの読み込みにつき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");
}

またはstatic変数をカウンターとして使用します。

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

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

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

21
birgire

これは非常に良い Birgire's answer /完全な答えよりもコメントですが、コードを書く必要があり、コメントは合いません。

その答えから、たとえadd_action()が2回呼び出されたとしても、OPサンプルコードでactionが一度追加された唯一の理由は、同じ優先順位が使用されるという事実にあるようです。それは真実ではない。

add_filterのコードで重要な部分は _wp_filter_build_unique_id() function callです。これは callback ごとに一意のIDを作成します。

関数名を保持する文字列のように単純な変数を使用する場合は、 "send_money_to_grandma"の場合、idは文字列自体と同じになります。したがって、優先順位が同じで、idも同じであれば、コールバックは1回追加されます。

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

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

最初の2つはそれぞれ文字列と2つの文字列の配列('send_money_to_grandma'array('MoneySender', 'send_to_grandma'))で表されるので、idは常に同じです。優先順位が同じであれば、コールバックが確実に1回追加されます。

他のすべての3つの場合では、idはオブジェクトインスタンスに依存します(無名関数はPHPのオブジェクトです)ので、コールバックはオブジェクトが同じ instance の場合にのみ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' ) );

1ページの読み込みあたり何ドル送っていますか?

WordPressが$sender1$sender2に対して生成するIDが異なるため、答えは2です。

この場合も同じです。

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

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

上記で私はクロージャ内で関数sent_to_grandmaを使用しました、そして、コードが同一であっても、2つのクロージャは\Closureオブジェクトの2つの異なるインスタンスであるので、WPは2つの異なるidsを作成しますたとえ優先順位が同じであっても。

8
gmazzap

同じアクションフック 同じアクション 同じ優先順位で追加することはできません

これは、複数のプラグインがサードパーティのプラグインの動作に依存して複数回発生するのを防ぐために行われます(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);

おばあちゃんは今彼女のポケットに1ドル(4、2、3とデフォルト:10)で死にます。

4