Newer
Older
/**
* @file
* Functions that need to be loaded on every Drupal request.
*/
Alex Pott
committed
use Drupal\Component\Datetime\DateTimePlus;
Angie Byron
committed
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Environment;
use Drupal\Component\Utility\SafeMarkup;
Alex Pott
committed
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\DrupalKernel;
catch
committed
use Drupal\Core\Extension\ExtensionDiscovery;
use Drupal\Core\Logger\RfcLogLevel;
Alex Pott
committed
use Drupal\Core\Site\Settings;
catch
committed
use Drupal\Core\Utility\Error;
catch
committed
use Symfony\Component\ClassLoader\ApcClassLoader;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Drupal\Core\Language\LanguageInterface;
Dries Buytaert
committed
/**
* Minimum supported version of PHP.
const DRUPAL_MINIMUM_PHP = '5.4.5';
Dries Buytaert
committed
/**
* Minimum recommended value of PHP memory_limit.
*
* @todo Reduce the memory required to install on some environments in
* https://www.drupal.org/node/2289201 and then decrease this limit.
Dries Buytaert
committed
*/
const DRUPAL_MINIMUM_PHP_MEMORY_LIMIT = '64M';
Dries Buytaert
committed
Angie Byron
committed
/**
* Error reporting level: display no errors.
*/
const ERROR_REPORTING_HIDE = 'hide';
/**
* Error reporting level: display errors and warnings.
*/
const ERROR_REPORTING_DISPLAY_SOME = 'some';
/**
* Error reporting level: display all messages.
*/
const ERROR_REPORTING_DISPLAY_ALL = 'all';
/**
* Error reporting level: display all messages, plus backtrace information.
*/
const ERROR_REPORTING_DISPLAY_VERBOSE = 'verbose';
/**
* First bootstrap phase: initialize configuration.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
const DRUPAL_BOOTSTRAP_CONFIGURATION = 0;
* Second bootstrap phase, initialize a kernel.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
const DRUPAL_BOOTSTRAP_KERNEL = 1;
Alex Pott
committed
* Third bootstrap phase: load code for subsystems and modules.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
Alex Pott
committed
const DRUPAL_BOOTSTRAP_CODE = 2;
* Final bootstrap phase: initialize language, path, theme, and modules.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
Alex Pott
committed
const DRUPAL_BOOTSTRAP_FULL = 3;
/**
* Role ID for anonymous users; should match what's in the "role" table.
*/
catch
committed
const DRUPAL_ANONYMOUS_RID = 'anonymous';
/**
* Role ID for authenticated users; should match what's in the "role" table.
*/
catch
committed
const DRUPAL_AUTHENTICATED_RID = 'authenticated';
Dries Buytaert
committed
/**
* The maximum number of characters in a module or theme name.
*/
const DRUPAL_EXTENSION_NAME_MAX_LENGTH = 50;
Dries Buytaert
committed
/**
Dries Buytaert
committed
* Time of the current request in seconds elapsed since the Unix Epoch.
Dries Buytaert
committed
*
Dries Buytaert
committed
* This differs from $_SERVER['REQUEST_TIME'], which is stored as a float
* since PHP 5.4.0. Float timestamps confuse most PHP functions
* (including date_create()).
*
* @see http://php.net/manual/reserved.variables.server.php
* @see http://php.net/manual/function.time.php
Dries Buytaert
committed
*/
Dries Buytaert
committed
define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
Dries Buytaert
committed
Dries Buytaert
committed
/**
* Regular expression to match PHP function names.
*
* @see http://php.net/manual/language.functions.php
Dries Buytaert
committed
*/
const DRUPAL_PHP_FUNCTION_PATTERN = '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*';
Dries Buytaert
committed
Angie Byron
committed
/**
* $config_directories key for active directory.
*
Jennifer Hodgdon
committed
* @see config_get_config_directory()
Angie Byron
committed
*/
const CONFIG_ACTIVE_DIRECTORY = 'active';
/**
* $config_directories key for staging directory.
*
Jennifer Hodgdon
committed
* @see config_get_config_directory()
Angie Byron
committed
*/
const CONFIG_STAGING_DIRECTORY = 'staging';
Angie Byron
committed
/**
* Defines the root directory of the Drupal installation.
*
* This strips two levels of directories off the current directory.
*/
define('DRUPAL_ROOT', dirname(dirname(__DIR__)));
Jennifer Hodgdon
committed
* Returns the appropriate configuration directory.
* @param bool $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 bool $reset
* Force a full search for matching directories even if one had been
* found previously. Defaults to FALSE.
* @param \Symfony\Component\HttpFoundation\Request $request
* (optional) The current request. Defaults to \Drupal::request() or a new
* request created from globals.
* @return string
* The path of the matching directory.@see default.settings.php
Jennifer Hodgdon
committed
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal\Core\DrupalKernel::getSitePath() instead. If the kernel is
* unavailable or the site path needs to be recalculated then
* Drupal\Core\DrupalKernel::findSitePath() can be used.
*/
function conf_path($require_settings = TRUE, $reset = FALSE, Request $request = NULL) {
if (!isset($request)) {
if (\Drupal::hasRequest()) {
$request = \Drupal::request();
}
// @todo Remove once external CLI scripts (Drush) are updated.
else {
$request = Request::createFromGlobals();
}
}
if (\Drupal::hasService('kernel')) {
$site_path = \Drupal::service('kernel')->getSitePath();
Angie Byron
committed
}
if (!isset($site_path) || empty($site_path)) {
$site_path = DrupalKernel::findSitePath($request, $require_settings);
return $site_path;
/**
Angie Byron
committed
* Returns the path of a configuration directory.
*
* @param string $type
* (optional) The type of config directory to return. Drupal core provides
* 'active' and 'staging'. Defaults to CONFIG_ACTIVE_DIRECTORY.
*
* @return string
* The configuration directory path.
*/
Angie Byron
committed
function config_get_config_directory($type = CONFIG_ACTIVE_DIRECTORY) {
global $config_directories;
Angie Byron
committed
if (!empty($config_directories[$type])) {
catch
committed
return $config_directories[$type];
}
Angie Byron
committed
throw new \Exception(format_string('The configuration directory type %type does not exist.', array('%type' => $type)));
}
catch
committed
* Returns and optionally sets the filename for a system resource.
*
* The filename, whether provided, cached, or retrieved from the database, is
* only returned if the file exists.
* 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
Jennifer Hodgdon
committed
* configuration. For example, a module 'foo' may legally be located
* core/modules/foo/foo.info.yml
* modules/foo/foo.info.yml
* sites/example.com/modules/foo/foo.info.yml
*
* Calling drupal_get_filename('module', 'foo') will give you one of
* the above, depending on where the module is located.
*
* The type of the item; one of 'core', 'profile', 'module', 'theme', or
* 'theme_engine'.
* The name of the item for which the filename is requested. Ignored for
* $type 'core'.
* @param $filename
* The filename of the item if it is to be set explicitly rather
* than by consulting the database.
*
* @return
Jennifer Hodgdon
committed
* The filename of the requested item or NULL if the item is not found.
function drupal_get_filename($type, $name, $filename = NULL) {
// The location of files will not change during the request, so do not use
// drupal_static().
catch
committed
static $files = array();
// Type 'core' only exists to simplify application-level logic; it always maps
// to the /core directory, whereas $name is ignored. It is only requested via
// drupal_get_path(). /core/core.info.yml does not exist, but is required
// since drupal_get_path() returns the dirname() of the returned pathname.
if ($type === 'core') {
return 'core/core.info.yml';
}
// Profiles are converted into modules in system_rebuild_module_data().
// @todo Remove false-exposure of profiles as modules.
$original_type = $type;
if ($type == 'profile') {
$type = 'module';
if (!isset($files[$type])) {
catch
committed
if (isset($filename)) {
catch
committed
elseif (!isset($files[$type][$name])) {
// If the pathname of the requested extension is not known, try to retrieve
// the list of extension pathnames from various providers, checking faster
// providers first.
// Retrieve the current module list (derived from the service container).
if ($type == 'module' && \Drupal::hasService('module_handler')) {
foreach (\Drupal::moduleHandler()->getModuleList() as $module_name => $module) {
$files[$type][$module_name] = $module->getPathname();
}
catch
committed
}
// If still unknown, retrieve the file list prepared in state by
// system_rebuild_module_data() and system_rebuild_theme_data().
if (!isset($files[$type][$name]) && \Drupal::hasService('state')) {
$files[$type] += \Drupal::state()->get('system.' . $type . '.files', array());
Angie Byron
committed
}
catch
committed
// If still unknown, perform a filesystem scan.
Angie Byron
committed
if (!isset($files[$type][$name])) {
$listing = new ExtensionDiscovery(DRUPAL_ROOT);
catch
committed
// Prevent an infinite recursion by this legacy function.
if ($original_type == 'profile') {
$listing->setProfileDirectories(array());
Angie Byron
committed
}
catch
committed
foreach ($listing->scan($original_type) as $extension_name => $file) {
$files[$type][$extension_name] = $file->getPathname();
Steven Wittens
committed
if (isset($files[$type][$name])) {
return $files[$type][$name];
}
/**
* Returns the path to a system item (module, theme, etc.).
*
* @param $type
* The type of the item; one of 'core', 'profile', 'module', 'theme', or
* 'theme_engine'.
* @param $name
* The name of the item for which the path is requested. Ignored for
* $type 'core'.
*
* @return
* The path to the requested item or an empty string if the item is not found.
*/
function drupal_get_path($type, $name) {
return dirname(drupal_get_filename($type, $name));
}
Dries Buytaert
committed
/**
catch
committed
* Sets an HTTP response header for the current page.
Dries Buytaert
committed
*
* 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
Dries Buytaert
committed
* The HTTP header name, or the special 'Status' header name.
Dries Buytaert
committed
* @param $value
Dries Buytaert
committed
* 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".
Dries Buytaert
committed
* @param $append
* Whether to append the value to an existing header or to replace it.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Symfony\Component\HttpFoundation\Response->headers->set().
* See https://drupal.org/node/2181523.
Dries Buytaert
committed
*/
function _drupal_add_http_header($name, $value, $append = FALSE) {
Dries Buytaert
committed
// The headers as name/value pairs.
Dries Buytaert
committed
$headers = &drupal_static('drupal_http_headers', array());
Dries Buytaert
committed
Dries Buytaert
committed
$name_lower = strtolower($name);
Dries Buytaert
committed
_drupal_set_preferred_header_name($name);
Dries Buytaert
committed
Dries Buytaert
committed
if ($value === FALSE) {
Dries Buytaert
committed
$headers[$name_lower] = FALSE;
Dries Buytaert
committed
}
Dries Buytaert
committed
elseif (isset($headers[$name_lower]) && $append) {
Dries Buytaert
committed
// Multiple headers with identical names may be combined using comma (RFC
// 2616, section 4.2).
Dries Buytaert
committed
$headers[$name_lower] .= ',' . $value;
Dries Buytaert
committed
}
else {
Dries Buytaert
committed
$headers[$name_lower] = $value;
Dries Buytaert
committed
}
}
/**
catch
committed
* Gets the HTTP response headers for the current page.
Dries Buytaert
committed
*
* @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.
catch
committed
*
Dries Buytaert
committed
* @return
* A string containing the header value, or FALSE if the header has been set,
* or NULL if the header has not been set.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Symfony\Component\HttpFoundation\Response->headers->get().
* See https://drupal.org/node/2181523.
Dries Buytaert
committed
*/
Dries Buytaert
committed
function drupal_get_http_header($name = NULL) {
Dries Buytaert
committed
$headers = &drupal_static('drupal_http_headers', array());
Dries Buytaert
committed
if (isset($name)) {
$name = strtolower($name);
return isset($headers[$name]) ? $headers[$name] : NULL;
}
else {
return $headers;
}
}
/**
catch
committed
* Sets the preferred name for the HTTP header.
*
Dries Buytaert
committed
* Header names are case-insensitive, but for maximum compatibility they should
* follow "common form" (see RFC 2616, section 4.2).
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* See https://drupal.org/node/2181523.
Dries Buytaert
committed
*/
function _drupal_set_preferred_header_name($name = NULL) {
static $header_names = array();
if (!isset($name)) {
return $header_names;
}
$header_names[strtolower($name)] = $name;
}
Angie Byron
committed
/**
Angie Byron
committed
* Translates a string to the current language or to a given language.
Angie Byron
committed
*
* The t() function serves two purposes. First, at run-time it translates
* user-visible text into the appropriate language. Second, various mechanisms
* that figure out what text needs to be translated work off t() -- the text
Dries Buytaert
committed
* inside t() calls is added to the database of strings to be translated.
* These strings are expected to be in English, so the first argument should
* always be in English. To enable a fully-translatable site, it is important
* that all human-readable text that will be displayed on the site or sent to
* a user is passed through the t() function, or a related function. See the
* @link http://drupal.org/node/322729 Localization API @endlink pages for
* more information, including recommendations on how to break up or not
* break up strings for translation.
*
* @section sec_translating_vars Translating Variables
* You should never use t() to translate variables, such as calling
* @code t($text); @endcode, unless the text that the variable holds has been
* passed through t() elsewhere (e.g., $text is one of several translated
* literal strings in an array). It is especially important never to call
* @code t($user_text); @endcode, where $user_text is some text that a user
* entered - doing that can lead to cross-site scripting and other security
* problems. However, you can use variable substitution in your string, to put
* variable text such as user names or link URLs into translated text. Variable
* substitution looks like this:
Angie Byron
committed
* @code
* $text = t("@name's blog", array('@name' => user_format_name($account)));
Angie Byron
committed
* @endcode
* Basically, you can put variables like @name into your string, and t() will
* substitute their sanitized values at translation time. (See the
* Localization API pages referenced above and the documentation of
Jennifer Hodgdon
committed
* format_string() for details about how to define variables in your string.)
* Translators can then rearrange the string as necessary for the language
* (e.g., in Spanish, it might be "blog de @name").
Angie Byron
committed
*
* @param $string
* A string containing the English string to translate.
* @param $args
* An associative array of replacements to make after translation. Based
* on the first character of the key, the value is escaped and/or themed.
* See format_string() for details.
Angie Byron
committed
* @param $options
* An associative array of additional options, with the following elements:
* - 'langcode' (defaults to the current language): The language code to
* translate to a language other than what is used to display the page.
* - 'context' (defaults to the empty context): The context the source string
* belongs to.
Angie Byron
committed
*
Angie Byron
committed
* @return
* The translated string.
Dries Buytaert
committed
*
* @see format_string()
Dries Buytaert
committed
* @ingroup sanitization
Angie Byron
committed
*/
function t($string, array $args = array(), array $options = array()) {
return \Drupal::translation()->translate($string, $args, $options);
Dries Buytaert
committed
}
/**
Angie Byron
committed
* Formats a string for HTML display by replacing variable placeholders.
*
Alex Pott
committed
* @see \Drupal\Component\Utility\String::format()
Dries Buytaert
committed
* @see t()
* @ingroup sanitization
*/
function format_string($string, array $args = array()) {
Alex Pott
committed
return String::format($string, $args);
Angie Byron
committed
}
/**
* 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.
*
Dries Buytaert
committed
* The function does not return FALSE for strings containing character codes
* above U+10FFFF, even though these are prohibited by RFC 3629.
catch
committed
*
* @return
* TRUE if the text is valid UTF-8, FALSE if not.
*
* @see \Drupal\Component\Utility\Unicode::validateUtf8()
return Unicode::validateUtf8($text);
Dries Buytaert
committed
* Returns the equivalent of Apache's $_SERVER['REQUEST_URI'] variable.
*
* Because $_SERVER['REQUEST_URI'] is only available on Apache, we generate an
* equivalent using other environment variables.
Dries Buytaert
committed
*
* @todo The above comment is incorrect: http://drupal.org/node/1547294.
Dries Buytaert
committed
function request_uri($omit_query_string = FALSE) {
if (isset($_SERVER['REQUEST_URI'])) {
$uri = $_SERVER['REQUEST_URI'];
}
else {
Dries Buytaert
committed
if (isset($_SERVER['argv'][0])) {
$uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['argv'][0];
elseif (isset($_SERVER['QUERY_STRING'])) {
$uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING'];
else {
$uri = $_SERVER['SCRIPT_NAME'];
}
// Prevent multiple slashes to avoid cross site requests via the Form API.
$uri = '/' . ltrim($uri, '/');
Dries Buytaert
committed
Dries Buytaert
committed
return $omit_query_string ? strtok($uri, '?') : $uri;
Dries Buytaert
committed
/**
catch
committed
* Logs an exception.
Dries Buytaert
committed
*
* This is a wrapper logging function which automatically decodes an exception.
Dries Buytaert
committed
*
* @param $type
* The category to which this message belongs.
* @param $exception
* The exception that is going to be logged.
* @param $message
* The message to store in the log. If empty, a text that contains all useful
Dries Buytaert
committed
* information about the passed-in exception is used.
Dries Buytaert
committed
* @param $variables
catch
committed
* Array of variables to replace in the message on display or
* NULL if message is already translated or not possible to
* translate.
Dries Buytaert
committed
* @param $severity
* The severity of the message, as per RFC 3164.
* @param $link
* A link to associate with the message.
*
catch
committed
* @see \Drupal\Core\Utility\Error::decodeException()
Dries Buytaert
committed
*/
function watchdog_exception($type, Exception $exception, $message = NULL, $variables = array(), $severity = RfcLogLevel::ERROR, $link = NULL) {
Dries Buytaert
committed
Angie Byron
committed
// Use a default value if $message is not set.
if (empty($message)) {
// The exception message is run through
// \Drupal\Component\Utility\String::checkPlain() by
// \Drupal\Core\Utility\Error:decodeException().
$message = '%type: !message in %function (line %line of %file).';
}
if ($link) {
$variables['link'] = $link;
}
$variables += Error::decodeException($exception);
\Drupal::logger($type)->log($severity, $message, $variables);
Dries Buytaert
committed
}
Jennifer Hodgdon
committed
* Sets a message to display to the user.
* Messages are stored in a session variable and displayed in the page template
* via the $messages theme variable.
Jennifer Hodgdon
committed
* Example usage:
* @code
* drupal_set_message(t('An error occurred and processing did not complete.'), 'error');
* @endcode
*
* @param string $message
* (optional) The translated message to be displayed to the user. For
* consistency with other messages, it should begin with a capital letter and
* end with a period.
* @param string $type
* (optional) The message's type. Defaults to 'status'. These values are
* supported:
* - 'status'
* - 'error'
Jennifer Hodgdon
committed
* @param bool $repeat
* (optional) If this is FALSE and the message is already set, then the
Dries Buytaert
committed
* message won't be repeated. Defaults to FALSE.
Jennifer Hodgdon
committed
*
* @return array|null
* A multidimensional array with keys corresponding to the set message types.
* The indexed array values of each contain the set messages for that type,
* and each message is an associative array with the following format:
* - safe: Boolean indicating whether the message string has been marked as
* safe. Non-safe strings will be escaped automatically.
* - message: The message string.
* So, the following is an example of the full return array structure:
* @code
* array(
* 'status' => array(
* array(
* 'safe' => TRUE,
* 'message' => 'A <em>safe</em> markup string.',
* ),
* array(
* 'safe' => FALSE,
* 'message' => "$arbitrary_user_input to escape.",
* ),
* ),
* );
* @endcode
* If there are no messages set, the function returns NULL.
Jennifer Hodgdon
committed
*
* @see drupal_get_messages()
* @see status-messages.html.twig
Dries Buytaert
committed
function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE) {
Alex Pott
committed
if (isset($message)) {
if (!isset($_SESSION['messages'][$type])) {
$_SESSION['messages'][$type] = array();
}
Alex Pott
committed
$new = array(
'safe' => SafeMarkup::isSafe($message),
'message' => $message,
);
if ($repeat || !in_array($new, $_SESSION['messages'][$type])) {
$_SESSION['messages'][$type][] = $new;
Gábor Hojtsy
committed
}
Dries Buytaert
committed
// Mark this page as being uncacheable.
Alex Pott
committed
\Drupal::service('page_cache_kill_switch')->trigger();
Steven Wittens
committed
}
// Messages not set when DB connection fails.
return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL;
Steven Wittens
committed
}
Jennifer Hodgdon
committed
* Returns all messages that have been set with drupal_set_message().
Jennifer Hodgdon
committed
* @param string $type
* (optional) Limit the messages returned by type. Defaults to NULL, meaning
* all types. These values are supported:
* - NULL
* - 'status'
* - 'warning'
* - 'error'
* @param bool $clear_queue
* (optional) If this is TRUE, the queue will be cleared of messages of the
* type specified in the $type parameter. Otherwise the queue will be left
* intact. Defaults to TRUE.
catch
committed
*
Jennifer Hodgdon
committed
* @return array
* An associative, nested array of messages grouped by message type, with
* the top-level keys as the message type. The messages returned are
* limited to the type specified in the $type parameter, if any. If there
* are no messages of the specified type, an empty array is returned. See
* drupal_set_message() for the array structure of individual messages.
Jennifer Hodgdon
committed
*
* @see drupal_set_message()
* @see status-messages.html.twig
function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
if ($messages = drupal_set_message()) {
foreach ($messages as $message_type => $message_typed_messages) {
foreach ($message_typed_messages as $key => $message) {
if ($message['safe']) {
$message['message'] = SafeMarkup::set($message['message']);
}
$messages[$message_type][$key] = $message['message'];
}
}
Dries Buytaert
committed
if ($type) {
if ($clear_queue) {
Gábor Hojtsy
committed
unset($_SESSION['messages'][$type]);
Dries Buytaert
committed
if (isset($messages[$type])) {
return array($type => $messages[$type]);
}
Dries Buytaert
committed
}
else {
if ($clear_queue) {
Gábor Hojtsy
committed
unset($_SESSION['messages']);
Dries Buytaert
committed
return $messages;
}
Dries Buytaert
committed
return array();
Steven Wittens
committed
}
/**
* Ensures Drupal is bootstrapped to the specified phase.
*
* In order to bootstrap Drupal from another PHP script, you can use this code:
* @code
* require_once '/path/to/drupal/core/vendor/autoload.php';
* require_once '/path/to/drupal/core/includes/bootstrap.inc';
* drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
* @endcode
*
* @param $phase
* A constant telling which phase to bootstrap to. Possible values:
Jennifer Hodgdon
committed
* - DRUPAL_BOOTSTRAP_CONFIGURATION: Initializes configuration.
* - DRUPAL_BOOTSTRAP_KERNEL: Initializes a kernel.
catch
committed
*
* @return int
Dries Buytaert
committed
* The most recently completed phase.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Interact directly with the kernel.
function drupal_bootstrap($phase = NULL) {
// Temporary variables used for booting later legacy phases.
/** @var \Drupal\Core\DrupalKernel $kernel */
static $kernel;
static $boot_level = 0;
Dries Buytaert
committed
if (isset($phase)) {
$request = Request::createFromGlobals();
for ($current_phase = $boot_level; $current_phase <= $phase; $current_phase++) {
Dries Buytaert
committed
switch ($current_phase) {
case DRUPAL_BOOTSTRAP_CONFIGURATION:
$classloader = require __DIR__ . '/../vendor/autoload.php';
$kernel = DrupalKernel::createFromRequest($request, $classloader, 'prod');
Dries Buytaert
committed
break;
case DRUPAL_BOOTSTRAP_KERNEL:
$kernel->boot();
break;
case DRUPAL_BOOTSTRAP_CODE:
Dries Buytaert
committed
case DRUPAL_BOOTSTRAP_FULL:
$kernel->prepareLegacyRequest($request);
Dries Buytaert
committed
break;
}
$boot_level = $phase;
}
return \Drupal::hasContainer() ? DRUPAL_BOOTSTRAP_CODE : DRUPAL_BOOTSTRAP_CONFIGURATION;
}
Dries Buytaert
committed
/**
catch
committed
* Returns the time zone of the current user.
Dries Buytaert
committed
*/
function drupal_get_user_timezone() {
global $user;
$config = \Drupal::config('system.date');
Angie Byron
committed
if ($user && $config->get('timezone.user.configurable') && $user->isAuthenticated() && $user->getTimezone()) {
return $user->getTimezone();
Dries Buytaert
committed
}
else {
// Ignore PHP strict notice if time zone has not yet been set in the php.ini
// configuration.
$config_data_default_timezone = $config->get('timezone.default');
Angie Byron
committed
return !empty($config_data_default_timezone) ? $config_data_default_timezone : @date_default_timezone_get();
Dries Buytaert
committed
}
}
Dries Buytaert
committed
/**
catch
committed
* Provides custom PHP error handling.
Dries Buytaert
committed
*
* @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
catch
committed
* An array that points to the active symbol table at the point the error
* occurred.
Dries Buytaert
committed
*/
function _drupal_error_handler($error_level, $message, $filename, $line, $context) {
require_once __DIR__ . '/errors.inc';
Dries Buytaert
committed
_drupal_error_handler_real($error_level, $message, $filename, $line, $context);
}
/**
catch
committed
* Provides custom PHP exception handling.
Dries Buytaert
committed
*
* 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 __DIR__ . '/errors.inc';
Angie Byron
committed
try {
// Log the message to the watchdog and return an error page to the user.
catch
committed
_drupal_log_error(Error::decodeException($exception), TRUE);
Angie Byron
committed
}
catch (Exception $exception2) {
// Another uncaught exception was thrown while handling the first one.
// If we are displaying errors, then do so with no possibility of a further uncaught exception being thrown.
Dries Buytaert
committed
if (error_displayable()) {
print '<h1>Additional uncaught exception thrown while handling exception.</h1>';
catch
committed
print '<h2>Original</h2><p>' . Error::renderExceptionSafe($exception) . '</p>';
print '<h2>Additional</h2><p>' . Error::renderExceptionSafe($exception2) . '</p><hr />';
Angie Byron
committed
}
}
Dries Buytaert
committed
}
Dries Buytaert
committed
/**
* Returns the current bootstrap phase for this Drupal process.
*
* The current phase is the one most recently completed by drupal_bootstrap().
*
* @see drupal_bootstrap()
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
Dries Buytaert
committed
*/
function drupal_get_bootstrap_phase() {
return drupal_bootstrap();
}
/**
* Returns the list of enabled modules.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal::moduleHandler()->getModuleList().
*
* @see \Drupal\Core\Extension\ModuleHandler::getModuleList()
*/
function module_list() {
$modules = array_keys(\Drupal::moduleHandler()->getModuleList());
return array_combine($modules, $modules);
}
/**
* Determines which modules are implementing a hook.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal::moduleHandler()->getImplementations($hook).
*
* @see \Drupal\Core\Extension\ModuleHandler::getImplementations()
*/
function module_implements($hook) {
return \Drupal::moduleHandler()->getImplementations($hook);
Dries Buytaert
committed
/**
catch
committed
* Returns the test prefix if this is an internal request from SimpleTest.
Dries Buytaert
committed
*
* @param string $new_prefix
* Internal use only. A new prefix to be stored.
*
* @return string|FALSE
Dries Buytaert
committed
* Either the simpletest prefix (the string "simpletest" followed by any
* number of digits) or FALSE if the user agent does not contain a valid
* HMAC and timestamp.
Dries Buytaert
committed
*/
function drupal_valid_test_ua($new_prefix = NULL) {
Dries Buytaert
committed
static $test_prefix;
if (isset($new_prefix)) {
$test_prefix = $new_prefix;
}
Dries Buytaert
committed
if (isset($test_prefix)) {
return $test_prefix;
}
// Unless the below User-Agent and HMAC validation succeeds, we are not in
// a test environment.
$test_prefix = FALSE;
Dries Buytaert
committed
// Perform a basic check on the User-Agent HTTP request header first. Any
// inbound request that uses the simpletest UA header needs to be validated.
Dries Buytaert
committed
if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^(simpletest\d+);(.+);(.+);(.+)$/", $_SERVER['HTTP_USER_AGENT'], $matches)) {
list(, $prefix, $time, $salt, $hmac) = $matches;
$check_string = $prefix . ';' . $time . ';' . $salt;
// Read the hash salt prepared by drupal_generate_test_ua().
// This function is called before settings.php is read and Drupal's error
// handlers are set up. While Drupal's error handling may be properly
// configured on production sites, the server's PHP error_reporting may not.
// Ensure that no information leaks on production sites.
$key_file = DRUPAL_ROOT . '/sites/simpletest/' . substr($prefix, 10) . '/.htkey';
if (!is_readable($key_file)) {
header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
exit;
}
$private_key = file_get_contents($key_file);
// The file properties add more entropy not easily accessible to others.
$key = $private_key . filectime(__FILE__) . fileinode(__FILE__);
Dries Buytaert
committed
$time_diff = REQUEST_TIME - $time;
$test_hmac = Crypt::hmacBase64($check_string, $key);
Dries Buytaert
committed
// Since we are making a local request a 5 second time window is allowed,
// and the HMAC must match.
if ($time_diff >= 0 && $time_diff <= 5 && $hmac === $test_hmac) {
Dries Buytaert
committed
$test_prefix = $prefix;
}
}
catch
committed
return $test_prefix;
Dries Buytaert
committed
}
/**
catch
committed
* Generates a user agent string with a HMAC and timestamp for simpletest.
Dries Buytaert
committed
*/
function drupal_generate_test_ua($prefix) {
static $key, $last_prefix;
if (!isset($key) || $last_prefix != $prefix) {
$last_prefix = $prefix;
$key_file = DRUPAL_ROOT . '/sites/simpletest/' . substr($prefix, 10) . '/.htkey';
// When issuing an outbound HTTP client request from within an inbound test
// request, then the outbound request has to use the same User-Agent header
// as the inbound request. A newly generated private key for the same test
// prefix would invalidate all subsequent inbound requests.
// @see \Drupal\Core\Http\Plugin\SimpletestHttpRequestSubscriber
if (DRUPAL_TEST_IN_CHILD_SITE && $parent_prefix = drupal_valid_test_ua()) {
if ($parent_prefix != $prefix) {
throw new \RuntimeException("Malformed User-Agent: Expected '$parent_prefix' but got '$prefix'.");
}
// If the file is not readable, a PHP warning is expected in this case.
$private_key = file_get_contents($key_file);
}
else {
// Generate and save a new hash salt for a test run.
// Consumed by drupal_valid_test_ua() before settings.php is loaded.
Angie Byron
committed
$private_key = Crypt::randomBytesBase64(55);
file_put_contents($key_file, $private_key);
}
// The file properties add more entropy not easily accessible to others.
$key = $private_key . filectime(__FILE__) . fileinode(__FILE__);
Dries Buytaert
committed
}
Dries Buytaert
committed
// Generate a moderately secure HMAC based on the database credentials.
$salt = uniqid('', TRUE);
$check_string = $prefix . ';' . time() . ';' . $salt;
Angie Byron
committed
return $check_string . ';' . Crypt::hmacBase64($check_string, $key);
Dries Buytaert
committed
}
* Enables use of the theme system without requiring database access.
*
* Loads and initializes the theme system for site installs, updates and when
* the site is in maintenance mode. This also applies when the database fails.
*
* @see _drupal_maintenance_theme()
*/
function drupal_maintenance_theme() {
require_once __DIR__ . '/theme.maintenance.inc';
_drupal_maintenance_theme();
Steven Wittens
committed
Dries Buytaert
committed
/**
catch
committed
* Returns TRUE if a Drupal installation is currently being attempted.
Dries Buytaert
committed
*/
function drupal_installation_attempted() {
// This cannot rely on the MAINTENANCE_MODE constant, since that would prevent
// tests from using the non-interactive installer, in which case Drupal
// only happens to be installed within the same request, but subsequently
// executed code does not involve the installer at all.
// @see install_drupal()
return isset($GLOBALS['install_state']) && empty($GLOBALS['install_state']['installation_finished']);
Dries Buytaert
committed
}
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
/**
* Gets the name of the currently active installation profile.
*
* When this function is called during Drupal's initial installation process,
* the name of the profile that's about to be installed is stored in the global
* installation state. At all other times, the "install_profile" setting will be
* available in settings.php.
*
* @return string|null $profile
* The name of the installation profile or NULL if no installation profile is
* currently active. This is the case for example during the first steps of
* the installer or during unit tests.
*/
function drupal_get_profile() {
global $install_state;
if (drupal_installation_attempted()) {
// If the profile has been selected return it.
if (isset($install_state['parameters']['profile'])) {
$profile = $install_state['parameters']['profile'];
}
else {
$profile = NULL;
}
}
else {
// Fall back to NULL, if there is no 'install_profile' setting.
$profile = Settings::get('install_profile');
}
return $profile;
}
/**
Dries Buytaert
committed
* Returns a list of languages set up on the site.
*
Dries Buytaert
committed
* @param $flags
* (optional) Specifies the state of the languages that have to be returned.
* It can be: LanguageInterface::STATE_CONFIGURABLE,
Angie Byron
committed
* LanguageInterface::STATE_LOCKED, or LanguageInterface::STATE_ALL.
Angie Byron
committed
*
Angie Byron
committed
* @return \Drupal\Core\Language\LanguageInterface[]
Dries Buytaert
committed
* An associative array of languages, keyed by the language code, ordered by