Passing a parameter to filter and action functions

Is a way to pass my own parameters to the function in add_filter or add_action.
For example take a look in the following code:

function my_content($content, $my_param)
{
do something...
using $my_param here ...
return $content;
}
add_filter('the_content', 'my_content', 10, 1);

Can I pass my own parameter? something like:

Read More
add_filter('the_content', 'my_content($my_param)', 10, 1)

or

add_filter('the_content', 'my_content', 10, 1, $my_param)

Related posts

Leave a Reply

12 comments

  1. By default this is not possible. There are workarounds if you do it the OOP way.
    You could create a class to store the values you want to use later.

    Example:

    /**
     * Stores a value and calls any existing function with this value.
     */
    class WPSE_Filter_Storage
    {
        /**
         * Filled by __construct(). Used by __call().
         *
         * @type mixed Any type you need.
         */
        private $values;
    
        /**
         * Stores the values for later use.
         *
         * @param  mixed $values
         */
        public function __construct( $values )
        {
            $this->values = $values;
        }
    
        /**
         * Catches all function calls except __construct().
         *
         * Be aware: Even if the function is called with just one string as an
         * argument it will be sent as an array.
         *
         * @param  string $callback Function name
         * @param  array  $arguments
         * @return mixed
         * @throws InvalidArgumentException
         */
        public function __call( $callback, $arguments )
        {
            if ( is_callable( $callback ) )
                return call_user_func( $callback, $arguments, $this->values );
    
            // Wrong function called.
            throw new InvalidArgumentException(
                sprintf( 'File: %1$s<br>Line %2$d<br>Not callable: %3$s',
                    __FILE__, __LINE__, print_r( $callback, TRUE )
                )
            );
        }
    }
    

    Now you can call the class with any function you want – if the function exists somewhere it will be called with your stored parameters.

    Let’s create a demo function …

    /**
     * Filter function.
     * @param  array $content
     * @param  array $numbers
     * @return string
     */
    function wpse_45901_add_numbers( $args, $numbers )
    {
        $content = $args[0];
        return $content . '<p>' . implode( ', ', $numbers ) . '</p>';
    }
    

    … and use it once …

    add_filter(
        'the_content',
        array (
            new WPSE_Filter_Storage( array ( 1, 3, 5 ) ),
            'wpse_45901_add_numbers'
        )
    );
    

    … and again …

    add_filter(
        'the_content',
        array (
            new WPSE_Filter_Storage( array ( 2, 4, 6 ) ),
            'wpse_45901_add_numbers'
        )
    );
    

    Output:

    enter image description here

    The key is reusability: You can reuse the class (and in our examples also the function).

    PHP 5.3+

    If you can use a PHP version 5.3 or newer closures will make that much easier:

    $param1 = '<p>This works!</p>';
    $param2 = 'This works too!';
    
    add_action( 'wp_footer', function() use ( $param1 ) {
            echo $param1;
        }, 11 
    );
    add_filter( 'the_content', function( $content ) use ( $param2 ) {
            return t5_param_test( $content, $param2 );
        }, 12
    );
    
    /**
     * Add a string to post content
     *
     * @param  string $content
     * @param  string $string This is $param2 in our example.
     * @return string
     */
    function t5_param_test( $content, $string )
    {
        return "$content <p><b>$string</b></p>";
    }
    

    The downside is that you cannot write unit tests for closures. 

  2. Use php Anonymous functions:

    $my_param = 'my theme name';
    add_filter('the_content', function ($content) use ($my_param) {
        //$my_param is available for you now
        if (is_page()) {
            $content = $my_param . ':<br>' . $content;
        }
        return $content;
    }, 10, 1);
    
  3. The correct, really short and most efficient way of passing whatever number of arguments to WP filters and actions is from @Wesam Alalem here, that uses the closure.

    I would only add that you could make it even clearer and much more flexible by separating the actual doer method from anonymous closure. For this you just call the method from the closure as follows (modified example from @Wesam Alalem answer).

    This way you can write as long or complicated logic as you wish lexically outside of the closure you use to call the actual doer.

    // ... inside some class
    
    private function myMethod() {
        $my_param = 'my theme name';
        add_filter('the_content', function ($content) use ($my_param) {
            // This is the anonymous closure that allows to pass 
            // whatever number of parameters you want via 'use' keyword.
            // This is just oneliner.
            // $my_param is available for you now via 'use' keyword above
            return $this->doThings($content, $my_param);
        }, 10, 2);
    }
    
    private function doThings($content, $my_param) {
        // Call here some other method to do some more things
        // however complicated you want.
        $morethings = '';
        if ($content = 'some more things') {
            $morethings = (new MoreClass())->get();
        }
        return $my_param . ':<br>' . $content . $morethings;
    }
    
  4. Create a function with the needed arguments that returns a function. Pass this function (anonymous function, also known as closure) to the wp hook.

    Shown here for an admin notice in wordpress backend.

    public function admin_notice_func( $message = '')
    {
    $class = 'error';
        $output = sprintf('<div class="%s"><p>%s</p></div>',$class, $message);
        $func = function() use($output) { print $output; };
        return $func;
    }
    $func = admin_notice_func('Message');
    add_action('admin_notices', $func);
    
  5. As mentioned on other answers, passing a parameter to the callback function is not possible by default. OOP and PHP anonymous function are workarounds BUT:

    1. Your code might not be OOP
    2. You might need to remove that filter afterwards

    If that is your case, there is another workaround for you to use: make yourself use of the add_filter and apply_filters functions to make that parameter you want to pass available in the callback function:

    // Workaround to "save" parameter to be passed to your callback function.
    add_filter( 'pass_param', function() use ( $param ){ return $param; } );
    
    // Hook your function to filter whatever you want to filter.
    add_filter( 'actual_filter', 'myCallback' );
    
    // Your callback function that actually filters whatever you want to filter.
    function myCallback()
    {
       // Get the param that we were not able to pass to this callback function.
       $param = apply_filters( 'pass_param', '' );
    
       // Do whatever with the workarounded-passed param so it can be used to filter.
       return $param;
    }
    
  6. Despite calling a function directly, do this in a more elegant way: pass an anonymous function as a callback.

    For example:

    I have a single function to translate the title, content, and excerpt from my posts. So, I need to pass to this main function some arguments saying who is calling.

    add_filter( 'the_title', function( $text ) { 
        return translate_text( $text, 'title', 'pl' );
    });
    
    add_filter( 'the_content', function( $text ) { 
        return translate_text( $text, 'content', 'pl' );
    });
    
    add_filter( 'the_excerpt', function( $text ) { 
        return translate_text( $text, 'excerpt', 'pl' );
    });
    

    So, the main function translate_text receives as many parameters as I want, just because I have passed an anonymous function as a callback.

  7. I agree that fuxia’s answer above gives the preferred approaches. But while I was trying to wrap my head around the OOP solution I hit upon a way of doing it that sets and then unsets both the filter and a global variable:

    function my_function() {
        
        // Declare the global variable and set it to something
        global $my_global;
        $my_global = 'something';
            
        // Add the filter
        add_filter( 'some_filter', 'my_filter_function' );
        
        // Do whatever it is that you needed the filter for
        echo $filtered_stuff; 
        
        // Remove the filter (So it doesn't mess up something else that executes later)
        remove_filter( 'some_filter', 'my_filter_function' );
    
        // Unset the global (Because we don't like globals floating around in our code)
        my_unset_function( 'my_global' );
        
    }
    
    function my_filter_function( $arg ) {
        
        // Declare the global
        global $my_global
    
        // Use $my_global to do something with $arg
        $arg = $arg . $my_global;
    
        return $arg;
    
    }
    
    function my_unset_function( $var_name ) {
    
        // Declare the global
        $GLOBALS[$var_name];
    
        // Unset the global
        unset($GLOBALS[$var_name];
    
    }
    

    I’m an untrained developer and I work strictly on my own sites, so please take this sketch with a grain of salt. It works for me, but if there’s something wrong with what I’m doing here I’d be grateful if someone more knowledgeable would point it out.

  8. In my OOP solution I simply used a class member variable which is called in the callback function.
    In this example the post_title is filtered by a searchterm:

    class MyClass
    {
      protected $searchterm = '';
    
      protected function myFunction()
      {
        query = [
          'numberposts' => -1,
          'post_type' => 'my_custom_posttype',
          'post_status' => 'publish'
        ];
    
        $this->searchterm = 'xyz';
        add_filter('posts_where', [$this, 'searchtermPostsWhere']);
        $myPosts = get_posts($query);
        remove_filter('posts_where', [$this, 'searchtermPostsWhere']);
      }
    
      public function searchtermPostsWhere($where)
      {
        $where .= ' AND ' . $GLOBALS['wpdb']->posts . '.post_title LIKE '%' . esc_sql(like_escape($this->searchterm)) . '%'';
        return $where;
      }
    }
    
  9. if you create your own hook, here is example.

    // lets say we have three parameters  [ https://codex.wordpress.org/Function_Reference/add_filter ]
    add_filter( 'filter_name', 'my_func', 10, 3 );
    my_func( $first, $second, $third ) {
      // code
    }
    

    then implement hook:

    // [ https://codex.wordpress.org/Function_Reference/apply_filters ]
    echo apply_filters( 'filter_name', $first, $second, $third );
    
  10. I know time have passed, but I had some problem with passing my own parameter till I found that the the 4th parameter in add_filter is number of passed parameters including the content to change. So if you pass 1 additional parameter, number should be 2 and not 1 in your case

    add_filter('the_content', 'my_content', 10, 2, $my_param)
    

    and using

    function my_content($content, $my_param) {...}
    
  11. I was hoping to do the same but since its not possible I guess a simple workaround is to call a different function like
    add_filter('the_content', 'my_content_filter', 10, 1);

    then my_content_filter() can just call my_content() passing any argument it wants.