10

How do I pass data between hooks that don't interact between them, or between a menu callback and a hook?

In the case the two hooks have a parameter in common, and that parameter is passed by reference, that is easy. What do I do when the hooks, or the menu callback and the hook, don't get a common parameter?

12

In Drupal 7, or higher, use a static variable handled with drupal_static().
drupal_static() is a function that handles a central storage for static variables. Differently from the variables declared using the static keyword, the static variables handled with drupal_static() are accessible from every function; this is possible because drupal_static() returns the variable's content by reference, allowing every function to alter it.

Suppose that you need to pass a value between a menu handler, and the implementation of hook_block_view(); you could use the following code.

function mymodule_menu() {
  return array('path/%' => array(
    'page callback' => 'mymodule_callback_function',
    'page arguments' => array(1),
  ));
}

function mymodule_callback_function($data) {
  $data_passer = &drupal_static('mymodule_block_data');

  $data_passer = $data;

  // Other logic specific to this page callback.
}

function mymodule_block_view($delta = '') {
  // $data_passer will now contain the value of $data, from above.
  $data_passer = &drupal_static('mymodule_block_data');

  // Change the block content basing on the content of $data_passer.
}

In the case that data need to be accessed more frequently, you should use a static local variable that would contain the value returned from drupal_static(). As static variables can only be initialized from literal value, and static variables cannot be assigned to references, the only working code is similar to the following one. (This code is taken from user_access().)

  // Use the advanced drupal_static() pattern, since this is called very often.
  static $drupal_static_fast;
  if (!isset($drupal_static_fast)) {
    $drupal_static_fast['perm'] = &drupal_static(__FUNCTION__);
  }
  $perm = &$drupal_static_fast['perm'];

The value returned from drupal_static() is reset every time Drupal bootstraps; if you need a value that is preserved between different pages, then you need to use a database table to store the value, or use variable_get()/variable_set().

Drupal 6 doesn't implement drupal_static(), but you could copy its code in a function defined in your own module.

function &mymodule_static($name, $default_value = NULL, $reset = FALSE) {
  static $data = array(), $default = array();

  // First check if dealing with a previously defined static variable.
  if (isset($data[$name]) || array_key_exists($name, $data)) {
    // Non-NULL $name and both $data[$name] and $default[$name] statics exist.
    if ($reset) {
      // Reset pre-existing static variable to its default value.
      $data[$name] = $default[$name];
    }
    return $data[$name];
  }

  // Neither $data[$name] nor $default[$name] static variables exist.
  if (isset($name)) {
    if ($reset) {
      // Reset was called before a default is set and yet a variable must be
      // returned.
      return $data;
    }
    // First call with new non-NULL $name. Initialize a new static variable.
    $default[$name] = $data[$name] = $default_value;
    return $data[$name];
  }

  // Reset all: ($name == NULL). This needs to be done one at a time so that
  // references returned by earlier invocations of drupal_static() also get
  // reset.
  foreach ($default as $name => $value) {
    $data[$name] = $value;
  }

  // As the function returns a reference, the return should always be a
  // variable.
  return $data;
}

Before using a static variable with drupal_static() (or the back ported function defined in your module), you should keep these considerations in mind:

  • The code works only when the code that sets the static variable runs before the code to get its value; if the execution order is not the thought one, the code doesn't work. When the order of execution is not clearly defined in Drupal documentation, there is the risk the order changes in future versions of Drupal; check the execution order doesn't change in the Drupal version for which you are implementing your code.
  • Drupal could have implemented a mechanism to share data between different hooks. For example, in the case of different implementations of hook_form_alter(), each implementation can share data with other hook_form_alter() implementations using $form_state; in the same way, form validation handlers, and form submission handlers, can share data using the $form_state parameter that is passed by reference. Before implementing your own code, verify it is possible to share data using a different mechanism already implemented by Drupal for the specific case.
  • Really appreciate the answer given here for the original poster. However my concern is that I am told that using drupal static variables doesn't scale very well - for sites handling a lot of requests, owing to the fact that the whole set of variables is loaded each time, for each session (or something like that.) I was told this by a colleague who had been working on a performance issue related to this. What do you think? They advised that using the Drupal cache would be a better way to pass variables around (assuming they aren't needed after the data had been received by the destination code) – therobyouknow Jul 2 '14 at 13:15
  • @therobyouknow "for sites handling a lot of requests, owing to the fact that the whole set of variables is loaded each time" that's the variables table, and not any static variable, which is a completely different thing. static variables themselves have negligible performance impact. Even in the variables table, you'd have to abuse the system a lot to cause issues. Economist.com can easily have several 100k hits in an hour, all of which load the variables table. Variables are not an issue, but of course, we only store small pieces of info in each variable. – Letharion Jul 2 '14 at 14:20

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