Project

General

Profile

Paste
Download (9.63 KB) Statistics
| Branch: | Revision:

root / drupal7 / includes / token.inc @ a8cee257

1
<?php
2

    
3
/**
4
 * @file
5
 * Drupal placeholder/token replacement system.
6
 *
7
 * API functions for replacing placeholders in text with meaningful values.
8
 *
9
 * For example: When configuring automated emails, an administrator enters
10
 * standard text for the email. Variables like the title of a node and the date
11
 * the email was sent can be entered as placeholders like [node:title] and
12
 * [date:short]. When a Drupal module prepares to send the email, it can call
13
 * the token_replace() function, passing in the text. The token system will
14
 * scan the text for placeholder tokens, give other modules an opportunity to
15
 * replace them with meaningful text, then return the final product to the
16
 * original module.
17
 *
18
 * Tokens follow the form: [$type:$name], where $type is a general class of
19
 * tokens like 'node', 'user', or 'comment' and $name is the name of a given
20
 * placeholder. For example, [node:title] or [node:created:since].
21
 *
22
 * In addition to raw text containing placeholders, modules may pass in an array
23
 * of objects to be used when performing the replacement. The objects should be
24
 * keyed by the token type they correspond to. For example:
25
 *
26
 * @code
27
 * // Load a node and a user, then replace tokens in the text.
28
 * $text = 'On [date:short], [user:name] read [node:title].';
29
 * $node = node_load(1);
30
 * $user = user_load(1);
31
 *
32
 * // [date:...] tokens use the current date automatically.
33
 * $data = array('node' => $node, 'user' => $user);
34
 * return token_replace($text, $data);
35
 * @endcode
36
 *
37
 * Some tokens may be chained in the form of [$type:$pointer:$name], where $type
38
 * is a normal token type, $pointer is a reference to another token type, and
39
 * $name is the name of a given placeholder. For example, [node:author:mail]. In
40
 * that example, 'author' is a pointer to the 'user' account that created the
41
 * node, and 'mail' is a placeholder available for any 'user'.
42
 *
43
 * @see token_replace()
44
 * @see hook_tokens()
45
 * @see hook_token_info()
46
 */
47

    
48
/**
49
 * Replaces all tokens in a given string with appropriate values.
50
 *
51
 * @param $text
52
 *   A string potentially containing replaceable tokens.
53
 * @param $data
54
 *   (optional) An array of keyed objects. For simple replacement scenarios
55
 *   'node', 'user', and others are common keys, with an accompanying node or
56
 *   user object being the value. Some token types, like 'site', do not require
57
 *   any explicit information from $data and can be replaced even if it is
58
 *   empty.
59
 * @param $options
60
 *   (optional) A keyed array of settings and flags to control the token
61
 *   replacement process. Supported options are:
62
 *   - language: A language object to be used when generating locale-sensitive
63
 *     tokens.
64
 *   - callback: A callback function that will be used to post-process the array
65
 *     of token replacements after they are generated. For example, a module
66
 *     using tokens in a text-only email might provide a callback to strip HTML
67
 *     entities from token values before they are inserted into the final text.
68
 *   - clear: A boolean flag indicating that tokens should be removed from the
69
 *     final text if no replacement value can be generated.
70
 *   - sanitize: A boolean flag indicating that tokens should be sanitized for
71
 *     display to a web browser. Defaults to TRUE. Developers who set this
72
 *     option to FALSE assume responsibility for running filter_xss(),
73
 *     check_plain() or other appropriate scrubbing functions before displaying
74
 *     data to users.
75
 *
76
 * @return
77
 *   Text with tokens replaced.
78
 */
79
function token_replace($text, array $data = array(), array $options = array()) {
80
  $text_tokens = token_scan($text);
81
  if (empty($text_tokens)) {
82
    return $text;
83
  }
84

    
85
  $replacements = array();
86
  foreach ($text_tokens as $type => $tokens) {
87
    $replacements += token_generate($type, $tokens, $data, $options);
88
    if (!empty($options['clear'])) {
89
      $replacements += array_fill_keys($tokens, '');
90
    }
91
  }
92

    
93
  // Optionally alter the list of replacement values.
94
  if (!empty($options['callback']) && function_exists($options['callback'])) {
95
    $function = $options['callback'];
96
    $function($replacements, $data, $options);
97
  }
98

    
99
  $tokens = array_keys($replacements);
100
  $values = array_values($replacements);
101

    
102
  return str_replace($tokens, $values, $text);
103
}
104

    
105
/**
106
 * Builds a list of all token-like patterns that appear in the text.
107
 *
108
 * @param $text
109
 *   The text to be scanned for possible tokens.
110
 *
111
 * @return
112
 *   An associative array of discovered tokens, grouped by type.
113
 */
