RTrouble passing arguments to action
문제
I know there are several posts on this topic. I have followed many of them as well as other resources on the web and thought I had it down, but it does not seem to be working. In my case, I have 2 functions hooked to 2 separate actions that need the same data.
$data = array('red', 'green', 'blue');
do_action('use_colors_here', $data);
add_action('wp_body_open', 'use_colors_here', 10, 1);
function use_colors_here($data) {
$out = '<h1>Colors</h1>';
$out .= '<p>Color 1: ' . $data[1] . '</p>';
$out .= '<p>Color 2: ' . $data[2] . '</p>';
echo $out;
}
do_action('use_colors_there', $data);
add_action('wp_enqueue_scripts', 'use_colors_there', 10, 1);
function use_colors_there($data) {
wp_enqueue_script('color-script-1', get_stylesheet_directory_uri() . '/js/colors_' . $data[1] . '.js');
wp_enqueue_script('color-script-2', get_stylesheet_directory_uri() . '/js/colors_' . $data[2] . '.js');
}
The expected behavior was for the values of $data[1]
and $data[2]
(green and blue respectively) to be accessible by the 2 functions and printed out in the markup each is responsible for adding to the page. Unfortunately, $data
was not accessible inside the functions and I end up with markup looking like:
<h1>Colors</h1>
<p>Color 1: </p>
<p>Color 2: </p>
What am I doing wrong?
UPDATE
After reading a bit closer, I see that I have to use the hook rather than the function name as the 1st argument of do_action
. So I changed it to:
do_action('wp_body_open', $data);
and it seems to be working.
FINAL CODE (using accepted answer)
function get_component_config() {
return ['red', 'green', 'blue'];
}
add_action('wp_body_open', 'use_colors_here');
function use_colors_here() {
$data = get_component_config();
$out = '<h1>Colors</h1>';
$out .= '<p>Color 1: ' . $data[1] . '</p>';
$out .= '<p>Color 2: ' . $data[2] . '</p>';
echo $out;
}
add_action('wp_enqueue_scripts', 'use_colors_there');
function use_colors_there() {
$data = get_component_config();
wp_enqueue_script('color-script-1', get_stylesheet_directory_uri() . '/js/colors_' . $data[1] . '.js');
wp_enqueue_script('color-script-2', get_stylesheet_directory_uri() . '/js/colors_' . $data[2] . '.js');
}
해결책
You have an action hook named use_colors_here
and you run the hook using do_action( 'use_colors_here', $data )
, but I don't see where you call add_action()
for that hook?
I mean, the do_action()
and add_action()
should be used like so:
// This adds a callback to the use_colors_here hook.
add_action( 'use_colors_here', 'some_function' );
// And this is the callback which accepts one parameter from the use_colors_here hook.
function some_function( $data ) {
var_dump( $data );
}
// And this do_action() executes the above callback (and other callbacks added to the
// `use_colors_here` hook).
$data = array( 'red', 'green', 'blue' );
do_action( 'use_colors_here', $data );
UPDATE (revised)
I see that I have to use the hook rather than the function name as the 1st argument of
do_action
Yes that's right, the first parameter for do_action()
is indeed the hook name, whereas the second and the rest of parameters are the parameters passed to the hook callbacks — but in the case of two or more parameters, you need to use the 4th parameter for add_action()
to specify the number of parameters that your callback accepts.
// This fires the some_hook hook which has two parameters.
do_action( 'some_hook', 'param 1', 'param 2' );
// So when adding your action, if you want (to access) both the above parameters
// from your callback, then you must set the 4th parameter below to 2.
add_action( 'some_hook', 'some_function', 10, 2 );
function some_function( $param1, $param2 ) {
var_dump( $param1, $param2 );
}
And do_action()
(although indirectly) actually uses call_user_func_array()
to run the actions for a specific (action) hook, or to run the callbacks registered to run on that hook, so if you understand how call_user_func_array()
works, then you'd also understand how do_action()
works.
function my_callback( $foo, $bar ) {
var_dump( $foo, $bar, __FUNCTION__ );
}
$data = 'using call_user_func_array()';
// this calls my_callback() directly
call_user_func_array( 'my_callback', array( $data, 'second param' ) );
add_action( 'hook_name', 'my_callback', 10, 2 );
$data = 'using do_action()';
// but this fires the "event"/hook named hook_name and then it calls
// my_callback() indirectly via call_user_func_array()
// i.e. it calls call_user_func_array( 'my_callback', array( $data, 'yay, it works!' ) )
do_action( 'hook_name', $data, 'yay, it works!' );
I just need to be able to access
$data
from inside my 2 functions,use_colors_here
anduse_color_there
Then you're doing it correctly, i.e. when you do do_action('wp_body_open', $data)
, the $data
will be passed to your 2 functions (and all other actions/callbacks for the hook).
But without calling do_action()
manually and without using the global
keyword or even $GLOBALS
, the other possible way to access the $data
variable is by using a closure; however, the drawback is that it'll be next to impossible to remove the specific action using remove_action()
— because closures are anonymous functions, so what name would we use when removing the action?
add_action( 'some_hook', 'some_function', 10, 2 );
// that can be easily removed like so:
remove_action( 'some_hook', 'some_function', 10 );
$data = array( 'red', 'green', 'blue' );
add_action( 'some_hook', function () use ( $data ) {
var_dump( $data );
} );
// now with that one, how do we remove it?
//remove_action( 'some_hook', ??, 10 );
Therefore, if you just wanted to get access to the data in the $data
, then you could use a custom function which returns the data:
function get_colors() {
return array( 'red', 'green', 'blue' );
}
add_action( 'wp_body_open', 'use_colors_here' );
function use_colors_here() {
$data = get_colors();
var_dump( $data );
}
I thought it would be better to keep everything in
functions.php
rather than adding more code toheader.php
... just for organizational purposes.
Yes (although not exactly "everything"), I thought the same as well. But in my original "Update" section, I was just saying you could (or that it's just fine if you) explicitly call your actions after the hook is fired. I.e.
// Let's say you define $data here. (in same file or block of code)
$data = array( 'red', 'green', 'blue' );
// So instead of..
do_action( 'wp_body_open', $data );
// You can do..
wp_body_open(); // this fires the wp_body_open hook
use_colors_here( $data );
// But that means, use_colors_here() should no longer be hooked on to the hook.
Does calling the function directly in
header.php
have any significant performance advantages?
No, but calling it directly means you can easily pass the $data
and other parameters..
Otherwise, for local variables like the $data
above, then we would need to use a closure in order to access the variable (and eventually the data inside or referenced to by that variable).