User login

Tokens in Drupal 7 session by Jeff Eaton

(Jeff Eaton kicks off.)

Canonical use for token is mailmerge.

User mail is hardcoded system in Drupal 6, it doesn't use token.

Pathauto. Everyone is using it, or should be.

Immediately we proposed it for inclusion in Drupal core. More than three years ago.

Really, it wasn't good enough for core.

Organization: An all-you-can-eat buffet of crazy-- too many modules made tokens available, with all different names, and no categorization.

Code reuse: Date formats are hell.

Localization: Like with date More and more tokens?

Performance: No, you can't have a menu-tree token. It would have to hit the menu tree, find the author's favorite food, bring all the potential replacements even if they aren't used.

Security: What might an admin put into a mail or node? The horrible -raw solution. With training and discipline, you could use the right token (the -raw one) for title.

Nonetheless, third-most downloaded

Token has been rewritten from scratch to get into core, and it is in core, and all five of those problems have been solved.

Changes in how it works:

Lazy generation: only build values if a token is used. You ask the token API to replace any tokens in a text, and it finds the three in there, and it looks up the replacement for those three tokens. Now we can provide very complex tokens, because the cost is only incurred when the token is used.

More context: Locale is passed to value-builders (the token as described above, plus the language and time zone and such, and then it is built right); Raw/filtered is set by module, when token-replace is called to replace the tokens in the text, pathauto for instance

Token chaining: [node:author:last-login:since], [file:owner:email] -- instead of many many tokens with really long awkward names, the first one says 'give me how long it has been since the author of this node logged in', the latter says 'give me the e-mail of the owner of the file'. This helps greatly in reuse of token replacement generation code.

Now in core for nodes, users, taxonomy, files, dates, site informaion and more.

Use in user notification e-mails, e-mail actions, path redirection actions... did you notice actions? Display a message to the user action etc. are token aware.

Tokens in code.

You have otter module.


Tokens are now always surrounded by square brackets.

Furthermore, tokens are always namespaced, so that they can be handed off to the module that cares about them.

otter is token type here, and owner:name is the actual token as far as the otter module is concerned. Still, the semicolon in there is also recognized as a potential chaining point. In this case, owner points to user tokens, and name is the user token.

How to use tokens in your code:

  $data = array(
    'node' => $node,
    'user' => $account,
  $text = "Hi, [user:name]: [node:author:name] posted [node:title]!";
  $text = token_replace($text, $data);

The convention is that if you have a chaining point, you should provide a sensible default value (so in theory

How to add tokens? Hooks!

function otter_token_info() {
  $info['types']['otter'] = array(
    'name' => t("Otter"),
    'description' => t("Tokens for otter data."),
  $info['tokens']['otter']['color'] = array(
    'name' => t("Color"),
    'description' => t("The color of the otter."),
  $info['tokens']['otter']['owner'] = array(
    'name' => t("Owner"),
    'description' => t("The otter's owner."),
    'type' => 'user',
  return $info;

The     'type' => 'user', is where the magic chaining takes place. It says that what the owner token really has available is everything done by user-type tokens.

function otter_tokens($type, $tokens, $data, $options) {
  $replacements = array();
  $sanitize = !empty($options['sanitize']);

  if ($type == 'otter' && !empty($data['otter'])) {
    $otter = $data['otter'];
    foreach ($tokens as $name => $original) {
      if ($name == 'otter') {
        if ($sanitize) {
          $value = check_plain($otter->name);
        else {
          $value = $otter->name;
        $replacements[$original] = $value;
  return $replacements;

This is well-documented in code:

You can replace other people's tokens, including core's.

When providing the sanitized version, you have to

To implement the chaining:

token_find_with_prefix($tokens, 'owner')


Look in core for users and files for a real working implementations of all this.

The dirty secret is that hook_token_info() is not used at all in token generation; it is only used in help text and such. So you don't get any metadata from there, it's not actually needed, you have to do your chaining yourself in hook_tokens().

Your module: I have text, and an object! Replace its tokens!
Token module (API): I'll extract tokens from the text, and ask modules applicable to each type to replace them.
Other modules: We'll build values for each key in the array token gave us!
Token: I'll use them to replace the tokens in your text with the actual values!

Token module still lives in contrib, even though all this stuff is in core.

(Dave Reid takes over here.)

Stuff that didn't make it into core:

  • Node translations- [node:source:title] - the original language title.
  • Field values - [node:my-cck-field:2]
  • Profile tokens, groan, coming...

Token help! An actual user interface! Autocomplete!

See token module in Examples module suite!

Clickable tokens, and inserts into your text field.

You can enter custom date functions in the token

Dave Reid made a form-inserting token. As in, put in your login form.

Could do the same with views.


Thank you for simplifying all the documentation

Wow -- this is really simple. I have been going around in circles trying to figure this out, should have skipped all the api docs and started on this page. Thank you -- saved me a whole night of work.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • You can use Markdown syntax to format and style the text. Also see Markdown Extra for tables, footnotes, and more.
  • 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> <img> <blockquote> <small> <h2> <h3> <h4> <h5> <h6> <sub> <sup> <p> <br> <strike> <table> <tr> <td> <thead> <th> <tbody> <tt> <output>
  • Lines and paragraphs break automatically.

More information about formatting options

By submitting this form, you accept the Mollom privacy policy.