2

I want to remove automatically the Home menu item, but only once, more precisely at the first run of Wordpress or at the first child theme activation/loading. Now I have a function in the functions.php of my child theme that checks if the Home menu item exists and delete it from the menu. Of course, this function runs every time when Wordpress loads. How to make it to run only once? I tried the add_filter_once() function, but I got only a PHP Fatal error: Call to undefined function add_filter_once().

function filter_wp_nav_menu_objects( $sorted_menu_items, $args ) { 

    foreach( $sorted_menu_items as $data ) {
        if ( in_array( "menu-item-home", $data->classes ) ) {
            wp_delete_post( $data->ID );
        }
    }

    return $sorted_menu_items;
} 

add_filter( 'wp_nav_menu_objects', 'filter_wp_nav_menu_objects', 10, 2 );
6
  • Why? What has deleting a post to do with this?
    – kero
    Commented Sep 15, 2017 at 15:59
  • @kero wordpress.stackexchange.com/a/60873/25187
    – Yuri
    Commented Sep 15, 2017 at 16:02
  • Ah okay, didn't know that. And what is the problem with running the filter "every time"?
    – kero
    Commented Sep 15, 2017 at 16:04
  • @kero Is not needed more than once (not efficient / productive).
    – Yuri
    Commented Sep 15, 2017 at 16:05
  • 1
    The problem I see here is: You don't know, which wp_nav_menu() call you are looking for. There can be several (and usually there are). So either you could save some config option, once you found it and removed it, set that option. But still, you'd need to add your filter to each run as to see if that option is set (and unset it on theme change, etc). I wouldn't mind the efficiency, your frontend should be served from cache anyway, and this isn't a too expensive task for an admin user to handle performance wise
    – kero
    Commented Sep 15, 2017 at 16:25

2 Answers 2

5

There is no native WordPress hook called add_filter_once(). This is an custom solution which will help you to run your hook only once.

For example if inside a loop or whatever situation you are facing. However, The basic idea is to check when you need to stop using your hook and simply remove_hook from WordPress and once all done, you need to registere it again.

example code:

function add_filter_once( $hook, $callback, $priority = 10, $args = 1 ) {
    $singular = function () use ( $hook, $callback, $priority, $args, &$singular ) {
        call_user_func_array( $callback, func_get_args() );
        remove_filter( $hook, $singular, $priority );
    };

    return add_filter( $hook, $singular, $priority, $args );
}

The best way to understand what I try to explain to visit this link

2
  • Overall this is good, however, for filters you need to return the value of the callback. You also need to move the $args argument to the add_filter call, instead of the remove_filter
    – Phil F
    Commented Apr 5, 2021 at 2:46
  • If you're removing $args from the remove_filter call then you don't need it in the using() list either.
    – Rup
    Commented Feb 23, 2022 at 15:35
1

An ugly but working way to accomplish this would be to have an global array, and inside your filter callback, you would check if the array already contains the hook name of the filter, you only want to apply once. If it turns out, that the value is already set, the callback will immediately return the input.

Technically, your filter will run every time, but the logic won't be applied each time, since it will skip for the 2nd execution and up.

This technique also works for class based filters, which somehow are not treated equally by WordPress, for example removing a class based filter is some sort of mess.

    // … inside a class

    public function addMyFilter()
    {
        add_filter('some_filter', [$this, 'someMethod']);
    }

    public function someMethod($val)
    {
        global $myFiltersThatWhereExecAlready;

        if (empty($myFiltersThatWhereExecAlready)) {
            $myFiltersThatWhereExecAlready = [];
        }

        if (in_array(__METHOD__, $myFiltersThatWhereExecAlready)) {
            return $val;
        }

        $myFiltersThatWhereExecAlready[] = __METHOD__;

        // … doing your filtering
    }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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