path.inc 8.58 KB
Newer Older
1
2
<?php
// $Id$
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

/**
 * @file
 * Functions to handle paths in Drupal, including path aliasing.
 *
 * These functions are not loaded for cached pages, but modules that need
 * to use them in hook_init() or hook exit() can make them available, by
 * executing "drupal_bootstrap(DRUPAL_BOOTSTRAP_PATH);".
 */

/**
 * Initialize the $_GET['q'] variable to the proper normal path.
 */
function drupal_init_path() {
  if (!empty($_GET['q'])) {
    $_GET['q'] = drupal_get_normal_path(trim($_GET['q'], '/'));
  }
  else {
    $_GET['q'] = drupal_get_normal_path(variable_get('site_frontpage', 'node'));
  }
}

/**
 * Given an alias, return its Drupal system URL if one exists. Given a Drupal
27
28
 * system URL return one of its aliases if such a one exists. Otherwise,
 * return FALSE.
29
30
31
32
33
34
35
36
 *
 * @param $action
 *   One of the following values:
 *   - wipe: delete the alias cache.
 *   - alias: return an alias for a given Drupal system path (if one exists).
 *   - source: return the Drupal system URL for a path alias (if one exists).
 * @param $path
 *   The path to investigate for corresponding aliases or system URLs.
37
38
39
40
 * @param $path_language
 *   Optional language code to search the path with. Defaults to the page language.
 *   If there's no path defined for that language it will search paths without
 *   language.
41
42
43
44
45
 *
 * @return
 *   Either a Drupal system path, an aliased path, or FALSE if no path was
 *   found.
 */
46
47
48
function drupal_lookup_path($action, $path = '', $path_language = '') {
  global $language;
  // $map is an array with language keys, holding arrays of Drupal paths to alias relations
49
50
51
  $map = &drupal_static(__FUNCTION__, array());
  $no_src = &drupal_static(__FUNCTION__ . ':no_src', array());
  $count = &drupal_static(__FUNCTION__ . ':count');
52
53

  $path_language = $path_language ? $path_language : $language->language;
54

55
56
  // Use $count to avoid looking up paths in subsequent calls if there simply are no aliases
  if (!isset($count)) {
57
    $count = db_query('SELECT COUNT(pid) FROM {url_alias}')->fetchField();
58
59
  }

60
61
  if ($action == 'wipe') {
    $map = array();
62
    $no_src = array();
63
    $count = NULL;
64
  }
65
  elseif ($count > 0 && $path != '') {
66
    if ($action == 'alias') {
67
68
      if (isset($map[$path_language][$path])) {
        return $map[$path_language][$path];
69
      }
70
      // Get the most fitting result falling back with alias without language
71
      $alias = db_query("SELECT dst FROM {url_alias} WHERE src = :src AND language IN(:language, '') ORDER BY language DESC", array(
72
73
        ':src' => $path,
        ':language' => $path_language))
74
        ->fetchField();
75
      $map[$path_language][$path] = $alias;
76
      return $alias;
77
    }
78
79
    // Check $no_src for this $path in case we've already determined that there
    // isn't a path that has this alias
80
    elseif ($action == 'source' && !isset($no_src[$path_language][$path])) {
81
      // Look for the value $path within the cached $map
82
83
84
      $src = '';
      if (!isset($map[$path_language]) || !($src = array_search($path, $map[$path_language]))) {
        // Get the most fitting result falling back with alias without language
85
86
87
        if ($src = db_query("SELECT src FROM {url_alias} WHERE dst = :dst AND language IN(:language, '') ORDER BY language DESC", array(
                     ':dst' => $path,
                     ':language' => $path_language))
88
            ->fetchField()) {
89
          $map[$path_language][$src] = $path;
90
91
92
93
94
        }
        else {
          // We can't record anything into $map because we do not have a valid
          // index and there is no need because we have not learned anything
          // about any Drupal path. Thus cache to $no_src.
95
          $no_src[$path_language][$path] = TRUE;
96
97
        }
      }
98
      return $src;
99
100
101
102
103
104
105
106
107
108
109
    }
  }

  return FALSE;
}

/**
 * Given an internal Drupal path, return the alias set by the administrator.
 *
 * @param $path
 *   An internal Drupal path.
110
111
 * @param $path_language
 *   An optional language code to look up the path in.
112
113
114
115
116
 *
 * @return
 *   An aliased path if one was found, or the original path if no alias was
 *   found.
 */
117
function drupal_get_path_alias($path, $path_language = '') {
118
  $result = $path;
119
  if ($alias = drupal_lookup_path('alias', $path, $path_language)) {
120
121
122
123
124
125
126
127
128
129
    $result = $alias;
  }
  return $result;
}

