2006-12-12 06:27:17 +00:00
< ? php
2004-09-09 05:51:08 +00:00
// $Id$
2003-11-18 19:44:36 +00:00
2004-07-13 07:21:14 +00:00
/**
* @ file
* Functions that need to be loaded on every Drupal request .
*/
2004-09-16 07:17:56 +00:00
2009-10-30 22:33:35 +00:00
/**
* The current system version .
*/
2010-03-21 16:53:36 +00:00
define ( 'VERSION' , '7.0-dev' );
2009-10-30 22:33:35 +00:00
/**
* Core API compatibility .
*/
define ( 'DRUPAL_CORE_COMPATIBILITY' , '7.x' );
/**
* Minimum supported version of PHP .
*/
define ( 'DRUPAL_MINIMUM_PHP' , '5.2.0' );
/**
* Minimum recommended value of PHP memory_limit .
*/
2010-01-15 14:25:35 +00:00
define ( 'DRUPAL_MINIMUM_PHP_MEMORY_LIMIT' , '40M' );
2009-10-30 22:33:35 +00:00
/**
* Minimum supported version of MySQL , if it is used .
*/
2010-01-31 18:31:46 +00:00
define ( 'DRUPAL_MINIMUM_MYSQL' , '5.0.15' );
2009-10-30 22:33:35 +00:00
/**
* Minimum supported version of PostgreSQL , if it is used .
*/
define ( 'DRUPAL_MINIMUM_PGSQL' , '8.3' );
2006-12-12 06:16:33 +00:00
/**
2009-12-30 08:16:55 +00:00
* Indicates that the item should never be removed unless explicitly selected .
*
* The item may be removed using cache_clear_all () with a cache ID .
2006-12-12 06:16:33 +00:00
*/
2004-09-15 20:34:35 +00:00
define ( 'CACHE_PERMANENT' , 0 );
2006-12-12 06:16:33 +00:00
/**
* Indicates that the item should be removed at the next general cache wipe .
*/
2004-09-15 20:34:35 +00:00
define ( 'CACHE_TEMPORARY' , - 1 );
2003-11-18 19:44:36 +00:00
2006-12-12 06:16:33 +00:00
/**
* Indicates that page caching is disabled .
*/
2005-04-11 19:05:52 +00:00
define ( 'CACHE_DISABLED' , 0 );
2006-12-12 06:16:33 +00:00
/**
* Indicates that page caching is enabled , using " normal " mode .
*/
2006-08-31 18:40:04 +00:00
define ( 'CACHE_NORMAL' , 1 );
2006-12-12 06:16:33 +00:00
2006-12-11 12:01:54 +00:00
/**
2008-05-26 17:12:55 +00:00
* Log message severity -- Emergency : system is unusable .
2007-04-10 10:10:27 +00:00
*
2008-01-08 10:35:43 +00:00
* @ see watchdog ()
* @ see watchdog_severity_levels ()
2006-12-11 12:01:54 +00:00
*/
2010-03-11 21:23:06 +00:00
define ( 'WATCHDOG_EMERGENCY' , 0 );
2008-05-26 17:12:55 +00:00
/**
* Log message severity -- Alert : action must be taken immediately .
*
* @ see watchdog ()
* @ see watchdog_severity_levels ()
*/
define ( 'WATCHDOG_ALERT' , 1 );
/**
* Log message severity -- Critical : critical conditions .
*
* @ see watchdog ()
* @ see watchdog_severity_levels ()
*/
define ( 'WATCHDOG_CRITICAL' , 2 );
/**
* Log message severity -- Error : error conditions .
*
* @ see watchdog ()
* @ see watchdog_severity_levels ()
*/
define ( 'WATCHDOG_ERROR' , 3 );
/**
* Log message severity -- Warning : warning conditions .
*
* @ see watchdog ()
* @ see watchdog_severity_levels ()
*/
define ( 'WATCHDOG_WARNING' , 4 );
/**
* Log message severity -- Notice : normal but significant condition .
*
* @ see watchdog ()
* @ see watchdog_severity_levels ()
*/
define ( 'WATCHDOG_NOTICE' , 5 );
/**
* Log message severity -- Informational : informational messages .
*
* @ see watchdog ()
* @ see watchdog_severity_levels ()
*/
define ( 'WATCHDOG_INFO' , 6 );
/**
* Log message severity -- Debug : debug - level messages .
*
* @ see watchdog ()
* @ see watchdog_severity_levels ()
*/
define ( 'WATCHDOG_DEBUG' , 7 );
2005-01-09 09:22:40 +00:00
2006-12-08 12:09:54 +00:00
/**
* First bootstrap phase : initialize configuration .
*/
2006-06-14 14:01:12 +00:00
define ( 'DRUPAL_BOOTSTRAP_CONFIGURATION' , 0 );
2006-12-08 12:09:54 +00:00
/**
2009-08-16 18:39:45 +00:00
* Second bootstrap phase : try to serve a cached page .
2006-12-08 12:09:54 +00:00
*/
2009-08-16 18:39:45 +00:00
define ( 'DRUPAL_BOOTSTRAP_PAGE_CACHE' , 1 );
2006-12-08 12:09:54 +00:00
/**
* Third bootstrap phase : initialize database layer .
*/
2006-06-14 14:01:12 +00:00
define ( 'DRUPAL_BOOTSTRAP_DATABASE' , 2 );
2006-12-08 12:09:54 +00:00
/**
2009-09-21 08:07:07 +00:00
* Fourth bootstrap phase : initialize the variable system .
2006-12-08 12:09:54 +00:00
*/
2009-09-21 08:07:07 +00:00
define ( 'DRUPAL_BOOTSTRAP_VARIABLES' , 3 );
2006-12-08 12:09:54 +00:00
/**
2009-09-21 08:07:07 +00:00
* Fifth bootstrap phase : initialize session handling .
2006-12-08 12:09:54 +00:00
*/
2009-09-21 08:07:07 +00:00
define ( 'DRUPAL_BOOTSTRAP_SESSION' , 4 );
2006-12-08 12:09:54 +00:00
/**
2009-09-21 08:07:07 +00:00
* Sixth bootstrap phase : set up the page header .
2008-10-22 20:44:53 +00:00
*/
2009-09-21 08:07:07 +00:00
define ( 'DRUPAL_BOOTSTRAP_PAGE_HEADER' , 5 );
2008-10-22 20:44:53 +00:00
/**
2009-09-21 08:07:07 +00:00
* Seventh bootstrap phase : find out language of the page .
2006-12-08 12:09:54 +00:00
*/
2009-09-21 08:07:07 +00:00
define ( 'DRUPAL_BOOTSTRAP_LANGUAGE' , 6 );
2006-12-08 12:09:54 +00:00
/**
* Final bootstrap phase : Drupal is fully loaded ; validate and fix
* input data .
*/
2009-10-24 05:13:44 +00:00
define ( 'DRUPAL_BOOTSTRAP_FULL' , 7 );
2005-07-23 05:57:27 +00:00
2006-12-11 12:00:07 +00:00
/**
* Role ID for anonymous users ; should match what ' s in the " role " table .
*/
2006-01-21 08:28:55 +00:00
define ( 'DRUPAL_ANONYMOUS_RID' , 1 );
2006-12-11 12:00:07 +00:00
/**
* Role ID for authenticated users ; should match what ' s in the " role " table .
*/
2006-01-21 08:28:55 +00:00
define ( 'DRUPAL_AUTHENTICATED_RID' , 2 );
2008-11-16 15:19:34 +00:00
/**
2009-04-11 23:01:35 +00:00
* The number of bytes in a kilobyte . For more information , visit
2008-11-16 15:19:34 +00:00
* http :// en . wikipedia . org / wiki / Kilobyte .
*/
define ( 'DRUPAL_KILOBYTE' , 1024 );
2009-12-02 19:26:23 +00:00
/**
* The language code used when no language is explicitly assigned .
*
2009-12-22 14:19:25 +00:00
* Defined by ISO639 - 2 for " Undetermined " .
2009-12-02 19:26:23 +00:00
*/
2009-12-22 14:19:25 +00:00
define ( 'LANGUAGE_NONE' , 'und' );
2009-12-02 19:26:23 +00:00
2007-03-26 01:32:22 +00:00
/**
2009-10-09 16:33:14 +00:00
* The type of language used to define the content language .
2007-03-26 01:32:22 +00:00
*/
2010-03-07 07:44:18 +00:00
define ( 'LANGUAGE_TYPE_CONTENT' , 'language_content' );
2007-03-26 01:32:22 +00:00
/**
2009-10-09 16:33:14 +00:00
* The type of language used to select the user interface .
2007-03-26 01:32:22 +00:00
*/
2010-03-07 07:44:18 +00:00
define ( 'LANGUAGE_TYPE_INTERFACE' , 'language' );
2007-03-26 01:32:22 +00:00
/**
2009-10-09 16:33:14 +00:00
* The type of language used for URLs .
2007-03-26 01:32:22 +00:00
*/
2009-10-09 16:33:14 +00:00
define ( 'LANGUAGE_TYPE_URL' , 'language_url' );
2007-03-26 01:32:22 +00:00
2009-01-20 03:18:41 +00:00
/**
* Language written left to right . Possible value of $language -> direction .
*/
define ( 'LANGUAGE_LTR' , 0 );
/**
* Language written right to left . Possible value of $language -> direction .
*/
define ( 'LANGUAGE_RTL' , 1 );
2008-09-17 07:11:59 +00:00
/**
* For convenience , define a short form of the request time global .
*/
2008-10-06 22:40:20 +00:00
define ( 'REQUEST_TIME' , $_SERVER [ 'REQUEST_TIME' ]);
2008-09-17 07:11:59 +00:00
2008-10-13 04:06:22 +00:00
/**
* @ name Title text filtering flags
* @ {
* Flags for use in drupal_set_title () .
*/
/**
* Flag for drupal_set_title (); text is not sanitized , so run check_plain () .
*/
define ( 'CHECK_PLAIN' , 0 );
/**
* Flag for drupal_set_title (); text has already been sanitized .
*/
define ( 'PASS_THROUGH' , - 1 );
2010-03-11 21:21:36 +00:00
/**
* @ } End of " Title text filtering flags " .
*/
2008-11-11 22:39:59 +00:00
/**
2008-11-16 19:41:14 +00:00
* Signals that the registry lookup cache should be reset .
2008-11-11 22:39:59 +00:00
*/
define ( 'REGISTRY_RESET_LOOKUP_CACHE' , 1 );
/**
2008-11-16 19:41:14 +00:00
* Signals that the registry lookup cache should be written to storage .
2008-11-11 22:39:59 +00:00
*/
define ( 'REGISTRY_WRITE_LOOKUP_CACHE' , 2 );
2005-05-12 11:21:35 +00:00
/**
2009-09-14 07:43:11 +00:00
* Start the timer with the specified name . If you start and stop the same
* timer multiple times , the measured intervals will be accumulated .
2005-05-12 11:21:35 +00:00
*
* @ param name
* The name of the timer .
*/
function timer_start ( $name ) {
global $timers ;
2008-10-16 21:16:06 +00:00
$timers [ $name ][ 'start' ] = microtime ( TRUE );
2006-02-27 13:46:01 +00:00
$timers [ $name ][ 'count' ] = isset ( $timers [ $name ][ 'count' ]) ? ++ $timers [ $name ][ 'count' ] : 1 ;
2005-05-12 11:21:35 +00:00
}
/**
* Read the current timer value without stopping the timer .
*
* @ param name
* The name of the timer .
* @ return
* The current timer value in ms .
*/
function timer_read ( $name ) {
global $timers ;
2006-11-12 00:21:15 +00:00
if ( isset ( $timers [ $name ][ 'start' ])) {
2008-10-16 21:16:06 +00:00
$stop = microtime ( TRUE );
2006-11-12 00:21:15 +00:00
$diff = round (( $stop - $timers [ $name ][ 'start' ]) * 1000 , 2 );
2005-05-12 11:21:35 +00:00
2006-11-12 00:21:15 +00:00
if ( isset ( $timers [ $name ][ 'time' ])) {
$diff += $timers [ $name ][ 'time' ];
}
return $diff ;
2006-09-06 06:43:03 +00:00
}
2009-10-18 11:36:49 +00:00
return $timers [ $name ][ 'time' ];
2005-05-12 11:21:35 +00:00
}
/**
* Stop the timer with the specified name .
*
* @ param name
* The name of the timer .
* @ return
2009-09-14 07:43:11 +00:00
* A timer array . The array contains the number of times the timer has been
* started and stopped ( count ) and the accumulated timer value in ms ( time ) .
2005-05-12 11:21:35 +00:00
*/
function timer_stop ( $name ) {
global $timers ;
2010-02-25 09:13:00 +00:00
if ( isset ( $timers [ $name ][ 'start' ])) {
$stop = microtime ( TRUE );
$diff = round (( $stop - $timers [ $name ][ 'start' ]) * 1000 , 2 );
if ( isset ( $timers [ $name ][ 'time' ])) {
$timers [ $name ][ 'time' ] += $diff ;
}
else {
$timers [ $name ][ 'time' ] = $diff ;
}
unset ( $timers [ $name ][ 'start' ]);
2009-10-18 11:36:49 +00:00
}
2005-05-12 11:21:35 +00:00
return $timers [ $name ];
}
2005-01-09 09:22:40 +00:00
2004-07-13 07:21:14 +00:00
/**
2006-04-12 08:42:47 +00:00
* Find the appropriate configuration directory .
2004-07-13 07:21:14 +00:00
*
2005-11-21 21:33:44 +00:00
* Try finding a matching configuration directory by stripping the website ' s
* hostname from left to right and pathname from right to left . The first
2006-08-25 05:42:00 +00:00
* configuration file found will be used ; the remaining will ignored . If no
2005-11-21 21:33:44 +00:00
* configuration file is found , return a default value '$confdir/default' .
2004-11-24 22:44:01 +00:00
*
2005-03-31 21:18:08 +00:00
* Example for a fictitious site installed at
2005-11-21 21:33:44 +00:00
* http :// www . drupal . org : 8080 / mysite / test / the 'settings.php' is searched in
* the following directories :
2004-12-29 19:56:25 +00:00
*
2005-11-21 21:33:44 +00:00
* 1. $confdir / 8080. www . drupal . org . mysite . test
* 2. $confdir / www . drupal . org . mysite . test
* 3. $confdir / drupal . org . mysite . test
* 4. $confdir / org . mysite . test
2004-12-29 19:56:25 +00:00
*
2005-11-21 21:33:44 +00:00
* 5. $confdir / 8080. www . drupal . org . mysite
* 6. $confdir / www . drupal . org . mysite
* 7. $confdir / drupal . org . mysite
* 8. $confdir / org . mysite
2004-12-29 19:56:25 +00:00
*
2005-11-21 21:33:44 +00:00
* 9. $confdir / 8080. www . drupal . org
* 10. $confdir / www . drupal . org
* 11. $confdir / drupal . org
* 12. $confdir / org
2004-12-29 19:56:25 +00:00
*
2005-11-21 21:33:44 +00:00
* 13. $confdir / default
2007-07-30 19:22:47 +00:00
*
2008-10-12 02:47:50 +00:00
* If a file named sites . php is present in the $confdir , it will be loaded
2008-12-20 18:24:41 +00:00
* prior to scanning for directories . It should define an associative array
* named $sites , which maps domains to directories . It should be in the form
2008-10-12 02:47:50 +00:00
* of :
*
* $sites = array (
* 'The url to alias' => 'A directory within the sites directory'
* );
*
* For example :
*
* $sites = array (
* 'devexample.com' => 'example.com' ,
* 'localhost/example' => 'example.com' ,
* );
*
* The above array will cause Drupal to look for a directory named
* " example.com " in the sites directory whenever a request comes from
* " example.com " , " devexample.com " , or " localhost/example " . That is useful
* on development servers , where the domain name may not be the same as the
2008-12-20 18:24:41 +00:00
* domain of the live server . Since Drupal stores file paths into the database
2008-10-12 02:47:50 +00:00
* ( files , system table , etc . ) this will ensure the paths are correct while
* accessed on development servers .
*
2007-07-30 19:22:47 +00:00
* @ param $require_settings
* Only configuration directories with an existing settings . php file
* will be recognized . Defaults to TRUE . During initial installation ,
* this is set to FALSE so that Drupal can detect a matching directory ,
* then create a new settings . php file in it .
* @ param reset
* Force a full search for matching directories even if one had been
* found previously .
* @ return
* The path of the matching directory .
2004-07-13 07:21:14 +00:00
*/
2007-07-30 19:22:47 +00:00
function conf_path ( $require_settings = TRUE , $reset = FALSE ) {
2009-04-02 20:39:45 +00:00
$conf = & drupal_static ( __FUNCTION__ , '' );
2003-11-18 19:44:36 +00:00
2007-07-30 19:22:47 +00:00
if ( $conf && ! $reset ) {
2004-11-24 22:44:01 +00:00
return $conf ;
}
2003-11-18 19:44:36 +00:00
2004-12-29 19:56:25 +00:00
$confdir = 'sites' ;
2008-10-12 02:47:50 +00:00
$sites = array ();
if ( file_exists ( DRUPAL_ROOT . '/' . $confdir . '/sites.php' )) {
// This will overwrite $sites with the desired mappings.
include ( DRUPAL_ROOT . '/' . $confdir . '/sites.php' );
}
2007-08-30 15:53:39 +00:00
$uri = explode ( '/' , $_SERVER [ 'SCRIPT_NAME' ] ? $_SERVER [ 'SCRIPT_NAME' ] : $_SERVER [ 'SCRIPT_FILENAME' ]);
2005-11-21 16:24:41 +00:00
$server = explode ( '.' , implode ( '.' , array_reverse ( explode ( ':' , rtrim ( $_SERVER [ 'HTTP_HOST' ], '.' )))));
2004-11-24 22:44:01 +00:00
for ( $i = count ( $uri ) - 1 ; $i > 0 ; $i -- ) {
for ( $j = count ( $server ); $j > 0 ; $j -- ) {
$dir = implode ( '.' , array_slice ( $server , - $j )) . implode ( '.' , array_slice ( $uri , 0 , $i ));
2008-10-12 02:47:50 +00:00
if ( isset ( $sites [ $dir ]) && file_exists ( DRUPAL_ROOT . '/' . $confdir . '/' . $sites [ $dir ])) {
$dir = $sites [ $dir ];
}
if ( file_exists ( DRUPAL_ROOT . '/' . $confdir . '/' . $dir . '/settings.php' ) || ( ! $require_settings && file_exists ( DRUPAL_ROOT . '/' . $confdir . '/' . $dir ))) {
2004-11-24 22:44:01 +00:00
$conf = " $confdir / $dir " ;
return $conf ;
}
2003-11-18 19:44:36 +00:00
}
}
2004-11-24 22:44:01 +00:00
$conf = " $confdir /default " ;
return $conf ;
2003-11-18 19:44:36 +00:00
}
2009-07-28 12:13:47 +00:00
/**
* Set appropriate server variables needed for command line scripts to work .
*
* This function can be called by command line scripts before bootstrapping
* Drupal , to ensure that the page loads with the desired server parameters .
* This is because many parts of Drupal assume that they are running in a web
* browser and therefore use information from the global PHP $_SERVER variable
* that does not get set when Drupal is run from the command line .
*
* In many cases , the default way in which this function populates the $_SERVER
* variable is sufficient , and it can therefore be called without passing in
* any input . However , command line scripts running on a multisite installation
* ( or on any installation that has settings . php stored somewhere other than
* the sites / default folder ) need to pass in the URL of the site to allow
* Drupal to detect the correct location of the settings . php file . Passing in
* the 'url' parameter is also required for functions like request_uri () to
* return the expected values .
*
* Most other parameters do not need to be passed in , but may be necessary in
* some cases ; for example , if Drupal ' s ip_address () function needs to return
* anything but the standard localhost value ( '127.0.0.1' ), the command line
* script should pass in the desired value via the 'REMOTE_ADDR' key .
*
* @ param $variables
* ( optional ) An associative array of variables within $_SERVER that should
* be replaced . If the special element 'url' is provided in this array , it
* will be used to populate some of the server defaults ; it should be set to
* the URL of the current page request , excluding any $_GET request but
* including the script name ( e . g . , http :// www . example . com / mysite / index . php ) .
*
* @ see conf_path ()
* @ see request_uri ()
* @ see ip_address ()
*/
function drupal_override_server_variables ( $variables = array ()) {
// Set defaults based on the provided URL.
if ( isset ( $variables [ 'url' ])) {
$url = parse_url ( $variables [ 'url' ]);
unset ( $variables [ 'url' ]);
}
else {
$url = array ();
}
$url += array (
'path' => '' ,
'host' => 'localhost' ,
);
$defaults = array (
'HTTP_HOST' => $url [ 'host' ],
'SCRIPT_NAME' => $url [ 'path' ],
'REMOTE_ADDR' => '127.0.0.1' ,
'REQUEST_METHOD' => 'GET' ,
'SERVER_NAME' => NULL ,
2009-09-19 10:38:47 +00:00
'SERVER_SOFTWARE' => NULL ,
2009-07-28 12:13:47 +00:00
'HTTP_USER_AGENT' => NULL ,
);
// Replace elements of the $_SERVER array, as appropriate.
$_SERVER = $variables + $_SERVER + $defaults ;
}
2006-04-21 06:39:00 +00:00
/**
2009-07-14 10:22:17 +00:00
* Initialize PHP environment .
2008-09-06 15:20:09 +00:00
*/
2009-07-14 10:22:17 +00:00
function drupal_environment_initialize () {
2008-09-06 15:20:09 +00:00
if ( ! isset ( $_SERVER [ 'HTTP_REFERER' ])) {
$_SERVER [ 'HTTP_REFERER' ] = '' ;
2006-04-21 06:39:00 +00:00
}
2008-09-08 21:24:30 +00:00
if ( ! isset ( $_SERVER [ 'SERVER_PROTOCOL' ]) || ( $_SERVER [ 'SERVER_PROTOCOL' ] != 'HTTP/1.0' && $_SERVER [ 'SERVER_PROTOCOL' ] != 'HTTP/1.1' )) {
$_SERVER [ 'SERVER_PROTOCOL' ] = 'HTTP/1.0' ;
}
2009-01-14 12:15:38 +00:00
2009-01-22 03:05:18 +00:00
if ( isset ( $_SERVER [ 'HTTP_HOST' ])) {
// As HTTP_HOST is user input, ensure it only contains characters allowed
// in hostnames. See RFC 952 (and RFC 2181).
// $_SERVER['HTTP_HOST'] is lowercased here per specifications.
$_SERVER [ 'HTTP_HOST' ] = strtolower ( $_SERVER [ 'HTTP_HOST' ]);
if ( ! drupal_valid_http_host ( $_SERVER [ 'HTTP_HOST' ])) {
// HTTP_HOST is invalid, e.g. if containing slashes it may be an attack.
header ( $_SERVER [ 'SERVER_PROTOCOL' ] . ' 400 Bad Request' );
exit ;
}
}
else {
// Some pre-HTTP/1.1 clients will not send a Host header. Ensure the key is
// defined for E_ALL compliance.
$_SERVER [ 'HTTP_HOST' ] = '' ;
2009-01-14 12:15:38 +00:00
}
2010-01-29 22:40:41 +00:00
// When clean URLs are enabled, emulate ?q=foo/bar using REQUEST_URI. It is
// not possible to append the query string using mod_rewrite without the B
// flag (this was added in Apache 2.2.8), because mod_rewrite unescapes the
// path before passing it on to PHP. This is a problem when the path contains
// e.g. "&" or "%" that have special meanings in URLs and must be encoded.
$_GET [ 'q' ] = request_path ();
2008-10-15 16:05:51 +00:00
// Enforce E_ALL, but allow users to set levels not part of E_ALL.
error_reporting ( E_ALL | error_reporting ());
2008-11-22 13:46:11 +00:00
2008-11-22 16:48:20 +00:00
// Override PHP settings required for Drupal to work properly.
// sites/default/default.settings.php contains more runtime settings.
// The .htaccess file contains settings that cannot be changed at runtime.
2008-11-22 13:46:11 +00:00
2008-11-22 16:48:20 +00:00
// Prevent PHP from generating HTML error messages.
2008-10-15 16:05:51 +00:00
ini_set ( 'html_errors' , 0 );
2008-11-22 16:48:20 +00:00
// Don't escape quotes when reading files from the database, disk, etc.
2008-11-22 13:46:11 +00:00
ini_set ( 'magic_quotes_runtime' , '0' );
2008-12-23 19:59:17 +00:00
// Use session cookies, not transparent sessions that puts the session id in
// the query string.
2009-06-06 20:15:23 +00:00
ini_set ( 'session.use_cookies' , '1' );
2008-12-23 19:59:17 +00:00
ini_set ( 'session.use_only_cookies' , '1' );
2008-12-26 11:00:23 +00:00
ini_set ( 'session.use_trans_sid' , '0' );
2008-12-23 19:59:17 +00:00
// Don't send HTTP headers using PHP's session handler.
ini_set ( 'session.cache_limiter' , 'none' );
2009-07-01 12:47:30 +00:00
// Use httponly session cookies.
ini_set ( 'session.cookie_httponly' , '1' );
2010-03-07 06:38:55 +00:00
// Set sane locale settings, to ensure consistent string, dates, times and
// numbers handling.
setlocale ( LC_ALL , 'C' );
2006-04-21 06:39:00 +00:00
}
2008-11-02 10:56:35 +00:00
/**
2009-01-22 03:05:18 +00:00
* Validate that a hostname ( for example $_SERVER [ 'HTTP_HOST' ]) is safe .
2008-11-02 10:56:35 +00:00
*
* @ return
* TRUE if only containing valid characters , or FALSE otherwise .
*/
2009-01-22 03:05:18 +00:00
function drupal_valid_http_host ( $host ) {
return preg_match ( '/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/' , $host );
2008-11-02 10:56:35 +00:00
}
2006-04-12 08:42:47 +00:00
/**
2007-04-30 14:37:36 +00:00
* Loads the configuration and sets the base URL , cookie domain , and
* session name correctly .
2006-04-12 08:42:47 +00:00
*/
2009-07-14 10:22:17 +00:00
function drupal_settings_initialize () {
2007-04-24 08:43:31 +00:00
global $base_url , $base_path , $base_root ;
2007-04-24 10:45:20 +00:00
// Export the following settings.php variables to the global namespace
2010-01-14 18:45:17 +00:00
global $databases , $db_prefix , $cookie_domain , $conf , $installed_profile , $update_free_access , $db_url , $drupal_hash_salt , $is_https , $base_secure_url , $base_insecure_url ;
2007-04-24 10:45:20 +00:00
$conf = array ();
2008-09-20 20:22:25 +00:00
if ( file_exists ( DRUPAL_ROOT . '/' . conf_path () . '/settings.php' )) {
include_once DRUPAL_ROOT . '/' . conf_path () . '/settings.php' ;
2007-05-08 16:36:55 +00:00
}
2006-04-12 08:42:47 +00:00
if ( isset ( $base_url )) {
// Parse fixed base URL from settings.php.
$parts = parse_url ( $base_url );
2009-09-05 13:05:31 +00:00
$http_protocol = $parts [ 'scheme' ];
2006-05-02 08:37:42 +00:00
if ( ! isset ( $parts [ 'path' ])) {
$parts [ 'path' ] = '' ;
}
2008-04-14 17:48:46 +00:00
$base_path = $parts [ 'path' ] . '/' ;
2006-04-12 08:42:47 +00:00
// Build $base_root (everything until first slash after "scheme://").
$base_root = substr ( $base_url , 0 , strlen ( $base_url ) - strlen ( $parts [ 'path' ]));
}
else {
// Create base URL
2009-09-05 13:05:31 +00:00
$http_protocol = ( isset ( $_SERVER [ 'HTTPS' ]) && $_SERVER [ 'HTTPS' ] == 'on' ) ? 'https' : 'http' ;
$base_root = $http_protocol . '://' . $_SERVER [ 'HTTP_HOST' ];
2007-08-30 15:53:39 +00:00
2009-09-05 13:05:31 +00:00
$base_url = $base_root ;
2007-08-30 15:53:39 +00:00
// $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not
// be modified by a visitor.
2010-01-29 22:40:41 +00:00
if ( $dir = rtrim ( dirname ( $_SERVER [ 'SCRIPT_NAME' ]), '\/' )) {
$base_path = $dir ;
2006-04-12 08:42:47 +00:00
$base_url .= $base_path ;
$base_path .= '/' ;
}
else {
$base_path = '/' ;
}
}
2009-09-05 13:05:31 +00:00
$is_https = $http_protocol == 'https' ;
$base_secure_url = str_replace ( 'http://' , 'https://' , $base_url );
$base_insecure_url = str_replace ( 'https://' , 'http://' , $base_url );
2007-04-30 14:37:36 +00:00
if ( $cookie_domain ) {
// If the user specifies the cookie domain, also use it for session name.
$session_name = $cookie_domain ;
}
else {
2007-07-25 10:06:44 +00:00
// Otherwise use $base_url as session name, without the protocol
// to use the same session identifiers across http and https.
list ( , $session_name ) = explode ( '://' , $base_url , 2 );
2010-01-03 01:23:49 +00:00
// HTTP_HOST can be modified by a visitor, but we already sanitized it
// in drupal_settings_initialize().
2007-04-30 14:37:36 +00:00
if ( ! empty ( $_SERVER [ 'HTTP_HOST' ])) {
2010-01-03 01:23:49 +00:00
$cookie_domain = $_SERVER [ 'HTTP_HOST' ];
2010-03-24 09:17:37 +00:00
// Strip leading periods, www., and port numbers from cookie domain.
$cookie_domain = ltrim ( $cookie_domain , '.' );
if ( strpos ( $cookie_domain , 'www.' ) === 0 ) {
$cookie_domain = substr ( $cookie_domain , 4 );
}
$cookie_domain = explode ( ':' , $cookie_domain );
$cookie_domain = '.' . $cookie_domain [ 0 ];
2007-04-30 14:37:36 +00:00
}
}
// Per RFC 2109, cookie domains must contain at least one dot other than the
// first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
if ( count ( explode ( '.' , $cookie_domain )) > 2 && ! is_numeric ( str_replace ( '.' , '' , $cookie_domain ))) {
ini_set ( 'session.cookie_domain' , $cookie_domain );
}
2009-09-05 13:05:31 +00:00
// To prevent session cookies from being hijacked, a user can configure the
// SSL version of their website to only transfer session cookies via SSL by
// using PHP's session.cookie_secure setting. The browser will then use two
// separate session cookies for the HTTPS and HTTP versions of the site. So we
// must use different session identifiers for HTTPS and HTTP to prevent a
// cookie collision.
if ( $is_https ) {
ini_set ( 'session.cookie_secure' , TRUE );
}
$prefix = ini_get ( 'session.cookie_secure' ) ? 'SSESS' : 'SESS' ;
session_name ( $prefix . md5 ( $session_name ));
2006-04-12 08:42:47 +00:00
}
2004-11-25 06:14:59 +00:00
/**
* Returns and optionally sets the filename for a system item ( module ,
2006-05-07 00:08:36 +00:00
* theme , etc . ) . The filename , whether provided , cached , or retrieved
2004-11-25 06:14:59 +00:00
* from the database , is only returned if the file exists .
*
2006-11-24 10:16:50 +00:00
* This function plays a key role in allowing Drupal ' s resources ( modules
* and themes ) to be located in different places depending on a site ' s
* configuration . For example , a module 'foo' may legally be be located
* in any of these three places :
*
* modules / foo / foo . module
* sites / all / modules / foo / foo . module
* sites / example . com / modules / foo / foo . module
*
* Calling drupal_get_filename ( 'module' , 'foo' ) will give you one of
* the above , depending on where the module is located .
*
2004-11-25 06:14:59 +00:00
* @ param $type
2010-02-17 03:55:45 +00:00
* The type of the item ( i . e . theme , theme_engine , module , profile ) .
2004-11-25 06:14:59 +00:00
* @ param $name
* The name of the item for which the filename is requested .
* @ param $filename
* The filename of the item if it is to be set explicitly rather
* than by consulting the database .
*
* @ return
* The filename of the requested item .
*/
2006-11-24 10:16:50 +00:00
function drupal_get_filename ( $type , $name , $filename = NULL ) {
2009-10-31 14:00:37 +00:00
// The location of files will not change during the request, so do not use
// drupal_static().
static $files = array ();
2004-11-25 06:14:59 +00:00
2005-10-22 15:14:46 +00:00
if ( ! isset ( $files [ $type ])) {
2004-11-25 06:14:59 +00:00
$files [ $type ] = array ();
}
2005-10-22 15:14:46 +00:00
if ( ! empty ( $filename ) && file_exists ( $filename )) {
2004-11-25 06:14:59 +00:00
$files [ $type ][ $name ] = $filename ;
}
2005-10-22 15:14:46 +00:00
elseif ( isset ( $files [ $type ][ $name ])) {
2004-11-25 06:14:59 +00:00
// nothing
}
2006-11-24 10:16:50 +00:00
// Verify that we have an active database connection, before querying
2008-12-20 18:24:41 +00:00
// the database. This is required because this function is called both
2006-11-24 10:16:50 +00:00
// before we have a database connection (i.e. during installation) and
// when a database connection fails.
2004-11-25 06:14:59 +00:00
else {
2009-08-04 04:02:26 +00:00
try {
2009-12-27 03:37:54 +00:00
if ( function_exists ( 'db_query' )) {
$file = db_query ( " SELECT filename FROM { system} WHERE name = :name AND type = :type " , array ( ':name' => $name , ':type' => $type )) -> fetchField ();
if ( file_exists ( $file )) {
$files [ $type ][ $name ] = $file ;
}
2009-08-04 04:02:26 +00:00
}
}
2009-12-27 03:37:54 +00:00
catch ( Exception $e ) {
2009-08-04 04:02:26 +00:00
// The database table may not exist because Drupal is not yet installed,
// or the database might be down. We have a fallback for this case so we
// hide the error completely.
}
// Fallback to searching the filesystem if the database could not find the
// file or the file returned by the database is not found.
if ( ! isset ( $files [ $type ][ $name ])) {
// We have a consistent directory naming: modules, themes...
$dir = $type . 's' ;
if ( $type == 'theme_engine' ) {
$dir = 'themes/engines' ;
2009-12-27 03:37:54 +00:00
$extension = 'engine' ;
2009-08-04 04:02:26 +00:00
}
elseif ( $type == 'theme' ) {
2009-12-27 03:37:54 +00:00
$extension = 'info' ;
2009-08-04 04:02:26 +00:00
}
else {
2009-12-27 03:37:54 +00:00
$extension = $type ;
2009-08-04 04:02:26 +00:00
}
2009-11-02 03:46:43 +00:00
if ( ! function_exists ( 'drupal_system_listing' )) {
require_once DRUPAL_ROOT . '/includes/common.inc' ;
}
2009-12-27 03:37:54 +00:00
// Scan the appropriate directories for all files with the requested
// extension, not just the file we are currently looking for. This
// prevents unnecessary scans from being repeated when this function is
// called more than once in the same page request.
$matches = drupal_system_listing ( " / \ . $extension $ / " , $dir , 'name' , 0 );
foreach ( $matches as $matched_name => $file ) {
$files [ $type ][ $matched_name ] = $file -> uri ;
2004-11-25 06:14:59 +00:00
}
}
}
2007-03-27 05:13:55 +00:00
if ( isset ( $files [ $type ][ $name ])) {
return $files [ $type ][ $name ];
}
2004-11-25 06:14:59 +00:00
}
2004-07-13 07:21:14 +00:00
/**
* Load the persistent variable table .
*
* The variable table is composed of values that have been saved in the table
* with variable_set () as well as those explicitly specified in the configuration
* file .
*/
2009-07-14 10:22:17 +00:00
function variable_initialize ( $conf = array ()) {
2006-08-25 05:42:00 +00:00
// NOTE: caching the variables improves performance by 20% when serving cached pages.
2009-11-10 22:06:09 +00:00
if ( $cached = cache_get ( 'variables' , 'cache_bootstrap' )) {
2007-04-25 21:34:32 +00:00
$variables = $cached -> data ;
2004-09-08 18:06:04 +00:00
}
else {
2008-10-06 14:26:54 +00:00
$variables = array_map ( 'unserialize' , db_query ( 'SELECT name, value FROM {variable}' ) -> fetchAllKeyed ());
2009-11-10 22:06:09 +00:00
cache_set ( 'variables' , $variables , 'cache_bootstrap' );
2004-09-08 18:06:04 +00:00
}
foreach ( $conf as $name => $value ) {
$variables [ $name ] = $value ;
2003-11-18 19:44:36 +00:00
}
2004-09-08 18:06:04 +00:00
return $variables ;
2003-11-18 19:44:36 +00:00
}
2004-07-13 07:21:14 +00:00
/**
* Return a persistent variable .
*
* @ param $name
* The name of the variable to return .
* @ param $default
* The default value to use if this variable has never been set .
2010-03-26 17:14:46 +00:00
*
2004-07-13 07:21:14 +00:00
* @ return
* The value of the variable .
2009-08-21 00:00:43 +00:00
*
2010-03-26 17:14:46 +00:00
* @ see variable_del ()
* @ see variable_set ()
2004-07-13 07:21:14 +00:00
*/
2009-01-31 16:50:57 +00:00
function variable_get ( $name , $default = NULL ) {
2003-11-18 19:44:36 +00:00
global $conf ;
return isset ( $conf [ $name ]) ? $conf [ $name ] : $default ;
}
2004-07-13 07:21:14 +00:00
/**
* Set a persistent variable .
*
* @ param $name
* The name of the variable to set .
* @ param $value
* The value to set . This can be any PHP data type ; these functions take care
* of serialization as necessary .
2009-08-21 00:00:43 +00:00
*
2010-03-26 17:14:46 +00:00
* @ see variable_del ()
* @ see variable_get ()
2004-07-13 07:21:14 +00:00
*/
2003-11-18 19:44:36 +00:00
function variable_set ( $name , $value ) {
global $conf ;
2008-08-21 19:36:39 +00:00
db_merge ( 'variable' ) -> key ( array ( 'name' => $name )) -> fields ( array ( 'value' => serialize ( $value ))) -> execute ();
2005-05-12 11:21:35 +00:00
2009-11-10 22:06:09 +00:00
cache_clear_all ( 'variables' , 'cache_bootstrap' );
2003-11-18 19:44:36 +00:00
$conf [ $name ] = $value ;
}
2004-07-13 07:21:14 +00:00
/**
* Unset a persistent variable .
*
* @ param $name
* The name of the variable to undefine .
2009-08-21 00:00:43 +00:00
*
2010-03-26 17:14:46 +00:00
* @ see variable_get ()
* @ see variable_set ()
2004-07-13 07:21:14 +00:00
*/
2003-11-18 19:44:36 +00:00
function variable_del ( $name ) {
2009-05-23 19:21:30 +00:00
global $conf ;
2008-10-06 14:26:54 +00:00
db_delete ( 'variable' )
-> condition ( 'name' , $name )
-> execute ();
2009-11-10 22:06:09 +00:00
cache_clear_all ( 'variables' , 'cache_bootstrap' );
2003-11-18 19:44:36 +00:00
2009-05-23 19:21:30 +00:00
unset ( $conf [ $name ]);
2003-11-18 19:44:36 +00:00
}
2004-07-13 07:21:14 +00:00
/**
* Retrieve the current page from the cache .
*
2009-01-19 10:46:52 +00:00
* Note : we do not serve cached pages to authenticated users , or to anonymous
* users when $_SESSION is non - empty . $_SESSION may contain status messages
* from a form submission , the contents of a shopping cart , or other user -
* specific content that should not be cached and displayed to other users .
*
2009-11-05 03:00:21 +00:00
* @ param $check_only
* ( optional ) Set to TRUE to only return whether a previous call found a
* cache entry .
*
2009-01-19 10:46:52 +00:00
* @ return
2009-06-02 06:58:17 +00:00
* The cache object , if the page was found in the cache , NULL otherwise .
2004-07-13 07:21:14 +00:00
*/
2009-11-05 03:00:21 +00:00
function drupal_page_get_cache ( $check_only = FALSE ) {
2009-06-02 06:58:17 +00:00
global $base_root ;
2009-11-05 03:00:21 +00:00
static $cache_hit = FALSE ;
if ( $check_only ) {
return $cache_hit ;
}
2003-11-18 19:44:36 +00:00
2009-06-02 06:58:17 +00:00
if ( drupal_page_is_cacheable ()) {
2009-11-05 03:00:21 +00:00
$cache = cache_get ( $base_root . request_uri (), 'cache_page' );
if ( $cache !== FALSE ) {
$cache_hit = TRUE ;
}
return $cache ;
2009-01-19 10:46:52 +00:00
}
2009-06-02 06:58:17 +00:00
}
/**
* Determine the cacheability of the current page .
*
* @ param $allow_caching
2009-11-05 03:00:21 +00:00
* Set to FALSE if you want to prevent this page to get cached .
*
2009-06-02 06:58:17 +00:00
* @ return
2009-11-05 03:00:21 +00:00
* TRUE if the current page can be cached , FALSE otherwise .
2009-06-02 06:58:17 +00:00
*/
function drupal_page_is_cacheable ( $allow_caching = NULL ) {
$allow_caching_static = & drupal_static ( __FUNCTION__ , TRUE );
if ( isset ( $allow_caching )) {
$allow_caching_static = $allow_caching ;
2003-11-18 19:44:36 +00:00
}
2009-06-02 06:58:17 +00:00
return $allow_caching_static && ( $_SERVER [ 'REQUEST_METHOD' ] == 'GET' || $_SERVER [ 'REQUEST_METHOD' ] == 'HEAD' )
2009-09-19 10:38:47 +00:00
&& ! drupal_is_cli ();
2003-11-18 19:44:36 +00:00
}
2009-08-24 00:14:23 +00:00
/**
* Call all init or exit hooks without including all modules .
*
* @ param $hook
* The name of the bootstrap hook we wish to invoke .
*/
function bootstrap_invoke_all ( $hook ) {
2009-11-22 08:20:51 +00:00
// _drupal_bootstrap_page_cache() already loaded the bootstrap modules, so we
// don't need to tell module_list() to reset its bootstrap list.
foreach ( module_list ( FALSE , TRUE ) as $module ) {
2009-08-24 00:14:23 +00:00
drupal_load ( 'module' , $module );
module_invoke ( $module , $hook );
}
}
2004-11-25 06:14:59 +00:00
/**
2006-05-07 00:08:36 +00:00
* Includes a file with the provided type and name . This prevents
2004-11-25 06:14:59 +00:00
* including a theme , engine , module , etc . , more than once .
*
* @ param $type
* The type of item to load ( i . e . theme , theme_engine , module ) .
* @ param $name
* The name of the item to load .
*
* @ return
* TRUE if the item is loaded or has already been loaded .
*/
function drupal_load ( $type , $name ) {
2009-10-31 14:00:37 +00:00
// Once a file is included this can't be reversed during a request so do not
// use drupal_static() here.
static $files = array ();
2004-11-25 06:14:59 +00:00
2005-05-14 09:23:47 +00:00
if ( isset ( $files [ $type ][ $name ])) {
2004-11-25 06:14:59 +00:00
return TRUE ;
}
$filename = drupal_get_filename ( $type , $name );
if ( $filename ) {
2008-09-20 20:22:25 +00:00
include_once DRUPAL_ROOT . '/' . $filename ;
2004-11-25 06:14:59 +00:00
$files [ $type ][ $name ] = TRUE ;
return TRUE ;
}
return FALSE ;
}
2009-04-22 09:45:03 +00:00
/**
* Set an HTTP response header for the current page .
*
* Note : When sending a Content - Type header , always include a 'charset' type ,
* too . This is necessary to avoid security bugs ( e . g . UTF - 7 XSS ) .
*
* @ param $name
2010-03-06 06:31:24 +00:00
* The HTTP header name , or the special 'Status' header name .
2009-04-22 09:45:03 +00:00
* @ param $value
2010-03-06 06:31:24 +00:00
* The HTTP header value ; if equal to FALSE , the specified header is unset .
* If $name is 'Status' , this is expected to be a status code followed by a
* reason phrase , e . g . " 404 Not Found " .
2009-04-22 09:45:03 +00:00
* @ param $append
* Whether to append the value to an existing header or to replace it .
*/
2010-03-06 06:31:24 +00:00
function drupal_add_http_header ( $name , $value , $append = FALSE ) {
2009-04-22 09:45:03 +00:00
// The headers as name/value pairs.
2010-03-06 06:31:24 +00:00
$headers = & drupal_static ( 'drupal_http_headers' , array ());
2009-04-22 09:45:03 +00:00
2010-03-06 06:31:24 +00:00
$name_lower = strtolower ( $name );
2009-04-24 08:16:56 +00:00
_drupal_set_preferred_header_name ( $name );
2009-04-22 09:45:03 +00:00
2010-03-06 06:31:24 +00:00
if ( $value === FALSE ) {
2009-04-24 08:16:56 +00:00
$headers [ $name_lower ] = FALSE ;
2009-04-22 09:45:03 +00:00
}
2009-04-24 08:16:56 +00:00
elseif ( isset ( $headers [ $name_lower ]) && $append ) {
2009-04-22 09:45:03 +00:00
// Multiple headers with identical names may be combined using comma (RFC
// 2616, section 4.2).
2009-04-24 08:16:56 +00:00
$headers [ $name_lower ] .= ',' . $value ;
2009-04-22 09:45:03 +00:00
}
else {
2009-04-24 08:16:56 +00:00
$headers [ $name_lower ] = $value ;
2009-04-22 09:45:03 +00:00
}
2009-04-24 08:16:56 +00:00
drupal_send_headers ( array ( $name => $headers [ $name_lower ]), TRUE );
2009-04-22 09:45:03 +00:00
}
/**
* Get the HTTP response headers for the current page .
*
* @ param $name
* An HTTP header name . If omitted , all headers are returned as name / value
* pairs . If an array value is FALSE , the header has been unset .
* @ return
* A string containing the header value , or FALSE if the header has been set ,
* or NULL if the header has not been set .
*/
2009-09-30 18:36:02 +00:00
function drupal_get_http_header ( $name = NULL ) {
2010-03-06 06:31:24 +00:00
$headers = & drupal_static ( 'drupal_http_headers' , array ());
2009-04-22 09:45:03 +00:00
if ( isset ( $name )) {
$name = strtolower ( $name );
return isset ( $headers [ $name ]) ? $headers [ $name ] : NULL ;
}
else {
return $headers ;
}
}
/**
* Header names are case - insensitive , but for maximum compatibility they should
* follow " common form " ( see RFC 2617 , section 4.2 ) .
*/
function _drupal_set_preferred_header_name ( $name = NULL ) {
static $header_names = array ();
if ( ! isset ( $name )) {
return $header_names ;
}
$header_names [ strtolower ( $name )] = $name ;
}
/**
2009-09-30 18:36:02 +00:00
* Send the HTTP response headers previously set using drupal_add_http_header () .
2009-04-22 09:45:03 +00:00
* Add default headers , unless they have been replaced or unset using
2009-09-30 18:36:02 +00:00
* drupal_add_http_header () .
2009-04-22 09:45:03 +00:00
*
* @ param $default_headers
* An array of headers as name / value pairs .
* @ param $single
* If TRUE and headers have already be sent , send only the specified header .
*/
function drupal_send_headers ( $default_headers = array (), $only_default = FALSE ) {
$headers_sent = & drupal_static ( __FUNCTION__ , FALSE );
2009-09-30 18:36:02 +00:00
$headers = drupal_get_http_header ();
2009-04-22 09:45:03 +00:00
if ( $only_default && $headers_sent ) {
$headers = array ();
}
$headers_sent = TRUE ;
$header_names = _drupal_set_preferred_header_name ();
foreach ( $default_headers as $name => $value ) {
$name_lower = strtolower ( $name );
if ( ! isset ( $headers [ $name_lower ])) {
$headers [ $name_lower ] = $value ;
$header_names [ $name_lower ] = $name ;
}
}
foreach ( $headers as $name_lower => $value ) {
2010-03-06 06:31:24 +00:00
if ( $name_lower == 'status' ) {
2009-04-22 09:45:03 +00:00
header ( $_SERVER [ 'SERVER_PROTOCOL' ] . ' ' . $value );
}
// Skip headers that have been unset.
elseif ( $value ) {
header ( $header_names [ $name_lower ] . ': ' . $value );
}
}
}
2004-07-13 07:21:14 +00:00
/**
* Set HTTP headers in preparation for a page response .
2005-10-09 21:51:43 +00:00
*
2009-04-22 09:45:03 +00:00
* Authenticated users are always given a 'no-cache' header , and will fetch a
* fresh page on every request . This prevents authenticated users from seeing
* locally cached pages .
*
* Also give each page a unique ETag . This will force clients to include both
* an If - Modified - Since header and an If - None - Match header when doing
* conditional requests for the page ( required by RFC 2616 , section 13.3 . 4 ),
* making the validation more robust . This is a workaround for a bug in Mozilla
* Firefox that is triggered when Drupal ' s caching is enabled and the user
* accesses Drupal via an HTTP proxy ( see
* https :// bugzilla . mozilla . org / show_bug . cgi ? id = 269303 ) : When an authenticated
* user requests a page , and then logs out and requests the same page again ,
* Firefox may send a conditional request based on the page that was cached
* locally when the user was logged in . If this page did not have an ETag
* header , the request only contains an If - Modified - Since header . The date will
* be recent , because with authenticated users the Last - Modified header always
* refers to the time of the request . If the user accesses Drupal via a proxy
* server , and the proxy already has a cached copy of the anonymous page with an
* older Last - Modified date , the proxy may respond with 304 Not Modified , making
* the client think that the anonymous and authenticated pageviews are
* identical .
2006-08-03 13:42:34 +00:00
*
2009-06-02 06:58:17 +00:00
* @ see drupal_page_set_cache ()
2004-07-13 07:21:14 +00:00
*/
2003-11-18 19:44:36 +00:00
function drupal_page_header () {
2009-04-22 09:45:03 +00:00
$headers_sent = & drupal_static ( __FUNCTION__ , FALSE );
if ( $headers_sent ) {
return TRUE ;
}
$headers_sent = TRUE ;
$default_headers = array (
'Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT' ,
'Last-Modified' => gmdate ( DATE_RFC1123 , REQUEST_TIME ),
'Cache-Control' => 'no-cache, must-revalidate, post-check=0, pre-check=0' ,
'ETag' => '"' . REQUEST_TIME . '"' ,
);
drupal_send_headers ( $default_headers );
2006-08-31 18:40:04 +00:00
}
2004-02-15 15:57:55 +00:00
2006-08-31 18:40:04 +00:00
/**
* Set HTTP headers in preparation for a cached page response .
*
2009-04-22 09:45:03 +00:00
* The headers allow as much as possible in proxies and browsers without any
* particular knowledge about the pages . Modules can override these headers
2009-09-30 18:36:02 +00:00
* using drupal_add_http_header () .
2006-08-31 18:40:04 +00:00
*
2009-04-22 09:45:03 +00:00
* If the request is conditional ( using If - Modified - Since and If - None - Match ),
* and the conditions match those currently in the cache , a 304 Not Modified
* response is sent .
*/
2009-06-02 06:58:17 +00:00
function drupal_serve_page_from_cache ( stdClass $cache ) {
2009-04-22 09:45:03 +00:00
// Negotiate whether to use compression.
$page_compression = variable_get ( 'page_compression' , TRUE ) && extension_loaded ( 'zlib' );
$return_compressed = $page_compression && isset ( $_SERVER [ 'HTTP_ACCEPT_ENCODING' ]) && strpos ( $_SERVER [ 'HTTP_ACCEPT_ENCODING' ], 'gzip' ) !== FALSE ;
// Get headers set in hook_boot(). Keys are lower-case.
2009-09-30 18:36:02 +00:00
$hook_boot_headers = drupal_get_http_header ();
2009-04-22 09:45:03 +00:00
// Headers generated in this function, that may be replaced or unset using
2009-09-30 18:36:02 +00:00
// drupal_add_http_headers(). Keys are mixed-case.
2009-04-22 09:45:03 +00:00
$default_headers = array ();
foreach ( $cache -> headers as $name => $value ) {
// In the case of a 304 response, certain headers must be sent, and the
// remaining may not (see RFC 2616, section 10.3.5). Do not override
// headers set in hook_boot().
$name_lower = strtolower ( $name );
if ( in_array ( $name_lower , array ( 'content-location' , 'expires' , 'cache-control' , 'vary' )) && ! isset ( $hook_boot_headers [ $name_lower ])) {
2009-09-30 18:36:02 +00:00
drupal_add_http_header ( $name , $value );
2009-04-22 09:45:03 +00:00
unset ( $cache -> headers [ $name ]);
}
}
// If a cache is served from a HTTP proxy without hitting the web server,
// the boot and exit hooks cannot be fired, so only allow caching in
2009-08-16 18:39:45 +00:00
// proxies if boot hooks are disabled. If the client send a session cookie,
// do not bother caching the page in a public proxy, because the cached copy
2009-04-22 09:45:03 +00:00
// will only be served to that particular user due to Vary: Cookie, unless
// the Vary header has been replaced or unset in hook_boot() (see below).
2009-08-16 18:39:45 +00:00
$max_age = ! variable_get ( 'page_cache_invoke_hooks' , TRUE ) && ( ! isset ( $_COOKIE [ session_name ()]) || isset ( $hook_boot_headers [ 'vary' ])) ? variable_get ( 'cache_lifetime' , 0 ) : 0 ;
2009-04-22 09:45:03 +00:00
$default_headers [ 'Cache-Control' ] = 'public, max-age=' . $max_age ;
// Entity tag should change if the output changes.
$etag = '"' . $cache -> created . '-' . intval ( $return_compressed ) . '"' ;
header ( 'Etag: ' . $etag );
2006-08-31 18:40:04 +00:00
2008-12-03 14:51:53 +00:00
// See if the client has provided the required HTTP headers.
$if_modified_since = isset ( $_SERVER [ 'HTTP_IF_MODIFIED_SINCE' ]) ? strtotime ( $_SERVER [ 'HTTP_IF_MODIFIED_SINCE' ]) : FALSE ;
2006-08-31 18:40:04 +00:00
$if_none_match = isset ( $_SERVER [ 'HTTP_IF_NONE_MATCH' ]) ? stripslashes ( $_SERVER [ 'HTTP_IF_NONE_MATCH' ]) : FALSE ;
if ( $if_modified_since && $if_none_match
&& $if_none_match == $etag // etag must match
2008-12-03 14:51:53 +00:00
&& $if_modified_since == $cache -> created ) { // if-modified-since must match
2008-09-08 21:24:30 +00:00
header ( $_SERVER [ 'SERVER_PROTOCOL' ] . ' 304 Not Modified' );
2009-04-22 09:45:03 +00:00
drupal_send_headers ( $default_headers );
2008-10-11 04:56:34 +00:00
return ;
2006-08-31 18:40:04 +00:00
}
2006-08-03 13:42:34 +00:00
2009-04-22 09:45:03 +00:00
// Send the remaining headers.
foreach ( $cache -> headers as $name => $value ) {
2009-09-30 18:36:02 +00:00
drupal_add_http_header ( $name , $value );
2009-04-22 09:45:03 +00:00
}
2006-07-02 19:25:34 +00:00
2009-04-22 09:45:03 +00:00
$default_headers [ 'Last-Modified' ] = gmdate ( DATE_RFC1123 , $cache -> created );
2006-07-02 19:25:34 +00:00
2009-04-22 09:45:03 +00:00
// HTTP/1.0 proxies does not support the Vary header, so prevent any caching
// by sending an Expires date in the past. HTTP/1.1 clients ignores the
// Expires header if a Cache-Control: max-age= directive is specified (see RFC
// 2616, section 14.9.3).
$default_headers [ 'Expires' ] = 'Sun, 19 Nov 1978 05:00:00 GMT' ;
drupal_send_headers ( $default_headers );
// Allow HTTP proxies to cache pages for anonymous users without a session
// cookie. The Vary header is used to indicates the set of request-header
// fields that fully determines whether a cache is permitted to use the
// response to reply to a subsequent request for a given URL without
// revalidation. If a Vary header has been set in hook_boot(), it is assumed
// that the module knows how to cache the page.
if ( ! isset ( $hook_boot_headers [ 'vary' ]) && ! variable_get ( 'omit_vary_cookie' )) {
header ( 'Vary: Cookie' );
2003-11-18 19:44:36 +00:00
}
2006-08-31 18:40:04 +00:00
2009-04-22 09:45:03 +00:00
if ( $page_compression ) {
header ( 'Vary: Accept-Encoding' , FALSE );
// If page_compression is enabled, the cache contains gzipped data.
if ( $return_compressed ) {
2009-11-02 03:12:05 +00:00
// $cache->data is already gzip'ed, so make sure zlib.output_compression
// does not compress it once more.
ini_set ( 'zlib.output_compression' , '0' );
2009-04-22 09:45:03 +00:00
header ( 'Content-Encoding: gzip' );
}
else {
// The client does not support compression, so unzip the data in the
// cache. Strip the gzip header and run uncompress.
$cache -> data = gzinflate ( substr ( substr ( $cache -> data , 10 ), 0 , - 8 ));
}
2006-08-31 18:40:04 +00:00
}
print $cache -> data ;
2003-11-18 19:44:36 +00:00
}
2009-08-24 00:14:23 +00:00
/**
* Define the critical hooks that force modules to always be loaded .
*/
function bootstrap_hooks () {
return array ( 'boot' , 'exit' , 'watchdog' );
}
2004-07-13 07:21:14 +00:00
/**
* Unserializes and appends elements from a serialized string .
*
* @ param $obj
* The object to which the elements are appended .
* @ param $field
* The attribute of $obj whose value should be unserialized .
*/
2004-01-13 19:25:37 +00:00
function drupal_unpack ( $obj , $field = 'data' ) {
if ( $obj -> $field && $data = unserialize ( $obj -> $field )) {
foreach ( $data as $key => $value ) {
if ( ! isset ( $obj -> $key )) {
$obj -> $key = $value ;
}
}
}
return $obj ;
}
2010-02-17 02:52:15 +00:00
/**
* Translate strings to the page language or a given language .
*
* Human - readable text that will be displayed somewhere within a page should
* be run through the t () function .
*
* Examples :
* @ code
* if ( ! $info || ! $info [ 'extension' ]) {
* form_set_error ( 'picture_upload' , t ( 'The uploaded file was not an image.' ));
* }
*
* $form [ 'submit' ] = array (
* '#type' => 'submit' ,
* '#value' => t ( 'Log in' ),
* );
* @ endcode
*
* Any text within t () can be extracted by translators and changed into
* the equivalent text in their native language .
*
* Special variables called " placeholders " are used to signal dynamic
* information in a string which should not be translated . Placeholders
* can also be used for text that may change from time to time ( such as
* link paths ) to be changed without requiring updates to translations .
*
* For example :
* @ code
* $output = t ( 'There are currently %members and %visitors online.' , array (
* '%members' => format_plural ( $total_users , '1 user' , '@count users' ),
* '%visitors' => format_plural ( $guests -> count , '1 guest' , '@count guests' )));
* @ endcode
*
* There are three styles of placeholders :
* - ! variable , which indicates that the text should be inserted as - is . This is
* useful for inserting variables into things like e - mail .
* @ code
* $message [] = t ( " If you don't want to receive such e-mails, you can change your settings at !url. " , array ( '!url' => url ( " user/ $account->uid " , array ( 'absolute' => TRUE ))));
* @ endcode
*
* - @ variable , which indicates that the text should be run through
* check_plain , to escape HTML characters . Use this for any output that ' s
* displayed within a Drupal page .
* @ code
* drupal_set_title ( $title = t ( " @name's blog " , array ( '@name' => format_username ( $account ))), PASS_THROUGH );
* @ endcode
*
* - % variable , which indicates that the string should be HTML escaped and
* highlighted with theme_placeholder () which shows up by default as
* < em > emphasized </ em >.
* @ code
* $message = t ( '%name-from sent %name-to an e-mail.' , array ( '%name-from' => format_username ( $user ), '%name-to' => format_username ( $account )));
* @ endcode
*
* When using t (), try to put entire sentences and strings in one t () call .
* This makes it easier for translators , as it provides context as to what
* each word refers to . HTML markup within translation strings is allowed , but
* should be avoided if possible . The exception are embedded links ; link
* titles add a context for translators , so should be kept in the main string .
*
* Here is an example of incorrect usage of t () :
* @ code
* $output .= t ( '<p>Go to the @contact-page.</p>' , array ( '@contact-page' => l ( t ( 'contact page' ), 'contact' )));
* @ endcode
*
* Here is an example of t () used correctly :
* @ code
* $output .= '<p>' . t ( 'Go to the <a href="@contact-page">contact page</a>.' , array ( '@contact-page' => url ( 'contact' ))) . '</p>' ;
* @ endcode
*
* Avoid escaping quotation marks wherever possible .
*
* Incorrect :
* @ code
* $output .= t ( 'Don\'t click me.' );
* @ endcode
*
* Correct :
* @ code
* $output .= t ( " Don't click me. " );
* @ endcode
*
* Because t () is designed for handling code - based strings , in almost all
* cases , the actual string and not a variable must be passed through t () .
*
* Extraction of translations is done based on the strings contained in t ()
* calls . If a variable is passed through t (), the content of the variable
* cannot be extracted from the file for translation .
*
* Incorrect :
* @ code
* $message = 'An error occurred.' ;
* drupal_set_message ( t ( $message ), 'error' );
* $output .= t ( $message );
* @ endcode
*
* Correct :
* @ code
* $message = t ( 'An error occurred.' );
* drupal_set_message ( $message , 'error' );
* $output .= $message ;
* @ endcode
*
* The only case in which variables can be passed safely through t () is when
* code - based versions of the same strings will be passed through t () ( or
* otherwise extracted ) elsewhere .
*
* In some cases , modules may include strings in code that can ' t use t ()
* calls . For example , a module may use an external PHP application that
* produces strings that are loaded into variables in Drupal for output .
* In these cases , module authors may include a dummy file that passes the
* relevant strings through t () . This approach will allow the strings to be
* extracted .
*
* Sample external ( non - Drupal ) code :
* @ code
* class Time {
* public $yesterday = 'Yesterday' ;
* public $today = 'Today' ;
* public $tomorrow = 'Tomorrow' ;
* }
* @ endcode
*
* Sample dummy file .
* @ code
* // Dummy function included in example.potx.inc.
* function example_potx () {
* $strings = array (
* t ( 'Yesterday' ),
* t ( 'Today' ),
* t ( 'Tomorrow' ),
* );
* // No return value needed, since this is a dummy function.
* }
* @ endcode
*
* Having passed strings through t () in a dummy function , it is then
* okay to pass variables through t () .
*
* Correct ( if a dummy file was used ) :
* @ code
* $time = new Time ();
* $output .= t ( $time -> today );
* @ endcode
*
* However tempting it is , custom data from user input or other non - code
* sources should not be passed through t () . Doing so leads to the following
* problems and errors :
* - The t () system doesn ' t support updates to existing strings . When user
* data is updated , the next time it ' s passed through t () a new record is
* created instead of an update . The database bloats over time and any
* existing translations are orphaned with each update .
* - The t () system assumes any data it receives is in English . User data may
* be in another language , producing translation errors .
* - The " Built-in interface " text group in the locale system is used to
* produce translations for storage in . po files . When non - code strings are
* passed through t (), they are added to this text group , which is rendered
* inaccurate since it is a mix of actual interface strings and various user
* input strings of uncertain origin .
*
* Incorrect :
* @ code
* $item = item_load ();
* $output .= check_plain ( t ( $item [ 'title' ]));
* @ endcode
*
* Instead , translation of these data can be done through the locale system ,
* either directly or through helper functions provided by contributed
* modules .
* @ see hook_locale ()
*
* During installation , st () is used in place of t () . Code that may be called
* during installation or during normal operation should use the get_t ()
* helper function .
* @ see st ()
* @ see get_t ()
*
* @ param $string
* A string containing the English string to translate .
* @ param $args
* An associative array of replacements to make after translation . Incidences
* of any key in this array are replaced with the corresponding value . Based
* on the first character of the key , the value is escaped and / or themed :
* - ! variable : inserted as is
* - @ variable : escape plain text to HTML ( check_plain )
* - % variable : escape text and theme as a placeholder for user - submitted
* content ( check_plain + theme_placeholder )
* @ param $options
* An associative array of additional options , with the following keys :
* - 'langcode' ( default to the current language ) The language code to
* translate to a language other than what is used to display the page .
* - 'context' ( default to the empty context ) The context the source string
* belongs to .
* @ return
* The translated string .
*/
function t ( $string , array $args = array (), array $options = array ()) {
2010-03-07 07:44:18 +00:00
global $language ;
2010-02-17 02:52:15 +00:00
static $custom_strings ;
// Merge in default.
if ( empty ( $options [ 'langcode' ])) {
2010-03-07 07:44:18 +00:00
$options [ 'langcode' ] = isset ( $language -> language ) ? $language -> language : 'en' ;
2010-02-17 02:52:15 +00:00
}
if ( empty ( $options [ 'context' ])) {
$options [ 'context' ] = '' ;
}
// First, check for an array of customized strings. If present, use the array
// *instead of* database lookups. This is a high performance way to provide a
// handful of string replacements. See settings.php for examples.
// Cache the $custom_strings variable to improve performance.
if ( ! isset ( $custom_strings [ $options [ 'langcode' ]])) {
$custom_strings [ $options [ 'langcode' ]] = variable_get ( 'locale_custom_strings_' . $options [ 'langcode' ], array ());
}
// Custom strings work for English too, even if locale module is disabled.
if ( isset ( $custom_strings [ $options [ 'langcode' ]][ $options [ 'context' ]][ $string ])) {
$string = $custom_strings [ $options [ 'langcode' ]][ $options [ 'context' ]][ $string ];
}
// Translate with locale module if enabled.
elseif ( function_exists ( 'locale' ) && $options [ 'langcode' ] != 'en' ) {
$string = locale ( $string , $options [ 'context' ], $options [ 'langcode' ]);
}
if ( empty ( $args )) {
return $string ;
}
else {
// Transform arguments before inserting them.
foreach ( $args as $key => $value ) {
switch ( $key [ 0 ]) {
case '@' :
// Escaped only.
$args [ $key ] = check_plain ( $value );
break ;
case '%' :
default :
// Escaped and placeholder.
$args [ $key ] = drupal_placeholder ( array ( 'text' => $value ));
break ;
case '!' :
// Pass-through.
}
}
return strtr ( $string , $args );
}
}
2005-11-29 20:37:19 +00:00
/**
* Encode special characters in a plain - text string for display as HTML .
2008-01-10 22:47:17 +00:00
*
2009-11-08 13:58:03 +00:00
* Also validates strings as UTF - 8 to prevent cross site scripting attacks on
2008-01-10 22:47:17 +00:00
* Internet Explorer 6.
2009-11-08 13:58:03 +00:00
*
* @ param $text
* The text to be checked or processed .
2010-03-26 17:14:46 +00:00
*
2009-11-08 13:58:03 +00:00
* @ return
* An HTML safe version of $text , or an empty string if $text is not
* valid UTF - 8.
2010-03-26 17:14:46 +00:00
*
* @ see drupal_validate_utf8 ()
2005-11-29 20:37:19 +00:00
*/
function check_plain ( $text ) {
2009-11-08 13:58:03 +00:00
// We do not want to use drupal_static() since PHP version will never change
// during a request.
static $php525 ;
if ( ! isset ( $php525 )) {
$php525 = version_compare ( PHP_VERSION , '5.2.5' , '>=' );
}
// We duplicate the preg_match() to validate strings as UTF-8 from
// drupal_validate_utf8() here. This avoids the overhead of an additional
// function call, since check_plain() may be called hundreds of times during
// a request. For PHP 5.2.5+, this check for valid UTF-8 should be handled
2010-03-26 17:14:46 +00:00
// internally by PHP in htmlspecialchars().
// See http://www.php.net/releases/5_2_5.php.
2009-11-08 13:58:03 +00:00
// @todo remove this when support for either IE6 or PHP < 5.2.5 is dropped.
if ( $php525 ) {
return htmlspecialchars ( $text , ENT_QUOTES , 'UTF-8' );
}
return ( preg_match ( '/^./us' , $text ) == 1 ) ? htmlspecialchars ( $text , ENT_QUOTES , 'UTF-8' ) : '' ;
2008-01-10 22:47:17 +00:00
}
/**
* Checks whether a string is valid UTF - 8.
*
* All functions designed to filter input should use drupal_validate_utf8
* to ensure they operate on valid UTF - 8 strings to prevent bypass of the
* filter .
*
* When text containing an invalid UTF - 8 lead byte ( 0xC0 - 0xFF ) is presented
* as UTF - 8 to Internet Explorer 6 , the program may misinterpret subsequent
* bytes . When these subsequent bytes are HTML control characters such as
* quotes or angle brackets , parts of the text that were deemed safe by filters
* end up in locations that are potentially unsafe ; An onerror attribute that
* is outside of a tag , and thus deemed safe by a filter , can be interpreted
* by the browser as if it were inside the tag .
*
2008-11-07 17:21:54 +00:00
* The function does not return FALSE for strings containing character codes
* above U + 10 FFFF , even though these are prohibited by RFC 3629.
2008-01-10 22:47:17 +00:00
*
* @ param $text
* The text to check .
* @ return
* TRUE if the text is valid UTF - 8 , FALSE if not .
*/
function drupal_validate_utf8 ( $text ) {
if ( strlen ( $text ) == 0 ) {
return TRUE ;
}
2008-11-07 17:21:54 +00:00
// With the PCRE_UTF8 modifier 'u', preg_match() fails silently on strings
// containing invalid UTF-8 byte sequences. It does not reject character
// codes above U+10FFFF (represented by 4 or more octets), though.
2008-01-10 22:47:17 +00:00
return ( preg_match ( '/^./us' , $text ) == 1 );
2005-11-29 20:37:19 +00:00
}
2004-07-13 07:21:14 +00:00
/**
2006-12-10 20:13:36 +00:00
* Since $_SERVER [ 'REQUEST_URI' ] is only available on Apache , we
* generate an equivalent using other environment variables .
2004-07-13 07:21:14 +00:00
*/
2003-11-18 19:44:36 +00:00
function request_uri () {
2006-12-10 20:13:36 +00:00
if ( isset ( $_SERVER [ 'REQUEST_URI' ])) {
$uri = $_SERVER [ 'REQUEST_URI' ];
}
else {
if ( isset ( $_SERVER [ 'argv' ])) {
2008-04-14 17:48:46 +00:00
$uri = $_SERVER [ 'SCRIPT_NAME' ] . '?' . $_SERVER [ 'argv' ][ 0 ];
2004-10-12 19:50:12 +00:00
}
2007-10-17 13:03:03 +00:00
elseif ( isset ( $_SERVER [ 'QUERY_STRING' ])) {
2008-04-14 17:48:46 +00:00
$uri = $_SERVER [ 'SCRIPT_NAME' ] . '?' . $_SERVER [ 'QUERY_STRING' ];
2004-10-12 19:50:12 +00:00
}
2007-10-17 13:03:03 +00:00
else {
$uri = $_SERVER [ 'SCRIPT_NAME' ];
}
2003-11-18 19:44:36 +00:00
}
2009-04-30 16:15:44 +00:00
// Prevent multiple slashes to avoid cross site requests via the Form API.
$uri = '/' . ltrim ( $uri , '/' );
2004-10-18 18:35:19 +00:00
2005-03-31 09:25:33 +00:00
return $uri ;
2003-11-18 19:44:36 +00:00
}
2003-12-13 14:10:23 +00:00
2004-07-13 07:21:14 +00:00
/**
* Log a system message .
*
* @ param $type
* The category to which this message belongs .
* @ param $message
2009-05-12 18:08:43 +00:00
* The message to store in the log . Keep $message translatable
* by not concatenating dynamic values into it ! Variables in the
* message should be added by using placeholder strings alongside
* the variables argument to declare the value of the placeholders .
* See t () for documentation on how $message and $variables interact .
2007-04-24 13:53:15 +00:00
* @ param $variables
* Array of variables to replace in the message on display or
* NULL if message is already translated or not possible to
* translate .
2005-01-09 09:22:40 +00:00
* @ param $severity
2009-05-12 18:08:43 +00:00
* The severity of the message , as per RFC 3164.
2004-07-13 07:21:14 +00:00
* @ param $link
* A link to associate with the message .
2007-04-30 11:12:35 +00:00
*
2008-01-08 10:35:43 +00:00
* @ see watchdog_severity_levels ()
2008-12-28 20:41:19 +00:00
* @ see hook_watchdog ()
2009-11-19 04:00:47 +00:00
* @ see DatabaseConnection :: rollback ()
* @ see DatabaseTransaction :: rollback ()
2004-07-13 07:21:14 +00:00
*/
2007-04-24 13:53:15 +00:00
function watchdog ( $type , $message , $variables = array (), $severity = WATCHDOG_NOTICE , $link = NULL ) {
2006-04-12 08:42:47 +00:00
global $user , $base_root ;
2006-03-26 14:11:38 +00:00
2008-08-21 19:36:39 +00:00
static $in_error_state = FALSE ;
2008-12-20 18:24:41 +00:00
// It is possible that the error handling will itself trigger an error. In that case, we could
// end up in an infinite loop. To avoid that, we implement a simple static semaphore.
2010-02-17 02:52:15 +00:00
if ( ! $in_error_state && function_exists ( 'module_implements' )) {
2008-08-21 19:36:39 +00:00
$in_error_state = TRUE ;
// Prepare the fields to be logged
2008-12-28 20:41:19 +00:00
$log_entry = array (
2008-08-21 19:36:39 +00:00
'type' => $type ,
'message' => $message ,
'variables' => $variables ,
'severity' => $severity ,
'link' => $link ,
'user' => $user ,
'request_uri' => $base_root . request_uri (),
2008-09-06 08:36:22 +00:00
'referer' => $_SERVER [ 'HTTP_REFERER' ],
2008-08-21 19:36:39 +00:00
'ip' => ip_address (),
2008-09-17 07:11:59 +00:00
'timestamp' => REQUEST_TIME ,
2007-04-10 10:10:27 +00:00
);
2008-08-21 19:36:39 +00:00
// Call the logging hooks to log/process the message
foreach ( module_implements ( 'watchdog' , TRUE ) as $module ) {
2008-12-28 20:41:19 +00:00
module_invoke ( $module , 'watchdog' , $log_entry );
2008-08-21 19:36:39 +00:00
}
2008-11-24 06:17:40 +00:00
// It is critical that the semaphore is only cleared here, in the parent
// watchdog() call (not outside the loop), to prevent recursive execution.
$in_error_state = FALSE ;
2006-03-26 14:11:38 +00:00
}
2003-12-10 23:09:31 +00:00
}
2004-07-13 07:21:14 +00:00
/**
2005-05-14 18:32:22 +00:00
* Set a message which reflects the status of the performed operation .
2004-07-13 07:21:14 +00:00
*
2005-05-14 18:32:22 +00:00
* If the function is called with no arguments , this function returns all set
* messages without clearing them .
2004-07-13 07:21:14 +00:00
*
2005-05-14 18:32:22 +00:00
* @ param $message
* The message should begin with a capital letter and always ends with a
* period '.' .
* @ param $type
* The type of the message . One of the following values are possible :
* - 'status'
2007-10-17 12:11:30 +00:00
* - 'warning'
2005-05-14 18:32:22 +00:00
* - 'error'
2008-01-02 14:29:32 +00:00
* @ param $repeat
* If this is FALSE and the message is already set , then the message won ' t
* be repeated .
2004-07-13 07:21:14 +00:00
*/
2008-01-02 14:29:32 +00:00
function drupal_set_message ( $message = NULL , $type = 'status' , $repeat = TRUE ) {
2006-02-10 05:42:11 +00:00
if ( $message ) {
2004-07-08 16:04:07 +00:00
if ( ! isset ( $_SESSION [ 'messages' ][ $type ])) {
$_SESSION [ 'messages' ][ $type ] = array ();
}
2008-01-02 14:29:32 +00:00
if ( $repeat || ! in_array ( $message , $_SESSION [ 'messages' ][ $type ])) {
$_SESSION [ 'messages' ][ $type ][] = $message ;
}
2009-06-02 06:58:17 +00:00
// Mark this page has being not cacheable.
drupal_page_is_cacheable ( FALSE );
2004-06-28 20:00:53 +00:00
}
2009-05-26 09:12:29 +00:00
// Messages not set when DB connection fails.
2005-10-22 15:14:46 +00:00
return isset ( $_SESSION [ 'messages' ]) ? $_SESSION [ 'messages' ] : NULL ;
2004-06-28 20:00:53 +00:00
}
2004-07-13 07:21:14 +00:00
/**
* Return all messages that have been set .
*
2006-07-13 13:14:25 +00:00
* @ param $type
* ( optional ) Only return messages of this type .
2006-09-07 07:11:15 +00:00
* @ param $clear_queue
* ( optional ) Set to FALSE if you do not want to clear the messages queue
2007-04-04 20:50:53 +00:00
* @ return
* An associative array , the key is the message type , the value an array
* of messages . If the $type parameter is passed , you get only that type ,
* or an empty array if there are no such messages . If $type is not passed ,
* all message types are returned , or an empty array if none exist .
2004-07-13 07:21:14 +00:00
*/
2006-09-07 07:11:15 +00:00
function drupal_get_messages ( $type = NULL , $clear_queue = TRUE ) {
2006-04-01 14:53:03 +00:00
if ( $messages = drupal_set_message ()) {
2006-07-13 13:14:25 +00:00
if ( $type ) {
2006-09-07 07:11:15 +00:00
if ( $clear_queue ) {
2007-12-08 14:06:23 +00:00
unset ( $_SESSION [ 'messages' ][ $type ]);
2006-09-07 07:11:15 +00:00
}
2007-04-04 20:50:53 +00:00
if ( isset ( $messages [ $type ])) {
return array ( $type => $messages [ $type ]);
}
2006-07-13 13:14:25 +00:00
}
else {
2006-09-07 07:11:15 +00:00
if ( $clear_queue ) {
2007-12-08 14:06:23 +00:00
unset ( $_SESSION [ 'messages' ]);
2006-09-07 07:11:15 +00:00
}
2006-07-13 13:14:25 +00:00
return $messages ;
}
2006-04-01 14:53:03 +00:00
}
2006-07-13 13:14:25 +00:00
return array ();
2004-06-28 20:00:53 +00:00
}
2005-06-07 18:54:37 +00:00
/**
2008-04-08 22:50:55 +00:00
* Check to see if an IP address has been blocked .
*
* Blocked IP addresses are stored in the database by default . However for
* performance reasons we allow an override in settings . php . This allows us
* to avoid querying the database at this critical stage of the bootstrap if
* an administrative interface for IP address blocking is not required .
*
2009-08-16 18:39:45 +00:00
* @ param $ip
2008-04-08 22:50:55 +00:00
* IP address to check .
2006-08-29 08:36:14 +00:00
* @ return bool
* TRUE if access is denied , FALSE if access is allowed .
2005-06-07 18:54:37 +00:00
*/
2008-04-08 22:50:55 +00:00
function drupal_is_denied ( $ip ) {
// Because this function is called on every page request, we first check
// for an array of IP addresses in settings.php before querying the
// database.
2009-01-31 16:50:57 +00:00
$blocked_ips = variable_get ( 'blocked_ips' );
2009-08-16 18:39:45 +00:00
$denied = FALSE ;
2008-04-08 22:50:55 +00:00
if ( isset ( $blocked_ips ) && is_array ( $blocked_ips )) {
2009-08-16 18:39:45 +00:00
$denied = in_array ( $ip , $blocked_ips );
2008-04-08 22:50:55 +00:00
}
2009-08-16 23:20:43 +00:00
// Only check if database.inc is loaded already. If
// $conf['page_cache_without_database'] = TRUE; is set in settings.php,
// then the database won't be loaded here so the IPs in the database
// won't be denied. However the user asked explicitly not to use the
// database and also in this case it's quite likely that the user relies
// on higher performance solutions like a firewall.
2009-08-16 18:39:45 +00:00
elseif ( function_exists ( 'db_is_active' )) {
$denied = ( bool ) db_query ( " SELECT 1 FROM { blocked_ips} WHERE ip = :ip " , array ( ':ip' => $ip )) -> fetchField ();
}
return $denied ;
}
/**
* Handle denied users .
*
* @ param $ip
* IP address to check . Prints a message and exits if access is denied .
*/
2009-08-16 23:20:43 +00:00
function drupal_block_denied ( $ip ) {
2009-08-16 18:39:45 +00:00
// Deny access to blocked IP addresses - t() is not yet available.
if ( drupal_is_denied ( $ip )) {
header ( $_SERVER [ 'SERVER_PROTOCOL' ] . ' 403 Forbidden' );
print 'Sorry, ' . check_plain ( ip_address ()) . ' has been banned.' ;
exit ();
2008-04-08 22:50:55 +00:00
}
2005-06-07 18:54:37 +00:00
}
2006-08-16 13:13:34 +00:00
/**
2006-08-25 05:42:00 +00:00
* Generates a default anonymous $user object .
2006-08-16 13:13:34 +00:00
*
* @ return Object - the user object .
*/
2006-08-18 19:24:52 +00:00
function drupal_anonymous_user ( $session = '' ) {
2006-08-16 13:13:34 +00:00
$user = new stdClass ();
$user -> uid = 0 ;
2007-05-25 15:04:42 +00:00
$user -> hostname = ip_address ();
2006-08-16 13:13:34 +00:00
$user -> roles = array ();
$user -> roles [ DRUPAL_ANONYMOUS_RID ] = 'anonymous user' ;
2006-08-18 19:24:52 +00:00
$user -> session = $session ;
2007-03-25 20:54:33 +00:00
$user -> cache = 0 ;
2006-08-16 13:13:34 +00:00
return $user ;
}
2005-06-22 20:19:58 +00:00
/**
* A string describing a phase of Drupal to load . Each phase adds to the
* previous one , so invoking a later phase automatically runs the earlier
2006-08-25 05:42:00 +00:00
* phases too . The most important usage is that if you want to access the
2005-06-22 20:19:58 +00:00
* Drupal database from a script without loading anything else , you can
2005-07-23 05:57:27 +00:00
* include bootstrap . inc , and call drupal_bootstrap ( DRUPAL_BOOTSTRAP_DATABASE ) .
2005-06-22 20:19:58 +00:00
*
* @ param $phase
2009-09-21 08:07:07 +00:00
* A constant . Allowed values are the DRUPAL_BOOTSTRAP_ * constants .
2009-08-16 18:39:45 +00:00
* @ param $new_phase
* A boolean , set to FALSE if calling drupal_bootstrap from inside a
* function called from drupal_bootstrap ( recursion ) .
2009-10-03 19:27:44 +00:00
* @ return
* The most recently completed phase .
2009-11-09 18:34:26 +00:00
*
2009-08-16 18:39:45 +00:00
*/
function drupal_bootstrap ( $phase = NULL , $new_phase = TRUE ) {
2009-11-21 14:06:46 +00:00
// Not drupal_static(), because does not depend on any run-time information.
static $phases = array (
2009-04-02 20:39:45 +00:00
DRUPAL_BOOTSTRAP_CONFIGURATION ,
2009-08-16 18:39:45 +00:00
DRUPAL_BOOTSTRAP_PAGE_CACHE ,
2009-04-02 20:39:45 +00:00
DRUPAL_BOOTSTRAP_DATABASE ,
DRUPAL_BOOTSTRAP_VARIABLES ,
2009-08-16 18:39:45 +00:00
DRUPAL_BOOTSTRAP_SESSION ,
DRUPAL_BOOTSTRAP_PAGE_HEADER ,
2009-04-02 20:39:45 +00:00
DRUPAL_BOOTSTRAP_LANGUAGE ,
DRUPAL_BOOTSTRAP_FULL ,
2009-11-21 14:06:46 +00:00
);
// Not drupal_static(), because the only legitimate API to control this is to
// call drupal_bootstrap() with a new phase parameter.
static $final_phase ;
// Not drupal_static(), because it's impossible to roll back to an earlier
// bootstrap state.
2010-01-14 21:17:56 +00:00
static $stored_phase = - 1 ;
2008-10-19 21:27:13 +00:00
2009-11-21 14:06:46 +00:00
// When not recursing, store the phase name so it's not forgotten while
// recursing.
if ( $new_phase ) {
$final_phase = $phase ;
}
2008-10-20 11:33:00 +00:00
if ( isset ( $phase )) {
2009-08-16 23:20:43 +00:00
// Call a phase if it has not been called before and is below the requested
// phase.
2010-01-14 21:17:56 +00:00
while ( $phases && $phase > $stored_phase && $final_phase > $stored_phase ) {
2008-10-19 21:27:13 +00:00
$current_phase = array_shift ( $phases );
2010-01-14 21:17:56 +00:00
// This function is re-entrant. Only update the completed phase when the
// current call actually resulted in a progress in the bootstrap process.
if ( $current_phase > $stored_phase ) {
$stored_phase = $current_phase ;
}
2009-11-05 03:00:21 +00:00
switch ( $current_phase ) {
case DRUPAL_BOOTSTRAP_CONFIGURATION :
_drupal_bootstrap_configuration ();
break ;
case DRUPAL_BOOTSTRAP_PAGE_CACHE :
_drupal_bootstrap_page_cache ();
break ;
case DRUPAL_BOOTSTRAP_DATABASE :
_drupal_bootstrap_database ();
break ;
case DRUPAL_BOOTSTRAP_VARIABLES :
_drupal_bootstrap_variables ();
break ;
case DRUPAL_BOOTSTRAP_SESSION :
require_once DRUPAL_ROOT . '/' . variable_get ( 'session_inc' , 'includes/session.inc' );
drupal_session_initialize ();
break ;
case DRUPAL_BOOTSTRAP_PAGE_HEADER :
_drupal_bootstrap_page_header ();
break ;
case DRUPAL_BOOTSTRAP_LANGUAGE :
drupal_language_initialize ();
break ;
case DRUPAL_BOOTSTRAP_FULL :
require_once DRUPAL_ROOT . '/includes/common.inc' ;
_drupal_bootstrap_full ();
break ;
}
2008-10-19 21:27:13 +00:00
}
2005-06-22 20:19:58 +00:00
}
2010-01-14 21:17:56 +00:00
return $stored_phase ;
2005-06-22 20:19:58 +00:00
}
2005-06-07 18:54:37 +00:00
2010-02-15 15:52:27 +00:00
/**
* Custom PHP error handler .
*
* @ param $error_level
* The level of the error raised .
* @ param $message
* The error message .
* @ param $filename
* The filename that the error was raised in .
* @ param $line
* The line number the error was raised at .
* @ param $context
* An array that points to the active symbol table at the point the error occurred .
*/
function _drupal_error_handler ( $error_level , $message , $filename , $line , $context ) {
require_once DRUPAL_ROOT . '/includes/errors.inc' ;
_drupal_error_handler_real ( $error_level , $message , $filename , $line , $context );
}
/**
* Custom PHP exception handler .
*
* Uncaught exceptions are those not enclosed in a try / catch block . They are
* always fatal : the execution of the script will stop as soon as the exception
* handler exits .
*
* @ param $exception
* The exception object that was thrown .
*/
function _drupal_exception_handler ( $exception ) {
require_once DRUPAL_ROOT . '/includes/errors.inc' ;
// Log the message to the watchdog and return an error page to the user.
_drupal_log_error ( _drupal_decode_exception ( $exception ), TRUE );
}
2008-08-21 19:36:39 +00:00
/**
2009-11-05 03:00:21 +00:00
* Bootstrap configuration : Setup script environment and load settings . php .
2008-08-21 19:36:39 +00:00
*/
2009-11-05 03:00:21 +00:00
function _drupal_bootstrap_configuration () {
2010-02-15 15:52:27 +00:00
// Set the Drupal custom error handler.
set_error_handler ( '_drupal_error_handler' );
set_exception_handler ( '_drupal_exception_handler' );
2009-11-05 03:00:21 +00:00
drupal_environment_initialize ();
// Start a page timer:
timer_start ( 'page' );
// Initialize the configuration, including variables from settings.php.
drupal_settings_initialize ();
2008-08-21 19:36:39 +00:00
}
2009-11-05 03:00:21 +00:00
/**
* Bootstrap page cache : Try to serve a page from cache .
*/
function _drupal_bootstrap_page_cache () {
global $user ;
// Allow specifying special cache handlers in settings.php, like
// using memcached or files for storing cache information.
2009-12-05 19:27:34 +00:00
require_once DRUPAL_ROOT . '/includes/cache.inc' ;
foreach ( variable_get ( 'cache_backends' , array ()) as $include ) {
require_once DRUPAL_ROOT . '/' . $include ;
}
2009-11-05 03:00:21 +00:00
// Check for a cache mode force from settings.php.
if ( variable_get ( 'page_cache_without_database' )) {
$cache_mode = CACHE_NORMAL ;
}
else {
drupal_bootstrap ( DRUPAL_BOOTSTRAP_VARIABLES , FALSE );
$cache_mode = variable_get ( 'cache' );
}
drupal_block_denied ( ip_address ());
// If there is no session cookie and cache is enabled (or forced), try
// to serve a cached page.
if ( ! isset ( $_COOKIE [ session_name ()]) && $cache_mode == CACHE_NORMAL ) {
// Make sure there is a user object because it's timestamp will be
// checked, hook_boot might check for anonymous user etc.
$user = drupal_anonymous_user ();
// Get the page from the cache.
$cache = drupal_page_get_cache ();
// If there is a cached page, display it.
if ( is_object ( $cache )) {
// If the skipping of the bootstrap hooks is not enforced, call
// hook_boot.
if ( variable_get ( 'page_cache_invoke_hooks' , TRUE )) {
bootstrap_invoke_all ( 'boot' );
2009-07-22 04:45:35 +00:00
}
2009-11-05 03:00:21 +00:00
header ( 'X-Drupal-Cache: HIT' );
drupal_serve_page_from_cache ( $cache );
// If the skipping of the bootstrap hooks is not enforced, call
// hook_exit.
if ( variable_get ( 'page_cache_invoke_hooks' , TRUE )) {
bootstrap_invoke_all ( 'exit' );
2009-04-22 09:45:03 +00:00
}
2009-11-05 03:00:21 +00:00
// We are done.
exit ;
}
}
}
2009-06-02 06:58:17 +00:00
2009-11-05 03:00:21 +00:00
/**
* Bootstrap database : Initialize database system and register autoload functions .
*/
function _drupal_bootstrap_database () {
// The user agent header is used to pass a database prefix in the request when
// running tests. However, for security reasons, it is imperative that we
// validate we ourselves made the request.
if ( isset ( $_SERVER [ 'HTTP_USER_AGENT' ]) && ( strpos ( $_SERVER [ 'HTTP_USER_AGENT' ], " simpletest " ) !== FALSE ) && ! drupal_valid_test_ua ( $_SERVER [ 'HTTP_USER_AGENT' ])) {
header ( $_SERVER [ 'SERVER_PROTOCOL' ] . ' 403 Forbidden' );
exit ;
}
// Initialize the database system. Note that the connection
// won't be initialized until it is actually requested.
require_once DRUPAL_ROOT . '/includes/database/database.inc' ;
2009-11-19 04:00:47 +00:00
// Set Drupal's watchdog as the logging callback.
Database :: setLoggingCallback ( 'watchdog' , WATCHDOG_NOTICE , WATCHDOG_ERROR );
2009-11-05 03:00:21 +00:00
// Register autoload functions so that we can access classes and interfaces.
spl_autoload_register ( 'drupal_autoload_class' );
spl_autoload_register ( 'drupal_autoload_interface' );
}
2009-08-17 20:32:30 +00:00
2009-11-05 03:00:21 +00:00
/**
* Bootstrap variables : Load system variables and all enabled bootstrap modules .
*/
function _drupal_bootstrap_variables () {
global $conf ;
// Load variables from the database, but do not overwrite variables set in settings.php.
$conf = variable_initialize ( isset ( $conf ) ? $conf : array ());
// Load bootstrap modules.
require_once DRUPAL_ROOT . '/includes/module.inc' ;
module_load_all ( TRUE );
}
/**
2010-01-25 10:38:35 +00:00
* Bootstrap page header : Invoke hook_boot (), initialize locking system , and send default HTTP headers .
2009-11-05 03:00:21 +00:00
*/
function _drupal_bootstrap_page_header () {
bootstrap_invoke_all ( 'boot' );
if ( ! drupal_page_get_cache ( TRUE ) && drupal_page_is_cacheable ()) {
header ( 'X-Drupal-Cache: MISS' );
}
2005-07-23 05:57:27 +00:00
2009-11-05 03:00:21 +00:00
// Prepare for non-cached page workflow.
require_once DRUPAL_ROOT . '/' . variable_get ( 'lock_inc' , 'includes/lock.inc' );
lock_initialize ();
2007-03-26 01:32:22 +00:00
2009-11-05 03:00:21 +00:00
if ( ! drupal_is_cli ()) {
ob_start ();
drupal_page_header ();
2005-06-22 20:19:58 +00:00
}
2005-06-07 18:54:37 +00:00
}
2009-11-05 03:00:21 +00:00
/**
* Returns the current bootstrap phase for this Drupal process .
*
* The current phase is the one most recently completed by drupal_bootstrap () .
*
* @ see drupal_bootstrap ()
*/
function drupal_get_bootstrap_phase () {
return drupal_bootstrap ();
}
2009-07-22 04:45:35 +00:00
/**
* Validate the HMAC and timestamp of a user agent header from simpletest .
*/
function drupal_valid_test_ua ( $user_agent ) {
global $databases ;
list ( $prefix , $time , $salt , $hmac ) = explode ( ';' , $user_agent );
$check_string = $prefix . ';' . $time . ';' . $salt ;
// We use the database credentials from settings.php to make the HMAC key, since
// the database is not yet initialized and we can't access any Drupal variables.
// The file properties add more entropy not easily accessible to others.
$filepath = DRUPAL_ROOT . '/includes/bootstrap.inc' ;
$key = sha1 ( serialize ( $databases ) . filectime ( $filepath ) . fileinode ( $filepath ), TRUE );
2009-08-20 07:24:40 +00:00
// The HMAC must match.
return $hmac == base64_encode ( hash_hmac ( 'sha1' , $check_string , $key , TRUE ));
2009-07-22 04:45:35 +00:00
}
/**
* Generate a user agent string with a HMAC and timestamp for simpletest .
*/
function drupal_generate_test_ua ( $prefix ) {
global $databases ;
static $key ;
if ( ! isset ( $key )) {
// We use the database credentials to make the HMAC key, since we
// check the HMAC before the database is initialized. filectime()
// and fileinode() are not easily determined from remote.
$filepath = DRUPAL_ROOT . '/includes/bootstrap.inc' ;
$key = sha1 ( serialize ( $databases ) . filectime ( $filepath ) . fileinode ( $filepath ), TRUE );
}
// Generate a moderately secure HMAC based on the database credentials.
$salt = uniqid ( '' , TRUE );
$check_string = $prefix . ';' . time () . ';' . $salt ;
return $check_string . ';' . base64_encode ( hash_hmac ( 'sha1' , $check_string , $key , TRUE ));
}
2005-07-27 01:58:43 +00:00
/**
2007-11-30 12:19:10 +00:00
* Enables use of the theme system without requiring database access .
*
* Loads and initializes the theme system for site installs , updates and when
2009-04-19 19:10:08 +00:00
* the site is in maintenance mode . This also applies when the database fails .
2007-11-30 12:19:10 +00:00
*
* @ see _drupal_maintenance_theme ()
2005-07-27 01:58:43 +00:00
*/
function drupal_maintenance_theme () {
2008-09-20 20:22:25 +00:00
require_once DRUPAL_ROOT . '/includes/theme.maintenance.inc' ;
2007-11-30 12:19:10 +00:00
_drupal_maintenance_theme ();
2005-07-27 01:58:43 +00:00
}
2006-09-01 08:44:53 +00:00
2009-07-28 12:13:47 +00:00
/**
* Return TRUE if a Drupal installation is currently being attempted .
*/
function drupal_installation_attempted () {
return defined ( 'MAINTENANCE_MODE' ) && MAINTENANCE_MODE == 'install' ;
}
2006-09-01 08:44:53 +00:00
/**
2008-12-30 16:43:20 +00:00
* Return the name of the localization function . Use in code that needs to
2006-09-01 08:44:53 +00:00
* run both during installation and normal operation .
*/
function get_t () {
static $t ;
2009-04-02 20:39:45 +00:00
// This is not converted to drupal_static because there is no point in
// resetting this as it can not change in the course of a request.
if ( ! isset ( $t )) {
2009-07-28 12:13:47 +00:00
$t = drupal_installation_attempted () ? 'st' : 't' ;
2006-09-01 08:44:53 +00:00
}
return $t ;
}
2007-03-26 01:32:22 +00:00
/**
2009-10-09 16:33:14 +00:00
* Initialize all the defined language types .
2007-03-26 01:32:22 +00:00
*/
2009-07-14 10:22:17 +00:00
function drupal_language_initialize () {
2009-10-09 16:33:14 +00:00
$types = language_types ();
2007-03-26 01:32:22 +00:00
// Ensure the language is correctly returned, even without multilanguage support.
// Useful for eg. XML/HTML 'lang' attributes.
2009-10-16 02:04:44 +00:00
if ( ! drupal_multilingual ()) {
2009-10-09 16:33:14 +00:00
$default = language_default ();
foreach ( $types as $type ) {
$GLOBALS [ $type ] = $default ;
}
2007-03-26 01:32:22 +00:00
}
else {
2008-09-20 20:22:25 +00:00
include_once DRUPAL_ROOT . '/includes/language.inc' ;
2009-10-09 16:33:14 +00:00
foreach ( $types as $type ) {
$GLOBALS [ $type ] = language_initialize ( $type );
}
2009-10-16 19:06:25 +00:00
// Allow modules to react on language system initialization in multilingual
// environments.
module_invoke_all ( 'language_init' , $types );
2007-03-26 01:32:22 +00:00
}
}
2009-10-09 16:33:14 +00:00
/**
* The built - in language types .
*
* @ return
* An array of key - values pairs where the key is the language type and the
* value is its configurability .
*/
function drupal_language_types () {
return array (
LANGUAGE_TYPE_INTERFACE => TRUE ,
2010-03-07 07:44:18 +00:00
LANGUAGE_TYPE_CONTENT => FALSE ,
2009-10-09 16:33:14 +00:00
LANGUAGE_TYPE_URL => FALSE ,
);
}
2009-10-16 02:04:44 +00:00
/**
* Return true if there is more than one language enabled .
*/
function drupal_multilingual () {
2010-03-07 07:30:40 +00:00
return variable_get ( 'language_count' , 1 ) > 1 ;
2009-10-16 02:04:44 +00:00
}
2009-10-09 16:33:14 +00:00
/**
* Return an array of the available language types .
*/
function language_types () {
return array_keys ( variable_get ( 'language_types' , drupal_language_types ()));
}
2007-03-26 01:32:22 +00:00
/**
* Get a list of languages set up indexed by the specified key
*
* @ param $field The field to index the list with .
*/
2009-04-02 20:39:45 +00:00
function language_list ( $field = 'language' ) {
$languages = & drupal_static ( __FUNCTION__ );
2007-03-26 01:32:22 +00:00
// Init language list
if ( ! isset ( $languages )) {
2009-10-16 02:04:44 +00:00
if ( drupal_multilingual () || module_exists ( 'locale' )) {
2008-10-06 14:26:54 +00:00
$languages [ 'language' ] = db_query ( 'SELECT * FROM {languages} ORDER BY weight ASC, name ASC' ) -> fetchAllAssoc ( 'language' );
2007-07-01 19:49:19 +00:00
}
else {
2007-09-01 12:50:47 +00:00
// No locale module, so use the default language only.
2007-07-01 19:49:19 +00:00
$default = language_default ();
$languages [ 'language' ][ $default -> language ] = $default ;
2007-03-26 01:32:22 +00:00
}
}
// Return the array indexed by the right field
if ( ! isset ( $languages [ $field ])) {
$languages [ $field ] = array ();
2007-04-13 08:56:59 +00:00
foreach ( $languages [ 'language' ] as $lang ) {
2007-03-26 01:32:22 +00:00
// Some values should be collected into an array
if ( in_array ( $field , array ( 'enabled' , 'weight' ))) {
$languages [ $field ][ $lang -> $field ][ $lang -> language ] = $lang ;
}
else {
$languages [ $field ][ $lang -> $field ] = $lang ;
}
}
}
return $languages [ $field ];
}
/**
* Default language used on the site
2007-05-30 08:08:59 +00:00
*
2007-05-29 14:37:49 +00:00
* @ param $property
* Optional property of the language object to return
2007-03-26 01:32:22 +00:00
*/
2007-05-29 14:37:49 +00:00
function language_default ( $property = NULL ) {
2007-11-11 16:14:45 +00:00
$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' => '' ));
2007-05-29 14:37:49 +00:00
return $property ? $language -> $property : $language ;
2007-03-26 01:32:22 +00:00
}
2007-05-25 15:04:42 +00:00
2010-01-29 22:40:41 +00:00
/**
* Returns the requested URL path of the page being viewed .
*
* Examples :
* - http :// example . com / node / 306 returns " node/306 " .
* - http :// example . com / drupalfolder / node / 306 returns " node/306 " while
* base_path () returns " /drupalfolder/ " .
* - http :// example . com / path / alias ( which is a path alias for node / 306 ) returns
* " path/alias " as opposed to the internal path .
*
* @ return
* The requested Drupal URL path .
*
* @ see current_path ()
*/
function request_path () {
static $path ;
if ( isset ( $path )) {
return $path ;
}
if ( isset ( $_GET [ 'q' ])) {
// This is a request with a ?q=foo/bar query string. $_GET['q'] is
// overwritten in drupal_path_initialize(), but request_path() is called
// very early in the bootstrap process, so the original value is saved in
// $path and returned in later calls.
$path = $_GET [ 'q' ];
}
elseif ( isset ( $_SERVER [ 'REQUEST_URI' ])) {
// This is a request using a clean URL. Extract the path from REQUEST_URI.
$request_path = strtok ( $_SERVER [ 'REQUEST_URI' ], '?' );
$base_path_len = strlen ( rtrim ( dirname ( $_SERVER [ 'SCRIPT_NAME' ]), '\/' ));
// Unescape and strip $base_path prefix, leaving q without a leading slash.
$path = substr ( urldecode ( $request_path ), $base_path_len + 1 );
}
else {
// This is the front page.
$path = '' ;
}
2010-03-10 19:36:14 +00:00
// Under certain conditions Apache's RewriteRule directive prepends the value
// assigned to $_GET['q'] with a slash. Moreover we can always have a trailing
// slash in place, hence we need to normalize $_GET['q'].
$path = trim ( $path , '/' );
2010-01-29 22:40:41 +00:00
return $path ;
}
2007-05-25 15:04:42 +00:00
/**
* If Drupal is behind a reverse proxy , we use the X - Forwarded - For header
2008-07-02 20:05:11 +00:00
* instead of $_SERVER [ 'REMOTE_ADDR' ], which would be the IP address of
2010-03-17 13:58:45 +00:00
* the proxy server , and not the client ' s . The actual header name can be
* configured by the reverse_proxy_header variable .
2007-05-25 15:04:42 +00:00
*
* @ return
2008-06-26 11:29:20 +00:00
* IP address of client machine , adjusted for reverse proxy and / or cluster
* environments .
2007-05-25 15:04:42 +00:00
*/
2009-04-02 20:39:45 +00:00
function ip_address () {
$ip_address = & drupal_static ( __FUNCTION__ );
2007-05-25 15:04:42 +00:00
2009-04-02 20:39:45 +00:00
if ( ! isset ( $ip_address )) {
2007-07-29 21:04:03 +00:00
$ip_address = $_SERVER [ 'REMOTE_ADDR' ];
2008-07-08 01:08:15 +00:00
2008-07-04 22:54:09 +00:00
if ( variable_get ( 'reverse_proxy' , 0 )) {
2010-03-17 13:58:45 +00:00
$reverse_proxy_header = variable_get ( 'reverse_proxy_header' , 'HTTP_X_FORWARDED_FOR' );
if ( ! empty ( $_SERVER [ $reverse_proxy_header ])) {
2008-07-04 22:54:09 +00:00
// If an array of known reverse proxy IPs is provided, then trust
// the XFF header if request really comes from one of them.
$reverse_proxy_addresses = variable_get ( 'reverse_proxy_addresses' , array ());
if ( ! empty ( $reverse_proxy_addresses ) && in_array ( $ip_address , $reverse_proxy_addresses , TRUE )) {
2009-03-18 09:21:21 +00:00
// The "X-Forwarded-For" header is a comma+space separated list of IP addresses,
// the left-most being the farthest downstream client. If there is more than
// one proxy, we are interested in the most recent one (i.e. last one in the list).
2010-03-17 13:58:45 +00:00
$ip_address_parts = explode ( ',' , $_SERVER [ $reverse_proxy_header ]);
2009-03-18 09:21:21 +00:00
$ip_address = trim ( array_pop ( $ip_address_parts ));
2008-07-04 22:54:09 +00:00
}
2007-11-26 08:06:52 +00:00
}
2008-06-26 11:29:20 +00:00
}
2007-05-25 15:04:42 +00:00
}
2007-07-29 17:28:23 +00:00
2007-07-29 21:04:03 +00:00
return $ip_address ;
2007-05-25 15:04:42 +00:00
}
2008-05-06 12:18:54 +00:00
2008-08-21 19:36:39 +00:00
/**
* @ ingroup schemaapi
* @ {
*/
/**
* Get the schema definition of a table , or the whole database schema .
*
* The returned schema will include any modifications made by any
* module that implements hook_schema_alter () .
*
* @ param $table
* The name of the table . If not given , the schema of all tables is returned .
* @ param $rebuild
* If true , the schema will be rebuilt instead of retrieved from the cache .
*/
function drupal_get_schema ( $table = NULL , $rebuild = FALSE ) {
static $schema = array ();
if ( empty ( $schema ) || $rebuild ) {
// Try to load the schema from cache.
if ( ! $rebuild && $cached = cache_get ( 'schema' )) {
$schema = $cached -> data ;
}
// Otherwise, rebuild the schema cache.
else {
$schema = array ();
// Load the .install files to get hook_schema.
// On some databases this function may be called before bootstrap has
// been completed, so we force the functions we need to load just in case.
2009-08-24 00:14:23 +00:00
if ( function_exists ( 'module_load_all_includes' )) {
2008-08-21 19:36:39 +00:00
// There is currently a bug in module_list() where it caches what it
// was last called with, which is not always what you want.
// module_load_all_includes() calls module_list(), but if this function
// is called very early in the bootstrap process then it will be
2008-12-20 18:24:41 +00:00
// uninitialized and therefore return no modules. Instead, we have to
2008-08-21 19:36:39 +00:00
// "prime" module_list() here to to values we want, specifically
// "yes rebuild the list and don't limit to bootstrap".
// TODO: Remove this call after http://drupal.org/node/222109 is fixed.
2009-08-24 00:14:23 +00:00
module_list ( TRUE , FALSE );
2008-08-21 19:36:39 +00:00
module_load_all_includes ( 'install' );
}
2009-01-04 16:15:54 +00:00
require_once DRUPAL_ROOT . '/includes/common.inc' ;
2008-08-21 19:36:39 +00:00
// Invoke hook_schema for all modules.
foreach ( module_implements ( 'schema' ) as $module ) {
2009-11-01 23:10:14 +00:00
// Cast the result of hook_schema() to an array, as a NULL return value
// would cause array_merge() to set the $schema variable to NULL as well.
// That would break modules which use $schema further down the line.
$current = ( array ) module_invoke ( $module , 'schema' );
2009-08-24 00:14:23 +00:00
_drupal_schema_initialize ( $module , $current );
2008-08-21 19:36:39 +00:00
$schema = array_merge ( $schema , $current );
}
2009-08-24 00:14:23 +00:00
drupal_alter ( 'schema' , $schema );
2008-11-30 01:05:16 +00:00
// If the schema is empty, avoid saving it: some database engines require
// the schema to perform queries, and this could lead to infinite loops.
if ( ! empty ( $schema ) && ( drupal_get_bootstrap_phase () == DRUPAL_BOOTSTRAP_FULL )) {
2008-08-21 19:36:39 +00:00
cache_set ( 'schema' , $schema );
}
}
}
if ( ! isset ( $table )) {
return $schema ;
}
elseif ( isset ( $schema [ $table ])) {
return $schema [ $table ];
}
else {
return FALSE ;
}
}
/**
* @ } End of " ingroup schemaapi " .
*/
2008-05-06 12:18:54 +00:00
/**
* @ ingroup registry
* @ {
*/
/**
* Confirm that an interface is available .
*
2009-08-24 00:14:23 +00:00
* This function is rarely called directly . Instead , it is registered as an
* spl_autoload () handler , and PHP calls it for us when necessary .
2008-05-06 12:18:54 +00:00
*
* @ param $interface
* The name of the interface to check or load .
* @ return
* TRUE if the interface is currently available , FALSE otherwise .
*/
function drupal_autoload_interface ( $interface ) {
return _registry_check_code ( 'interface' , $interface );
}
/**
* Confirm that a class is available .
*
2009-08-24 00:14:23 +00:00
* This function is rarely called directly . Instead , it is registered as an
* spl_autoload () handler , and PHP calls it for us when necessary .
2008-05-06 12:18:54 +00:00
*
* @ param $class
* The name of the class to check or load .
* @ return
* TRUE if the class is currently available , FALSE otherwise .
*/
function drupal_autoload_class ( $class ) {
return _registry_check_code ( 'class' , $class );
}
/**
2008-10-31 02:18:22 +00:00
* Helper to check for a resource in the registry .
2008-11-11 22:39:59 +00:00
*
* @ param $type
2008-11-16 19:41:14 +00:00
* The type of resource we are looking up , or one of the constants
2008-11-11 22:39:59 +00:00
* REGISTRY_RESET_LOOKUP_CACHE or REGISTRY_WRITE_LOOKUP_CACHE , which
* signal that we should reset or write the cache , respectively .
* @ param $name
* The name of the resource , or NULL if either of the REGISTRY_ * constants
* is passed in .
* @ return
* TRUE if the resource was found , FALSE if not .
* NULL if either of the REGISTRY_ * constants is passed in as $type .
2008-05-06 12:18:54 +00:00
*/
2008-11-11 22:39:59 +00:00
function _registry_check_code ( $type , $name = NULL ) {
static $lookup_cache , $cache_update_needed ;
2009-10-26 13:00:54 +00:00
if ( $type == 'class' && class_exists ( $name ) || $type == 'interface' && interface_exists ( $name )) {
return TRUE ;
}
2008-11-11 22:39:59 +00:00
if ( ! isset ( $lookup_cache )) {
2008-11-29 23:21:22 +00:00
$lookup_cache = array ();
2009-11-10 22:06:09 +00:00
if ( $cache = cache_get ( 'lookup_cache' , 'cache_bootstrap' )) {
2008-11-29 23:21:22 +00:00
$lookup_cache = $cache -> data ;
}
2008-11-11 22:39:59 +00:00
}
2008-11-16 19:41:14 +00:00
2008-11-11 22:39:59 +00:00
// When we rebuild the registry, we need to reset this cache so
// we don't keep lookups for resources that changed during the rebuild.
if ( $type == REGISTRY_RESET_LOOKUP_CACHE ) {
$cache_update_needed = TRUE ;
$lookup_cache = NULL ;
return ;
}
// Called from drupal_page_footer, we write to permanent storage if there
// changes to the lookup cache for this request.
if ( $type == REGISTRY_WRITE_LOOKUP_CACHE ) {
if ( $cache_update_needed ) {
2009-11-10 22:06:09 +00:00
cache_set ( 'lookup_cache' , $lookup_cache , 'cache_bootstrap' );
2008-11-11 22:39:59 +00:00
}
return ;
}
2008-11-16 19:41:14 +00:00
2009-10-22 01:15:15 +00:00
// $type is either 'interface' or 'class', so we only need the first letter to
// keep the cache key unique.
2008-11-11 22:39:59 +00:00
$cache_key = $type [ 0 ] . $name ;
if ( isset ( $lookup_cache [ $cache_key ])) {
if ( $lookup_cache [ $cache_key ]) {
2009-10-26 13:00:54 +00:00
require DRUPAL_ROOT . '/' . $lookup_cache [ $cache_key ];
2008-11-11 22:39:59 +00:00
}
2009-10-26 13:00:54 +00:00
return ( bool ) $lookup_cache [ $cache_key ];
2008-11-11 22:39:59 +00:00
}
2008-11-16 19:41:14 +00:00
2008-11-02 06:33:56 +00:00
// This function may get called when the default database is not active, but
// there is no reason we'd ever want to not use the default database for
// this query.
2009-01-25 12:19:32 +00:00
$file = Database :: getConnection ( 'default' , 'default' ) -> query ( " SELECT filename FROM { registry} WHERE name = :name AND type = :type " , array (
2008-10-06 14:26:54 +00:00
':name' => $name ,
':type' => $type ,
))
-> fetchField ();
2008-11-11 22:39:59 +00:00
// Flag that we've run a lookup query and need to update the cache.
$cache_update_needed = TRUE ;
// Misses are valuable information worth caching, so cache even if
// $file is FALSE.
$lookup_cache [ $cache_key ] = $file ;
2008-05-06 12:18:54 +00:00
if ( $file ) {
2009-10-26 13:00:54 +00:00
require DRUPAL_ROOT . '/' . $file ;
2008-05-06 12:18:54 +00:00
return TRUE ;
}
2008-11-11 22:39:59 +00:00
else {
return FALSE ;
2008-05-06 12:18:54 +00:00
}
}
/**
* Rescan all enabled modules and rebuild the registry .
*
2009-08-24 00:14:23 +00:00
* Rescans all code in modules or includes directories , storing the location of
* each interface or class in the database .
2008-05-06 12:18:54 +00:00
*/
2008-08-02 19:01:02 +00:00
function registry_rebuild () {
2009-12-28 10:48:51 +00:00
system_rebuild_module_data ();
registry_update ();
}
/**
* Update the registry based on the latest files listed in the database .
*
* This function should be used when system_rebuild_module_data () does not need
* to be called , because it is already known that the list of files in the
* { system } table matches those in the file system .
*
* @ see registry_rebuild ()
*/
function registry_update () {
2008-09-20 20:22:25 +00:00
require_once DRUPAL_ROOT . '/includes/registry.inc' ;
2009-12-28 10:48:51 +00:00
_registry_update ();
2008-05-06 12:18:54 +00:00
}
/**
* @ } End of " ingroup registry " .
*/
2009-04-02 20:39:45 +00:00
/**
* Central static variable storage .
*
2009-11-20 06:12:45 +00:00
* All functions requiring a static variable to persist or cache data within
* a single page request are encouraged to use this function unless it is
* absolutely certain that the static variable will not need to be reset during
* the page request . By centralizing static variable storage through this
* function , other functions can rely on a consistent API for resetting any
* other function ' s static variables .
*
* Example :
* @ code
* function language_list ( $field = 'language' ) {
* $languages = & drupal_static ( __FUNCTION__ );
* if ( ! isset ( $languages )) {
* // If this function is being called for the first time after a reset,
* // query the database and execute any other code needed to retrieve
* // information about the supported languages.
* ...
* }
* if ( ! isset ( $languages [ $field ])) {
* // If this function is being called for the first time for a particular
* // index field, then execute code needed to index the information already
* // available in $languages by the desired field.
* ...
* }
* // Subsequent invocations of this function for a particular index field
* // skip the above two code blocks and quickly return the already indexed
* // information.
* return $languages [ $field ];
* }
* function locale_translate_overview_screen () {
* // When building the content for the translations overview page, make
* // sure to get completely fresh information about the supported languages.
* drupal_static_reset ( 'language_list' );
* ...
* }
* @ endcode
*
* In a few cases , a function can have certainty that there is no legitimate
* use - case for resetting that function ' s static variable . This is rare ,
* because when writing a function , it ' s hard to forecast all the situations in
* which it will be used . A guideline is that if a function ' s static variable
* does not depend on any information outside of the function that might change
* during a single page request , then it ' s ok to use the " static " keyword
* instead of the drupal_static () function .
*
* Example :
* @ code
* function actions_do ( ... ) {
* // $stack tracks the number of recursive calls.
* static $stack ;
* $stack ++ ;
* if ( $stack > variable_get ( 'actions_max_stack' , 35 )) {
* ...
* return ;
* }
* ...
* $stack -- ;
* }
* @ endcode
*
* In a few cases , a function needs a resettable static variable , but the
* function is called many times ( 100 + ) during a single page request , so
* every microsecond of execution time that can be removed from the function
* counts . These functions can use a more cumbersome , but faster variant of
2010-01-07 04:54:18 +00:00
* calling drupal_static () . It works by storing the reference returned by
* drupal_static () in the calling function ' s own static variable , thereby
* removing the need to call drupal_static () for each iteration of the function .
* Conceptually , it replaces :
* @ code
* $foo = & drupal_static ( __FUNCTION__ );
* @ endcode
* with :
* @ code
* // Unfortunately, this does not work.
* static $foo = & drupal_static ( __FUNCTION__ );
* @ endcode
* However , the above line of code does not work , because PHP only allows static
* variables to be initializied by literal values , and does not allow static
* variables to be assigned to references .
* - http :// php . net / manual / en / language . variables . scope . php #language.variables.scope.static
* - http :// php . net / manual / en / language . variables . scope . php #language.variables.scope.references
* The example below shows the syntax needed to work around both limitations .
2010-03-26 17:14:46 +00:00
* For benchmarks and more information , see http :// drupal . org / node / 619666.
2009-11-20 06:12:45 +00:00
*
* Example :
* @ code
* function user_access ( $string , $account = NULL ) {
* // Use the advanced drupal_static() pattern, since this is called very often.
2010-01-07 04:54:18 +00:00
* static $drupal_static_fast ;
* if ( ! isset ( $drupal_static_fast )) {
* $drupal_static_fast [ 'perm' ] = & drupal_static ( __FUNCTION__ );
* }
* $perm = & $drupal_static_fast [ 'perm' ];
2009-11-20 06:12:45 +00:00
* ...
* }
* @ endcode
*
2009-04-02 20:39:45 +00:00
* @ param $name
* Globally unique name for the variable . For a function with only one static ,
* variable , the function name ( e . g . via the PHP magic __FUNCTION__ constant )
2009-05-24 17:39:35 +00:00
* is recommended . For a function with multiple static variables add a
2009-04-02 20:39:45 +00:00
* distinguishing suffix to the function name for each one .
* @ param $default_value
* Optional default value .
* @ param $reset
* TRUE to reset a specific named variable , or all variables if $name is NULL .
* Resetting every variable should only be used , for example , for running
* unit tests with a clean environment . Should be used only though via
2009-11-08 20:36:12 +00:00
* function drupal_static_reset () and the return value should not be used in
* this case .
2009-04-02 20:39:45 +00:00
*
* @ return
2009-11-08 20:36:12 +00:00
* Returns a variable by reference .
2009-11-20 06:12:45 +00:00
*
* @ see drupal_static_reset ()
2009-04-02 20:39:45 +00:00
*/
function & drupal_static ( $name , $default_value = NULL , $reset = FALSE ) {
2009-11-08 20:36:12 +00:00
static $data = array (), $default = array ();
if ( ! isset ( $name )) {
2009-11-15 21:41:06 +00:00
// All variables are reset. This needs to be done one at a time so that
// references returned by earlier invocations of drupal_static() also get
// reset.
foreach ( $default as $name => $value ) {
$data [ $name ] = $value ;
}
2009-11-08 20:36:12 +00:00
// As the function returns a reference, the return should always be a
// variable.
return $data ;
}
2009-04-02 20:39:45 +00:00
if ( $reset ) {
2009-11-08 20:36:12 +00:00
// The reset means the default is loaded.
if ( array_key_exists ( $name , $default )) {
$data [ $name ] = $default [ $name ];
2009-04-02 20:39:45 +00:00
}
else {
2009-11-08 20:36:12 +00:00
// Reset was called before a default is set and yet a variable must be
// returned.
return $data ;
2009-04-02 20:39:45 +00:00
}
}
2009-11-08 20:36:12 +00:00
elseif ( ! array_key_exists ( $name , $data )) {
// Store the default value internally and also copy it to the reference to
// be returned.
$default [ $name ] = $data [ $name ] = $default_value ;
2009-04-02 20:39:45 +00:00
}
return $data [ $name ];
}
/**
* Reset one or all centrally stored static variable ( s ) .
*
* @ param $name
* Name of the static variable to reset . Omit to reset all variables .
*/
function drupal_static_reset ( $name = NULL ) {
drupal_static ( $name , NULL , TRUE );
}
2009-09-19 10:38:47 +00:00
/**
* Detect whether the current script is running in a command - line environment .
*/
function drupal_is_cli () {
return ( ! isset ( $_SERVER [ 'SERVER_SOFTWARE' ]) && ( php_sapi_name () == 'cli' || ( is_numeric ( $_SERVER [ 'argc' ]) && $_SERVER [ 'argc' ] > 0 )));
}
2009-12-22 14:47:14 +00:00
/**
* Formats text for emphasized display in a placeholder inside a sentence .
* Used automatically by t () .
*
* @ param $variables
* An associative array containing :
* - text : The text to format ( plain - text ) .
*
* @ return
* The formatted text ( html ) .
*/
function drupal_placeholder ( $variables ) {
return '<em class="placeholder">' . check_plain ( $variables [ 'text' ]) . '</em>' ;
}
2010-02-17 22:44:52 +00:00
/**
*
* Register a function for execution on shutdown .
*
* Wrapper for register_shutdown_function () which catches thrown exceptions
* to avoid " Exception thrown without a stack frame in Unknown " .
*
* @ param $callback
* The shutdown function to register .
* @ param $parameters
* It is possible to pass parameters to the shutdown function by passing
* additional parameters .
* @ return
* Array of shutdown functions to be executed .
*
* @ see register_shutdown_function ()
*/
function & drupal_register_shutdown_function ( $callback = NULL , $parameters = NULL ) {
// We cannot use drupal_static() here because the static cache is reset
// during batch processing, which breaks batch handling.
static $callbacks = array ();
if ( isset ( $callback )) {
// Only register the internal shutdown function once.
if ( empty ( $callbacks )) {
register_shutdown_function ( '_drupal_shutdown_function' );
}
$args = func_get_args ();
array_shift ( $args );
// Save callback and arguments
$callbacks [] = array ( 'callback' => $callback , 'arguments' => $args );
}
return $callbacks ;
}
/**
* Internal function used to execute registered shutdown functions .
*/
function _drupal_shutdown_function () {
$callbacks = & drupal_register_shutdown_function ();
try {
while ( list ( $key , $callback ) = each ( $callbacks )) {
call_user_func_array ( $callback [ 'callback' ], $callback [ 'arguments' ]);
}
}
catch ( Exception $exception ) {
require_once DRUPAL_ROOT . '/includes/errors.inc' ;
// The theme has already been printed so it doesn't make much sense to use
// drupal_exception_handler() because that would display the maintenaince
// page below the usual page. For now, just print the error for debugging.
// @todo: Improve this.
print t ( '%type: %message in %function (line %line of %file).' , _drupal_decode_exception ( $exception ));
}
}