0

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');
}
3
  • i'm a little confused, none of your action names seem to match, you tried to fire an action named use_colors_here but no functions were added to that action so it does nothing. Then you have a function with that same name added to an unrelated action wp_body_open, but you try to use a $data argument, but wp_body_open does not provide any arguments! And then, you fire off a 3rd action use_colors_there that has nothing added to it either, so that doesn't do anything ( was this a typo? _there? _here? ).
    – Tom J Nowell
    Feb 9 at 10:18
  • I suspect you've fallen into the X Y problem trap. Would I be right that your actual problem is displaying the colours on the body open action? And that all of the action stuff you asked about it just you trying to figure out how to solve that problem? I also see you have a use_colors_there function, but it's not used anywhere
    – Tom J Nowell
    Feb 9 at 10:23
  • @TomJNowell - yes that sounds about right. I believe I've misused add_action. I'm trying to pass $data to both functions, use_colors_here and use_colors_there, which hook to wp_body_open and wp_enqueue_scripts respectively.
    – Daveh0
    Feb 9 at 10:30
0

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 and use_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 to header.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).

11
  • I'm a little confused. I thought I was following the example correctly, but I guess not. In the end, I just need to be able to access $data from inside my 2 functions, use_colors_here and use_color_there.
    – Daveh0
    Feb 9 at 10:35
  • In that case, then you can't do it the way you've done it. Because the $data variable is indeed not accessible from inside the functions. However, you could try using closure - or yes, that do_action('wp_body_open', $data); also works - but I wonder why are you calling the hook manually..
    – Sally CJ
    Feb 9 at 10:39
  • what do you mean by "calling the hook manually"?
    – Daveh0
    Feb 9 at 10:43
  • 1
    @Daveh0 it's best to think of hooks and actions as events on a timeline. add_action is the same as "when this event happens, run this", and do_action is the same as "omg the event is happening, run all the things we were asked to run". It makes more sense that way, especially since the order of things matters ( if you ask it to run something on init after init has happened, it won't run, as that requires time travel, it's already happened! It's like trying to board a train after it's left )
    – Tom J Nowell
    Feb 9 at 11:52
  • 1
    @Daveh0 does the "Update 2" help?
    – Sally CJ
    Feb 9 at 14:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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