/**
 * Given a path alias, return the internal path it represents.
 *
 * @param $path
 *   A Drupal path alias.
130
131
 * @param $path_language
 *   An optional language code to look up the path in.
132
133
134
135
136
 *
 * @return
 *   The internal path represented by the alias, or the original alias if no
 *   internal path was found.
 */
137
function drupal_get_normal_path($path, $path_language = '') {
138
  $result = $path;
139
  if ($src = drupal_lookup_path('source', $path, $path_language)) {
140
141
    $result = $src;
  }
142
143
144
  if (function_exists('custom_url_rewrite_inbound')) {
    // Modules may alter the inbound request path by reference.
    custom_url_rewrite_inbound($result, $path, $path_language);
145
146
147
148
149
150
151
  }
  return $result;
}

/**
 * Return a component of the current Drupal path.
 *
152
 * When viewing a page at the path "admin/build/types", for example, arg(0)
153
 * returns "admin", arg(1) returns "content", and arg(2) returns "types".
154
155
 *
 * Avoid use of this function where possible, as resulting code is hard to read.
156
157
158
159
 * In menu callback functions, attempt to use named arguments. See the explanation
 * in menu.inc for how to construct callbacks that take arguments. When attempting
 * to use this function to load an element from the current path, e.g. loading the
 * node on a node page, please use menu_get_object() instead.
160
161
162
163
164
165
 *
 * @param $index
 *   The index of the component, where each component is separated by a '/'
 *   (forward-slash), and where the first component has an index of 0 (zero).
 *
 * @return
166
 *   The component specified by $index, or NULL if the specified component was
167
168
 *   not found.
 */
169
function arg($index = NULL, $path = NULL) {
170
  $arguments = &drupal_static(__FUNCTION__);
171

172
173
  if (!isset($path)) {
    $path = $_GET['q'];
174
  }
175
176
177
178
179
180
181
182
  if (!isset($arguments[$path])) {
    $arguments[$path] = explode('/', $path);
  }
  if (!isset($index)) {
    return $arguments[$path];
  }
  if (isset($arguments[$path][$index])) {
    return $arguments[$path][$index];
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  }
}

/**
 * Get the title of the current page, for display on the page and in the title bar.
 *
 * @return
 *   The current page's title.
 */
function drupal_get_title() {
  $title = drupal_set_title();

  // during a bootstrap, menu.inc is not included and thus we cannot provide a title
  if (!isset($title) && function_exists('menu_get_active_title')) {
    $title = check_plain(menu_get_active_title());
  }

  return $title;
}

/**
 * Set the title of the current page, for display on the page and in the title bar.
 *
 * @param $title
 *   Optional string value to assign to the page title; or if set to NULL
 *   (default), leaves the current title unchanged.
209
210
211
212
213
 * @param $output
 *   Optional flag - normally should be left as CHECK_PLAIN. Only set to
 *   PASS_THROUGH if you have already removed any possibly dangerous code
 *   from $title using a function like check_plain() or filter_xss(). With this
 *   flag the string will be passed through unchanged.
214
215
216
217
 *
 * @return
 *   The updated title of the current page.
 */
218
function drupal_set_title($title = NULL, $output = CHECK_PLAIN) {
219
  $stored_title = &drupal_static(__FUNCTION__);
220
221

  if (isset($title)) {
222
    $stored_title = ($output == PASS_THROUGH) ? $title : check_plain($title);
223
  }
224

225
226
  return $stored_title;
}
227
228
229
230
231
232
233
234

/**
 * Check if the current page is the front page.
 *
 * @return
 *   Boolean value: TRUE if the current page is the front page; FALSE if otherwise.
 */
function drupal_is_front_page() {
235
  $is_front_page = &drupal_static(__FUNCTION__);
236
237
238
239
240
241
242
243

  if (!isset($is_front_page)) {
    // As drupal_init_path updates $_GET['q'] with the 'site_frontpage' path,
    // we can check it against the 'site_frontpage' variable.
    $is_front_page = ($_GET['q'] == drupal_get_normal_path(variable_get('site_frontpage', 'node')));
  }

  return $is_front_page;
244
}
245
246
247
248
249
250
251
252
253
254
255
256
257

/**
 * Check if a path matches any pattern in a set of patterns.
 *
 * @param $path
 *   The path to match.
 * @param $patterns
 *   String containing a set of patterns separated by \n, \r or \r\n.
 *
 * @return
 *   Boolean value: TRUE if the path matches a pattern, FALSE otherwise.
 */
function drupal_match_path($path, $patterns) {
258
  $regexps = &drupal_static(__FUNCTION__);
259

260
  if (!isset($regexps[$patterns])) {
261
    $regexps[$patterns] = '/^(' . preg_replace(array('/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/'), array('|', '.*', '\1' . preg_quote(variable_get('site_frontpage', 'node'), '/') . '\2'), preg_quote($patterns, '/')) . ')$/';
262
  }
263
  return (bool)preg_match($regexps[$patterns], $path);
264
}