specfile
[plewww.git] / includes / bootstrap.inc
1 <?php
2 // $Id: bootstrap.inc 144 2007-03-28 07:52:20Z thierry $
3
4 /**
5  * @file
6  * Functions that need to be loaded on every Drupal request.
7  */
8
9 define('CACHE_PERMANENT', 0);
10 define('CACHE_TEMPORARY', -1);
11
12 define('CACHE_DISABLED', 0);
13 define('CACHE_ENABLED', 1);
14
15 define('WATCHDOG_NOTICE', 0);
16 define('WATCHDOG_WARNING', 1);
17 define('WATCHDOG_ERROR', 2);
18
19 define('DRUPAL_BOOTSTRAP_DATABASE', 0);
20 define('DRUPAL_BOOTSTRAP_SESSION', 1);
21 define('DRUPAL_BOOTSTRAP_PAGE_CACHE', 2);
22 define('DRUPAL_BOOTSTRAP_PATH', 3);
23 define('DRUPAL_BOOTSTRAP_FULL', 4);
24
25 // these values should match the'role' table
26 define('DRUPAL_ANONYMOUS_RID', 1);
27 define('DRUPAL_AUTHENTICATED_RID', 2);
28
29 /**
30  * Start the timer with the specified name.  If you start and stop
31  * the same timer multiple times, the measured intervals will be
32  * accumulated.
33  *
34  * @param name
35  *   The name of the timer.
36  */
37 function timer_start($name) {
38   global $timers;
39
40   list($usec, $sec) = explode(' ', microtime());
41   $timers[$name]['start'] = (float)$usec + (float)$sec;
42   $timers[$name]['count'] = isset($timers[$name]['count']) ? ++$timers[$name]['count'] : 1;
43 }
44
45 /**
46  * Read the current timer value without stopping the timer.
47  *
48  * @param name
49  *   The name of the timer.
50  * @return
51  *   The current timer value in ms.
52  */
53 function timer_read($name) {
54   global $timers;
55
56   list($usec, $sec) = explode(' ', microtime());
57   $stop = (float)$usec + (float)$sec;
58   $diff = round(($stop - $timers[$name]['start']) * 1000, 2);
59
60   return $timers[$name]['time'] + $diff;
61 }
62
63 /**
64  * Stop the timer with the specified name.
65  *
66  * @param name
67  *   The name of the timer.
68  * @return
69  *   A timer array.  The array contains the number of times the
70  *   timer has been started and stopped (count) and the accumulated
71  *   timer value in ms (time).
72  */
73 function timer_stop($name) {
74   global $timers;
75
76   $timers[$name]['time'] = timer_read($name);
77   unset($timers[$name]['start']);
78
79   return $timers[$name];
80 }
81
82 /**
83  * Find the appropriate configuration directory.
84  *
85  * Try finding a matching configuration directory by stripping the website's
86  * hostname from left to right and pathname from right to left. The first
87  * configuration file found will be used, the remaining will ignored. If no
88  * configuration file is found, return a default value '$confdir/default'.
89  *
90  * Example for a fictitious site installed at
91  * http://www.drupal.org:8080/mysite/test/ the 'settings.php' is searched in
92  * the following directories:
93  *
94  *  1. $confdir/8080.www.drupal.org.mysite.test
95  *  2. $confdir/www.drupal.org.mysite.test
96  *  3. $confdir/drupal.org.mysite.test
97  *  4. $confdir/org.mysite.test
98  *
99  *  5. $confdir/8080.www.drupal.org.mysite
100  *  6. $confdir/www.drupal.org.mysite
101  *  7. $confdir/drupal.org.mysite
102  *  8. $confdir/org.mysite
103  *
104  *  9. $confdir/8080.www.drupal.org
105  * 10. $confdir/www.drupal.org
106  * 11. $confdir/drupal.org
107  * 12. $confdir/org
108  *
109  * 13. $confdir/default
110  */
111 function conf_path() {
112   static $conf = '';
113
114   if ($conf) {
115     return $conf;
116   }
117
118   $confdir = 'sites';
119   $uri = explode('/', $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_FILENAME']);
120   $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.')))));
121   for ($i = count($uri) - 1; $i > 0; $i--) {
122     for ($j = count($server); $j > 0; $j--) {
123       $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
124       if (file_exists("$confdir/$dir/settings.php")) {
125         $conf = "$confdir/$dir";
126         return $conf;
127       }
128     }
129   }
130   $conf = "$confdir/default";
131   return $conf;
132 }
133
134 /**
135  * Unsets all disallowed global variables. See $allowed for what's allowed.
136  */
137 function drupal_unset_globals() {
138   if (ini_get('register_globals')) {
139     $allowed = array('_ENV' => 1, '_GET' => 1, '_POST' => 1, '_COOKIE' => 1, '_FILES' => 1, '_SERVER' => 1, '_REQUEST' => 1, 'access_check' => 1, 'GLOBALS' => 1);
140     foreach ($GLOBALS as $key => $value) {
141       if (!isset($allowed[$key])) {
142         unset($GLOBALS[$key]);
143       }
144     }
145   }
146 }
147
148 /**
149  * Loads the configuration and sets the base URL correctly.
150  */
151 function conf_init() {
152   global $db_url, $db_prefix, $base_url, $base_path, $base_root, $conf;
153   $conf = array();
154   require_once './'. conf_path() .'/settings.php';
155
156   if (isset($base_url)) {
157     // Parse fixed base URL from settings.php.
158     $parts = parse_url($base_url);
159     if (!isset($parts['path'])) {
160       $parts['path'] = '';
161     }
162     $base_path = $parts['path'] . '/';
163     // Build $base_root (everything until first slash after "scheme://").
164     $base_root = substr($base_url, 0, strlen($base_url) - strlen($parts['path']));
165   }
166   else {
167     // Create base URL
168     $base_root = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
169     $base_url = $base_root .= '://'. $_SERVER['HTTP_HOST'];
170     if ($dir = trim(dirname($_SERVER['SCRIPT_NAME']), '\,/')) {
171       $base_path = "/$dir";
172       $base_url .= $base_path;
173       $base_path .= '/';
174     }
175     else {
176       $base_path = '/';
177     }
178   }
179 }
180
181 /**
182  * Returns and optionally sets the filename for a system item (module,
183  * theme, etc.).  The filename, whether provided, cached, or retrieved
184  * from the database, is only returned if the file exists.
185  *
186  * @param $type
187  *   The type of the item (i.e. theme, theme_engine, module).
188  * @param $name
189  *   The name of the item for which the filename is requested.
190  * @param $filename
191  *   The filename of the item if it is to be set explicitly rather
192  *   than by consulting the database.
193  *
194  * @return
195  *   The filename of the requested item.
196  */
197 function drupal_get_filename($type, $name, $filename = NULL) {
198   static $files = array();
199
200   if (!isset($files[$type])) {
201     $files[$type] = array();
202   }
203
204   if (!empty($filename) && file_exists($filename)) {
205     $files[$type][$name] = $filename;
206   }
207   elseif (isset($files[$type][$name])) {
208     // nothing
209   }
210   elseif (($file = db_result(db_query("SELECT filename FROM {system} WHERE name = '%s' AND type = '%s'", $name, $type))) && file_exists($file)) {
211     $files[$type][$name] = $file;
212   }
213   else {
214     $config = conf_path();
215     $dir = (($type == 'theme_engine') ? 'themes/engines' : "${type}s");
216     $file = (($type == 'theme_engine') ? "$name.engine" : "$name.$type");
217
218     foreach (array("$config/$dir/$file", "$config/$dir/$name/$file", "$dir/$file", "$dir/$name/$file") as $file) {
219       if (file_exists($file)) {
220         $files[$type][$name] = $file;
221         break;
222       }
223     }
224   }
225
226   return $files[$type][$name];
227 }
228
229 /**
230  * Load the persistent variable table.
231  *
232  * The variable table is composed of values that have been saved in the table
233  * with variable_set() as well as those explicitly specified in the configuration
234  * file.
235  */
236 function variable_init($conf = array()) {
237   // NOTE: caching the variables improves performance with 20% when serving cached pages.
238   if ($cached = cache_get('variables')) {
239     $variables = unserialize($cached->data);
240   }
241   else {
242     $result = db_query('SELECT * FROM {variable}');
243     while ($variable = db_fetch_object($result)) {
244       $variables[$variable->name] = unserialize($variable->value);
245     }
246     cache_set('variables', serialize($variables));
247   }
248
249   foreach ($conf as $name => $value) {
250     $variables[$name] = $value;
251   }
252
253   return $variables;
254 }
255
256 /**
257  * Return a persistent variable.
258  *
259  * @param $name
260  *   The name of the variable to return.
261  * @param $default
262  *   The default value to use if this variable has never been set.
263  * @return
264  *   The value of the variable.
265  */
266 function variable_get($name, $default) {
267   global $conf;
268
269   return isset($conf[$name]) ? $conf[$name] : $default;
270 }
271
272 /**
273  * Set a persistent variable.
274  *
275  * @param $name
276  *   The name of the variable to set.
277  * @param $value
278  *   The value to set. This can be any PHP data type; these functions take care
279  *   of serialization as necessary.
280  */
281 function variable_set($name, $value) {
282   global $conf;
283
284   db_lock_table('variable');
285   db_query("DELETE FROM {variable} WHERE name = '%s'", $name);
286   db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, serialize($value));
287   db_unlock_tables();
288
289   cache_clear_all('variables');
290
291   $conf[$name] = $value;
292 }
293
294 /**
295  * Unset a persistent variable.
296  *
297  * @param $name
298  *   The name of the variable to undefine.
299  */
300 function variable_del($name) {
301   global $conf;
302
303   db_query("DELETE FROM {variable} WHERE name = '%s'", $name);
304   cache_clear_all('variables');
305
306   unset($conf[$name]);
307 }
308
309 /**
310  * Return data from the persistent cache.
311  *
312  * @param $key
313  *   The cache ID of the data to retrieve.
314  */
315 function cache_get($key) {
316   global $user;
317
318   // Garbage collection necessary when enforcing a minimum cache lifetime
319   $cache_flush = variable_get('cache_flush', 0);
320   if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= time())) {
321     // Time to flush old cache data
322     db_query("DELETE FROM {cache} WHERE expire != %d AND expire <= %d", CACHE_PERMANENT, $cache_flush);
323     variable_set('cache_flush', 0);
324   }
325
326   $cache = db_fetch_object(db_query("SELECT data, created, headers, expire FROM {cache} WHERE cid = '%s'", $key));
327   if (isset($cache->data)) {
328     // If the data is permanent or we're not enforcing a minimum cache lifetime
329     // always return the cached data.
330     if ($cache->expire == CACHE_PERMANENT || !variable_get('cache_lifetime', 0)) {
331       $cache->data = db_decode_blob($cache->data);
332     }
333     // If enforcing a minimum cache lifetime, validate that the data is
334     // currently valid for this user before we return it by making sure the
335     // cache entry was created before the timestamp in the current session's
336     // cache timer.  The cache variable is loaded into the $user object by
337     // sess_read() in session.inc.
338     else {
339       if (isset($user->cache) && ($user->cache > $cache->created)) {
340         // This cache data is too old and thus not valid for us, ignore it.
341         return 0;
342       }
343       else {
344         $cache->data = db_decode_blob($cache->data);
345       }
346     }
347     return $cache;
348   }
349   return 0;
350 }
351
352 /**
353  * Store data in the persistent cache.
354  *
355  * @param $cid
356  *   The cache ID of the data to store.
357  * @param $data
358  *   The data to store in the cache. Complex data types must be serialized first.
359  * @param $expire
360  *   One of the following values:
361  *   - CACHE_PERMANENT: Indicates that the item should never be removed unless
362  *     explicitly told to using cache_clear_all() with a cache ID.
363  *   - CACHE_TEMPORARY: Indicates that the item should be removed at the next
364  *     general cache wipe.
365  *   - A Unix timestamp: Indicates that the item should be kept at least until
366  *     the given time, after which it behaves like CACHE_TEMPORARY.
367  * @param $headers
368  *   A string containing HTTP header information for cached pages.
369  */
370 function cache_set($cid, $data, $expire = CACHE_PERMANENT, $headers = NULL) {
371   db_lock_table('cache');
372   db_query("UPDATE {cache} SET data = %b, created = %d, expire = %d, headers = '%s' WHERE cid = '%s'", $data, time(), $expire, $headers, $cid);
373   if (!db_affected_rows()) {
374     @db_query("INSERT INTO {cache} (cid, data, created, expire, headers) VALUES ('%s', %b, %d, %d, '%s')", $cid, $data, time(), $expire, $headers);
375   }
376   db_unlock_tables();
377 }
378
379 /**
380  * Expire data from the cache.
381  *
382  * @param $cid
383  *   If set, the cache ID to delete. Otherwise, all cache entries that can
384  *   expire are deleted.
385  *
386  * @param $wildcard
387  *   If set to true, the $cid is treated as a substring to match rather than a
388  *   complete ID.
389  */
390 function cache_clear_all($cid = NULL, $wildcard = false) {
391   global $user;
392
393   if (empty($cid)) {
394     if (variable_get('cache_lifetime', 0)) {
395       // We store the time in the current user's $user->cache variable which
396       // will be saved into the sessions table by sess_write().  We then
397       // simulate that the cache was flushed for this user by not returning
398       // cached data that was cached before the timestamp.
399       $user->cache = time();
400
401       $cache_flush = variable_get('cache_flush', 0);
402       if ($cache_flush == 0) {
403         // This is the first request to clear the cache, start a timer.
404         variable_set('cache_flush', time());
405       }
406       else if (time() > ($cache_flush + variable_get('cache_lifetime', 0))) {
407         // Clear the cache for everyone, cache_flush_delay seconds have
408         // passed since the first request to clear the cache.
409         db_query("DELETE FROM {cache} WHERE expire != %d AND expire < %d", CACHE_PERMANENT, time());
410         variable_set('cache_flush', 0);
411       }
412     }
413     else {
414       // No minimum cache lifetime, flush all temporary cache entries now.
415       db_query("DELETE FROM {cache} WHERE expire != %d AND expire < %d", CACHE_PERMANENT, time());
416     }
417   }
418   else {
419     if ($wildcard) {
420       db_query("DELETE FROM {cache} WHERE cid LIKE '%%%s%%'", $cid);
421     }
422     else {
423       db_query("DELETE FROM {cache} WHERE cid = '%s'", $cid);
424     }
425   }
426 }
427
428 /**
429  * Retrieve the current page from the cache.
430  *
431  * Note, we do not serve cached pages when status messages are waiting (from
432  * a redirected form submission which was completed).
433  * Because the output handler is not activated, the resulting page will not
434  * get cached either.
435  */
436 function page_get_cache() {
437   global $user, $base_root;
438
439   $cache = NULL;
440
441   if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && count(drupal_set_message()) == 0) {
442     $cache = cache_get($base_root . request_uri());
443
444     if (empty($cache)) {
445       ob_start();
446     }
447   }
448
449   return $cache;
450 }
451
452 /**
453  * Call all init or exit hooks without including all modules.
454  *
455  * @param $hook
456  *   The name of the bootstrap hook we wish to invoke.
457  */
458 function bootstrap_invoke_all($hook) {
459   foreach (module_list(FALSE, TRUE) as $module) {
460     drupal_load('module', $module);
461     module_invoke($module, $hook);
462  }
463 }
464
465 /**
466  * Includes a file with the provided type and name.  This prevents
467  * including a theme, engine, module, etc., more than once.
468  *
469  * @param $type
470  *   The type of item to load (i.e. theme, theme_engine, module).
471  * @param $name
472  *   The name of the item to load.
473  *
474  * @return
475  *   TRUE if the item is loaded or has already been loaded.
476  */
477 function drupal_load($type, $name) {
478   static $files = array();
479
480   if (isset($files[$type][$name])) {
481     return TRUE;
482   }
483
484   $filename = drupal_get_filename($type, $name);
485
486   if ($filename) {
487     include_once "./$filename";
488     $files[$type][$name] = TRUE;
489
490     return TRUE;
491   }
492
493   return FALSE;
494 }
495
496 /**
497  * Set HTTP headers in preparation for a page response.
498  *
499  * @see page_set_cache
500  */
501 function drupal_page_header() {
502   if (variable_get('cache', 0) && $cache = page_get_cache()) {
503     bootstrap_invoke_all('init');
504     // Set default values:
505     $date = gmdate('D, d M Y H:i:s', $cache->created) .' GMT';
506     $etag = '"'. md5($date) .'"';
507
508     // Check http headers:
509     $modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] == $date : NULL;
510     if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && ($timestamp = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) > 0) {
511       $modified_since = $cache->created <= $timestamp;
512     }
513     else {
514       $modified_since = NULL;
515     }
516     $none_match = !empty($_SERVER['HTTP_IF_NONE_MATCH']) ? $_SERVER['HTTP_IF_NONE_MATCH'] == $etag : NULL;
517
518     // The type checking here is very important, be careful when changing entries.
519     if (($modified_since !== NULL || $none_match !== NULL) && $modified_since !== false && $none_match !== false) {
520       header('HTTP/1.0 304 Not Modified');
521       exit();
522     }
523
524     // Send appropriate response:
525     header("Last-Modified: $date");
526     header("ETag: $etag");
527
528     // Determine if the browser accepts gzipped data.
529     if (@strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === false && function_exists('gzencode')) {
530       // Strip the gzip header and run uncompress.
531       $cache->data = gzinflate(substr(substr($cache->data, 10), 0, -8));
532     }
533     elseif (function_exists('gzencode')) {
534       header('Content-Encoding: gzip');
535     }
536
537     // Send the original request's headers.  We send them one after
538     // another so PHP's header() function can deal with duplicate
539     // headers.
540     $headers = explode("\n", $cache->headers);
541     foreach ($headers as $header) {
542       header($header);
543     }
544
545     print $cache->data;
546     bootstrap_invoke_all('exit');
547     exit();
548   }
549   else {
550     header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
551     header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
552     header("Cache-Control: no-store, no-cache, must-revalidate");
553     header("Cache-Control: post-check=0, pre-check=0", false);
554     header("Pragma: no-cache");
555   }
556 }
557
558 /**
559  * Define the critical hooks that force modules to always be loaded.
560  */
561 function bootstrap_hooks() {
562   return array('init', 'exit');
563 }
564
565 /**
566  * Unserializes and appends elements from a serialized string.
567  *
568  * @param $obj
569  *   The object to which the elements are appended.
570  * @param $field
571  *   The attribute of $obj whose value should be unserialized.
572  */
573 function drupal_unpack($obj, $field = 'data') {
574   if ($obj->$field && $data = unserialize($obj->$field)) {
575     foreach ($data as $key => $value) {
576       if (!isset($obj->$key)) {
577         $obj->$key = $value;
578       }
579     }
580   }
581   return $obj;
582 }
583
584 /**
585  * Return the URI of the referring page.
586  */
587 function referer_uri() {
588   if (isset($_SERVER['HTTP_REFERER'])) {
589     return $_SERVER['HTTP_REFERER'];
590   }
591 }
592
593 /**
594  * Encode special characters in a plain-text string for display as HTML.
595  */
596 function check_plain($text) {
597   return htmlspecialchars($text, ENT_QUOTES);
598 }
599
600 /**
601  * Since request_uri() is only available on Apache, we generate an
602  * equivalent using other environment variables.
603  */
604 function request_uri() {
605
606   if (isset($_SERVER['REQUEST_URI'])) {
607     $uri = $_SERVER['REQUEST_URI'];
608   }
609   else {
610     if (isset($_SERVER['argv'])) {
611       $uri = $_SERVER['PHP_SELF'] .'?'. $_SERVER['argv'][0];
612     }
613     else {
614       $uri = $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING'];
615     }
616   }
617
618   return $uri;
619 }
620
621 /**
622  * Log a system message.
623  *
624  * @param $type
625  *   The category to which this message belongs.
626  * @param $message
627  *   The message to store in the log.
628  * @param $severity
629  *   The severity of the message. One of the following values:
630  *   - WATCHDOG_NOTICE
631  *   - WATCHDOG_WARNING
632  *   - WATCHDOG_ERROR
633  * @param $link
634  *   A link to associate with the message.
635  */
636 function watchdog($type, $message, $severity = WATCHDOG_NOTICE, $link = NULL) {
637   global $user, $base_root;
638
639   $current_db = db_set_active();
640
641   // Note: log the exact, entire absolute URL.
642   $request_uri = $base_root . request_uri();
643
644   db_query("INSERT INTO {watchdog} (uid, type, message, severity, link, location, referer, hostname, timestamp) VALUES (%d, '%s', '%s', %d, '%s', '%s', '%s', '%s', %d)", $user->uid, $type, $message, $severity, $link, $request_uri, referer_uri(), $_SERVER['REMOTE_ADDR'], time());
645
646   if ($current_db) {
647     db_set_active($current_db);
648   }
649 }
650
651 /**
652  * Set a message which reflects the status of the performed operation.
653  *
654  * If the function is called with no arguments, this function returns all set
655  * messages without clearing them.
656  *
657  * @param $message
658  *   The message should begin with a capital letter and always ends with a
659  *   period '.'.
660  * @param $type
661  *   The type of the message. One of the following values are possible:
662  *   - 'status'
663  *   - 'error'
664  */
665 function drupal_set_message($message = NULL, $type = 'status') {
666   if ($message) {
667     if (!isset($_SESSION['messages'])) {
668       $_SESSION['messages'] = array();
669     }
670
671     if (!isset($_SESSION['messages'][$type])) {
672       $_SESSION['messages'][$type] = array();
673     }
674
675     $_SESSION['messages'][$type][] = $message;
676   }
677
678   // messages not set when DB connection fails
679   return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL;
680 }
681
682 /**
683  * Return all messages that have been set.
684  *
685  * As a side effect, this function clears the message queue.
686  */
687 function drupal_get_messages() {
688   if ($messages = drupal_set_message()) {
689     unset($_SESSION['messages']);
690   }
691
692   return $messages;
693 }
694
695 /**
696  * Perform an access check for a given mask and rule type. Rules are usually created via admin/access/rules page.
697  */
698 function drupal_is_denied($type, $mask) {
699   $allow = db_fetch_object(db_query("SELECT * FROM {access} WHERE status = 1 AND type = '%s' AND LOWER('%s') LIKE LOWER(mask)", $type, $mask));
700   $deny = db_fetch_object(db_query("SELECT * FROM {access} WHERE status = 0 AND type = '%s' AND LOWER('%s') LIKE LOWER(mask)", $type, $mask));
701
702   return $deny && !$allow;
703 }
704
705 /**
706  * A string describing a phase of Drupal to load. Each phase adds to the
707  * previous one, so invoking a later phase automatically runs the earlier
708  * phases too. The most important usage is that if you want to access
709  * Drupal database from a script without loading anything else, you can
710  * include bootstrap.inc, and call drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE).
711  *
712  * @param $phase
713  *   A constant. Allowed values are:
714  *     DRUPAL_BOOTSTRAP_DATABASE: initialize database layer.
715  *     DRUPAL_BOOTSTRAP_SESSION: initialize session handling.
716  *     DRUPAL_BOOTSTRAP_PAGE_CACHE: load bootstrap.inc and module.inc, start
717  *       the variable system and try to serve a page from the cache.
718  *     DRUPAL_BOOTSTRAP_FULL: Drupal is fully loaded, validate and fix input
719  *       data.
720  */
721 function drupal_bootstrap($phase) {
722   static $phases = array(DRUPAL_BOOTSTRAP_DATABASE, DRUPAL_BOOTSTRAP_SESSION, DRUPAL_BOOTSTRAP_PAGE_CACHE, DRUPAL_BOOTSTRAP_PATH, DRUPAL_BOOTSTRAP_FULL);
723
724   while (!is_null($current_phase = array_shift($phases))) {
725     _drupal_bootstrap($current_phase);
726     if ($phase == $current_phase) {
727       return;
728     }
729   }
730 }
731
732 function _drupal_bootstrap($phase) {
733   global $conf;
734
735   switch ($phase) {
736     case DRUPAL_BOOTSTRAP_DATABASE:
737       drupal_unset_globals();
738       // Initialize the configuration
739       conf_init();
740       // Initialize the default database.
741       require_once './includes/database.inc';
742       db_set_active();
743       break;
744
745     case DRUPAL_BOOTSTRAP_SESSION:
746       require_once './includes/session.inc';
747       session_set_save_handler("sess_open", "sess_close", "sess_read", "sess_write", "sess_destroy", "sess_gc");
748       register_shutdown_function('session_write_close');
749       session_start();
750       break;
751
752     case DRUPAL_BOOTSTRAP_PAGE_CACHE:
753       require_once './includes/module.inc';
754       // Start a page timer:
755       timer_start('page');
756
757       // deny access to hosts which were banned. t() is not yet available.
758       if (drupal_is_denied('host', $_SERVER['REMOTE_ADDR'])) {
759         header('HTTP/1.0 403 Forbidden');
760         print 'Sorry, '. $_SERVER['REMOTE_ADDR']. ' has been banned.';
761         exit();
762       }
763
764       // Initialize configuration variables, using values from conf.php if available.
765       $conf = variable_init(isset($conf) ? $conf : array());
766       drupal_page_header();
767       break;
768
769     case DRUPAL_BOOTSTRAP_PATH:
770       require_once './includes/path.inc';
771       // Initialize $_GET['q'] prior to loading modules and invoking hook_init().
772       drupal_init_path();
773       break;
774
775     case DRUPAL_BOOTSTRAP_FULL:
776       require_once './includes/common.inc';
777       _drupal_bootstrap_full();
778       break;
779   }
780 }
781
782 /**
783  * Enables use of the theme system without requiring database access. Since
784  * there is not database access no theme will be enabled and the default
785  * themeable functions will be called. Some themeable functions can not be used
786  * without the full Drupal API loaded. For example, theme_page() is
787  * unavailable and theme_maintenance_page() must be used in its place.
788  */
789 function drupal_maintenance_theme() {
790   global $theme;
791   require_once './includes/path.inc';
792   require_once './includes/theme.inc';
793   require_once './includes/common.inc';
794   require_once './includes/unicode.inc';
795   unicode_check();
796   $theme = '';
797 }