114
function token_scan($text) {
115
  // Matches tokens with the following pattern: [$type:$name]
116
  // $type and $name may not contain  [ ] characters.
117
  // $type may not contain : or whitespace characters, but $name may.
118
  preg_match_all('/
119
    \[             # [ - pattern start
120
    ([^\s\[\]:]*)  # match $type not containing whitespace : [ or ]
121
    :              # : - separator
122
    ([^\[\]]*)     # match $name not containing [ or ]
123
    \]             # ] - pattern end
124
    /x', $text, $matches);
125

    
126
  $types = $matches[1];
127
  $tokens = $matches[2];
128

    
129
  // Iterate through the matches, building an associative array containing
130
  // $tokens grouped by $types, pointing to the version of the token found in
131
  // the source text. For example, $results['node']['title'] = '[node:title]';
132
  $results = array();
133
  for ($i = 0; $i < count($tokens); $i++) {
134
    $results[$types[$i]][$tokens[$i]] = $matches[0][$i];
135
  }
136

    
137
  return $results;
138
}
139

    
140
/**
141
 * Generates replacement values for a list of tokens.
142
 *
143
 * @param $type
144
 *   The type of token being replaced. 'node', 'user', and 'date' are common.
145
 * @param $tokens
146
 *   An array of tokens to be replaced, keyed by the literal text of the token
147
 *   as it appeared in the source text.
148
 * @param $data
149
 *   (optional) An array of keyed objects. For simple replacement scenarios
150
 *   'node', 'user', and others are common keys, with an accompanying node or
151
 *   user object being the value. Some token types, like 'site', do not require
152
 *   any explicit information from $data and can be replaced even if it is
153
 *   empty.
154
 * @param $options
155
 *   (optional) A keyed array of settings and flags to control the token
156
 *   replacement process. Supported options are:
157
 *   - language: A language object to be used when generating locale-sensitive
158
 *     tokens.
159
 *   - callback: A callback function that will be used to post-process the
160
 *     array of token replacements after they are generated. Can be used when
161
 *     modules require special formatting of token text, for example URL
162
 *     encoding or truncation to a specific length.
163
 *   - sanitize: A boolean flag indicating that tokens should be sanitized for
164
 *     display to a web browser. Developers who set this option to FALSE assume
165
 *     responsibility for running filter_xss(), check_plain() or other
166
 *     appropriate scrubbing functions before displaying data to users.
167
 *
168
 * @return
169
 *   An associative array of replacement values, keyed by the original 'raw'
170
 *   tokens that were found in the source text. For example:
171
 *   $results['[node:title]'] = 'My new node';
172
 *
173
 * @see hook_tokens()
174
 * @see hook_tokens_alter()
175
 */
176
function token_generate($type, array $tokens, array $data = array(), array $options = array()) {
177
  $options += array('sanitize' => TRUE);
178
  $replacements = module_invoke_all('tokens', $type, $tokens, $data, $options);
179

    
180
  // Allow other modules to alter the replacements.
181
  $context = array(
182
    'type' => $type,
183
    'tokens' => $tokens,
184
    'data' => $data,
185
    'options' => $options,
186
  );
187
  drupal_alter('tokens', $replacements, $context);
188

    
189
  return $replacements;
190
}
191

    
192
/**
193
 * Returns a list of tokens that begin with a specific prefix.
194
 *
195
 * Used to extract a group of 'chained' tokens (such as [node:author:name])
196
 * from the full list of tokens found in text. For example:
197
 * @code
198
 *   $data = array(
199
 *     'author:name' => '[node:author:name]',
200
 *     'title'       => '[node:title]',
201
 *     'created'     => '[node:created]',
202
 *   );
203
 *   $results = token_find_with_prefix($data, 'author');
204
 *   $results == array('name' => '[node:author:name]');
205
 * @endcode
206
 *
207
 * @param $tokens
208
 *   A keyed array of tokens, and their original raw form in the source text.
209
 * @param $prefix
210
 *   A textual string to be matched at the beginning of the token.
211
 * @param $delimiter
212
 *   An optional string containing the character that separates the prefix from
213
 *   the rest of the token. Defaults to ':'.
214
 *
215
 * @return
216
 *   An associative array of discovered tokens, with the prefix and delimiter
217
 *   stripped from the key.
218
 */
219
function token_find_with_prefix(array $tokens, $prefix, $delimiter = ':') {
220
  $results = array();
221
  foreach ($tokens as $token => $raw) {
222
    $parts = explode($delimiter, $token, 2);
223
    if (count($parts) == 2 && $parts[0] == $prefix) {
224
      $results[$parts[1]] = $raw;
225
    }
226
  }
227
  return $results;
228
}
229

    
230
/**
231
 * Returns metadata describing supported tokens.
232
 *
233
 * The metadata array contains token type, name, and description data as well
234
 * as an optional pointer indicating that the token chains to another set of
235
 * tokens.
236
 *
237
 * For example:
238
 * @code
239
 *   $data['types']['node'] = array(
240
 *     'name' => t('Nodes'),
241
 *     'description' => t('Tokens related to node objects.'),
242
 *   );
243
 *   $data['tokens']['node']['title'] = array(
244
 *     'name' => t('Title'),
245
 *     'description' => t('The title of the current node.'),
246
 *   );
247
 *   $data['tokens']['node']['author'] = array(
248
 *     'name' => t('Author'),
249
 *     'description' => t('The author of the current node.'),
250
 *     'type' => 'user',
251
 *   );
252
 * @endcode
253
 *
254
 * @return
255
 *   An associative array of token information, grouped by token type.
256
 */
257
function token_info() {
258
  $data = &drupal_static(__FUNCTION__);
259
  if (!isset($data)) {
260
    $data = module_invoke_all('token_info');
261
    drupal_alter('token_info', $data);
262
  }
263
  return $data;
264
}