can I pass arguments to my function through add_action?

can I do something like that? to pass arguments to my function? I already studied add_action doc but did not figure out how to do it. What the exact syntax to pass two arguments would look like. In particular how to pass text & integer arguments.

function recent_post_by_author($author,$number_of_posts) {
  some commands;
}
add_action('thesis_hook_before_post','recent_post_by_author',10,'author,2')

UPDATE

Read More

it seems to me that it is done somehow through do_action but how? 🙂

Related posts

Leave a Reply

16 comments

  1. can I do something like that? to pass arguments to my function?

    Yes you can! The trick really is in what type of function you pass to add_action and what you expect from do_action.

    • ‘my_function_name’
    • array( instance, ‘instance_function_name’)
    • ‘StaticClassName::a_function_on_static_class’
    • anonymous
    • lambda
    • closure

    We can do it with a closure.

    // custom args for hook
    
    $args = array (
        'author'        =>  6, // id
        'posts_per_page'=>  1, // max posts
    );
    
    // subscribe to the hook w/custom args
    
    add_action('thesis_hook_before_post', 
               function() use ( $args ) { 
                   recent_post_by_author( $args ); });
    
    
    // trigger the hook somewhere
    
    do_action( 'thesis_hook_before_post' );
    
    
    // renders a list of post tiles by author
    
    function recent_post_by_author( $args ) {
    
        // merge w/default args
        $args = wp_parse_args( $args, array (
            'author'        =>  -1,
            'orderby'       =>  'post_date',
            'order'         =>  'ASC',
            'posts_per_page'=>  25
        ));
    
        // pull the user's posts
        $user_posts = get_posts( $args );
    
        // some commands
        echo '<ul>';
        foreach ( $user_posts as $post ) {
            echo "<li>$post->post_title</li>";
        }
        echo '</ul>';
    }
    

    Here is a simplified example of a closure working

    $total = array();
    
    add_action('count_em_dude', function() use (&$total) { $total[] = count($total); } );
    
    do_action ('count_em_dude' );
    do_action ('count_em_dude' );
    do_action ('count_em_dude' );
    do_action ('count_em_dude' );
    do_action ('count_em_dude' );
    do_action ('count_em_dude' );
    do_action ('count_em_dude' );
    
    echo implode ( ', ', $total ); // 0, 1, 2, 3, 4, 5, 6
    

    Anonymous vs. Closure

    add_action ('custom_action', function(){ echo 'anonymous functions work without args!'; } ); //
    
    add_action ('custom_action', function($a, $b, $c, $d){ echo 'anonymous functions work but default args num is 1, the rest are null - '; var_dump(array($a,$b,$c,$d)); } ); // a
    
    add_action ('custom_action', function($a, $b, $c, $d){ echo 'anonymous functions work if you specify number of args after priority - '; var_dump(array($a,$b,$c,$d)); }, 10, 4 ); // a,b,c,d
    
    // CLOSURE
    
    $value = 12345;
    add_action ('custom_action', function($a, $b, $c, $d) use ($value) { echo 'closures allow you to include values - '; var_dump(array($a,$b,$c,$d, $value)); }, 10, 4 ); // a,b,c,d, 12345
    
    // DO IT!
    
    do_action( 'custom_action', 'aa', 'bb', 'cc', 'dd' ); 
    

    Proxy Function Class

    class ProxyFunc {
        public $args = null;
        public $func = null;
        public $location = null;
        public $func_args = null;
        function __construct($func, $args, $location='after', $action='', $priority = 10, $accepted_args = 1) {
            $this->func = $func;
            $this->args = is_array($args) ? $args : array($args);
            $this->location = $location;
            if( ! empty($action) ){
                // (optional) pass action in constructor to automatically subscribe
                add_action($action, $this, $priority, $accepted_args );
            }
        }
        function __invoke() {
            // current arguments passed to invoke
            $this->func_args = func_get_args();
    
            // position of stored arguments
            switch($this->location){
                case 'after':
                    $args = array_merge($this->func_args, $this->args );
                    break;
                case 'before':
                    $args = array_merge($this->args, $this->func_args );
                    break;
                case 'replace':
                    $args = $this->args;
                    break;
                case 'reference':
                    // only pass reference to this object
                    $args = array($this);
                    break;
                default:
                    // ignore stored args
                    $args = $this->func_args;
            }
    
            // trigger the callback
            call_user_func_array( $this->func, $args );
    
            // clear current args
            $this->func_args = null;
        }
    }
    

    Example Usage #1

    $proxyFunc = new ProxyFunc(
        function() {
            echo "<pre>"; print_r( func_get_args() ); wp_die();
        },
        array(1,2,3), 'after'
    );
    
    add_action('TestProxyFunc', $proxyFunc );
    do_action('TestProxyFunc', 'Hello World', 'Goodbye'); // Hello World, 1, 2, 3
    

    Example Usage #2

    $proxyFunc = new ProxyFunc(
        function() {
            echo "<pre>"; print_r( func_get_args() ); wp_die();
        },                  // callback function
        array(1,2,3),       // stored args
        'after',            // position of stored args
        'TestProxyFunc',    // (optional) action
        10,                 // (optional) priority
        2                   // (optional) increase the action args length.
    );
    do_action('TestProxyFunc', 'Hello World', 'Goodbye'); // Hello World, Goodbye, 1, 2, 3
    
  2. Instead of:

    add_action('thesis_hook_before_post','recent_post_by_author',10,'author,2')
    

    it should be:

    add_action('thesis_hook_before_post','recent_post_by_author',10,2)
    

    …where 2 is the number of arguments and 10 is the priority in which the function will be executed. You don’t list your arguments in add_action. This initially tripped me up. Your function then looks like this:

    function function_name ( $arg1, $arg2 ) { /* do stuff here */ }
    

    Both the add_action and function go in functions.php and you specify your arguments in the template file (page.php for example) with do_action like so:

    do_action( 'name-of-action', $arg1, $arg2 );
    

    Hope this helps.

  3. Build custom WP functions with classes

    This is easy with classes, as you can set object variables with the constructor, and use them in any class method. So for an example, here’s how adding meta boxes could work in classes…

    // Array to pass to class
    $data = array(
        "meta_id" => "custom_wp_meta",
        "a" => true,
        "b" => true,
        // etc...
    );
    
    // Init class
    $var = new yourWpClass ($data);
    
    // Class
    class yourWpClass {
    
        // Pass $data var to class
        function __construct($init) {
            $this->box = $init; // Get data in var
            $this->meta_id = $init["meta_id"];
            add_action( 'add_meta_boxes', array(&$this, '_reg_meta') );
        }
        public function _reg_meta() {
            add_meta_box(
                $this->meta_id,
                // etc ....
            );
        }
    }
    

    If you consider __construct($arg) the same as function functionname($arg) then you should be able to avoid global variables and pass all the information you need through to any functions in the class object.

    These pages seem to be good points of reference when building wordpress meta / plugins ->

  4. 7 Ways to pass data to add_action function

    1. Through do_action (if you yourself create actions)
    2. wp_localize_script approach (if you need to pass data to JavaScript)
    3. Using use in Closures/Anonymous/Lamda functions
    4. Make use of arrow functions (PHP 7.4+)
    5. Use add_filter, apply_filters as a transport (clever way)
    6. Hack the scope with global or $GLOBALS (if you are desperate)
    7. Use set_transient, get_transient and other functions as a transport (in case of exotic necesities)

    #1 Through do_action

    if you have access to the code where the action fires, pass variables through do_action:

    /**
    * Our client code
    *
    * Here we recieve required variables.
    */
    function bar($data1, $data2, $data3) {
        /**
         * It's not necessary that names of these variables match 
         * the names of the variables we pass bellow in do_action.
         */
    
        echo $data1 . $data2 . $data3;
    }
    add_action( 'foo', 'bar', 10, 3 );
    
    /**
     * The code where action fires
     *
     * Here we pass required variables.
     */
    $data1 = '1';
    $data2 = '2';
    $data3 = '3';
    //...
    do_action( 'foo', $data1, $data2, $data3 /*, .... */ );
    

    #2 wp_localize_script approach

    if you need to pass variable to JavaScript, this is the best way of doing it.

    functions.php

    /**
     * Enqueue script
     */
    add_action( 'wp_enqueue_scripts', function() {
        wp_enqueue_script( 'my_script', get_template_directory_uri() . '/assets/js/my-script.js', array( 'jquery' ), false, false );
    } );
    
    /**
     * Pass data to the script as an object with name `my_data`
     */
    add_action( 'wp_enqueue_scripts', function(){
        wp_localize_script( 'my_script', 'my_data', [
            'bar' => 'some data',
            'foo' => 'something else'
        ] );
    } );
    

    my-script.js

    alert(my_data.bar); // "some data"
    alert(my_data.foo); // "something else"
    

    Basically the same but without wp_localize_script:

    functions.php

    add_action( 'wp_enqueue_scripts', function(){
        echo <<<EOT
        <script> 
        window.my_data = { 'bar' : 'somedata', 'foo' : 'something else' };
        </script>;
        EOT;
    
        wp_enqueue_script( 'my_script', get_template_directory_uri() . '/assets/js/my-script.js', array( 'jquery' ), false, false );
    }, 10, 1 );
    

    #3 Using use in Closures/Anonymous/Lamda functions

    if you don’t have access to the code where the action fires you can slip the data as follows (PHP 5.3+):

    $data1 = '1';
    $data2 = '2';
    $data3 = '3';
    
    add_action( 'init', function() use ($data1, $data2, $data3) {
        echo $data1 . $data2 . $data3; // 123
    });
    

    #4 Make use of arrow functions (PHP 7.4+)

    Basically the same as the #3 example but more concise, as the arrow functions involve variables from the parent scope without using use:

    $data1 = '1';
    $data2 = '2';
    $data3 = '3';
    
    add_action( 'init', fn() => print( $data1 . $data2 . $data3 ) ); // prints "123"
    

    #5 Use add_filter, apply_filters as a transport

    You can create a function with add_filter that will return value when you call apply_filters:

    /**
     * Register the data with the filter functions
     */
    add_filter( 'data_1', function() { return '1'; } );
    add_filter( 'data_2', function() { return '2'; } );
    add_filter( 'data_3', fn() => '3' ); // or in concise way with arrow function
    
    function foo() {
        /**
         * Get the previously registered data
         */
        echo apply_filters( 'data_1', null ) . 
             apply_filters( 'data_2', null ) . 
             apply_filters( 'data_3', null ); // 123
    }
    add_action( 'init', 'foo'); 
    

    I have seen the approach applied in many plugins.

    #6 Hack the scope with global or $GLOBALS (kiddy way)

    If you don’t worry about the scope, use global, example #1:

    $data1 = '1';
    $data2 = '2';
    $data3 = '3';
    
    function foo() {
        global $data1, $data2, $data3;
    
        echo $data1 . $data2 . $data3; // 123
    }
    add_action( 'init', 'foo' );
    

    Example #2 Using $GLOBALS instead of global

    $data1 = '1';
    $data2 = '2';
    $data3 = '3';
    
    function foo() {
        echo $GLOBALS['data1'] . $GLOBALS['data2'] . $GLOBALS['data3']; // 123
    }
    add_action( 'init', 'foo' );
    

    #7 Use set_transient, get_transient, set_query_var, get_query_var as a transport

    Example #1: Let’s say there is a shortcode that prints a form, that subsequently gets submitted and handled via AJAX, and the data is coming from the form must be sent by email that should be gotten from the shortcode parameters.

    1. Init shortcode
    2. Parse and print shortcode, remember parameters to transients

    — Within Ajax handler —

    1. Get required parameters from the transient and send the email.

    Example #2: Before WordPress 5.5 came out, some people had passed parameters within wp_query by get/set_query_vars to pass them to the template parts, these can be used as well.

    Mix them up and use. Cheers.

  5. Basically the do_action is placed where the action should be executed, and it needs a name plus your custom parameters.

    When you come to call the function using add_action, pass the name of your do_action() as your first argument, and the function name as the second. So something like:

    function recent_post_by_author($author,$number_of_posts) {
      some commands;
    }
    add_action('get_the_data','recent_post_by_author',10,'author,2');
    

    This is where it’s executed

    do_action('get_the_data',$author,$number_of_posts);
    

    Should hopefully work.

  6. Pass in vars from the local scope FIRST, then pass the fn SECOND:

    $fn = function() use($pollId){ 
       echo "<p>NO POLLS FOUND FOR POLL ID $pollId</p>"; 
    };
    add_action('admin_notices', $fn);
    
  7. I use closure for PHP 5.3+. I can then pass the default values and mine without globals. (example for add_filter)

    ...
    $tt="try this";
    
    add_filter( 'the_posts', function($posts,$query=false) use ($tt) {
    echo $tt;
    print_r($posts);
    return  $posts;
    } );
    
  8. Well, this is old, but it has no accepted answer. Reviving so that Google searchers have some hope.

    If you have an existing add_action call that doesn’t accept arguments like this:

    function my_function() {
      echo 100;
    }
    
    add_action('wp_footer', 'my_function');
    

    You can pass an argument to that function by using an anonymous function as the callback like this:

    function my_function($number) {
      echo $number;
    }
    
    $number = 101;
    add_action('wp_footer', function() { global $number; my_function($number); });
    

    Depending on your use case, you might need to use different forms of callback, possibly even using properly declared functions, as sometimes you may encounter trouble with scope.

  9. I ran into the same thing today, and since all answers here are either unclear, irrelevant or excessive, I thought I’d provide the simple straight-forward answer.

    Like the most popular answer here already states, you should use an anonymous function to achieve what you’d like to do. But, what deserves some extra attention IMO, is the the benefit of passing the available parameters of the action to your function.

    If somewhere, an action hook is defined like this:

    do_action('cool_action_name', $first_param, $second_param);
    

    You can pass the values of $first_param and $second_param to your own function, and add your own params like this:

    add_action('cool_action_name', 
        function ($first_param, $second_param) {
            // Assuming you're working in a class, so $this is the scope.
            $this->your_cool_method($first_param, $second_param, 'something_else'); 
        }
    );
    

    Then you can use all the values in your method, like this:

    public function your_cool_method($first_param, $second_param, $something_else)
    {
        // Do something with the params.
    }
    
  10. I ran into the same issue and solved it by using global variables. Like so:

    global $myvar;
    $myvar = value;
    add_action('hook', 'myfunction');
    
    function myfunction() {
        global $myvar;
    }
    

    A bit sloppy but it works.

  11. Why not simply as this:

    function recent_post_by_author_related($author,$number_of_posts) {
        // some commands;
    }
    
    function recent_post_by_author() {
        recent_post_by_author_related($foo, $bar);
    }
    add_action('thesis_hook_before_post','recent_post_by_author')
    
  12. Just wanted to add this here so that when I search for this next year, I know where to find this. This is modified from the latest WordPress documentation.

    Here is how to do it from WordPress’s documentation.

    // The action callback function.
    function example_callback( $arg1, $arg2 ) {
        // (maybe) do something with the args to get a value.
        return $value;
    }
    add_action( 'example_action', 'example_callback', 10, 2 ); 
    // the 2 above specifies the number of args
    
    /*
     * Trigger the actions by calling the 'example_callback()' function
     * that's hooked onto `example_action` above.
     *
     * - 'example_action' is the action hook.
     * - $arg1 and $arg2 are the additional arguments passed to the callback.
     */
    $value = do_action( 'example_action', $arg1, $arg2 );
    
  13. If you want to pass parameters to the callable function, instead of the do_action, you can call an anonymous function. Example:

    // Route Web Requests
    add_action('shutdown', function() {
        Router::singleton()->routeRequests('app.php');
    });
    

    You see that do_action('shutdown') don’t accept any parameters, but routeRequests does.

  14. I have made a code to send parameters and process.

    function recibe_data_post() {
    
    $post_data = $_POST;
    
    if (isset($post_data)) {
    
        if (isset($post_data['lista_negra'])) {
    
            $args = array (
                'btn'  =>  'lista_negra',
                'estado'=>  $post_data['lista_negra'],
            );
    
            add_action('template_redirect',
                       function() use ( $args ) {
                           recibe_parametros_btn( $args ); });
        }
        if (isset($post_data['seleccionado'])) {
            $args = array (
                'btn'  =>  'seleccionado',
                'estado'=>  $post_data['seleccionado'],
            );
    
            add_action('template_redirect',
                       function() use ( $args ) {
                           recibe_parametros_btn( $args ); });
    
            }
        }
    }
    
        add_action( 'init', 'recibe_data_post' );
    
    function recibe_parametros_btn( $args ) {
    
    $data_enc = json_encode($args);
    $data_dec = json_decode($data_enc);
    
    $btn = $data_dec->btn;
    $estado = $data_dec->estado;
    
    fdav_procesa_botones($btn, $estado);
    
    }
    
    function fdav_procesa_botones($btn, int $estado) {
    
    $post_id = get_the_ID();
    $data = get_post($post_id);
    
    if ( $estado == 1 ) {
        update_field($btn, 0, $post_id);
        } elseif ( $estado == 0 ) {
           update_field($btn, 1, $post_id);
        }
    
    }