4

I am trying to understand some fundamentals of php with regards to adding new functions to actions. I found a tutorial where he adds a new function to the save_post action…

add_action('save_post', 'log_when_saved'); 

function log_when_saved($post_id){ do something with $post_id };

Is my understanding correct that when we fire the action elsewhere via do_action('save_post', $post_ID, $post, $update) the parameters we pass it at this time are automatically & instantly available to use in our new function we are adding to the action and that is what is going on here?

When we write add_action('save_post', ‘log_when_saved’); we are adding some new function to be run when the action is fired. This new function to be run can automatically use the variable values that were defined when we fired the action via the do_action. Is this correct?

What if we wanted to pass in the $post and $update parameters to this new function also… would we have to do the following…

add_action('save_post', 'log_when_saved'); 

function log_when_saved($post_id, $post, $update){ do something with $post_id, $post, $update};

One of the fundamental things I am trying to understand is do the parameters that we are passing our new function strictly have to be in the order that they were defined in do_action('save_post', $post_ID, $post, $update) and similarly, would you have to call all 3 if we wanted to get the last parameter $update to use in our function?

With regards to naming rules, could we also do the following…

add_action('save_post', 'log_when_saved'); 

function log_when_saved($some_random_variable_name){ do something with $post_id };

and it would know that $some_random_variable_name would be the post id because is first defined argument in our do_action(‘save_post’, $post_ID, $post, $update) statement?

Thank you in advance,

2 Answers 2

8

For this example lets say we have the following

do_action('bt_custom_action', get_the_ID(), get_the_title(), get_the_content());

The arguments that will be passed to add_action would be in this order

  1. the post id
  2. the post title
  3. the post content

By default if we hook into our do_action without any arguments, like this

add_action('bt_custom_action', 'bt_callback_func');

Our call back function "gets" one argument, in this case the ID

function bt_callback_func ($id) {
    echo $id;
}

In order to get the post content we would need to do something like this

add_action('bt_custom_action', 'bt_callback_func', 10, 3);

This will pass three arguments to our callback function and to use them we would do something like this

function bt_callback_func ($id, $title, $content) {
    echo $id . '<br>' . $title . '<br>' . $content;
}

Now to finally answer your question about getting a specific argument.

If we go by this example

add_action('bt_custom_action', 'bt_callback_func', 10, 3);

we know that our callback function will get three arguments, but we only need the content. We don't have to set parameters for each and every expected argument. We can use PHPs ..., see Variable-length argument lists.

So now our callback function will look like this

function bt_callback_func (...$args) {
    echo args[2];
}

Because in our add action we told that our callback will expect three arguments and we know that the third argument is what we need (the content), using ... we now created a variable that will contain all passed argumnets.

In this case it will contain an array with three elements in order. ID, title and then content.

We know that content is last, third, in our array. We can target it directly like this $args[2].

Hope this helps =]

1
  • Great explanation, exactly what I was looking for thanks. The ... token looks like a life-saver and it's great to know how this works in more detail.
    – Lewis
    Commented Feb 19, 2021 at 20:14
6

Everything Butterend_Toast says is correct, but I want to touch on why it works that way.

Under the hood, do_action() and apply_filters() are just calling the core PHP function call_user_func_array(). When you run add_action() you are storing a reference to a callable, with a number representing how many arguments that function accepts.

add_action(
    'save_post', // Hook name
    'log_when_saved', // Callable.
    10, // Priority
    3 // How many arguments the callable can accept.
);

So when you call do_action(), WordPress uses call_user_func_array() with that callable, along with an array of whichever arguments were passed to do_action().

To avoid certain errors, and to allow developers to use functions that accept fewer arguments than were passed to do_action(), WordPress will truncate the array passed to call_user_func_array() to match he number of accepted arguments given in the add_action() call. If it didn't do this then you would get a fatal error if your function didn't accept all the variables.

So, the variables that you can use in your callback function are determined by:

  1. Which arguments were passed to do_action().
  2. How many variables you chose to accept when running add_action().

The name of the variables does not matter, but the order does. This is how all PHP functions work. If I write this function:

function add_numbers( $one, $two ) {
    return $one + $two;
}

The first argument passed to the function will be given the variable name $one inside that function, and the second will be given the name $two. The variable names used outside the function don't matter:

$a = 1;
$b = 3;
$c = add_numbers( $a, $b ); // Works fine.
$d = add_numbers( 2, 4 ); // Even if I don't pass named variables.

The same is true for hooks. The parameters passed to do_action() are passed as arguments to call_user_func_array() in order. So if I hook like this:

add_action( 'save_post', 'log_when_saved', 10, 3 );

Then this works:

function log_when_saved( $post_id, $post, $update ) {
    echo $post_id;
}

And so does this:

function log_when_saved( $a $b, $c ) {
    echo $a;
}

But regardless of the name, $a will always be the post ID passed to do_action() because it's the first argument. So in this example, $b is the post ID:

function log_when_saved( $b, $a, $c ) {
    echo $b;
}

That's why it's a good practice to match the variable names to what was being passed. Not for any technical reason, but to make it easier to understand.

This will not work:

function log_when_saved( $a, $b, $c ) {
    echo $post_id;
}

Because $post_id is undefined. If you want to use the ID of the post being saved, you need to use the one passed as the first argument.


This all raises the question of how to know which variables you can or need to accept in your hook callbacks. That's where the documentation comes in. If you look at the documentation for save_post you will see which arguments are passed to callback functions, so you know which argument represents which kind of value.

1
  • Thanks a lot for the info. I was wondering if the arguments passed to the save_post action were therefore the ones that were usable in the callbacks so that's great to have it all clarified!
    – Lewis
    Commented Feb 19, 2021 at 20:11

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.