Link to home
Create AccountLog in
Avatar of Brad Bansner
Brad BansnerFlag for United States of America

asked on

Cannot kill plugin add_action on WordPress/WooCommerce website

There is a plugin installed on our WordPress site that has a function like this:


public function sendNewInvoiceRequest( $order_id ) {
...etc...
}

Open in new window

The only reference to that function in the plugin looks like this:


class Vertex_Connector {
    private function activate_vertex_services() {         try {             $service = new Vertex_Connector_Service( $this->vertex_connector, $this->version );             if ( $service->is_configured() ) {                 $this->loader->add_action( 'woocommerce_update_order', $service, 'sendNewInvoiceRequest' );             }         }     } }

Open in new window


In my own functions.php, inside of some conditionals, I want to kill that add_action altogether, which I attempt like this:


remove_action( 'woocommerce_update_order', 'sendNewInvoiceRequest' );

Open in new window

However, even after doing so, the sendNewInvoiceRequest() function continues to fire, which I can demonstrate by logging.


Is there some way for me to make that plugin stop?

Avatar of gr8gonzo
gr8gonzo
Flag of United States of America image

1. Are you certain that the action has been added BEFORE you have called remove_action?


2. Are you certain that the action has not already been executed before you have called remove_action?


3. Your add_action code is custom here and includes the object $service. Is there any chance that the callback is "sendNewInvoiceRequest" method on the $service object?

Avatar of Brad Bansner

ASKER

1. I can see through logging that my remove_action statement definitely happens after the first sendNewInvoiceRequest() call. So I assume that means the action has been added.


2. No, I am sure the action is being executed at least once before I remove it. What I am trying to do is remove subsequent calls to the sendNewInvoiceRequest() function.


3. Honestly, I don't understand what $service is doing. I always thought add_action accepts two arguments at the beginning (other than priority and number of variables later on), so I'm not sure why that third argument is allowed in the beginning, or what it does.

Does the Vertex_Connector_Service class have a method called sendNewInvoiceRequest? Is that the function being called?

Just for some context, a PHP callback (which is the name for the situation where you store the NAME of a function and then later on, PHP executes that function by its name) can either be a regular function like this:


<?php
function foo()
{
}

Open in new window


Or a method inside of a class like this:

<?php
class Foo
{
  function bar()
  {
  }
}

Open in new window


When you want to do a callback to a regular function (a function that isn't inside of a class), you simply pass the name of the function, like this:


add_action("action_name", "foo");

Open in new window


But if you wanted to call a method inside of a class, you pass it an array where the first entry is the class instance, and the second entry is the method name like this:


$my_instance_of_Foo = new Foo();
add_action("action_name", array($my_instance_of_Foo, "bar"));

Open in new window


So my suspicion is that this line:

$this->loader->add_action( 'woocommerce_update_order', $service, 'sendNewInvoiceRequest' ); 

Open in new window


...is simply a wrapper that is basically calling this:

add_action( 'woocommerce_update_order', array($service, 'sendNewInvoiceRequest')); 

Open in new window


If that's true, then calling remove_action without the $service, like this:


remove_action( 'woocommerce_update_order', 'sendNewInvoiceRequest' ); 

Open in new window


...will not work because PHP won't be able to find a hook that is simply the non-class function called "sendNewInvoiceRequest".


You would need to ensure you got the correct $service object (the same one in the original add_action call) and then use it in your remove_action request.


Does the Vertex_Connector_Service class have a method called sendNewInvoiceRequest? Is that the function being called?

The class Vertex_Connector_Service is defined as extending Vertex_Connector_Options, and within the Vertex_Connector_Service class is the public function sendNewInvoiceRequest.


The sendNewInvoiceRequest function is only referenced in two places in the plugin. Once as described above, the second time the add_action statement in my original question.

Everything else you described makes sense, thank you for that explanation. I have no idea how I would retrieve the correct $service object, as that is part of a plugin, and my function is in functions.php.


Unfortunately, this plugin (once it is installed) just does its thing anytime a WooCommerce order is updated, and I have not found a way to simply stop it under certain circumstances.


If I were allowed to modify the plugin, this would be easy, but I'm trying to avoid that for obvious reasons.

Well, you CAN do it manually. It's ugly, but it would work.


Basically, there's a global variable $wp_filter, which is the storage point for all Wordpress hooks/actions/filters. It's an array, where the key is the name of the hook, and the value is an instance of the WP_Hook class / object. That WP_Hook class contains an array called callbacks, where the key is the priority, and the value is an array of arrays that is each a callback. So for example, if you had an action called "foo" and a callback called "bar" that ran at priority 10, you could access it like this:


echo $wp_filter["foo"][10][0]["function"]; // OUTPUT: "bar"

Open in new window


So if you wanted to loop through all of the callbacks for that action and then remove any callbacks that used the method name of "sendNewInvoiceRequest", you could do this:


function removeAllFiltersWithName($hook_name, $callback_name)
{
  global $wp_filter;
  if (!isset( $wp_filter[$hook_name])) { return; }
  foreach($wp_filter[$hook_name] as $wp_hook)
  {
    foreach($wp_hook->callbacks as $priority => $callbacks_for_priority)
    {
      foreach($callbacks_for_priority as $idx => $callback)
      {
        $callback_name_is_match = (is_array($callback["function"]) && $callback["function"][1] == $callback_name) || 
                                  (!is_array($callback["function"]) && $callback["function"] == $callback_name);
        if($callback_name_is_match)
        {
          // We found a callback to remove, let the WP handling take over.
          remove_filter($hook_name, $callback["function"]);
        }
      }
    }
  }
}

Open in new window

I haven't tested the above, but it should work in theory (I just wrote it by looking at the structure of the WP source).


Then you'd use it like this:

removeAllFiltersWithName("woocommerce_update_order", "sendNewInvoiceRequest");

Open in new window


Now, be aware that this will remove ALL callbacks that have that function name "sendNewInvoiceRequest". I'm guessing this is probably safe and what you want, but just be cautious if you ever use it somewhere else where the method name is a little more generic. 

I tried that function. This line loops three times:


foreach( $wp_filter[$hook_name] as $wp_hook ) {

Open in new window

But the next line doesn't return any results:


foreach( $wp_hook->callbacks as $priority => $callbacks_for_priority ) {

Open in new window

The text sendNewInvoiceRequest only appears in one array:

000000001fe875330000000028552eb4sendNewInvoiceRequest] => Array
        (             [function] => Array                 (                     [0] => Vertex_Connector_Service Object                         (                             [api:protected] => Vertex_Api Object                                 (                                 )                             [plugin_name:protected] => vertex-connector                             [version] => 2.0.2.0                         )                     [1] => sendNewInvoiceRequest                 )             [accepted_args] => 1         )

Open in new window

I'm attaching the full result, where it says Array at the beginning of the line, that is one of the three returned by the first foreach above. I am wanting to kill anything related to sendNewInvoiceRequest or Vertex.


result.txt

ASKER CERTIFIED SOLUTION
Avatar of gr8gonzo
gr8gonzo
Flag of United States of America image

Link to home
membership
Create an account to see this answer
Signing up is free. No credit card required.
Create Account

Oh wow, that seems to do it. I will dig into it further, but that seems like a winner. It is pretty compact, I don't see it as an issue, especially since there seems to be no other way of overriding the plugin.