从插件类中移除操作,强制使用全局实例

时间:2018-08-01 作者:rrwebdev

我一直在拼命寻找解决这个问题的办法。这个网站(和其他网站)上到处都有这个问题的例子,但没有一个答案对我有帮助。如果您想跳过背景来回答我的问题,请在代码片段之后结束。

How I got here:我需要删除插件在未实例化的类中添加的操作。也就是说,在所讨论的类中没有这样的全局实例化(示例):

global $Class_name;
$Class_name = new This_Class;
在插件(Woocommerce服务)中,添加了如下操作(必须删除):

add_action( \'woocommerce_email_after_order_table\', array( $this, \'add_tracking_info_to_emails\' ), 10, 3 );
我已经看到并阅读了许多关于如何成功删除操作的文章。这些建议都很有帮助,建议如下:

1) 确保正时正确。只有在首先添加操作后,才能删除该操作。

2) 您必须使用添加操作时使用的相同优先级来删除该操作。

但是,当要删除添加到没有实例的类中的操作时。。。他们没有达到目标。大多数人建议通过传入类名本身(在本例中为“WC\\u Connect\\u Loader”)以这种方式删除操作:

remove_action( \'woocommerce_email_after_order_table\', array( \'WC_Connect_Loader\', \'add_tracking_info_to_emails\' ), 10 );
一些人声称上述方法对他们有效,所以我尝试了很多次,但都没有成功。我确信我是在遵循研究的指导。1) 我确认,我正在尝试删除添加后的操作(检查)。2) 我确认我使用与添加操作时相同的优先级删除操作,在本例中为10(检查)。我不知所措。

经过大量调试和实验后,很明显,我不能仅仅通过传入类名来删除该操作。最后,我不得不修改插件本身(我讨厌)来创建类“WC\\u Connect\\u Loader”的全局实例,然后在尝试删除该操作时传入该实例。

在这篇文章/问题中,我加入了代码的调试版本,以显示我如何能够确认哪些工作正常,哪些不正常。我确信有比wp\\u mail()更好的方法,但这对我来说是最简单的。我让WP给我发了一封电子邮件,让我确定成功/失败。

在以下两种情况下,只有轻微的变化。我已经对更改的行进行了注释。

This does NOT work:

//woocommerce-services.php:
...
public function attach_hooks() {
...
add_action( \'woocommerce_email_after_order_table\', array( $this, \'add_tracking_info_to_emails\' ), 10, 3 );
...
}
...
if ( ! defined( \'WC_UNIT_TESTING\' ) ) {
    new WC_Connect_Loader();
}



//functions.php
...
add_action(\'woocommerce_init\',\'remove_add_tracking_info_to_emails\',999);
function remove_add_tracking_info_to_emails() {
    if (remove_action(\'woocommerce_email_after_order_table\',array(\'WC_Connect_Loader\',\'add_tracking_info_to_emails\'),10)){
        wp_mail(\'[email protected]\',\'Successfully removed the hook\',\'Message\');
    }
    else {
        wp_mail(\'[email protected]\',\'Failed to remove the hook\',\'Message\');
    }
}

This DOES work:

//woocommerce-services.php
...
public function attach_hooks() {
...
add_action( \'woocommerce_email_after_order_table\', array( $this, \'add_tracking_info_to_emails\' ), 10, 3 );
...
}
...
if ( ! defined( \'WC_UNIT_TESTING\' ) ) {
        global $WC_Connect; // created global
    $WC_Connect = new WC_Connect_Loader(); // Class \'WC_Connect_Loader\' now has a global instance
}


// functions.php
...
add_action(\'woocommerce_init\',\'remove_add_tracking_info_to_emails\',999);
function remove_add_tracking_info_to_emails() {
    global $WC_Connect; // Global instance of class
    if (remove_action(\'woocommerce_email_after_order_table\',array($WC_Connect,\'add_tracking_info_to_emails\'),10)){ // Pass in global instance here
        wp_mail(\'[email protected]\',\'Successfully removed the hook\',\'Message\');
    }
    else {
        wp_mail(\'[email protected]\',\'Failed to remove the hook\',\'Message\');
    }
}
My question:为什么我不能像许多人所建议和声称的那样传入类名?为什么我必须传入类实例才能工作?

我似乎找不到任何能帮助我理解这一点的东西。我希望通过发布以下问题获得两个结果中的一个:

1) 有人可能会解释这一限制是不可避免的,插件应该正确编写才能扩展。希望其他人能从我花在这方面的时间和那些回应中受益。

2) 我错过了一些东西,也没有我想象的那么聪明。在这种情况下,我敢打赌,我不是第一个遇到这个问题的人,我会从这个问题中受益匪浅。

1 个回复
最合适的回答,由SO网友:Tom J Nowell 整理而成

因为这些都不一样,意味着不同的事情:

  • array( $this, \'method\' ) = $this->method();
  • array( \'classname\', \'method\' ) = classname::method();

    由于您试图取消挂钩的不是静态方法,因此您需要对对象的引用,这就是为什么第二个示例有效,而第一个示例无效的原因。

结束