这段代码将运行多少次?(或者,奶奶有多有钱?)

时间:2015-11-24 作者:C C

假设的例子,但现实世界的适用性(对于像我这样的学习者)。

给定此代码:

<?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 times 在我的笔记本电脑电池耗尽之前。

First questions: 我们给奶奶寄了多少钱?是1美元、2美元、100美元还是200美元(或其他什么?)

如果你也能解释一下你的答案,那就太棒了。

Second questions: 如果我们想确保只给奶奶寄1美元,最好的方法是什么?第一次发送$1时设置为“true”的全局变量(信号量)?或者是否有其他测试来查看某个操作是否已经发生并防止其多次触发?

Third question: 这是插件开发人员担心的事情吗?我意识到我的示例很愚蠢,但我同时考虑了性能问题和其他意外的副作用(例如,如果函数更新/插入到数据库中)。

3 个回复
最合适的回答,由SO网友:birgire 整理而成

以下是对此的一些随机想法:

问题#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)是identical.

我们可以检查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\' );
如果我们想确保只给奶奶寄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 表格通过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" );
    }
}
如果我们想每天给她寄一次钱,那么我们可以使用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\' );
但这对性能非常不利。。。还有我们的钱包;-)

SO网友:gmazzap

这更多的是对非常好的Birgire\'s answer 而不是一个完整的答案,但必须编写代码,注释不合适。

从答案来看,似乎在OP示例代码中添加一次操作的唯一原因是,即使add_action() 调用两次是因为使用了相同的优先级。那不是真的。

在代码中add_filter 一个重要部分是_wp_filter_build_unique_id() 函数调用,它为每个回调创建唯一的id。

如果使用一个简单的变量,例如保存函数名的字符串,例如。"send_money_to_grandma", 然后id将等于字符串本身,因此如果优先级相同,id也相同,则会添加一次回调。

然而,事情并不总是那么简单。回调可能是callable 在PHP中:

函数名静态类方法动态类方法可调用对象闭包(匿名函数)前两个分别由字符串和两个字符串组成的数组表示(\'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,因为WordPress为$sender1$sender2 是不同的。

在这种情况下也会发生同样的情况:

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

add_action( \'init\', function() {
   sent_to_grandma();
} );
上面我使用了函数sent_to_grandma 在闭包内部,即使代码相同,这两个闭包也是\\Closure 对象,因此WP将创建两个不同的ID,这将导致添加两次操作,即使优先级相同。

SO网友:tao

无法添加same actionsame action hook, 使用same priority.

这样做是为了防止多个插件依赖第三方插件的行为发生多次(想想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美元)。

相关推荐

About Hooks and Filters

嗯,我很难理解动作和过滤器之间的区别。我确实在代码中使用动作,但我是一个新手,甚至连一点过滤器都不知道。我去过codex,以及NickTheGeek、BillErickson、GaryJones等的多个网站,但没有去过vein。如果你能用简单的话告诉我,并举例说明动作、过滤器和挂钩的基本内容和区别。非常感谢。