Simpletest Coverage - includes/bootstrap.inc

1 <?php
2 // $Id: bootstrap.inc,v 1.293 2009/08/04 04:02:25 webchick Exp $
3
4 /**
5 * @file
6 * Functions that need to be loaded on every Drupal request.
7 */
8
9 /**
10 * Indicates that the item should never be removed unless explicitly told to
11 * using cache_clear_all() with a cache ID.
12 */
13 define('CACHE_PERMANENT', 0);
14
15 /**
16 * Indicates that the item should be removed at the next general cache wipe.
17 */
18 define('CACHE_TEMPORARY', -1);
19
20 /**
21 * Indicates that page caching is disabled.
22 */
23 define('CACHE_DISABLED', 0);
24
25 /**
26 * Indicates that page caching is enabled, using "normal" mode.
27 */
28 define('CACHE_NORMAL', 1);
29
30 /**
31 * Indicates that page caching is using "aggressive" mode. This bypasses
32 * loading any modules for additional speed, which may break functionality in
33 * modules that expect to be run on each page load.
34 */
35 define('CACHE_AGGRESSIVE', 2);
36
37 /**
38 * Log message severity -- Emergency: system is unusable.
39 *
40 * @see watchdog()
41 * @see watchdog_severity_levels()
42 */
43 define('WATCHDOG_EMERG', 0);
44
45 /**
46 * Log message severity -- Alert: action must be taken immediately.
47 *
48 * @see watchdog()
49 * @see watchdog_severity_levels()
50 */
51 define('WATCHDOG_ALERT', 1);
52
53 /**
54 * Log message severity -- Critical: critical conditions.
55 *
56 * @see watchdog()
57 * @see watchdog_severity_levels()
58 */
59 define('WATCHDOG_CRITICAL', 2);
60
61 /**
62 * Log message severity -- Error: error conditions.
63 *
64 * @see watchdog()
65 * @see watchdog_severity_levels()
66 */
67 define('WATCHDOG_ERROR', 3);
68
69 /**
70 * Log message severity -- Warning: warning conditions.
71 *
72 * @see watchdog()
73 * @see watchdog_severity_levels()
74 */
75 define('WATCHDOG_WARNING', 4);
76
77 /**
78 * Log message severity -- Notice: normal but significant condition.
79 *
80 * @see watchdog()
81 * @see watchdog_severity_levels()
82 */
83 define('WATCHDOG_NOTICE', 5);
84
85 /**
86 * Log message severity -- Informational: informational messages.
87 *
88 * @see watchdog()
89 * @see watchdog_severity_levels()
90 */
91 define('WATCHDOG_INFO', 6);
92
93 /**
94 * Log message severity -- Debug: debug-level messages.
95 *
96 * @see watchdog()
97 * @see watchdog_severity_levels()
98 */
99 define('WATCHDOG_DEBUG', 7);
100
101 /**
102 * First bootstrap phase: initialize configuration.
103 */
104 define('DRUPAL_BOOTSTRAP_CONFIGURATION', 0);
105
106 /**
107 * Second bootstrap phase: try to call a non-database cache
108 * fetch routine.
109 */
110 define('DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE', 1);
111
112 /**
113 * Third bootstrap phase: initialize database layer.
114 */
115 define('DRUPAL_BOOTSTRAP_DATABASE', 2);
116
117 /**
118 * Fourth bootstrap phase: identify and reject banned hosts.
119 */
120 define('DRUPAL_BOOTSTRAP_ACCESS', 3);
121
122 /**
123 * Fifth bootstrap phase: initialize session handling.
124 */
125 define('DRUPAL_BOOTSTRAP_SESSION', 4);
126
127 /**
128 * Sixth bootstrap phase: initialize the variable system.
129 */
130 define('DRUPAL_BOOTSTRAP_VARIABLES', 5);
131
132 /**
133 * Seventh bootstrap phase: load bootstrap.inc and module.inc, start
134 * the variable system and try to serve a page from the cache.
135 */
136 define('DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE', 6);
137
138 /**
139 * Eighth bootstrap phase: find out language of the page.
140 */
141 define('DRUPAL_BOOTSTRAP_LANGUAGE', 7);
142
143 /**
144 * Nineth bootstrap phase: set $_GET['q'] to Drupal path of request.
145 */
146 define('DRUPAL_BOOTSTRAP_PATH', 8);
147
148 /**
149 * Final bootstrap phase: Drupal is fully loaded; validate and fix
150 * input data.
151 */
152 define('DRUPAL_BOOTSTRAP_FULL', 9);
153
154 /**
155 * Role ID for anonymous users; should match what's in the "role" table.
156 */
157 define('DRUPAL_ANONYMOUS_RID', 1);
158
159 /**
160 * Role ID for authenticated users; should match what's in the "role" table.
161 */
162 define('DRUPAL_AUTHENTICATED_RID', 2);
163
164 /**
165 * The number of bytes in a kilobyte. For more information, visit
166 * http://en.wikipedia.org/wiki/Kilobyte.
167 */
168 define('DRUPAL_KILOBYTE', 1024);
169
170 /**
171 * No language negotiation. The default language is used.
172 */
173 define('LANGUAGE_NEGOTIATION_NONE', 0);
174
175 /**
176 * Path based negotiation with fallback to default language
177 * if no defined path prefix identified.
178 */
179 define('LANGUAGE_NEGOTIATION_PATH_DEFAULT', 1);
180
181 /**
182 * Path based negotiation with fallback to user preferences
183 * and browser language detection if no defined path prefix
184 * identified.
185 */
186 define('LANGUAGE_NEGOTIATION_PATH', 2);
187
188 /**
189 * Domain based negotiation with fallback to default language
190 * if no language identified by domain.
191 */
192 define('LANGUAGE_NEGOTIATION_DOMAIN', 3);
193
194 /**
195 * Language written left to right. Possible value of $language->direction.
196 */
197 define('LANGUAGE_LTR', 0);
198
199 /**
200 * Language written right to left. Possible value of $language->direction.
201 */
202 define('LANGUAGE_RTL', 1);
203
204 /**
205 * For convenience, define a short form of the request time global.
206 */
207 define('REQUEST_TIME', $_SERVER['REQUEST_TIME']);
208
209 /**
210 * @name Title text filtering flags
211 * @{
212 * Flags for use in drupal_set_title().
213 */
214
215 /**
216 * Flag for drupal_set_title(); text is not sanitized, so run check_plain().
217 */
218 define('CHECK_PLAIN', 0);
219
220 /**
221 * Flag for drupal_set_title(); text has already been sanitized.
222 */
223 define('PASS_THROUGH', -1);
224
225 /**
226 * Signals that the registry lookup cache should be reset.
227 */
228 define('REGISTRY_RESET_LOOKUP_CACHE', 1);
229
230 /**
231 * Signals that the registry lookup cache should be written to storage.
232 */
233 define('REGISTRY_WRITE_LOOKUP_CACHE', 2);
234
235 /**
236 * @} End of "Title text filtering flags".
237 */
238
239
240 /**
241 * Start the timer with the specified name. If you start and stop
242 * the same timer multiple times, the measured intervals will be
243 * accumulated.
244 *
245 * @param name
246 * The name of the timer.
247 */
248 function timer_start($name) {
249 global $timers;
250
251 $timers[$name]['start'] = microtime(TRUE);
252 $timers[$name]['count'] = isset($timers[$name]['count']) ? ++$timers[$name]['count'] : 1;
253 }
254
255 /**
256 * Read the current timer value without stopping the timer.
257 *
258 * @param name
259 * The name of the timer.
260 * @return
261 * The current timer value in ms.
262 */
263 function timer_read($name) {
264 global $timers;
265
266 if (isset($timers[$name]['start'])) {
267 $stop = microtime(TRUE);
268 $diff = round(($stop - $timers[$name]['start']) * 1000, 2);
269
270 if (isset($timers[$name]['time'])) {
271 $diff += $timers[$name]['time'];
272 }
273 return $diff;
274 }
275 }
276
277 /**
278 * Stop the timer with the specified name.
279 *
280 * @param name
281 * The name of the timer.
282 * @return
283 * A timer array. The array contains the number of times the
284 * timer has been started and stopped (count) and the accumulated
285 * timer value in ms (time).
286 */
287 function timer_stop($name) {
288 global $timers;
289
290 $timers[$name]['time'] = timer_read($name);
291 unset($timers[$name]['start']);
292
293 return $timers[$name];
294 }
295
296 /**
297 * Find the appropriate configuration directory.
298 *
299 * Try finding a matching configuration directory by stripping the website's
300 * hostname from left to right and pathname from right to left. The first
301 * configuration file found will be used; the remaining will ignored. If no
302 * configuration file is found, return a default value '$confdir/default'.
303 *
304 * Example for a fictitious site installed at
305 * http://www.drupal.org:8080/mysite/test/ the 'settings.php' is searched in
306 * the following directories:
307 *
308 * 1. $confdir/8080.www.drupal.org.mysite.test
309 * 2. $confdir/www.drupal.org.mysite.test
310 * 3. $confdir/drupal.org.mysite.test
311 * 4. $confdir/org.mysite.test
312 *
313 * 5. $confdir/8080.www.drupal.org.mysite
314 * 6. $confdir/www.drupal.org.mysite
315 * 7. $confdir/drupal.org.mysite
316 * 8. $confdir/org.mysite
317 *
318 * 9. $confdir/8080.www.drupal.org
319 * 10. $confdir/www.drupal.org
320 * 11. $confdir/drupal.org
321 * 12. $confdir/org
322 *
323 * 13. $confdir/default
324 *
325 * If a file named sites.php is present in the $confdir, it will be loaded
326 * prior to scanning for directories. It should define an associative array
327 * named $sites, which maps domains to directories. It should be in the form
328 * of:
329 *
330 * $sites = array(
331 * 'The url to alias' => 'A directory within the sites directory'
332 * );
333 *
334 * For example:
335 *
336 * $sites = array(
337 * 'devexample.com' => 'example.com',
338 * 'localhost/example' => 'example.com',
339 * );
340 *
341 * The above array will cause Drupal to look for a directory named
342 * "example.com" in the sites directory whenever a request comes from
343 * "example.com", "devexample.com", or "localhost/example". That is useful
344 * on development servers, where the domain name may not be the same as the
345 * domain of the live server. Since Drupal stores file paths into the database
346 * (files, system table, etc.) this will ensure the paths are correct while
347 * accessed on development servers.
348 *
349 * @param $require_settings
350 * Only configuration directories with an existing settings.php file
351 * will be recognized. Defaults to TRUE. During initial installation,
352 * this is set to FALSE so that Drupal can detect a matching directory,
353 * then create a new settings.php file in it.
354 * @param reset
355 * Force a full search for matching directories even if one had been
356 * found previously.
357 * @return
358 * The path of the matching directory.
359 */
360 function conf_path($require_settings = TRUE, $reset = FALSE) {
361 $conf = &drupal_static(__FUNCTION__, '');
362
363 if ($conf && !$reset) {
364 return $conf;
365 }
366
367 $confdir = 'sites';
368
369 $sites = array();
370 if (file_exists(DRUPAL_ROOT . '/' . $confdir . '/sites.php')) {
371 // This will overwrite $sites with the desired mappings.
372 include(DRUPAL_ROOT . '/' . $confdir . '/sites.php');
373 }
374
375 $uri = explode('/', $_SERVER['SCRIPT_NAME'] ? $_SERVER['SCRIPT_NAME'] : $_SERVER['SCRIPT_FILENAME']);
376 $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.')))));
377 for ($i = count($uri) - 1; $i > 0; $i--) {
378 for ($j = count($server); $j > 0; $j--) {
379 $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
380 if (isset($sites[$dir]) && file_exists(DRUPAL_ROOT . '/' . $confdir . '/' . $sites[$dir])) {
381 $dir = $sites[$dir];
382 }
383 if (file_exists(DRUPAL_ROOT . '/' . $confdir . '/' . $dir . '/settings.php') || (!$require_settings && file_exists(DRUPAL_ROOT . '/' . $confdir . '/' . $dir))) {
384 $conf = "$confdir/$dir";
385 return $conf;
386 }
387 }
388 }
389 $conf = "$confdir/default";
390 return $conf;
391 }
392
393 /**
394 * Set appropriate server variables needed for command line scripts to work.
395 *
396 * This function can be called by command line scripts before bootstrapping
397 * Drupal, to ensure that the page loads with the desired server parameters.
398 * This is because many parts of Drupal assume that they are running in a web
399 * browser and therefore use information from the global PHP $_SERVER variable
400 * that does not get set when Drupal is run from the command line.
401 *
402 * In many cases, the default way in which this function populates the $_SERVER
403 * variable is sufficient, and it can therefore be called without passing in
404 * any input. However, command line scripts running on a multisite installation
405 * (or on any installation that has settings.php stored somewhere other than
406 * the sites/default folder) need to pass in the URL of the site to allow
407 * Drupal to detect the correct location of the settings.php file. Passing in
408 * the 'url' parameter is also required for functions like request_uri() to
409 * return the expected values.
410 *
411 * Most other parameters do not need to be passed in, but may be necessary in
412 * some cases; for example, if Drupal's ip_address() function needs to return
413 * anything but the standard localhost value ('127.0.0.1'), the command line
414 * script should pass in the desired value via the 'REMOTE_ADDR' key.
415 *
416 * @param $variables
417 * (optional) An associative array of variables within $_SERVER that should
418 * be replaced. If the special element 'url' is provided in this array, it
419 * will be used to populate some of the server defaults; it should be set to
420 * the URL of the current page request, excluding any $_GET request but
421 * including the script name (e.g., http://www.example.com/mysite/index.php).
422 *
423 * @see conf_path()
424 * @see request_uri()
425 * @see ip_address()
426 */
427 function drupal_override_server_variables($variables = array()) {
428 // Set defaults based on the provided URL.
429 if (isset($variables['url'])) {
430 $url = parse_url($variables['url']);
431 unset($variables['url']);
432 }
433 else {
434 $url = array();
435 }
436 $url += array(
437 'path' => '',
438 'host' => 'localhost',
439 );
440 $defaults = array(
441 'HTTP_HOST' => $url['host'],
442 'SCRIPT_NAME' => $url['path'],
443 'REMOTE_ADDR' => '127.0.0.1',
444 'REQUEST_METHOD' => 'GET',
445 'SERVER_NAME' => NULL,
446 'SERVER_SOFTWARE' => 'PHP CLI',
447 'HTTP_USER_AGENT' => NULL,
448 );
449 // Replace elements of the $_SERVER array, as appropriate.
450 $_SERVER = $variables + $_SERVER + $defaults;
451 }
452
453 /**
454 * Initialize PHP environment.
455 */
456 function drupal_environment_initialize() {
457 if (!isset($_SERVER['HTTP_REFERER'])) {
458 $_SERVER['HTTP_REFERER'] = '';
459 }
460 if (!isset($_SERVER['SERVER_PROTOCOL']) || ($_SERVER['SERVER_PROTOCOL'] != 'HTTP/1.0' && $_SERVER['SERVER_PROTOCOL'] != 'HTTP/1.1')) {
461 $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.0';
462 }
463
464 if (isset($_SERVER['HTTP_HOST'])) {
465 // As HTTP_HOST is user input, ensure it only contains characters allowed
466 // in hostnames. See RFC 952 (and RFC 2181).
467 // $_SERVER['HTTP_HOST'] is lowercased here per specifications.
468 $_SERVER['HTTP_HOST'] = strtolower($_SERVER['HTTP_HOST']);
469 if (!drupal_valid_http_host($_SERVER['HTTP_HOST'])) {
470 // HTTP_HOST is invalid, e.g. if containing slashes it may be an attack.
471 header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
472 exit;
473 }
474 }
475 else {
476 // Some pre-HTTP/1.1 clients will not send a Host header. Ensure the key is
477 // defined for E_ALL compliance.
478 $_SERVER['HTTP_HOST'] = '';
479 }
480
481 // Enforce E_ALL, but allow users to set levels not part of E_ALL.
482 error_reporting(E_ALL | error_reporting());
483
484 // Override PHP settings required for Drupal to work properly.
485 // sites/default/default.settings.php contains more runtime settings.
486 // The .htaccess file contains settings that cannot be changed at runtime.
487
488 // Prevent PHP from generating HTML error messages.
489 ini_set('html_errors', 0);
490 // Don't escape quotes when reading files from the database, disk, etc.
491 ini_set('magic_quotes_runtime', '0');
492 // Use session cookies, not transparent sessions that puts the session id in
493 // the query string.
494 ini_set('session.use_cookies', '1');
495 ini_set('session.use_only_cookies', '1');
496 ini_set('session.use_trans_sid', '0');
497 // Don't send HTTP headers using PHP's session handler.
498 ini_set('session.cache_limiter', 'none');
499 // Use httponly session cookies.
500 ini_set('session.cookie_httponly', '1');
501 }
502
503 /**
504 * Validate that a hostname (for example $_SERVER['HTTP_HOST']) is safe.
505 *
506 * @return
507 * TRUE if only containing valid characters, or FALSE otherwise.
508 */
509 function drupal_valid_http_host($host) {
510 return preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host);
511 }
512
513 /**
514 * Loads the configuration and sets the base URL, cookie domain, and
515 * session name correctly.
516 */
517 function drupal_settings_initialize() {
518 global $base_url, $base_path, $base_root;
519
520 // Export the following settings.php variables to the global namespace
521 global $databases, $db_prefix, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url;
522 $conf = array();
523
524 if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) {
525 include_once DRUPAL_ROOT . '/' . conf_path() . '/settings.php';
526 }
527
528 if (isset($base_url)) {
529 // Parse fixed base URL from settings.php.
530 $parts = parse_url($base_url);
531 if (!isset($parts['path'])) {
532 $parts['path'] = '';
533 }
534 $base_path = $parts['path'] . '/';
535 // Build $base_root (everything until first slash after "scheme://").
536 $base_root = substr($base_url, 0, strlen($base_url) - strlen($parts['path']));
537 }
538 else {
539 // Create base URL
540 $base_root = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
541
542 $base_url = $base_root .= '://' . $_SERVER['HTTP_HOST'];
543
544 // $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not
545 // be modified by a visitor.
546 if ($dir = trim(dirname($_SERVER['SCRIPT_NAME']), '\,/')) {
547 $base_path = "/$dir";
548 $base_url .= $base_path;
549 $base_path .= '/';
550 }
551 else {
552 $base_path = '/';
553 }
554 }
555
556 if ($cookie_domain) {
557 // If the user specifies the cookie domain, also use it for session name.
558 $session_name = $cookie_domain;
559 }
560 else {
561 // Otherwise use $base_url as session name, without the protocol
562 // to use the same session identifiers across http and https.
563 list( , $session_name) = explode('://', $base_url, 2);
564 // We escape the hostname because it can be modified by a visitor.
565 if (!empty($_SERVER['HTTP_HOST'])) {
566 $cookie_domain = check_plain($_SERVER['HTTP_HOST']);
567 }
568 }
569 // To prevent session cookies from being hijacked, a user can configure the
570 // SSL version of their website to only transfer session cookies via SSL by
571 // using PHP's session.cookie_secure setting. The browser will then use two
572 // separate session cookies for the HTTPS and HTTP versions of the site. So we
573 // must use different session identifiers for HTTPS and HTTP to prevent a
574 // cookie collision.
575 if (ini_get('session.cookie_secure')) {
576 $session_name .= 'SSL';
577 }
578 // Strip leading periods, www., and port numbers from cookie domain.
579 $cookie_domain = ltrim($cookie_domain, '.');
580 if (strpos($cookie_domain, 'www.') === 0) {
581 $cookie_domain = substr($cookie_domain, 4);
582 }
583 $cookie_domain = explode(':', $cookie_domain);
584 $cookie_domain = '.' . $cookie_domain[0];
585 // Per RFC 2109, cookie domains must contain at least one dot other than the
586 // first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
587 if (count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) {
588 ini_set('session.cookie_domain', $cookie_domain);
589 }
590 session_name('SESS' . md5($session_name));
591 }
592
593 /**
594 * Returns and optionally sets the filename for a system item (module,
595 * theme, etc.). The filename, whether provided, cached, or retrieved
596 * from the database, is only returned if the file exists.
597 *
598 * This function plays a key role in allowing Drupal's resources (modules
599 * and themes) to be located in different places depending on a site's
600 * configuration. For example, a module 'foo' may legally be be located
601 * in any of these three places:
602 *
603 * modules/foo/foo.module
604 * sites/all/modules/foo/foo.module
605 * sites/example.com/modules/foo/foo.module
606 *
607 * Calling drupal_get_filename('module', 'foo') will give you one of
608 * the above, depending on where the module is located.
609 *
610 * @param $type
611 * The type of the item (i.e. theme, theme_engine, module).
612 * @param $name
613 * The name of the item for which the filename is requested.
614 * @param $filename
615 * The filename of the item if it is to be set explicitly rather
616 * than by consulting the database.
617 *
618 * @return
619 * The filename of the requested item.
620 */
621 function drupal_get_filename($type, $name, $filename = NULL) {
622 $files = &drupal_static(__FUNCTION__, array());
623
624 if (!isset($files[$type])) {
625 $files[$type] = array();
626 }
627
628 if (!empty($filename) && file_exists($filename)) {
629 $files[$type][$name] = $filename;
630 }
631 elseif (isset($files[$type][$name])) {
632 // nothing
633 }
634 // Verify that we have an active database connection, before querying
635 // the database. This is required because this function is called both
636 // before we have a database connection (i.e. during installation) and
637 // when a database connection fails.
638 else {
639 try {
640 $file = db_query("SELECT filename FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField();
641 if (file_exists($file)) {
642 $files[$type][$name] = $file;
643 }
644 }
645 catch (PDOException $e) {
646 // The database table may not exist because Drupal is not yet installed,
647 // or the database might be down. We have a fallback for this case so we
648 // hide the error completely.
649 }
650 // Fallback to searching the filesystem if the database could not find the
651 // file or the file returned by the database is not found.
652 if (!isset($files[$type][$name])) {
653 // We have a consistent directory naming: modules, themes...
654 $dir = $type . 's';
655 if ($type == 'theme_engine') {
656 $dir = 'themes/engines';
657 $mask = "/$name\.engine$/";
658 }
659 elseif ($type == 'theme') {
660 $mask = "/$name\.info$/";
661 }
662 else {
663 $mask = "/$name\.$type$/";
664 }
665
666 if (drupal_function_exists('drupal_system_listing')) {
667 $matches = drupal_system_listing($mask, $dir, 'name', 0);
668 if (!empty($matches[$name]->filepath)) {
669 $files[$type][$name] = $matches[$name]->filepath;
670 }
671 }
672 }
673 }
674
675 if (isset($files[$type][$name])) {
676 return $files[$type][$name];
677 }
678 }
679
680 /**
681 * Load the persistent variable table.
682 *
683 * The variable table is composed of values that have been saved in the table
684 * with variable_set() as well as those explicitly specified in the configuration
685 * file.
686 */
687 function variable_initialize($conf = array()) {
688 // NOTE: caching the variables improves performance by 20% when serving cached pages.
689 if ($cached = cache_get('variables', 'cache')) {
690 $variables = $cached->data;
691 }
692 else {
693 $variables = array_map('unserialize', db_query('SELECT name, value FROM {variable}')->fetchAllKeyed());
694 cache_set('variables', $variables);
695 }
696
697 foreach ($conf as $name => $value) {
698 $variables[$name] = $value;
699 }
700
701 return $variables;
702 }
703
704 /**
705 * Return a persistent variable.
706 *
707 * @param $name
708 * The name of the variable to return.
709 * @param $default
710 * The default value to use if this variable has never been set.
711 * @return
712 * The value of the variable.
713 */
714 function variable_get($name, $default = NULL) {
715 global $conf;
716
717 return isset($conf[$name]) ? $conf[$name] : $default;
718 }
719
720 /**
721 * Set a persistent variable.
722 *
723 * @param $name
724 * The name of the variable to set.
725 * @param $value
726 * The value to set. This can be any PHP data type; these functions take care
727 * of serialization as necessary.
728 */
729 function variable_set($name, $value) {
730 global $conf;
731
732 db_merge('variable')->key(array('name' => $name))->fields(array('value' => serialize($value)))->execute();
733
734 cache_clear_all('variables', 'cache');
735
736 $conf[$name] = $value;
737 }
738
739 /**
740 * Unset a persistent variable.
741 *
742 * @param $name
743 * The name of the variable to undefine.
744 */
745 function variable_del($name) {
746 global $conf;
747
748 db_delete('variable')
749 ->condition('name', $name)
750 ->execute();
751 cache_clear_all('variables', 'cache');
752
753 unset($conf[$name]);
754 }
755
756 /**
757 * Retrieve the current page from the cache.
758 *
759 * Note: we do not serve cached pages to authenticated users, or to anonymous
760 * users when $_SESSION is non-empty. $_SESSION may contain status messages
761 * from a form submission, the contents of a shopping cart, or other user-
762 * specific content that should not be cached and displayed to other users.
763 *
764 * @return
765 * The cache object, if the page was found in the cache, NULL otherwise.
766 */
767 function drupal_page_get_cache() {
768 global $base_root;
769
770 if (drupal_page_is_cacheable()) {
771 return cache_get($base_root . request_uri(), 'cache_page');
772 }
773 }
774
775 /**
776 * Determine the cacheability of the current page.
777 *
778 * @param $allow_caching
779 * Set to FALSE if you want to prevent this page to get cached.
780 * @return
781 * TRUE if the current page can be cached, FALSE otherwise.
782 */
783 function drupal_page_is_cacheable($allow_caching = NULL) {
784 $allow_caching_static = &drupal_static(__FUNCTION__, TRUE);
785 if (isset($allow_caching)) {
786 $allow_caching_static = $allow_caching;
787 }
788
789 return $allow_caching_static && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')
790 && $_SERVER['SERVER_SOFTWARE'] !== 'PHP CLI';
791 }
792
793 /**
794 * Includes a file with the provided type and name. This prevents
795 * including a theme, engine, module, etc., more than once.
796 *
797 * @param $type
798 * The type of item to load (i.e. theme, theme_engine, module).
799 * @param $name
800 * The name of the item to load.
801 *
802 * @return
803 * TRUE if the item is loaded or has already been loaded.
804 */
805 function drupal_load($type, $name) {
806 $files = &drupal_static(__FUNCTION__, array());
807
808 if (isset($files[$type][$name])) {
809 return TRUE;
810 }
811
812 $filename = drupal_get_filename($type, $name);
813
814 if ($filename) {
815 include_once DRUPAL_ROOT . '/' . $filename;
816 $files[$type][$name] = TRUE;
817
818 return TRUE;
819 }
820
821 return FALSE;
822 }
823
824 /**
825 * Set an HTTP response header for the current page.
826 *
827 * Note: When sending a Content-Type header, always include a 'charset' type,
828 * too. This is necessary to avoid security bugs (e.g. UTF-7 XSS).
829 *
830 * @param $name
831 * The HTTP header name, or a status code followed by a reason phrase, e.g.
832 * "404 Not Found".
833 * @param $value
834 * The HTTP header value; if omitted, the specified header is unset.
835 * @param $append
836 * Whether to append the value to an existing header or to replace it.
837 */
838 function drupal_set_header($name = NULL, $value = NULL, $append = FALSE) {
839 // The headers as name/value pairs.
840 $headers = &drupal_static(__FUNCTION__, array());
841
842 if (!isset($name)) {
843 return $headers;
844 }
845
846 // Save status codes using the special key ":status".
847 if (preg_match('/^\d{3} /', $name)) {
848 $value = $name;
849 $name = $name_lower = ':status';
850 }
851 else {
852 $name_lower = strtolower($name);
853 }
854 _drupal_set_preferred_header_name($name);
855
856 if (!isset($value)) {
857 $headers[$name_lower] = FALSE;
858 }
859 elseif (isset($headers[$name_lower]) && $append) {
860 // Multiple headers with identical names may be combined using comma (RFC
861 // 2616, section 4.2).
862 $headers[$name_lower] .= ',' . $value;
863 }
864 else {
865 $headers[$name_lower] = $value;
866 }
867 drupal_send_headers(array($name => $headers[$name_lower]), TRUE);
868 }
869
870 /**
871 * Get the HTTP response headers for the current page.
872 *
873 * @param $name
874 * An HTTP header name. If omitted, all headers are returned as name/value
875 * pairs. If an array value is FALSE, the header has been unset.
876 * @return
877 * A string containing the header value, or FALSE if the header has been set,
878 * or NULL if the header has not been set.
879 */
880 function drupal_get_header($name = NULL) {
881 $headers = drupal_set_header();
882 if (isset($name)) {
883 $name = strtolower($name);
884 return isset($headers[$name]) ? $headers[$name] : NULL;
885 }
886 else {
887 return $headers;
888 }
889 }
890
891 /**
892 * Header names are case-insensitive, but for maximum compatibility they should
893 * follow "common form" (see RFC 2617, section 4.2).
894 */
895 function _drupal_set_preferred_header_name($name = NULL) {
896 static $header_names = array();
897
898 if (!isset($name)) {
899 return $header_names;
900 }
901 $header_names[strtolower($name)] = $name;
902 }
903
904 /**
905 * Send the HTTP response headers previously set using drupal_set_header().
906 * Add default headers, unless they have been replaced or unset using
907 * drupal_set_header().
908 *
909 * @param $default_headers
910 * An array of headers as name/value pairs.
911 * @param $single
912 * If TRUE and headers have already be sent, send only the specified header.
913 */
914 function drupal_send_headers($default_headers = array(), $only_default = FALSE) {
915 $headers_sent = &drupal_static(__FUNCTION__, FALSE);
916 $headers = drupal_get_header();
917 if ($only_default && $headers_sent) {
918 $headers = array();
919 }
920 $headers_sent = TRUE;
921
922 $header_names = _drupal_set_preferred_header_name();
923 foreach ($default_headers as $name => $value) {
924 $name_lower = strtolower($name);
925 if (!isset($headers[$name_lower])) {
926 $headers[$name_lower] = $value;
927 $header_names[$name_lower] = $name;
928 }
929 }
930 foreach ($headers as $name_lower => $value) {
931 if ($name_lower == ':status') {
932 header($_SERVER['SERVER_PROTOCOL'] . ' ' . $value);
933 }
934 // Skip headers that have been unset.
935 elseif ($value) {
936 header($header_names[$name_lower] . ': ' . $value);
937 }
938 }
939 }
940
941 /**
942 * Set HTTP headers in preparation for a page response.
943 *
944 * Authenticated users are always given a 'no-cache' header, and will fetch a
945 * fresh page on every request. This prevents authenticated users from seeing
946 * locally cached pages.
947 *
948 * Also give each page a unique ETag. This will force clients to include both
949 * an If-Modified-Since header and an If-None-Match header when doing
950 * conditional requests for the page (required by RFC 2616, section 13.3.4),
951 * making the validation more robust. This is a workaround for a bug in Mozilla
952 * Firefox that is triggered when Drupal's caching is enabled and the user
953 * accesses Drupal via an HTTP proxy (see
954 * https://bugzilla.mozilla.org/show_bug.cgi?id=269303): When an authenticated
955 * user requests a page, and then logs out and requests the same page again,
956 * Firefox may send a conditional request based on the page that was cached
957 * locally when the user was logged in. If this page did not have an ETag
958 * header, the request only contains an If-Modified-Since header. The date will
959 * be recent, because with authenticated users the Last-Modified header always
960 * refers to the time of the request. If the user accesses Drupal via a proxy
961 * server, and the proxy already has a cached copy of the anonymous page with an
962 * older Last-Modified date, the proxy may respond with 304 Not Modified, making
963 * the client think that the anonymous and authenticated pageviews are
964 * identical.
965 *
966 * @see drupal_page_set_cache()
967 */
968 function drupal_page_header() {
969 $headers_sent = &drupal_static(__FUNCTION__, FALSE);
970 if ($headers_sent) {
971 return TRUE;
972 }
973 $headers_sent = TRUE;
974
975 $default_headers = array(
976 'Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT',
977 'Last-Modified' => gmdate(DATE_RFC1123, REQUEST_TIME),
978 'Cache-Control' => 'no-cache, must-revalidate, post-check=0, pre-check=0',
979 'ETag' => '"' . REQUEST_TIME . '"',
980 );
981 drupal_send_headers($default_headers);
982 }
983
984 /**
985 * Set HTTP headers in preparation for a cached page response.
986 *
987 * The headers allow as much as possible in proxies and browsers without any
988 * particular knowledge about the pages. Modules can override these headers
989 * using drupal_set_header().
990 *
991 * If the request is conditional (using If-Modified-Since and If-None-Match),
992 * and the conditions match those currently in the cache, a 304 Not Modified
993 * response is sent.
994 */
995 function drupal_serve_page_from_cache(stdClass $cache) {
996 // Negotiate whether to use compression.
997 $page_compression = variable_get('page_compression', TRUE) && extension_loaded('zlib');
998 $return_compressed = $page_compression && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE;
999
1000 // Get headers set in hook_boot(). Keys are lower-case.
1001 $hook_boot_headers = drupal_get_header();
1002
1003 // Headers generated in this function, that may be replaced or unset using
1004 // drupal_set_headers(). Keys are mixed-case.
1005 $default_headers = array();
1006
1007 foreach ($cache->headers as $name => $value) {
1008 // In the case of a 304 response, certain headers must be sent, and the
1009 // remaining may not (see RFC 2616, section 10.3.5). Do not override
1010 // headers set in hook_boot().
1011 $name_lower = strtolower($name);
1012 if (in_array($name_lower, array('content-location', 'expires', 'cache-control', 'vary')) && !isset($hook_boot_headers[$name_lower])) {
1013 drupal_set_header($name, $value);
1014 unset($cache->headers[$name]);
1015 }
1016 }
1017
1018 // If a cache is served from a HTTP proxy without hitting the web server,
1019 // the boot and exit hooks cannot be fired, so only allow caching in
1020 // proxies with aggressive caching. If the client send a session cookie, do
1021 // not bother caching the page in a public proxy, because the cached copy
1022 // will only be served to that particular user due to Vary: Cookie, unless
1023 // the Vary header has been replaced or unset in hook_boot() (see below).
1024 $max_age = variable_get('cache') == CACHE_AGGRESSIVE && (!isset($_COOKIE[session_name()]) || isset($hook_boot_headers['vary'])) ? variable_get('cache_lifetime', 0) : 0;
1025 $default_headers['Cache-Control'] = 'public, max-age=' . $max_age;
1026
1027 // Entity tag should change if the output changes.
1028 $etag = '"' . $cache->created . '-' . intval($return_compressed) . '"';
1029 header('Etag: ' . $etag);
1030
1031 // See if the client has provided the required HTTP headers.
1032 $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;
1033 $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE;
1034
1035 if ($if_modified_since && $if_none_match
1036 && $if_none_match == $etag // etag must match
1037 && $if_modified_since == $cache->created) { // if-modified-since must match
1038 header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified');
1039 drupal_send_headers($default_headers);
1040 return;
1041 }
1042
1043 // Send the remaining headers.
1044 foreach ($cache->headers as $name => $value) {
1045 drupal_set_header($name, $value);
1046 }
1047
1048 $default_headers['Last-Modified'] = gmdate(DATE_RFC1123, $cache->created);
1049
1050 // HTTP/1.0 proxies does not support the Vary header, so prevent any caching
1051 // by sending an Expires date in the past. HTTP/1.1 clients ignores the
1052 // Expires header if a Cache-Control: max-age= directive is specified (see RFC
1053 // 2616, section 14.9.3).
1054 $default_headers['Expires'] = 'Sun, 19 Nov 1978 05:00:00 GMT';
1055
1056 drupal_send_headers($default_headers);
1057
1058 // Allow HTTP proxies to cache pages for anonymous users without a session
1059 // cookie. The Vary header is used to indicates the set of request-header
1060 // fields that fully determines whether a cache is permitted to use the
1061 // response to reply to a subsequent request for a given URL without
1062 // revalidation. If a Vary header has been set in hook_boot(), it is assumed
1063 // that the module knows how to cache the page.
1064 if (!isset($hook_boot_headers['vary']) && !variable_get('omit_vary_cookie')) {
1065 header('Vary: Cookie');
1066 }
1067
1068 if ($page_compression) {
1069 header('Vary: Accept-Encoding', FALSE);
1070 // If page_compression is enabled, the cache contains gzipped data.
1071 if ($return_compressed) {
1072 header('Content-Encoding: gzip');
1073 }
1074 else {
1075 // The client does not support compression, so unzip the data in the
1076 // cache. Strip the gzip header and run uncompress.
1077 $cache->data = gzinflate(substr(substr($cache->data, 10), 0, -8));
1078 }
1079 }
1080
1081 print $cache->data;
1082 }
1083
1084 /**
1085 * Unserializes and appends elements from a serialized string.
1086 *
1087 * @param $obj
1088 * The object to which the elements are appended.
1089 * @param $field
1090 * The attribute of $obj whose value should be unserialized.
1091 */
1092 function drupal_unpack($obj, $field = 'data') {
1093 if ($obj->$field && $data = unserialize($obj->$field)) {
1094 foreach ($data as $key => $value) {
1095 if (!isset($obj->$key)) {
1096 $obj->$key = $value;
1097 }
1098 }
1099 }
1100 return $obj;
1101 }
1102
1103 /**
1104 * Encode special characters in a plain-text string for display as HTML.
1105 *
1106 * Uses drupal_validate_utf8 to prevent cross site scripting attacks on
1107 * Internet Explorer 6.
1108 */
1109 function check_plain($text) {
1110 return drupal_validate_utf8($text) ? htmlspecialchars($text, ENT_QUOTES) : '';
1111 }
1112
1113 /**
1114 * Checks whether a string is valid UTF-8.
1115 *
1116 * All functions designed to filter input should use drupal_validate_utf8
1117 * to ensure they operate on valid UTF-8 strings to prevent bypass of the
1118 * filter.
1119 *
1120 * When text containing an invalid UTF-8 lead byte (0xC0 - 0xFF) is presented
1121 * as UTF-8 to Internet Explorer 6, the program may misinterpret subsequent
1122 * bytes. When these subsequent bytes are HTML control characters such as
1123 * quotes or angle brackets, parts of the text that were deemed safe by filters
1124 * end up in locations that are potentially unsafe; An onerror attribute that
1125 * is outside of a tag, and thus deemed safe by a filter, can be interpreted
1126 * by the browser as if it were inside the tag.
1127 *
1128 * The function does not return FALSE for strings containing character codes
1129 * above U+10FFFF, even though these are prohibited by RFC 3629.
1130 *
1131 * @param $text
1132 * The text to check.
1133 * @return
1134 * TRUE if the text is valid UTF-8, FALSE if not.
1135 */
1136 function drupal_validate_utf8($text) {
1137 if (strlen($text) == 0) {
1138 return TRUE;
1139 }
1140 // With the PCRE_UTF8 modifier 'u', preg_match() fails silently on strings
1141 // containing invalid UTF-8 byte sequences. It does not reject character
1142 // codes above U+10FFFF (represented by 4 or more octets), though.
1143 return (preg_match('/^./us', $text) == 1);
1144 }
1145
1146 /**
1147 * Since $_SERVER['REQUEST_URI'] is only available on Apache, we
1148 * generate an equivalent using other environment variables.
1149 */
1150 function request_uri() {
1151
1152 if (isset($_SERVER['REQUEST_URI'])) {
1153 $uri = $_SERVER['REQUEST_URI'];
1154 }
1155 else {
1156 if (isset($_SERVER['argv'])) {
1157 $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['argv'][0];
1158 }
1159 elseif (isset($_SERVER['QUERY_STRING'])) {
1160 $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING'];
1161 }
1162 else {
1163 $uri = $_SERVER['SCRIPT_NAME'];
1164 }
1165 }
1166 // Prevent multiple slashes to avoid cross site requests via the Form API.
1167 $uri = '/' . ltrim($uri, '/');
1168
1169 return $uri;
1170 }
1171
1172 /**
1173 * Log a system message.
1174 *
1175 * @param $type
1176 * The category to which this message belongs.
1177 * @param $message
1178 * The message to store in the log. Keep $message translatable
1179 * by not concatenating dynamic values into it! Variables in the
1180 * message should be added by using placeholder strings alongside
1181 * the variables argument to declare the value of the placeholders.
1182 * See t() for documentation on how $message and $variables interact.
1183 * @param $variables
1184 * Array of variables to replace in the message on display or
1185 * NULL if message is already translated or not possible to
1186 * translate.
1187 * @param $severity
1188 * The severity of the message, as per RFC 3164.
1189 * @param $link
1190 * A link to associate with the message.
1191 *
1192 * @see watchdog_severity_levels()
1193 * @see hook_watchdog()
1194 */
1195 function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
1196 global $user, $base_root;
1197
1198 static $in_error_state = FALSE;
1199
1200 // It is possible that the error handling will itself trigger an error. In that case, we could
1201 // end up in an infinite loop. To avoid that, we implement a simple static semaphore.
1202 if (!$in_error_state) {
1203 $in_error_state = TRUE;
1204
1205 // Prepare the fields to be logged
1206 $log_entry = array(
1207 'type' => $type,
1208 'message' => $message,
1209 'variables' => $variables,
1210 'severity' => $severity,
1211 'link' => $link,
1212 'user' => $user,
1213 'request_uri' => $base_root . request_uri(),
1214 'referer' => $_SERVER['HTTP_REFERER'],
1215 'ip' => ip_address(),
1216 'timestamp' => REQUEST_TIME,
1217 );
1218
1219 // Call the logging hooks to log/process the message
1220 foreach (module_implements('watchdog', TRUE) as $module) {
1221 module_invoke($module, 'watchdog', $log_entry);
1222 }
1223
1224 // It is critical that the semaphore is only cleared here, in the parent
1225 // watchdog() call (not outside the loop), to prevent recursive execution.
1226 $in_error_state = FALSE;
1227 }
1228 }
1229
1230 /**
1231 * Set a message which reflects the status of the performed operation.
1232 *
1233 * If the function is called with no arguments, this function returns all set
1234 * messages without clearing them.
1235 *
1236 * @param $message
1237 * The message should begin with a capital letter and always ends with a
1238 * period '.'.
1239 * @param $type
1240 * The type of the message. One of the following values are possible:
1241 * - 'status'
1242 * - 'warning'
1243 * - 'error'
1244 * @param $repeat
1245 * If this is FALSE and the message is already set, then the message won't
1246 * be repeated.
1247 */
1248 function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
1249 if ($message) {
1250 if (!isset($_SESSION['messages'][$type])) {
1251 $_SESSION['messages'][$type] = array();
1252 }
1253
1254 if ($repeat || !in_array($message, $_SESSION['messages'][$type])) {
1255 $_SESSION['messages'][$type][] = $message;
1256 }
1257
1258 // Mark this page has being not cacheable.
1259 drupal_page_is_cacheable(FALSE);
1260 }
1261
1262 // Messages not set when DB connection fails.
1263 return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL;
1264 }
1265
1266 /**
1267 * Return all messages that have been set.
1268 *
1269 * @param $type
1270 * (optional) Only return messages of this type.
1271 * @param $clear_queue
1272 * (optional) Set to FALSE if you do not want to clear the messages queue
1273 * @return
1274 * An associative array, the key is the message type, the value an array
1275 * of messages. If the $type parameter is passed, you get only that type,
1276 * or an empty array if there are no such messages. If $type is not passed,
1277 * all message types are returned, or an empty array if none exist.
1278 */
1279 function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
1280 if ($messages = drupal_set_message()) {
1281 if ($type) {
1282 if ($clear_queue) {
1283 unset($_SESSION['messages'][$type]);
1284 }
1285 if (isset($messages[$type])) {
1286 return array($type => $messages[$type]);
1287 }
1288 }
1289 else {
1290 if ($clear_queue) {
1291 unset($_SESSION['messages']);
1292 }
1293 return $messages;
1294 }
1295 }
1296 return array();
1297 }
1298
1299 /**
1300 * Check to see if an IP address has been blocked.
1301 *
1302 * Blocked IP addresses are stored in the database by default. However for
1303 * performance reasons we allow an override in settings.php. This allows us
1304 * to avoid querying the database at this critical stage of the bootstrap if
1305 * an administrative interface for IP address blocking is not required.
1306 *
1307 * @param $ip string
1308 * IP address to check.
1309 * @return bool
1310 * TRUE if access is denied, FALSE if access is allowed.
1311 */
1312 function drupal_is_denied($ip) {
1313 // Because this function is called on every page request, we first check
1314 // for an array of IP addresses in settings.php before querying the
1315 // database.
1316 $blocked_ips = variable_get('blocked_ips');
1317 if (isset($blocked_ips) && is_array($blocked_ips)) {
1318 return in_array($ip, $blocked_ips);
1319 }
1320 else {
1321 return (bool)db_query("SELECT 1 FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField();
1322 }
1323 }
1324
1325 /**
1326 * Generates a default anonymous $user object.
1327 *
1328 * @return Object - the user object.
1329 */
1330 function drupal_anonymous_user($session = '') {
1331 $user = new stdClass();
1332 $user->uid = 0;
1333 $user->hostname = ip_address();
1334 $user->roles = array();
1335 $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
1336 $user->session = $session;
1337 $user->cache = 0;
1338 return $user;
1339 }
1340
1341 /**
1342 * A string describing a phase of Drupal to load. Each phase adds to the
1343 * previous one, so invoking a later phase automatically runs the earlier
1344 * phases too. The most important usage is that if you want to access the
1345 * Drupal database from a script without loading anything else, you can
1346 * include bootstrap.inc, and call drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE).
1347 *
1348 * @param $phase
1349 * A constant. Allowed values are:
1350 * DRUPAL_BOOTSTRAP_CONFIGURATION: initialize configuration.
1351 * DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE: try to call a non-database cache fetch routine.
1352 * DRUPAL_BOOTSTRAP_DATABASE: initialize database layer.
1353 * DRUPAL_BOOTSTRAP_ACCESS: identify and reject banned hosts.
1354 * DRUPAL_BOOTSTRAP_SESSION: initialize session handling.
1355 * DRUPAL_BOOTSTRAP_VARIABLES: initialize variable handling.
1356 * DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE: load bootstrap.inc and module.inc, start
1357 * the variable system and try to serve a page from the cache.
1358 * DRUPAL_BOOTSTRAP_LANGUAGE: identify the language used on the page.
1359 * DRUPAL_BOOTSTRAP_PATH: set $_GET['q'] to Drupal path of request.
1360 * DRUPAL_BOOTSTRAP_FULL: Drupal is fully loaded, validate and fix input data.
1361 */
1362 function drupal_bootstrap($phase = NULL) {
1363 $phases = &drupal_static(__FUNCTION__ . '_phases', array(
1364 DRUPAL_BOOTSTRAP_CONFIGURATION,
1365 DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE,
1366 DRUPAL_BOOTSTRAP_DATABASE,
1367 DRUPAL_BOOTSTRAP_ACCESS,
1368 DRUPAL_BOOTSTRAP_SESSION,
1369 DRUPAL_BOOTSTRAP_VARIABLES,
1370 DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE,
1371 DRUPAL_BOOTSTRAP_LANGUAGE,
1372 DRUPAL_BOOTSTRAP_PATH,
1373 DRUPAL_BOOTSTRAP_FULL,
1374 ));
1375 $completed_phase = &drupal_static(__FUNCTION__ . '_completed_phase', -1);
1376
1377 if (isset($phase)) {
1378 while ($phases && $phase > $completed_phase) {
1379 $current_phase = array_shift($phases);
1380 _drupal_bootstrap($current_phase);
1381 $completed_phase = $current_phase;
1382 }
1383 }
1384 return $completed_phase;
1385 }
1386
1387 /**
1388 * Return the current bootstrap phase for this Drupal process. The
1389 * current phase is the one most recently completed by
1390 * drupal_bootstrap().
1391 *
1392 * @see drupal_bootstrap
1393 */
1394 function drupal_get_bootstrap_phase() {
1395 return drupal_bootstrap();
1396 }
1397
1398 function _drupal_bootstrap($phase) {
1399 global $conf, $user;
1400
1401 switch ($phase) {
1402
1403 case DRUPAL_BOOTSTRAP_CONFIGURATION:
1404 drupal_environment_initialize();
1405 // Start a page timer:
1406 timer_start('page');
1407 // Initialize the configuration, including variables from settings.php.
1408 drupal_settings_initialize();
1409 break;
1410
1411 case DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE:
1412 // Allow specifying special cache handlers in settings.php, like
1413 // using memcached or files for storing cache information.
1414 require_once DRUPAL_ROOT . '/' . variable_get('cache_inc', 'includes/cache.inc');
1415 // If the page_cache_fastpath is set to TRUE in settings.php and
1416 // page_cache_fastpath (implemented in the special implementation of
1417 // cache.inc) printed the page and indicated this with a returned TRUE
1418 // then we are done.
1419 if (variable_get('page_cache_fastpath', FALSE) && page_cache_fastpath()) {
1420 exit;
1421 }
1422 break;
1423
1424 case DRUPAL_BOOTSTRAP_DATABASE:
1425 // The user agent header is used to pass a database prefix in the request when
1426 // running tests. However, for security reasons, it is imperative that we
1427 // validate we ourselves made the request.
1428 if (isset($_SERVER['HTTP_USER_AGENT']) && (strpos($_SERVER['HTTP_USER_AGENT'], "simpletest") !== FALSE) && !drupal_valid_test_ua($_SERVER['HTTP_USER_AGENT'])) {
1429 header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
1430 exit;
1431 }
1432 // Initialize the database system. Note that the connection
1433 // won't be initialized until it is actually requested.
1434 require_once DRUPAL_ROOT . '/includes/database/database.inc';
1435 // Register autoload functions so that we can access classes and interfaces.
1436 spl_autoload_register('drupal_autoload_class');
1437 spl_autoload_register('drupal_autoload_interface');
1438 break;
1439
1440 case DRUPAL_BOOTSTRAP_ACCESS:
1441 // Deny access to blocked IP addresses - t() is not yet available.
1442 if (drupal_is_denied(ip_address())) {
1443 header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
1444 print 'Sorry, ' . check_plain(ip_address()) . ' has been banned.';
1445 exit();
1446 }
1447 break;
1448
1449 case DRUPAL_BOOTSTRAP_SESSION:
1450 require_once DRUPAL_ROOT . '/' . variable_get('session_inc', 'includes/session.inc');
1451 drupal_session_initialize();
1452 break;
1453
1454 case DRUPAL_BOOTSTRAP_VARIABLES:
1455 // Load variables from the database, but do not overwrite variables set in settings.php.
1456 $conf = variable_initialize(isset($conf) ? $conf : array());
1457 break;
1458
1459 case DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE:
1460 $cache_mode = variable_get('cache', CACHE_DISABLED);
1461
1462 // Get the page from the cache.
1463 if ($cache_mode != CACHE_DISABLED) {
1464 $cache = drupal_page_get_cache();
1465 }
1466 else {
1467 $cache = FALSE;
1468 }
1469
1470 // If the skipping of the bootstrap hooks is not enforced, call hook_boot.
1471 if (!$cache || $cache_mode != CACHE_AGGRESSIVE) {
1472 // Load module handling.
1473 require_once DRUPAL_ROOT . '/includes/module.inc';
1474 module_invoke_all('boot');
1475 }
1476
1477 // If there is a cached page, display it.
1478 if ($cache) {
1479 header('X-Drupal-Cache: HIT');
1480 drupal_serve_page_from_cache($cache);
1481 // If the skipping of the bootstrap hooks is not enforced, call hook_exit.
1482 if ($cache_mode != CACHE_AGGRESSIVE) {
1483 module_invoke_all('exit');
1484 }
1485 // We are done.
1486 exit;
1487 }
1488
1489 if (!$cache && drupal_page_is_cacheable()) {
1490 header('X-Drupal-Cache: MISS');
1491 }
1492
1493 // Prepare for non-cached page workflow.
1494 if ($_SERVER['SERVER_SOFTWARE'] !== 'PHP CLI') {
1495 ob_start();
1496 drupal_page_header();
1497 }
1498 break;
1499
1500 case DRUPAL_BOOTSTRAP_LANGUAGE:
1501 drupal_language_initialize();
1502 break;
1503
1504 case DRUPAL_BOOTSTRAP_PATH:
1505 require_once DRUPAL_ROOT . '/includes/path.inc';
1506 // Initialize $_GET['q'] prior to loading modules and invoking hook_init().
1507 drupal_path_initialize();
1508 break;
1509
1510 case DRUPAL_BOOTSTRAP_FULL:
1511 require_once DRUPAL_ROOT . '/includes/common.inc';
1512 _drupal_bootstrap_full();
1513 break;
1514 }
1515 }
1516
1517 /**
1518 * Validate the HMAC and timestamp of a user agent header from simpletest.
1519 */
1520 function drupal_valid_test_ua($user_agent) {
1521 global $databases;
1522
1523 list($prefix, $time, $salt, $hmac) = explode(';', $user_agent);
1524 $check_string = $prefix . ';' . $time . ';' . $salt;
1525 // We use the database credentials from settings.php to make the HMAC key, since
1526 // the database is not yet initialized and we can't access any Drupal variables.
1527 // The file properties add more entropy not easily accessible to others.
1528 $filepath = DRUPAL_ROOT . '/includes/bootstrap.inc';
1529 $key = sha1(serialize($databases) . filectime($filepath) . fileinode($filepath), TRUE);
1530 $time_diff = REQUEST_TIME - $time;
1531 // Since we are making a local request, a 2 second time window is allowed,
1532 // and the HMAC must match.
1533 return (($time_diff >= 0) && ($time_diff < 3) && ($hmac == base64_encode(hash_hmac('sha1', $check_string, $key, TRUE))));
1534 }
1535
1536 /**
1537 * Generate a user agent string with a HMAC and timestamp for simpletest.
1538 */
1539 function drupal_generate_test_ua($prefix) {
1540 global $databases;
1541 static $key;
1542
1543 if (!isset($key)) {
1544 // We use the database credentials to make the HMAC key, since we
1545 // check the HMAC before the database is initialized. filectime()
1546 // and fileinode() are not easily determined from remote.
1547 $filepath = DRUPAL_ROOT . '/includes/bootstrap.inc';
1548 $key = sha1(serialize($databases) . filectime($filepath) . fileinode($filepath), TRUE);
1549 }
1550 // Generate a moderately secure HMAC based on the database credentials.
1551 $salt = uniqid('', TRUE);
1552 $check_string = $prefix . ';' . time() . ';' . $salt;
1553 return $check_string . ';' . base64_encode(hash_hmac('sha1', $check_string, $key, TRUE));
1554 }
1555
1556 /**
1557 * Enables use of the theme system without requiring database access.
1558 *
1559 * Loads and initializes the theme system for site installs, updates and when
1560 * the site is in maintenance mode. This also applies when the database fails.
1561 *
1562 * @see _drupal_maintenance_theme()
1563 */
1564 function drupal_maintenance_theme() {
1565 require_once DRUPAL_ROOT . '/includes/theme.maintenance.inc';
1566 _drupal_maintenance_theme();
1567 }
1568
1569 /**
1570 * Return TRUE if a Drupal installation is currently being attempted.
1571 */
1572 function drupal_installation_attempted() {
1573 return defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install';
1574 }
1575
1576 /**
1577 * Return the name of the localization function. Use in code that needs to
1578 * run both during installation and normal operation.
1579 */
1580 function get_t() {
1581 static $t;
1582 // This is not converted to drupal_static because there is no point in
1583 // resetting this as it can not change in the course of a request.
1584 if (!isset($t)) {
1585 $t = drupal_installation_attempted() ? 'st' : 't';
1586 }
1587 return $t;
1588 }
1589
1590 /**
1591 * Choose a language for the current page, based on site and user preferences.
1592 */
1593 function drupal_language_initialize() {
1594 global $language, $user;
1595
1596 // Ensure the language is correctly returned, even without multilanguage support.
1597 // Useful for eg. XML/HTML 'lang' attributes.
1598 if (variable_get('language_count', 1) == 1) {
1599 $language = language_default();
1600 }
1601 else {
1602 include_once DRUPAL_ROOT . '/includes/language.inc';
1603 $language = language_initialize();
1604 }
1605 }
1606
1607 /**
1608 * Get a list of languages set up indexed by the specified key
1609 *
1610 * @param $field The field to index the list with.
1611 */
1612 function language_list($field = 'language') {
1613 $languages = &drupal_static(__FUNCTION__);
1614 // Init language list
1615 if (!isset($languages)) {
1616 if (variable_get('language_count', 1) > 1 || module_exists('locale')) {
1617 $languages['language'] = db_query('SELECT * FROM {languages} ORDER BY weight ASC, name ASC')->fetchAllAssoc('language');
1618 }
1619 else {
1620 // No locale module, so use the default language only.
1621 $default = language_default();
1622 $languages['language'][$default->language] = $default;
1623 }
1624 }
1625
1626 // Return the array indexed by the right field
1627 if (!isset($languages[$field])) {
1628 $languages[$field] = array();
1629 foreach ($languages['language'] as $lang) {
1630 // Some values should be collected into an array
1631 if (in_array($field, array('enabled', 'weight'))) {
1632 $languages[$field][$lang->$field][$lang->language] = $lang;
1633 }
1634 else {
1635 $languages[$field][$lang->$field] = $lang;
1636 }
1637 }
1638 }
1639 return $languages[$field];
1640 }
1641
1642 /**
1643 * Default language used on the site
1644 *
1645 * @param $property
1646 * Optional property of the language object to return
1647 */
1648 function language_default($property = NULL) {
1649 $language = variable_get('language_default', (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => ''));
1650 return $property ? $language->$property : $language;
1651 }
1652
1653 /**
1654 * If Drupal is behind a reverse proxy, we use the X-Forwarded-For header
1655 * instead of $_SERVER['REMOTE_ADDR'], which would be the IP address of
1656 * the proxy server, and not the client's. If Drupal is run in a cluster
1657 * we use the X-Cluster-Client-Ip header instead.
1658 *
1659 * @return
1660 * IP address of client machine, adjusted for reverse proxy and/or cluster
1661 * environments.
1662 */
1663 function ip_address() {
1664 $ip_address = &drupal_static(__FUNCTION__);
1665
1666 if (!isset($ip_address)) {
1667 $ip_address = $_SERVER['REMOTE_ADDR'];
1668
1669 if (variable_get('reverse_proxy', 0)) {
1670 if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) {
1671 // If an array of known reverse proxy IPs is provided, then trust
1672 // the XFF header if request really comes from one of them.
1673 $reverse_proxy_addresses = variable_get('reverse_proxy_addresses', array());
1674 if (!empty($reverse_proxy_addresses) && in_array($ip_address, $reverse_proxy_addresses, TRUE)) {
1675 // The "X-Forwarded-For" header is a comma+space separated list of IP addresses,
1676 // the left-most being the farthest downstream client. If there is more than
1677 // one proxy, we are interested in the most recent one (i.e. last one in the list).
1678 $ip_address_parts = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
1679 $ip_address = trim(array_pop($ip_address_parts));
1680 }
1681 }
1682
1683 // When Drupal is run in a cluster environment, REMOTE_ADDR contains the IP
1684 // address of a server in the cluster, while the IP address of the client is
1685 // stored in HTTP_X_CLUSTER_CLIENT_IP.
1686 if (array_key_exists('HTTP_X_CLUSTER_CLIENT_IP', $_SERVER)) {
1687 $ip_address = $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
1688 }
1689 }
1690 }
1691
1692 return $ip_address;
1693 }
1694
1695 /**
1696 * @ingroup schemaapi
1697 * @{
1698 */
1699
1700 /**
1701 * Get the schema definition of a table, or the whole database schema.
1702 *
1703 * The returned schema will include any modifications made by any
1704 * module that implements hook_schema_alter().
1705 *
1706 * @param $table
1707 * The name of the table. If not given, the schema of all tables is returned.
1708 * @param $rebuild
1709 * If true, the schema will be rebuilt instead of retrieved from the cache.
1710 */
1711 function drupal_get_schema($table = NULL, $rebuild = FALSE) {
1712 static $schema = array();
1713
1714 if (empty($schema) || $rebuild) {
1715 // Try to load the schema from cache.
1716 if (!$rebuild && $cached = cache_get('schema')) {
1717 $schema = $cached->data;
1718 }
1719 // Otherwise, rebuild the schema cache.
1720 else {
1721 $schema = array();
1722 // Load the .install files to get hook_schema.
1723 // On some databases this function may be called before bootstrap has
1724 // been completed, so we force the functions we need to load just in case.
1725 if (drupal_function_exists('module_load_all_includes')) {
1726
1727 // There is currently a bug in module_list() where it caches what it
1728 // was last called with, which is not always what you want.
1729 // module_load_all_includes() calls module_list(), but if this function
1730 // is called very early in the bootstrap process then it will be
1731 // uninitialized and therefore return no modules. Instead, we have to
1732 // "prime" module_list() here to to values we want, specifically
1733 // "yes rebuild the list and don't limit to bootstrap".
1734 // TODO: Remove this call after http://drupal.org/node/222109 is fixed.
1735 module_list(TRUE);
1736 module_load_all_includes('install');
1737 }
1738
1739 require_once DRUPAL_ROOT . '/includes/common.inc';
1740 // Invoke hook_schema for all modules.
1741 foreach (module_implements('schema') as $module) {
1742 $current = module_invoke($module, 'schema');
1743 if (drupal_function_exists('_drupal_schema_initialize')) {
1744 _drupal_schema_initialize($module, $current);
1745 }
1746
1747 $schema = array_merge($schema, $current);
1748 }
1749
1750 if (drupal_function_exists('drupal_alter')) {
1751 drupal_alter('schema', $schema);
1752 }
1753
1754 // If the schema is empty, avoid saving it: some database engines require
1755 // the schema to perform queries, and this could lead to infinite loops.
1756 if (!empty($schema) && (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL)) {
1757 cache_set('schema', $schema);
1758 }
1759 }
1760 }
1761
1762 if (!isset($table)) {
1763 return $schema;
1764 }
1765 elseif (isset($schema[$table])) {
1766 return $schema[$table];
1767 }
1768 else {
1769 return FALSE;
1770 }
1771 }
1772
1773 /**
1774 * @} End of "ingroup schemaapi".
1775 */
1776
1777
1778 /**
1779 * @ingroup registry
1780 * @{
1781 */
1782
1783 /**
1784 * Confirm that a function is available.
1785 *
1786 * If the function is already available, this function does nothing.
1787 * If the function is not available, it tries to load the file where the
1788 * function lives. If the file is not available, it returns false, so that it
1789 * can be used as a drop-in replacement for function_exists().
1790 *
1791 * @param $function
1792 * The name of the function to check or load.
1793 * @return
1794 * TRUE if the function is now available, FALSE otherwise.
1795 */
1796 function drupal_function_exists($function) {
1797 static $checked = array();
1798 static $maintenance;
1799
1800 if (!isset($maintenance)) {
1801 $maintenance = defined('MAINTENANCE_MODE');
1802 }
1803
1804 if ($maintenance) {
1805 return function_exists($function);
1806 }
1807
1808 if (isset($checked[$function])) {
1809 return $checked[$function];
1810 }
1811 $checked[$function] = FALSE;
1812
1813 if (function_exists($function)) {
1814 $checked[$function] = TRUE;
1815 return TRUE;
1816 }
1817
1818 $checked[$function] = _registry_check_code('function', $function);
1819
1820 return $checked[$function];
1821 }
1822
1823 /**
1824 * Confirm that an interface is available.
1825 *
1826 * This function parallels drupal_function_exists(), but is rarely
1827 * called directly. Instead, it is registered as an spl_autoload()
1828 * handler, and PHP calls it for us when necessary.
1829 *
1830 * @param $interface
1831 * The name of the interface to check or load.
1832 * @return
1833 * TRUE if the interface is currently available, FALSE otherwise.
1834 */
1835 function drupal_autoload_interface($interface) {
1836 return _registry_check_code('interface', $interface);
1837 }
1838
1839 /**
1840 * Confirm that a class is available.
1841 *
1842 * This function parallels drupal_function_exists(), but is rarely
1843 * called directly. Instead, it is registered as an spl_autoload()
1844 * handler, and PHP calls it for us when necessary.
1845 *
1846 * @param $class
1847 * The name of the class to check or load.
1848 * @return
1849 * TRUE if the class is currently available, FALSE otherwise.
1850 */
1851 function drupal_autoload_class($class) {
1852 return _registry_check_code('class', $class);
1853 }
1854
1855 /**
1856 * Helper to check for a resource in the registry.
1857 *
1858 * @param $type
1859 * The type of resource we are looking up, or one of the constants
1860 * REGISTRY_RESET_LOOKUP_CACHE or REGISTRY_WRITE_LOOKUP_CACHE, which
1861 * signal that we should reset or write the cache, respectively.
1862 * @param $name
1863 * The name of the resource, or NULL if either of the REGISTRY_* constants
1864 * is passed in.
1865 * @return
1866 * TRUE if the resource was found, FALSE if not.
1867 * NULL if either of the REGISTRY_* constants is passed in as $type.
1868 */
1869 function _registry_check_code($type, $name = NULL) {
1870 static $lookup_cache, $cache_update_needed;
1871
1872 if (!isset($lookup_cache)) {
1873 $lookup_cache = array();
1874 if ($cache = cache_get('lookup_cache', 'cache_registry')) {
1875 $lookup_cache = $cache->data;
1876 }
1877 }
1878
1879 // When we rebuild the registry, we need to reset this cache so
1880 // we don't keep lookups for resources that changed during the rebuild.
1881 if ($type == REGISTRY_RESET_LOOKUP_CACHE) {
1882 $cache_update_needed = TRUE;
1883 $lookup_cache = NULL;
1884 return;
1885 }
1886
1887 // Called from drupal_page_footer, we write to permanent storage if there
1888 // changes to the lookup cache for this request.
1889 if ($type == REGISTRY_WRITE_LOOKUP_CACHE) {
1890 if ($cache_update_needed) {
1891 cache_set('lookup_cache', $lookup_cache, 'cache_registry');
1892 }
1893 return;
1894 }
1895
1896 // $type can be one of 'function', 'interface' or 'class', so we only need the
1897 // first letter to keep the cache key unique.
1898 $cache_key = $type[0] . $name;
1899 if (isset($lookup_cache[$cache_key])) {
1900 if ($lookup_cache[$cache_key]) {
1901 require_once DRUPAL_ROOT . '/' . $lookup_cache[$cache_key];
1902 }
1903 return $lookup_cache[$cache_key];
1904 }
1905
1906 // This function may get called when the default database is not active, but
1907 // there is no reason we'd ever want to not use the default database for
1908 // this query.
1909 $file = Database::getConnection('default', 'default')->query("SELECT filename FROM {registry} WHERE name = :name AND type = :type", array(
1910 ':name' => $name,
1911 ':type' => $type,
1912 ))
1913 ->fetchField();
1914
1915 // Flag that we've run a lookup query and need to update the cache.
1916 $cache_update_needed = TRUE;
1917
1918 // Misses are valuable information worth caching, so cache even if
1919 // $file is FALSE.
1920 $lookup_cache[$cache_key] = $file;
1921
1922 if ($file) {
1923 require_once DRUPAL_ROOT . '/' . $file;
1924 return TRUE;
1925 }
1926 else {
1927 return FALSE;
1928 }
1929 }
1930
1931 /**
1932 * Rescan all enabled modules and rebuild the registry.
1933 *
1934 * Rescans all code in modules or includes directory, storing a mapping of
1935 * each function, file, and hook implementation in the database.
1936 */
1937 function registry_rebuild() {
1938 require_once DRUPAL_ROOT . '/includes/registry.inc';
1939 _registry_rebuild();
1940 }
1941
1942 /**
1943 * @} End of "ingroup registry".
1944 */
1945
1946 /**
1947 * Central static variable storage.
1948 *
1949 * @param $name
1950 * Globally unique name for the variable. For a function with only one static,
1951 * variable, the function name (e.g. via the PHP magic __FUNCTION__ constant)
1952 * is recommended. For a function with multiple static variables add a
1953 * distinguishing suffix to the function name for each one.
1954 * @param $default_value
1955 * Optional default value.
1956 * @param $reset
1957 * TRUE to reset a specific named variable, or all variables if $name is NULL.
1958 * Resetting every variable should only be used, for example, for running
1959 * unit tests with a clean environment. Should be used only though via
1960 * function drupal_static_reset().
1961 *
1962 * @return
1963 * Returns a variable by reference if $reset is FALSE.
1964 */
1965 function &drupal_static($name, $default_value = NULL, $reset = FALSE) {
1966 static $data = array();
1967
1968 // Reset a single value, or all values.
1969 if ($reset) {
1970 if (isset($name)) {
1971 unset($data[$name]);
1972 }
1973 else {
1974 $data = array();
1975 }
1976 // We must return a reference to a variable.
1977 $dummy = NULL;
1978 return $dummy;
1979 }
1980
1981 if (!isset($data[$name])) {
1982 $data[$name] = $default_value;
1983 }
1984
1985 return $data[$name];
1986 }
1987
1988 /**
1989 * Reset one or all centrally stored static variable(s).
1990 *
1991 * @param $name
1992 * Name of the static variable to reset. Omit to reset all variables.
1993 */
1994 function drupal_static_reset($name = NULL) {
1995 drupal_static($name, NULL, TRUE);
1996 }
1997

Legend

Missed
lines code that were not excersized during program execution.
Covered
lines code were excersized during program execution.
Comment/non executable
Comment or non-executable line of code.
Dead
lines of code that according to xdebug could not be executed. This is counted as coverage code because in almost all cases it is code that runnable.