drupal/includes/bootstrap.inc

441 lines
11 KiB
PHP
Raw Normal View History

<?php
/* $Id$ */
/**
* @file
* Functions that need to be loaded on every Drupal request.
*/
/**
* Locate the appropriate configuration file.
*
* Try finding a matching configuration file by stripping the website's
* URI from left to right. If no configuration file is found, return the
* default value, "conf".
*/
function conf_init() {
$uri = $_SERVER['PHP_SELF'];
$file = strtolower(strtr($_SERVER['HTTP_HOST'] . substr($uri, 0, strrpos($uri, '/')), '/:', '..'));
while (strlen($file) > 4) {
if (file_exists('includes/'. $file .'.php')) {
return $file;
}
else {
$file = substr($file, strpos($file, '.') + 1);
}
}
return 'conf';
}
/**
* 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.
*/
function variable_init($conf = array()) {
$result = db_query('SELECT * FROM {variable} ');
while ($variable = db_fetch_object($result)) {
if (!isset($conf[$variable->name])) {
$conf[$variable->name] = unserialize($variable->value);
}
}
return $conf;
}
/**
* 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.
* @return
* The value of the variable.
*/
function variable_get($name, $default) {
global $conf;
return isset($conf[$name]) ? $conf[$name] : $default;
}
/**
* 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.
*/
function variable_set($name, $value) {
global $conf;
db_query("DELETE FROM {variable} WHERE name = '%s'", $name);
db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, serialize($value));
$conf[$name] = $value;
}
/**
* Unset a persistent variable.
*
* @param $name
* The name of the variable to undefine.
*/
function variable_del($name) {
global $conf;
db_query("DELETE FROM {variable} WHERE name = '%s'", $name);
unset($conf[$name]);
}
/**
* Return data from the persistent cache.
*
* @param $key
* The cache ID of the data to retrieve.
*/
function cache_get($key) {
$cache = db_fetch_object(db_query("SELECT data, created, headers FROM {cache} WHERE cid = '%s'", $key));
return isset($cache->data) ? $cache : 0;
}
/**
* Store data in the persistent cache.
*
* @param $cid
* The cache ID of the data to store.
* @param $data
* The data to store in the cache. Complex data types must be serialized first.
* @param $expire
* Whether the data should be removed from the cache when a cache expiration
* is triggered.
* @param $headers
* A string containing HTTP header information for cached pages.
*/
function cache_set($cid, $data, $expire = 0, $headers = NULL) {
$data = db_encode_blob($data);
db_query("UPDATE {cache} SET data = '$data', created = " . time() . ", expire = $expire, headers = '$headers' WHERE cid = '$cid'");
if (!db_affected_rows()) {
db_query("INSERT INTO {cache} (cid, data, created, expire, headers) VALUES('$cid', '$data', " . time() . ", $expire, '$headers')");
}
}
/**
* Expire data from the cache.
*
* @param $cid
* If set, the cache ID to delete. Otherwise, all cache entries that can expire
* are deleted.
The Input formats - filter patch has landed. I still need to make update instructions for modules and update the hook docs. Here's an overview of the changes: 1) Multiple Input formats: they are complete filter configurations (what filters to use, in what order and with which settings). Input formats are admin-definable, and usage of them is role-dependant. For example, you can set it up so that regular users can only use limited HTML, while admins can free HTML without any tag limitations. The input format can be chosen per content item (nodes, comments, blocks, ...) when you add/edit them. If only a single format is available, there is no choice, and nothing changes with before. The default install (and the upgrade) contains a basic set of formats which should satisfy the average user's needs. 2) Filters have toggles Because now you might want to enable a filter only on some input formats, an explicit toggle is provided by the filter system. Modules do not need to worry about it and filters that still have their own on/off switch should get rid of it. 3) Multiple filters per module This was necessary to accomodate the next change, and it's also a logical extension of the filter system. 4) Embedded PHP is now a filter Thanks to the multiple input formats, I was able to move the 'embedded PHP' feature from block.module, page.module and book.module into a simple filter which executes PHP code. This filter is part of filter.module, and by default there is an input format 'PHP', restricted to the administrator only, which contains this filter. This change means that block.module now passes custom block contents through the filter system. As well as from reducing code duplication and avoiding two type selectors for page/book nodes, you can now combine PHP code with other filters. 5) User-supplied PHP code now requires <?php ?> tags. This is required for teasers to work with PHP code. Because PHP evaluation is now just another step in the filter process, we can't do this. Also, because teasers are generated before filtering, this would result in errors when the teaser generation would cut off a piece of PHP code. Also, regular PHP syntax explicitly includes the <?php ?> tags for PHP files, so it makes sense to use the same convention for embedded PHP in Drupal. 6) Filter caching was added. Benchmarking shows that even for a simple setup (basic html filtering + legacy URL rewriting), filtercache can offer speedups. Unlike the old filtercache, this uses the normal cache table. 7) Filtertips were moved from help into a hook_filter_tips(). This was required to accomodate the fact that there are multiple filters per module, and that filter settings are format dependant. Shoehorning filter tips into _help was ugly and silly. The display of the filter tips is done through the input format selector, so filter_tips_short() no longer exists. 8) A more intelligent linebreak convertor was added, which doesn't stop working if you use block-level tags and which adds <p> tags.
2004-08-10 18:34:29 +00:00
*
* @param $wildcard
* If set to true, the $cid is treated as a substring to match rather than a
* complete ID.
*/
The Input formats - filter patch has landed. I still need to make update instructions for modules and update the hook docs. Here's an overview of the changes: 1) Multiple Input formats: they are complete filter configurations (what filters to use, in what order and with which settings). Input formats are admin-definable, and usage of them is role-dependant. For example, you can set it up so that regular users can only use limited HTML, while admins can free HTML without any tag limitations. The input format can be chosen per content item (nodes, comments, blocks, ...) when you add/edit them. If only a single format is available, there is no choice, and nothing changes with before. The default install (and the upgrade) contains a basic set of formats which should satisfy the average user's needs. 2) Filters have toggles Because now you might want to enable a filter only on some input formats, an explicit toggle is provided by the filter system. Modules do not need to worry about it and filters that still have their own on/off switch should get rid of it. 3) Multiple filters per module This was necessary to accomodate the next change, and it's also a logical extension of the filter system. 4) Embedded PHP is now a filter Thanks to the multiple input formats, I was able to move the 'embedded PHP' feature from block.module, page.module and book.module into a simple filter which executes PHP code. This filter is part of filter.module, and by default there is an input format 'PHP', restricted to the administrator only, which contains this filter. This change means that block.module now passes custom block contents through the filter system. As well as from reducing code duplication and avoiding two type selectors for page/book nodes, you can now combine PHP code with other filters. 5) User-supplied PHP code now requires <?php ?> tags. This is required for teasers to work with PHP code. Because PHP evaluation is now just another step in the filter process, we can't do this. Also, because teasers are generated before filtering, this would result in errors when the teaser generation would cut off a piece of PHP code. Also, regular PHP syntax explicitly includes the <?php ?> tags for PHP files, so it makes sense to use the same convention for embedded PHP in Drupal. 6) Filter caching was added. Benchmarking shows that even for a simple setup (basic html filtering + legacy URL rewriting), filtercache can offer speedups. Unlike the old filtercache, this uses the normal cache table. 7) Filtertips were moved from help into a hook_filter_tips(). This was required to accomodate the fact that there are multiple filters per module, and that filter settings are format dependant. Shoehorning filter tips into _help was ugly and silly. The display of the filter tips is done through the input format selector, so filter_tips_short() no longer exists. 8) A more intelligent linebreak convertor was added, which doesn't stop working if you use block-level tags and which adds <p> tags.
2004-08-10 18:34:29 +00:00
function cache_clear_all($cid = NULL, $wildcard = false) {
if (empty($cid)) {
db_query("DELETE FROM {cache} WHERE expire <> 0");
}
else {
The Input formats - filter patch has landed. I still need to make update instructions for modules and update the hook docs. Here's an overview of the changes: 1) Multiple Input formats: they are complete filter configurations (what filters to use, in what order and with which settings). Input formats are admin-definable, and usage of them is role-dependant. For example, you can set it up so that regular users can only use limited HTML, while admins can free HTML without any tag limitations. The input format can be chosen per content item (nodes, comments, blocks, ...) when you add/edit them. If only a single format is available, there is no choice, and nothing changes with before. The default install (and the upgrade) contains a basic set of formats which should satisfy the average user's needs. 2) Filters have toggles Because now you might want to enable a filter only on some input formats, an explicit toggle is provided by the filter system. Modules do not need to worry about it and filters that still have their own on/off switch should get rid of it. 3) Multiple filters per module This was necessary to accomodate the next change, and it's also a logical extension of the filter system. 4) Embedded PHP is now a filter Thanks to the multiple input formats, I was able to move the 'embedded PHP' feature from block.module, page.module and book.module into a simple filter which executes PHP code. This filter is part of filter.module, and by default there is an input format 'PHP', restricted to the administrator only, which contains this filter. This change means that block.module now passes custom block contents through the filter system. As well as from reducing code duplication and avoiding two type selectors for page/book nodes, you can now combine PHP code with other filters. 5) User-supplied PHP code now requires <?php ?> tags. This is required for teasers to work with PHP code. Because PHP evaluation is now just another step in the filter process, we can't do this. Also, because teasers are generated before filtering, this would result in errors when the teaser generation would cut off a piece of PHP code. Also, regular PHP syntax explicitly includes the <?php ?> tags for PHP files, so it makes sense to use the same convention for embedded PHP in Drupal. 6) Filter caching was added. Benchmarking shows that even for a simple setup (basic html filtering + legacy URL rewriting), filtercache can offer speedups. Unlike the old filtercache, this uses the normal cache table. 7) Filtertips were moved from help into a hook_filter_tips(). This was required to accomodate the fact that there are multiple filters per module, and that filter settings are format dependant. Shoehorning filter tips into _help was ugly and silly. The display of the filter tips is done through the input format selector, so filter_tips_short() no longer exists. 8) A more intelligent linebreak convertor was added, which doesn't stop working if you use block-level tags and which adds <p> tags.
2004-08-10 18:34:29 +00:00
if ($wildcard) {
db_query("DELETE FROM {cache} WHERE cid LIKE '%%%s%%'", $cid);
}
else {
db_query("DELETE FROM {cache} WHERE cid = '%s'", $cid);
}
}
}
/**
* Store the current page in the cache.
*/
function page_set_cache() {
global $user, $base_url;
if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET') {
// This will fail in some cases, see page_get_cache() for the explanation.
if ($data = ob_get_contents()) {
if (function_exists('gzencode')) {
if (version_compare(phpversion(), '4.2', '>=')) {
$data = gzencode($data, 9, FORCE_GZIP);
}
else {
$data = gzencode($data, FORCE_GZIP);
}
}
cache_set($base_url . request_uri(), $data, 1, drupal_get_headers());
}
}
}
/**
* Retrieve the current page from the cache.
*
* Note, we do not serve cached pages when status messages are waiting (from
* a redirected form submission which was completed).
* Because the output handler is not activated, the resulting page will not
* get cached either.
*/
function page_get_cache() {
global $user, $base_url;
$cache = NULL;
if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && count(drupal_set_message()) == 0) {
$cache = cache_get($base_url . request_uri());
if (empty($cache)) {
ob_start();
}
}
return $cache;
}
/**
* Set HTTP headers in preparation for a page response.
*/
function drupal_page_header() {
if (variable_get('dev_timer', 0)) {
timer_start();
}
if (variable_get('cache', 0)) {
if ($cache = page_get_cache()) {
// Set default values:
$date = gmdate('D, d M Y H:i:s', $cache->created) .' GMT';
$etag = '"'. md5($date) .'"';
// Check http headers:
$modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] == $date : NULL;
if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && ($timestamp = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) != -1) {
$modified_since = $cache->created <= $timestamp;
}
else {
$modified_since = NULL;
}
$none_match = !empty($_SERVER['HTTP_IF_NONE_MATCH']) ? $_SERVER['HTTP_IF_NONE_MATCH'] == $etag : NULL;
// The type checking here is very important, be careful when changing entries.
if (($modified_since !== NULL || $none_match !== NULL) && $modified_since !== false && $none_match !== false) {
header('HTTP/1.0 304 Not Modified');
exit();
}
// Send appropriate response:
header("Last-Modified: $date");
header("ETag: $etag");
2004-02-15 15:57:55 +00:00
// Determine if the browser accepts gzipped data.
if (@strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === false && function_exists('gzencode')) {
// Strip the gzip header and run uncompress.
$cache->data = gzinflate(substr(substr($cache->data, 10), 0, -8));
}
elseif (function_exists('gzencode')) {
header('Content-Encoding: gzip');
}
// Send the original request's headers. We send them one after
// another so PHP's header() function can deal with duplicate
// headers.
$headers = explode("\n", $cache->headers);
2004-02-15 15:57:55 +00:00
foreach ($headers as $header) {
header($header);
}
print db_decode_blob($cache->data);
// Call all init() and exit() hooks without including all modules.
// Only use those hooks for critical operations.
foreach (bootstrap_hooks() as $hook) {
module_invoke_all($hook);
}
exit();
}
}
}
/**
* Define the critical hooks that force modules to always be loaded.
*/
function bootstrap_hooks() {
return array('init', 'exit');
}
/**
* 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.
*/
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;
}
/**
* Return the URI of the referring page.
*/
function referer_uri() {
if (isset($_SERVER['HTTP_REFERER'])) {
return check_url($_SERVER['HTTP_REFERER']);
}
}
/**
* Return a component of the current Drupal path.
*
* When viewing a page at the path "admin/node/configure", for example, arg(0)
* would return "admin", arg(1) would return "node", and arg(2) would return
* "configure".
*
* Avoid use of this function where possible, as resulting code is hard to read.
* Instead, attempt to use named arguments in menu callback functions. See the
* explanation in menu.inc for how to construct callbacks that take arguments.
*/
function arg($index) {
static $arguments, $q;
if (empty($arguments) || $q != $_GET['q']) {
$arguments = explode('/', $_GET['q']);
}
if (array_key_exists($index, $arguments)) {
return $arguments[$index];
}
}
/**
* Prepare user input for use in a database query, preventing SQL injection attacks.
*/
function check_query($text) {
return addslashes($text);
}
/**
* Prepare user input for use in a URI.
*
* We replace ( and ) with their entity equivalents to prevent XSS attacks.
*/
function check_url($uri) {
$uri = htmlspecialchars($uri, ENT_QUOTES);
$uri = strtr($uri, array('(' => '&040;', ')' => '&041;'));
return $uri;
}
/**
* Since request_uri() is only available on Apache, we generate an
* equivalent using other environment vars.
*/
function request_uri() {
if (isset($_SERVER['REQUEST_URI'])) {
$uri = $_SERVER['REQUEST_URI'];
}
else {
$uri = $_SERVER['PHP_SELF'] .'?'. $_SERVER['argv'][0];
}
return check_url($uri);
}
/**
* Begin a global timer, for benchmarking of page execution time.
*/
function timer_start() {
global $timer;
list($usec, $sec) = explode(' ', microtime());
$timer = (float)$usec + (float)$sec;
}
/**
* Log a system message.
*
* @param $type
* The category to which this message belongs.
* @param $message
* The message to store in the log.
* @param $link
* A link to associate with the message.
*/
function watchdog($type, $message, $link = NULL) {
global $user;
2003-12-18 13:58:59 +00:00
db_query("INSERT INTO {watchdog} (uid, type, message, link, location, hostname, timestamp) VALUES (%d, '%s', '%s', '%s', '%s', '%s', %d)", $user->uid, $type, $message, $link, request_uri(), $_SERVER['REMOTE_ADDR'], time());
}
/**
* @name Page messages
*
* Functions to get and set the message of the current page.
* @{
*/
/**
* Set a message for the user to see.
*
* The message is stored in the session so that it can persist through a redirect.
*
* If called with no arguments, this function returns all set messages without
* clearing them.
*/
function drupal_set_message($message = NULL, $type = 'status') {
if (isset($message)) {
if (!isset($_SESSION['messages'])) {
$_SESSION['messages'] = array();
}
if (!isset($_SESSION['messages'][$type])) {
$_SESSION['messages'][$type] = array();
}
$_SESSION['messages'][$type][] = $message;
}
return $_SESSION['messages'];
}
/**
* Return all messages that have been set.
*
* As a side effect, this function clears the message queue.
*/
function drupal_get_messages() {
$messages = drupal_set_message();
$_SESSION['messages'] = array();
return $messages;
}
/* @} */
unset($conf);
$config = conf_init();
include_once "includes/$config.php";
include_once 'includes/database.inc';
include_once 'includes/session.inc';
include_once 'includes/module.inc';
// Initialize configuration variables, using values from conf.php if available.
$conf = variable_init(isset($conf) ? $conf : array());
?>