20 APIs in 20 Days: Remember to Bring Your Tokens

May
20

This post is an entry in our 20 APIs in 20 Days series. Learn more about how best practices lead to sustainable development at www.trellon.com.

Often, the most important part of a website is the users of the site itself. They are unique individuals who form the basis of thriving communities. It's vital to support users by delivering content in predictable and personalized ways.

One of the big challenges you are going to face is providing personalized information to users without writing a lot of boilerplate code. Also, it's important that you be able to easily enter unique data into strings that will end up being displayed to users.

Thankfully, Drupal offers a convenient method for inserting variables within content through the Token module. As opposed to simple string replacement, the Token module offers developers a uniform way to work with strings that can be reused in different parts of the system. It naturally integrates with other modules, and reduces the total amount of content a developer would need to write in order to create a personalized web site.

Translate This!

Before we dive into the Token module it's important to understand the role it fills within the Drupal ecosystem. There are number of methods of string replacement within Drupal that you'll want to use depending on the situation. The most commonly used of these is the translation system that is built into Drupal itself.

The translation system is very simple and it is used almost exclusively through the t() function. For instance, if we wanted to show the user a translated welcome message we could do this:

<?php
  drupal_set_message
(t('Welcome to the site, take a look around!'));
?>

As long as we provide the correct translation files for the user's language then this string will be translated. However, there is a bit of an issue here. Beyond language, there is no personalization being used. Thankfully the translate function can do string replacement as well!

<?php
 
global $user;
 
drupal_set_message(
   
t('Welcome to the site @user, take a look around!',
    array(
'@user' => $user->name)
  );
?>

Here we see our first token (the @user). This string ends up being replaced by the user's username. The symbol at the beginning of the token signifies what kind of filtering should be used on the value being inserted into the string, helping you keep your code secure.

  • !variable: inserted as is
  • @variable: escape plain text to HTML (check_plain)
  • %variable: escape text and theme as a placeholder for user-submitted
    content (check_plain + theme_placeholder)

The astute may also realize another limitation of this method, we can't have string for both singular and plural phrases. Drupal also provides a method to let us easily format a plural string using format_plural().

<?php
  format_plural
($comment_count, '1 comment', '@count comments');
?>

In the case of format_plural() the @count variable is a special one and is always used for the count inserted as the first argument.

It's worthwhile to note that these functions should only be used for interface translation. The strings that are being translated should be static and thus not a value the user has entered. However, this does allow your module to be translatable along with providing user tailored strings.

The Token API

The Token API is an excellent tool for dynamic text replacement. It allows developers to replace strings (called tokens) with variable content within a larger string. Many will already be familiar with a certain kind of token seen in the user module shipped with every Drupal package. The user module makes extensive use of tokens to generate personalized e-mails to users. For example:

!username,

Thank you for registering at !site. You may now log in to !login_uri using the following username and password:

username: !username
password: !password

You may also log in by clicking on this link or copying and pasting it in your browser:

!login_url

This is a one-time login, so it can be used only once.

After logging in, you will be redirected to !edit_uri so you can change your password.

--  !site team

In this code, the tokens are !username, !password, !login_url, !edit_uri and !site team. These tokens are replaced with the user's account name, password and login URL.

Unfortunately since the token module isn't a part of Drupal core (until Drupal 7) the user module is unable to take full advantage of it. So what does a piece of text look like to the user when using the token module?
Using the same example from the user module:

[user-name],

Thank you for registering at !site. You may now log in to [login_uri] using the following username and password:

username: [user-name]
password: [password]

You may also log in by clicking on this link or copying and pasting it in your browser:

[login_url]

This is a one-time login, so it can be used only once.

After logging in, you will be redirected to [edit_uri] so you can change your password.

--  [site team]

So, besides the idea of efficiency of code, why provide an API for something that seems so insignificant? After all, PHP already has built in string replacement functions and you could always build a string around the variables. The answer is that string replacement in Drupal is a lot more advanced than simple variable swaps, you gain a number of important benefits using this method:

  • Standardization - Tokens provide a standard way to do tokens making it easier for the end user to understand.
  • Portability - The Token API allows developers to define their own tokens that can be used by other modules and also to use tokens already provided by other modules.
  • Reference - The Token API provides listings of all the tokens available in a site. This reduces the potential for duplication of code, generating the exact same tokens in different places in the system.

Using Tokens in Your Code

Enough with the talk and time to get our hands dirty. As with many other APIs, there is a very good reason to use the Token API: it is extremely easy. Let's say you wanted to replace a piece of text with values from a node object:

<?php
  $result
= token_replace($original, 'node', $node);
?>

or in the case of our user e-mail

<?php
 
global $user;
 
$result = token_replace($user_mail, 'user', $user);
?>

That's all there is to it! Token is also nice in that all tokens that are considered to be global in scope (such as the site URL) will be available regardless of what kind of object is being used to be doing token replacement.

A Token of my Own

What if you have custom data from your module and you want to provide tokens for it?
That's just as easy to do. Say you had a module that allowed token replacement for parts of an e-mail you just use hook_token_values.

<?php
function mymodule_token_values($type, $object = NULL, $options = array()) {
 
$tokens = array();
  if (
$type == 'mail') {
   
$mail = $object;
   
$tokens = array(
     
'subject' => $mail->subject,
     
'body'    => $mail->body,
     
'to'      => $mail->to,
     
'cc'      => $mail->cc,
    );
  }

  return $tokens;
}
?>

Now there are a number of tokens available that users have access to. You can provide help for your users just as easily by using hook_token_list.

<?php
function mymodule_token_list($type = 'all') {
  if (
$type == 'mail' || $type == 'all') {
   
$tokens['mail'] = array(
     
'subject' => t('The subject of the e-mail'),
     
'body'    => t('The body of the e-mail'),
     
'to'      => t('The address the e-mail is being sent to.'),
     
'cc'      => t('Additional recipients of the e-mail.'),
    );

    return $tokens;
  }
}
?>

That's All Folks

That's all there is to it. As you've seen, it's very easy to start replacing tokens in no time at all. If you want to dig deeper there are a few additional things you can do (such as modify the tokens provided by other modules) and the API documentation is attached to this post.

Token module has been included in Drupal 7 core, meaning you get token support out of the box. Woohoo! As you can tell, we're all excited about this upcoming release :)

There is still more to come with 20 APIs in 20 days. Make sure to stick around!

AttachmentSize
Token API Documentation.txt29.31 KB

1 Comment

Thanks for the great write

Thanks for the great write up. I have another question though. What if you want to make tokens that already exist, available to other modules? I have a token that exists via cck and I want to modify the module Bulk Notify to let me add CCK field tokens. No idea how to do it and I don't think the maintainer plans on implementing it for awhile.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.

More information about formatting options