8

I'm updating one of my plugins and I'm a little stuck with deprecating functions.

Originally, my plugin had a global variable and the plugin's main class was instantiated and stored in the global variable. This way users could use the global to access functions in the plugin class.

$GLOBALS['my_custom_plugin'] = new my_custom_plugin();

Then, for example, in my FAQ I had code that showed how to remove one of my class' functions from a specific hook and add to to a different hook:

function move_input(){ 
    global $my_custom_plugin;
    remove_action( 'before_main_content', array( $my_custom_plugin, 'display_input') );
    add_action( 'after_main_content', array( $my_custom_plugin, 'display_input' ) );
}
add_action( 'wp_head' , 'move_input' );

Now, in my update the display_input() function has been moved to another class and I want to let people know how to access it. I tried replacing the original function (in the main plugin class) with the following deprecation notice:

public function display_input() { 
    _deprecated_function( 'display_price', '2.0', 'my_custom_plugin()->display->display_input' );
    return $this->display->display_input();
}

However, the add_action and remove_action functions don't seem to trigger the deprecation notice. Weirdly, completely removing the function doesn't cause an error either even though array( $my_custom_plugin, 'display_input') doesn't exist.

If someone tries to access the function directly:

$my_custom_plugin->display_input();

Then I do see the debug notices. Is this the expected outcome for _deprecated_function()? or am I missing something? Is it possible to show a debug notice when someone tries to remove or add an action using a deprecated function?

Update

I realized that I was simply not seeing the debug message for the add_action as I was adding it pretty low on the page. #facepalm! However, I am still not seeing any debug notice for the remove_action.

  • Interesting call_user_func(function(){trigger_error('hello?');}); works, but it fails in a callback registered per add_action. – fuxia Feb 6 '14 at 21:42
  • Preparing for facepalm, but is that a question or a statement? – helgatheviking Feb 6 '14 at 21:55
  • Just a test result. I am surprised too. – fuxia Feb 6 '14 at 21:58
  • Ok, so I've not really overlooked anything, that's just how it is? – helgatheviking Feb 6 '14 at 22:12
  • 2
    Oh, Query Monitor has hidden the message in my setup. The message for add_action() was actually there. remove_action() cannot be be handled like that. – fuxia Feb 7 '14 at 1:29
2

Non existing Callbacks

One of the nice things is, that neither do_action(), nor apply_filters() do trigger an error if a callback isn't present. This means that its the safest way to insert plugin data into templates: If a plugin is turned off and do_action()/apply_filters() don't find the callback in the global $wp_filters array, nothing happens.

Error Output

Now when you're calling remove_filter() in the function/method that originally hooked the callback, the callback simply will be removed from the global array, which means that the callback will never be executed as it's not registered anymore.

The solution is simple: Remove the callback after it was triggered, by removing it from within the callback itself.

Removing Callbacks

We all know that WPs plugin "API" is a pain when it comes to removing. The problem mainly is the strange construct to add "unique" names to keys in the global $wp_filter; array. A very simple solution is to just use __METHOD__ and call filters that you want to remove in static context:

class FOoooo
{
    public function __construct()
    {
        add_filter( 'hook', array( __CLASS__, 'bar' ) );
    }
    public static function bar( $baz )
    {
        remove_filter( current_filter(), __METHOD__ );

        return $baz;
    }
}

While this isn't nice, it's ... sort of a solution for some use cases. But don't even think about removing a closure.

Above just removes the callback, still executes it. Still you can go further and use remove_all_actions() (or remove_all_filters()).

// Check if a callback is attached and tell about the deprecated stuff
if ( has_action( 'before_main_content' ) )
    _deprecated_function( 'display_price', '2.0', 'my_custom_plugin()->display->display_input' );
// Remove the callback
remove_all_actions( 'before_main_content' );

You maybe could even go further, extract the callback from the global filters array and re-attach it to the new hook/filter (if they're compatible).

  • Thanks Kaiser! This is an awesome and detailed answer. I will try to test it out tomorrow. – helgatheviking Feb 11 '14 at 22:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.