假设的示例,但在现实世界中适用(对于像我这样学习的人)。

给出以下代码:我的WP网站并登录。我在Admin中浏览了几页。在笔记本电脑电池没电之前,“ init”动作总共触发了100次。

第一个问题:我们寄给奶奶多少钱?是$ 1,$ 2,$ 100还是$ 200(还是其他?)

如果您还可以解释您的答案,那将是很棒的事情。

第二个问题:如果我们想确保我们只寄给奶奶1美元,这样做的最佳方法是什么?全局变量(信号量)在我们第一次发送$ 1时设置为“ true”吗?还是有其他测试来检查某个动作是否已经发生并阻止其多次触发?

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

评论

必须承认,这是长期以来最好的问题之一;-)

#1 楼

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

问题#1


我们寄了多少钱给奶奶?


/>对于100页的加载,我们向她发送了100 x $ 1 = $ 100。

这里实际上是指100 x do_action( 'init' )调用。

没关系,我们用两次添加了它: br />
我们可以检查add_action只是add_filter的包装器,该包装器构造了全局$wp_filter数组: :

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


,那么我们将向她发送2 x每页加载$ 1或每200页加载$ 200。

如果回调函数不同,则相同:

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


问题#2


如果我们要确保仅发送$ 1的祖母


如果我们只希望在每次页面加载时发送一次,则应该这样做:

因为init挂钩仅触发一次。我们可能还有其他每个页面加载会触发多次的钩子。

我们打电话给:

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


但是如果someaction触发10次会发生什么情况每页加载量?

我们可以使用

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


调整send_money_to_grandma()函数,或使用静态变量作为计数器: br />
add_action( 'init','send_money_to_grandma' );


如果只想运行一次(曾经!),则可以通过Options API在wp_options表中注册一个选项:

add_action( 'someaction ','send_money_to_grandma' );


如果我们想每天给她汇款一次,那么我们可以使用Transient API。甚至可以使用wp-cron。

请注意,您可能会调用ajax。

还有一些方法可以检查这些内容,例如使用DOING_AJAX

还可能存在重定向,这可能会中断流。

然后我们可能只想限制后端,而不是is_admin()! is_admin()

问题#3


是插件开发人员担心的事情吗?


是的,这很重要。如果我们想让我们的祖母非常我们很乐意做:

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


但这对性能和我们的钱包都非常不利;-)

评论


哇-谢谢你这么详尽的回应;这极大地帮助了!

– C C
15年11月24日在23:05

不用客气-我真的很喜欢你怎么说你的问题;-) @CC

–birgire
2015年11月24日23:26

归根结底,我们要让钱包和奶奶开心,所以这一切都是为了找到完美的和谐/平衡;-)

–Pieter Goosen
2015年11月25日下午4:32

+1是一个非常不错的答案,但是值得一说的是,添加操作的时间还取决于回调id,并且在处理对象时,事情要复杂一些。我会写一个答案...

– gmazzap♦
15年11月25日在12:29

谢谢@gmazzap-是的,那太好了,因为我没有介绍第三个键$ idx和_wp_filter_build_unique_id(),只显示了它;-)

–birgire
2015年11月25日12:50

#2 楼

比起完整的答案,这更像是对Birgire很好的答案的评论,但是必须编写代码,注释不适合。

从答案看来,似乎添加动作的唯一原因是即使在示例代码中两次调用了add_action(),在OP示例代码中只有一次,这是使用相同优先级的事实。

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中的一个对象),因此仅当该对象是相同的实例,需要注意的是,相同的实例和相同的类是两个不同的事物。

以这个示例为例:



答案是2,因为WordPress为$sender1$sender2生成的id不同。 />
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' ) );


上面我在闭包内部使用了函数sent_to_grandma,即使代码相同,两个闭包也是\Closure对象的2个不同实例,因此WP将创建2个不同的ID,即使优先级是一样。

#3 楼

您不能将具有相同优先级的相同动作添加到相同的动作挂钩。

这样做是为了防止多个插件依赖第三方插件的动作发生多次(请考虑woocommerce)以及所有第三方插件,例如网关付款集成等)。
因此,如果不指定优先级,祖母仍然很贫穷: br />
奶奶现在死了,口袋里有4美元(1、2、3和默认值:10)。