Tokens in Drupal 7

This article is a followup to Creating Tokens in Drupal 6. Part of the token module was moved into core for Drupal 7, meaning it is even easier to create and use tokens programatically. I'm going to walk through how to use tokens programatically in Drupal 7, both using existing tokens and creating new ones.

The token replacement process always begins with a call to token_replace(), which takes a string of text, scans it for tokens, calculates the value of those tokens, and replaces the tokens with their values. Let's assume I have a module that wants to override the submitted text for a node, allowing users to enter token strings per-node-type to be displayed on the node view. Of course we could do this with a pretty simple template override in the theme, but we'll assume that this needs to be quickly changable without touching any files.

The first thing I'm going to do is create the administrator interface for this module by hooking into the node type form. For the sake of brevity, I'll skip that part here, but I've attached a screenshot of the end product, which is a textfield and a list of tokens that are available in this field. When this form is submitted, this string is saved to a variable we'll access in a moment. Let's say I save this form with a good informative submitted text; something like "[user:name] wuz here [date:short]".

Next, when we view a node that has a token override set, we're going to want to grab that variable using variable_get(), and fill in the tokens using the node we're viewing. To do this, we'll call token_replace() with our relevant data.

<?php
function lc_preprocess_node(&$vars) {

 
//Grab our tokenized string for this node type
 
$format = variable_get('node_submitted_format_' . $vars['node']->type, FALSE);

  if (
$vars['display_submitted'] && !empty($format)) {

   
//Build an array of the data we need for this replacement.
    //These are: the node, the created date, and the author.
   
$data = array(
     
'node' => $vars['node'],
     
'date' => $vars['node']->created,
     
'user' => user_load($vars['uid']),
    );
   
   
//Pass it to token_replace() to work the magic.
   
$vars['submitted'] = token_replace($format, $data);
  }
}
?>

So now we've gone from "[user:name] wuz here [date:short]" to "Fluffy wuz here 7/11/11." The thing about tokens is that they're only flexible to a point. Generic tokens can only take you so far, since you can't really pass arguments to them (except for the [date:custom] token). Let's imagine a situation where you need to show the season instead of the actual date. We're going to create a token to cover you for this important use case. First, we'll start by implementing hook_token_info().

<?php
function lc_token_info() {
 
$tokens = array(
   
'tokens' => array(
     
'date' => array(
       
'season' => array(
         
'name' => t('Season'),
         
'description' => t('The season of the year.'),
        )
      )
    )
  );
  return
$tokens;
}
?>

This crazy nested array says we have a date token, keyed season (this is what you will put after the colon: i.e. [date:season], and gives it some human readable information. Next, we need to implement hook_tokens(), which is where we actually provide a value for our season token.

<?php
function lc_tokens($type, $tokens, $data = array(), $options = array()) {
 
//We fill up $replacements with any values we need.
 
$replacements = array();
 
  if(
$type == 'date' && !empty($data['date'])) {
   
$date = $data['date'];

    foreach(
$tokens as $name => $original) {
      switch(
$name) {
        case
'season':
         
$seasons = array("Winter","Spring","Summer","Autumn");
         
//Grab the month off the timestamp and convert it to a season
          //This is not accurate, just a ballpark.
         
$replacements[$original] = $seasons[(int)((date("n", $date) %12)/3)];
      }
    }
  }

  return
$replacements;
}
?>

Now, "[user:name] wuz here [date:season] '[date:custom:y]" becomes "Fluffy wuz here Summer '11."