Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • rklusman2/UNL-CMS
  • yzha1/UNL-CMS
  • pear/UNL-CMS
  • bbieber2/UNL-CMS
  • tsteiner2/UNL-CMS
  • erasmussen2/UNL-CMS
  • UNL-Information-Services/UNL-CMS
7 results
Show changes
Showing
with 1553 additions and 928 deletions
<?php
/**
* @file
* Batch processing API for processes to run in multiple HTTP requests.
......@@ -21,6 +20,7 @@
* @param $id
* The ID of the batch to load. When a progressive batch is being processed,
* the relevant ID is found in $_REQUEST['id'].
*
* @return
* An array representing the batch, or FALSE if no batch was found.
*/
......@@ -36,7 +36,7 @@ function batch_load($id) {
}
/**
* State-based dispatcher for the batch processing page.
* Renders the batch processing page based on the current state of the batch.
*
* @see _batch_shutdown()
*/
......@@ -94,7 +94,7 @@ function _batch_page() {
}
/**
* Initialize the batch processing.
* Initializes the batch processing.
*
* JavaScript-enabled clients are identified by the 'has_js' cookie set in
* drupal.js. If no JavaScript-enabled page has been visited during the current
......@@ -110,7 +110,7 @@ function _batch_start() {
}
/**
* Output a batch processing page with JavaScript support.
* Outputs a batch processing page with JavaScript support.
*
* This initializes the batch and error messages. Note that in JavaScript-based
* processing, the batch processing page is displayed only once and updated via
......@@ -144,7 +144,7 @@ function _batch_progress_page_js() {
}
/**
* Do one execution pass in JavaScript-mode and return progress to the browser.
* Does one execution pass with JavaScript and returns progress to the browser.
*
* @see _batch_progress_page_js()
* @see _batch_process()
......@@ -164,7 +164,7 @@ function _batch_do() {
}
/**
* Output a batch processing page without JavaScript support.
* Outputs a batch processing page without JavaScript support.
*
* @see _batch_process()
*/
......@@ -228,7 +228,7 @@ function _batch_progress_page_nojs() {
}
/**
* Process sets in a batch.
* Processes sets in a batch.
*
* If the batch was marked for progressive execution (default), this executes as
* many operations in batch sets until an execution time of 1 second has been
......@@ -370,7 +370,7 @@ function _batch_process() {
}
/**
* Helper function for _batch_process(): returns the formatted percentage.
* Formats the percent completion for a batch set.
*
* @param $total
* The total number of operations.
......@@ -379,11 +379,14 @@ function _batch_process() {
* rather than an integer in the case of a multi-step operation that is not
* yet complete; in that case, the fractional part of $current represents the
* fraction of the operation that has been completed.
*
* @return
* The properly formatted percentage, as a string. We output percentages
* using the correct number of decimal places so that we never print "100%"
* until we are finished, but we also never print more decimal places than
* are meaningful.
*
* @see _batch_process()
*/
function _batch_api_percentage($total, $current) {
if (!$total || $total == $current) {
......@@ -410,7 +413,7 @@ function _batch_api_percentage($total, $current) {
}
/**
* Return the batch set being currently processed.
* Returns the batch set being currently processed.
*/
function &_batch_current_set() {
$batch = &batch_get();
......@@ -418,7 +421,7 @@ function &_batch_current_set() {
}
/**
* Retrieve the next set in a batch.
* Retrieves the next set in a batch.
*
* If there is a subsequent set in this batch, assign it as the new set to
* process and execute its form submit handler (if defined), which may add
......@@ -442,7 +445,7 @@ function _batch_next_set() {
}
/**
* End the batch processing.
* Ends the batch processing.
*
* Call the 'finished' callback of each batch set to allow custom handling of
* the results and resolve page redirection.
......@@ -521,7 +524,10 @@ function _batch_finished() {
}
/**
* Shutdown function; store the current batch data for the next request.
* Shutdown function: Stores the current batch data for the next request.
*
* @see _batch_page()
* @see drupal_register_shutdown_function()
*/
function _batch_shutdown() {
if ($batch = batch_get()) {
......@@ -531,4 +537,3 @@ function _batch_shutdown() {
->execute();
}
}
<?php
/**
* @file
* Queue handlers used by the Batch API.
*
* Those implementations:
* - ensure FIFO ordering,
* - let an item be repeatedly claimed until it is actually deleted (no notion
* of lease time or 'expire' date), to allow multipass operations.
* These implementations:
* - Ensure FIFO ordering.
* - Allow an item to be repeatedly claimed until it is actually deleted (no
* notion of lease time or 'expire' date), to allow multipass operations.
*/
/**
* Batch queue implementation.
* Defines a batch queue.
*
* Stale items from failed batches are cleaned from the {queue} table on cron
* using the 'created' date.
*/
class BatchQueue extends SystemQueue {
/**
* Overrides SystemQueue::claimItem().
*
* Unlike SystemQueue::claimItem(), this method provides a default lease
* time of 0 (no expiration) instead of 30. This allows the item to be
* claimed repeatedly until it is deleted.
*/
public function claimItem($lease_time = 0) {
$item = db_query_range('SELECT data, item_id FROM {queue} q WHERE name = :name ORDER BY item_id ASC', 0, 1, array(':name' => $this->name))->fetchObject();
if ($item) {
......@@ -29,9 +35,9 @@ class BatchQueue extends SystemQueue {
}
/**
* Retrieve all remaining items in the queue.
* Retrieves all remaining items in the queue.
*
* This is specific to Batch API and is not part of the DrupalQueueInterface,
* This is specific to Batch API and is not part of the DrupalQueueInterface.
*/
public function getAllItems() {
$result = array();
......@@ -44,10 +50,17 @@ class BatchQueue extends SystemQueue {
}
/**
* Batch queue implementation used for non-progressive batches.
* Defines a batch queue for non-progressive batches.
*/
class BatchMemoryQueue extends MemoryQueue {
/**
* Overrides MemoryQueue::claimItem().
*
* Unlike MemoryQueue::claimItem(), this method provides a default lease
* time of 0 (no expiration) instead of 30. This allows the item to be
* claimed repeatedly until it is deleted.
*/
public function claimItem($lease_time = 0) {
if (!empty($this->queue)) {
reset($this->queue);
......@@ -57,9 +70,9 @@ class BatchMemoryQueue extends MemoryQueue {
}
/**
* Retrieve all remaining items in the queue.
* Retrieves all remaining items in the queue.
*
* This is specific to Batch API and is not part of the DrupalQueueInterface,
* This is specific to Batch API and is not part of the DrupalQueueInterface.
*/
public function getAllItems() {
$result = array();
......
......@@ -8,7 +8,7 @@
/**
* The current system version.
*/
define('VERSION', '7.8');
define('VERSION', '7.22');
/**
* Core API compatibility.
......@@ -25,6 +25,21 @@ define('DRUPAL_MINIMUM_PHP', '5.2.4');
*/
define('DRUPAL_MINIMUM_PHP_MEMORY_LIMIT', '32M');
/**
* Error reporting level: display no errors.
*/
define('ERROR_REPORTING_HIDE', 0);
/**
* Error reporting level: display errors and warnings.
*/
define('ERROR_REPORTING_DISPLAY_SOME', 1);
/**
* Error reporting level: display all messages.
*/
define('ERROR_REPORTING_DISPLAY_ALL', 2);
/**
* Indicates that the item should never be removed unless explicitly selected.
*
......@@ -43,9 +58,9 @@ define('CACHE_TEMPORARY', -1);
* Logging severity levels as defined in RFC 3164.
*
* The WATCHDOG_* constant definitions correspond to the logging severity levels
* defined in RFC 3164, section 4.1.1. PHP supplies predefined LOG_* constants
* defined in RFC 3164, section 4.1.1. PHP supplies predefined LOG_* constants
* for use in the syslog() function, but their values on Windows builds do not
* correspond to RFC 3164. The associated PHP bug report was closed with the
* correspond to RFC 3164. The associated PHP bug report was closed with the
* comment, "And it's also not a bug, as Windows just have less log levels,"
* and "So the behavior you're seeing is perfectly normal."
*
......@@ -68,32 +83,32 @@ define('WATCHDOG_EMERGENCY', 0);
define('WATCHDOG_ALERT', 1);
/**
* Log message severity -- Critical: critical conditions.
* Log message severity -- Critical conditions.
*/
define('WATCHDOG_CRITICAL', 2);
/**
* Log message severity -- Error: error conditions.
* Log message severity -- Error conditions.
*/
define('WATCHDOG_ERROR', 3);
/**
* Log message severity -- Warning: warning conditions.
* Log message severity -- Warning conditions.
*/
define('WATCHDOG_WARNING', 4);
/**
* Log message severity -- Notice: normal but significant condition.
* Log message severity -- Normal but significant conditions.
*/
define('WATCHDOG_NOTICE', 5);
/**
* Log message severity -- Informational: informational messages.
* Log message severity -- Informational messages.
*/
define('WATCHDOG_INFO', 6);
/**
* Log message severity -- Debug: debug-level messages.
* Log message severity -- Debug-level messages.
*/
define('WATCHDOG_DEBUG', 7);
......@@ -137,8 +152,7 @@ define('DRUPAL_BOOTSTRAP_PAGE_HEADER', 5);
define('DRUPAL_BOOTSTRAP_LANGUAGE', 6);
/**
* Final bootstrap phase: Drupal is fully loaded; validate and fix
* input data.
* Final bootstrap phase: Drupal is fully loaded; validate and fix input data.
*/
define('DRUPAL_BOOTSTRAP_FULL', 7);
......@@ -153,8 +167,9 @@ define('DRUPAL_ANONYMOUS_RID', 1);
define('DRUPAL_AUTHENTICATED_RID', 2);
/**
* The number of bytes in a kilobyte. For more information, visit
* http://en.wikipedia.org/wiki/Kilobyte.
* The number of bytes in a kilobyte.
*
* For more information, visit http://en.wikipedia.org/wiki/Kilobyte.
*/
define('DRUPAL_KILOBYTE', 1024);
......@@ -191,10 +206,14 @@ define('LANGUAGE_LTR', 0);
define('LANGUAGE_RTL', 1);
/**
* For convenience, define a short form of the request time global.
* Time of the current request in seconds elapsed since the Unix Epoch.
*
* 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()).
*
* REQUEST_TIME is a float with microseconds since PHP 5.4.0, but float
* timestamps confuses most of the PHP functions (including date_create()).
* @see http://php.net/manual/reserved.variables.server.php
* @see http://php.net/manual/function.time.php
*/
define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
......@@ -275,7 +294,7 @@ define('DRUPAL_PHP_FUNCTION_PATTERN', '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'
* means that assigning an offset via arrayAccess will only apply while the
* object is in scope and will not be written back to the persistent cache.
* This follows a similar pattern to static vs. persistent caching in
* procedural code. Extending classes may wish to alter this behaviour, for
* procedural code. Extending classes may wish to alter this behavior, for
* example by overriding offsetSet() and adding an automatic call to persist().
*
* @see SchemaCache
......@@ -285,12 +304,12 @@ abstract class DrupalCacheArray implements ArrayAccess {
/**
* A cid to pass to cache_set() and cache_get().
*/
private $cid;
protected $cid;
/**
* A bin to pass to cache_set() and cache_get().
*/
private $bin;
protected $bin;
/**
* An array of keys to add to the cache at the end of the request.
......@@ -303,7 +322,7 @@ abstract class DrupalCacheArray implements ArrayAccess {
protected $storage = array();
/**
* Constructor.
* Constructs a DrupalCacheArray object.
*
* @param $cid
* The cid for the array being cached.
......@@ -319,10 +338,16 @@ abstract class DrupalCacheArray implements ArrayAccess {
}
}
/**
* Implements ArrayAccess::offsetExists().
*/
public function offsetExists($offset) {
return $this->offsetGet($offset) !== NULL;
}
/**
* Implements ArrayAccess::offsetGet().
*/
public function offsetGet($offset) {
if (isset($this->storage[$offset]) || array_key_exists($offset, $this->storage)) {
return $this->storage[$offset];
......@@ -332,10 +357,16 @@ abstract class DrupalCacheArray implements ArrayAccess {
}
}
/**
* Implements ArrayAccess::offsetSet().
*/
public function offsetSet($offset, $value) {
$this->storage[$offset] = $value;
}
/**
* Implements ArrayAccess::offsetUnset().
*/
public function offsetUnset($offset) {
unset($this->storage[$offset]);
}
......@@ -375,32 +406,31 @@ abstract class DrupalCacheArray implements ArrayAccess {
abstract protected function resolveCacheMiss($offset);
/**
* Immediately write a value to the persistent cache.
* Writes a value to the persistent cache immediately.
*
* @param $cid
* The cache ID.
* @param $bin
* The cache bin.
* @param $data
* The data to write to the persistent cache.
* @param $lock
* Whether to acquire a lock before writing to cache.
*/
protected function set($cid, $data, $bin, $lock = TRUE) {
protected function set($data, $lock = TRUE) {
// Lock cache writes to help avoid stampedes.
// To implement locking for cache misses, override __construct().
$lock_name = $cid . ':' . $bin;
$lock_name = $this->cid . ':' . $this->bin;
if (!$lock || lock_acquire($lock_name)) {
if ($cached = cache_get($cid, $bin)) {
if ($cached = cache_get($this->cid, $this->bin)) {
$data = $cached->data + $data;
}
cache_set($cid, $data, $bin);
cache_set($this->cid, $data, $this->bin);
if ($lock) {
lock_release($lock_name);
}
}
}
/**
* Destructs the DrupalCacheArray object.
*/
public function __destruct() {
$data = array();
foreach ($this->keysToPersist as $offset => $persist) {
......@@ -409,16 +439,18 @@ abstract class DrupalCacheArray implements ArrayAccess {
}
}
if (!empty($data)) {
$this->set($this->cid, $data, $this->bin);
$this->set($data);
}
}
}
/**
* Start the timer with the specified name. If you start and stop the same
* timer multiple times, the measured intervals will be accumulated.
* Starts the timer with the specified name.
*
* @param name
* If you start and stop the same timer multiple times, the measured intervals
* will be accumulated.
*
* @param $name
* The name of the timer.
*/
function timer_start($name) {
......@@ -429,10 +461,11 @@ function timer_start($name) {
}
/**
* Read the current timer value without stopping the timer.
* Reads the current timer value without stopping the timer.
*
* @param name
* @param $name
* The name of the timer.
*
* @return
* The current timer value in ms.
*/
......@@ -452,10 +485,11 @@ function timer_read($name) {
}
/**
* Stop the timer with the specified name.
* Stops the timer with the specified name.
*
* @param name
* @param $name
* The name of the timer.
*
* @return
* 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).
......@@ -479,56 +513,12 @@ function timer_stop($name) {
}
/**
* Finds the appropriate configuration directory.
*
* Finds a matching configuration directory by stripping the website's
* hostname from left to right and pathname from right to left. The first
* configuration file found will be used and the remaining ones will be ignored.
* If no configuration file is found, return a default value '$confdir/default'.
*
* With a site located at http://www.example.com:8080/mysite/test/, the file,
* settings.php, is searched for in the following directories:
*
* 1. $confdir/8080.www.example.com.mysite.test
* 2. $confdir/www.example.com.mysite.test
* 3. $confdir/example.com.mysite.test
* 4. $confdir/com.mysite.test
*
* 5. $confdir/8080.www.example.com.mysite
* 6. $confdir/www.example.com.mysite
* 7. $confdir/example.com.mysite
* 8. $confdir/com.mysite
* Returns the appropriate configuration directory.
*
* 9. $confdir/8080.www.example.com
* 10. $confdir/www.example.com
* 11. $confdir/example.com
* 12. $confdir/com
*
* 13. $confdir/default
*
* If a file named sites.php is present in the $confdir, it will be loaded
* prior to scanning for directories. It should define an associative array
* named $sites, which maps domains to directories. It should be in the form
* of:
* @code
* $sites = array(
* 'The url to alias' => 'A directory within the sites directory'
* );
* @endcode
* For example:
* @code
* $sites = array(
* 'devexample.com' => 'example.com',
* 'localhost.example' => 'example.com',
* );
* @endcode
* 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
* domain of the live server. Since Drupal stores file paths into the database
* (files, system table, etc.) this will ensure the paths are correct while
* accessed on development servers.
* Returns the configuration path based on the site's hostname, port, and
* pathname. Uses find_conf_path() to find the current configuration directory.
* See default.settings.php for examples on how the URL is converted to a
* directory.
*
* @param bool $require_settings
* Only configuration directories with an existing settings.php file
......@@ -541,6 +531,8 @@ function timer_stop($name) {
*
* @return
* The path of the matching directory.
*
* @see default.settings.php
*/
function conf_path($require_settings = TRUE, $reset = FALSE) {
$conf = &drupal_static(__FUNCTION__, '');
......@@ -594,7 +586,7 @@ function conf_path($require_settings = TRUE, $reset = FALSE) {
}
/**
* Set appropriate server variables needed for command line scripts to work.
* Sets 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.
......@@ -656,7 +648,7 @@ function drupal_override_server_variables($variables = array()) {
}
/**
* Initialize PHP environment.
* Initializes the PHP environment.
*/
function drupal_environment_initialize() {
if (!isset($_SERVER['HTTP_REFERER'])) {
......@@ -715,7 +707,7 @@ function drupal_environment_initialize() {
}
/**
* Validate that a hostname (for example $_SERVER['HTTP_HOST']) is safe.
* Validates that a hostname (for example $_SERVER['HTTP_HOST']) is safe.
*
* @return
* TRUE if only containing valid characters, or FALSE otherwise.
......@@ -725,13 +717,12 @@ function drupal_valid_http_host($host) {
}
/**
* Loads the configuration and sets the base URL, cookie domain, and
* session name correctly.
* Sets the base URL, cookie domain, and session name from configuration.
*/
function drupal_settings_initialize() {
global $base_url, $base_path, $base_root;
// Export the following settings.php variables to the global namespace
// Export these settings.php variables to the global namespace.
global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url;
$conf = array();
......@@ -749,7 +740,6 @@ function drupal_settings_initialize() {
if (isset($base_url)) {
// Parse fixed base URL from settings.php.
$parts = parse_url($base_url);
$http_protocol = $parts['scheme'];
if (!isset($parts['path'])) {
$parts['path'] = '';
}
......@@ -758,7 +748,7 @@ function drupal_settings_initialize() {
$base_root = substr($base_url, 0, strlen($base_url) - strlen($parts['path']));
}
else {
// Create base URL
// Create base URL.
$http_protocol = $is_https ? 'https' : 'http';
$base_root = $http_protocol . '://' . $_SERVER['HTTP_HOST'];
......@@ -784,7 +774,7 @@ function drupal_settings_initialize() {
}
else {
// Otherwise use $base_url as session name, without the protocol
// to use the same session identifiers across http and https.
// to use the same session identifiers across HTTP and HTTPS.
list( , $session_name) = explode('://', $base_url, 2);
// HTTP_HOST can be modified by a visitor, but we already sanitized it
// in drupal_settings_initialize().
......@@ -818,9 +808,10 @@ function drupal_settings_initialize() {
}
/**
* Returns and optionally sets the filename for a system item (module,
* theme, etc.). The filename, whether provided, cached, or retrieved
* from the database, is only returned if the file exists.
* 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
......@@ -843,13 +834,18 @@ function drupal_settings_initialize() {
* than by consulting the database.
*
* @return
* The filename of the requested item.
* 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().
static $files = array();
static $files = array(), $dirs = array();
// Profiles are a special case: they have a fixed location and naming.
if ($type == 'profile') {
$profile_filename = "profiles/$name/$name.profile";
$files[$type][$name] = file_exists($profile_filename) ? $profile_filename : FALSE;
}
if (!isset($files[$type])) {
$files[$type] = array();
}
......@@ -894,16 +890,19 @@ function drupal_get_filename($type, $name, $filename = NULL) {
$extension = $type;
}
if (!function_exists('drupal_system_listing')) {
require_once DRUPAL_ROOT . '/includes/common.inc';
}
// 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("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0);
foreach ($matches as $matched_name => $file) {
$files[$type][$matched_name] = $file->uri;
if (!isset($dirs[$dir][$extension])) {
$dirs[$dir][$extension] = TRUE;
if (!function_exists('drupal_system_listing')) {
require_once DRUPAL_ROOT . '/includes/common.inc';
}
// 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("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0);
foreach ($matches as $matched_name => $file) {
$files[$type][$matched_name] = $file->uri;
}
}
}
}
......@@ -914,11 +913,11 @@ function drupal_get_filename($type, $name, $filename = NULL) {
}
/**
* Load the persistent variable table.
* Loads 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.
* with variable_set() as well as those explicitly specified in the
* configuration file.
*/
function variable_initialize($conf = array()) {
// NOTE: caching the variables improves performance by 20% when serving
......@@ -963,7 +962,7 @@ function variable_initialize($conf = array()) {
* The default value to use if this variable has never been set.
*
* @return
* The value of the variable.
* The value of the variable. Unserialization is taken care of as necessary.
*
* @see variable_del()
* @see variable_set()
......@@ -1025,7 +1024,7 @@ function variable_del($name) {
}
/**
* Retrieve the current page from the cache.
* Retrieves the current page from the cache.
*
* Note: we do not serve cached pages to authenticated users, or to anonymous
* users when $_SESSION is non-empty. $_SESSION may contain status messages
......@@ -1057,7 +1056,7 @@ function drupal_page_get_cache($check_only = FALSE) {
}
/**
* Determine the cacheability of the current page.
* Determines the cacheability of the current page.
*
* @param $allow_caching
* Set to FALSE if you want to prevent this page to get cached.
......@@ -1076,7 +1075,7 @@ function drupal_page_is_cacheable($allow_caching = NULL) {
}
/**
* Invoke a bootstrap hook in all bootstrap modules that implement it.
* Invokes a bootstrap hook in all bootstrap modules that implement it.
*
* @param $hook
* The name of the bootstrap hook to invoke.
......@@ -1098,8 +1097,9 @@ function bootstrap_invoke_all($hook) {
}
/**
* Includes a file with the provided type and name. This prevents
* including a theme, engine, module, etc., more than once.
* Includes a file with the provided type and name.
*
* This prevents including a theme, engine, module, etc., more than once.
*
* @param $type
* The type of item to load (i.e. theme, theme_engine, module).
......@@ -1131,7 +1131,7 @@ function drupal_load($type, $name) {
}
/**
* Set an HTTP response header for the current page.
* Sets 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).
......@@ -1167,11 +1167,12 @@ function drupal_add_http_header($name, $value, $append = FALSE) {
}
/**
* Get the HTTP response headers for the current page.
* Gets 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.
......@@ -1188,6 +1189,8 @@ function drupal_get_http_header($name = NULL) {
}
/**
* Sets the preferred name for the HTTP header.
*
* Header names are case-insensitive, but for maximum compatibility they should
* follow "common form" (see RFC 2617, section 4.2).
*/
......@@ -1201,14 +1204,16 @@ function _drupal_set_preferred_header_name($name = NULL) {
}
/**
* Send the HTTP response headers previously set using drupal_add_http_header().
* Add default headers, unless they have been replaced or unset using
* drupal_add_http_header().
* Sends the HTTP response headers that were previously set, adding defaults.
*
* Headers are set in drupal_add_http_header(). Default headers are not set
* if they have been replaced or unset using drupal_add_http_header().
*
* @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.
* @param array $default_headers
* (optional) An array of headers as name/value pairs.
* @param bool $only_default
* (optional) If TRUE and headers have already been sent, send only the
* specified headers.
*/
function drupal_send_headers($default_headers = array(), $only_default = FALSE) {
$headers_sent = &drupal_static(__FUNCTION__, FALSE);
......@@ -1231,14 +1236,14 @@ function drupal_send_headers($default_headers = array(), $only_default = FALSE)
header($_SERVER['SERVER_PROTOCOL'] . ' ' . $value);
}
// Skip headers that have been unset.
elseif ($value) {
elseif ($value !== FALSE) {
header($header_names[$name_lower] . ': ' . $value);
}
}
}
/**
* Set HTTP headers in preparation for a page response.
* Sets HTTP headers in preparation for a page response.
*
* Authenticated users are always given a 'no-cache' header, and will fetch a
* fresh page on every request. This prevents authenticated users from seeing
......@@ -1281,7 +1286,7 @@ function drupal_page_header() {
}
/**
* Set HTTP headers in preparation for a cached page response.
* Sets HTTP headers in preparation for a cached page response.
*
* The headers allow as much as possible in proxies and browsers without any
* particular knowledge about the pages. Modules can override these headers
......@@ -1360,7 +1365,9 @@ function drupal_serve_page_from_cache(stdClass $cache) {
// 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');
// UNL Change!
drupal_add_http_header('Vary', 'Cookie', TRUE);
// End UNL Change!
}
if ($page_compression) {
......@@ -1384,7 +1391,7 @@ function drupal_serve_page_from_cache(stdClass $cache) {
}
/**
* Define the critical hooks that force modules to always be loaded.
* Defines the critical hooks that force modules to always be loaded.
*/
function bootstrap_hooks() {
return array('boot', 'exit', 'watchdog', 'language_init');
......@@ -1415,10 +1422,11 @@ function drupal_unpack($obj, $field = 'data') {
* 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
* inside t() calls is added to the database of strings to be translated. So,
* 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
* 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.
......@@ -1436,10 +1444,11 @@ function drupal_unpack($obj, $field = 'data') {
* $text = t("@name's blog", array('@name' => format_username($account)));
* @endcode
* Basically, you can put variables like @name into your string, and t() will
* substitute their sanitized values at translation time (see $args below or
* the Localization API pages referenced above for details). Translators can
* then rearrange the string as necessary for the language (e.g., in Spanish,
* it might be "blog de @name").
* substitute their sanitized values at translation time. (See the
* Localization API pages referenced above and the documentation of
* 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").
*
* During the Drupal installation phase, some resources used by t() wil not be
* available to code that needs localization. See st() and get_t() for
......@@ -1448,16 +1457,9 @@ function drupal_unpack($obj, $field = 'data') {
* @param $string
* A string containing the English string to translate.
* @param $args
* An associative array of replacements to make after translation.
* Occurrences in $string of any key in $args are replaced with the
* corresponding value, after sanitization. The sanitization function depends
* on the first character of the key:
* - !variable: Inserted as is. Use this for text that has already been
* sanitized.
* - @variable: Escaped to HTML using check_plain(). Use this for anything
* displayed on a page on the site.
* - %variable: Escaped as a placeholder for user-submitted content using
* drupal_placeholder(), which shows up as <em>emphasized</em> text.
* 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.
* @param $options
* An associative array of additional options, with the following elements:
* - 'langcode' (defaults to the current language): The language code to
......@@ -1470,6 +1472,7 @@ function drupal_unpack($obj, $field = 'data') {
*
* @see st()
* @see get_t()
* @see format_string()
* @ingroup sanitization
*/
function t($string, array $args = array(), array $options = array()) {
......@@ -1503,30 +1506,67 @@ function t($string, array $args = array(), array $options = array()) {
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;
return format_string($string, $args);
}
}
case '%':
default:
// Escaped and placeholder.
$args[$key] = drupal_placeholder($value);
break;
/**
* Formats a string for HTML display by replacing variable placeholders.
*
* This function replaces variable placeholders in a string with the requested
* values and escapes the values so they can be safely displayed as HTML. It
* should be used on any unknown text that is intended to be printed to an HTML
* page (especially text that may have come from untrusted users, since in that
* case it prevents cross-site scripting and other security problems).
*
* In most cases, you should use t() rather than calling this function
* directly, since it will translate the text (on non-English-only sites) in
* addition to formatting it.
*
* @param $string
* A string containing placeholders.
* @param $args
* An associative array of replacements to make. Occurrences in $string of
* any key in $args are replaced with the corresponding value, after optional
* sanitization and formatting. The type of sanitization and formatting
* depends on the first character of the key:
* - @variable: Escaped to HTML using check_plain(). Use this as the default
* choice for anything displayed on a page on the site.
* - %variable: Escaped to HTML and formatted using drupal_placeholder(),
* which makes it display as <em>emphasized</em> text.
* - !variable: Inserted as is, with no sanitization or formatting. Only use
* this for text that has already been prepared for HTML display (for
* example, user-supplied text that has already been run through
* check_plain() previously, or is expected to contain some limited HTML
* tags and has already been run through filter_xss() previously).
*
* @see t()
* @ingroup sanitization
*/
function format_string($string, array $args = array()) {
// Transform arguments before inserting them.
foreach ($args as $key => $value) {
switch ($key[0]) {
case '@':
// Escaped only.
$args[$key] = check_plain($value);
break;
case '!':
// Pass-through.
}
case '%':
default:
// Escaped and placeholder.
$args[$key] = drupal_placeholder($value);
break;
case '!':
// Pass-through.
}
return strtr($string, $args);
}
return strtr($string, $args);
}
/**
* Encode special characters in a plain-text string for display as HTML.
* Encodes special characters in a plain-text string for display as HTML.
*
* Also validates strings as UTF-8 to prevent cross site scripting attacks on
* Internet Explorer 6.
......@@ -1565,6 +1605,7 @@ function check_plain($text) {
*
* @param $text
* The text to check.
*
* @return
* TRUE if the text is valid UTF-8, FALSE if not.
*/
......@@ -1579,11 +1620,12 @@ function drupal_validate_utf8($text) {
}
/**
* Since $_SERVER['REQUEST_URI'] is only available on Apache, we
* generate an equivalent using other environment variables.
* 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.
*/
function request_uri() {
if (isset($_SERVER['REQUEST_URI'])) {
$uri = $_SERVER['REQUEST_URI'];
}
......@@ -1605,7 +1647,7 @@ function request_uri() {
}
/**
* Log an exception.
* Logs an exception.
*
* This is a wrapper function for watchdog() which automatically decodes an
* exception.
......@@ -1646,7 +1688,7 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia
}
/**
* Log a system message.
* Logs a system message.
*
* @param $type
* The category to which this message belongs. Can be any string, but the
......@@ -1662,8 +1704,16 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia
* NULL if message is already translated or not possible to
* translate.
* @param $severity
* The severity of the message, as per RFC 3164. Possible values are
* WATCHDOG_ERROR, WATCHDOG_WARNING, etc.
* The severity of the message; one of the following values as defined in
* @link http://www.faqs.org/rfcs/rfc3164.html RFC 3164: @endlink
* - WATCHDOG_EMERGENCY: Emergency, system is unusable.
* - WATCHDOG_ALERT: Alert, action must be taken immediately.
* - WATCHDOG_CRITICAL: Critical conditions.
* - WATCHDOG_ERROR: Error conditions.
* - WATCHDOG_WARNING: Warning conditions.
* - WATCHDOG_NOTICE: (default) Normal but significant conditions.
* - WATCHDOG_INFO: Informational messages.
* - WATCHDOG_DEBUG: Debug-level messages.
* @param $link
* A link to associate with the message.
*
......@@ -1680,6 +1730,9 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO
if (!$in_error_state && function_exists('module_implements')) {
$in_error_state = TRUE;
// The user object may not exist in all conditions, so 0 is substituted if needed.
$user_uid = isset($user->uid) ? $user->uid : 0;
// Prepare the fields to be logged
$log_entry = array(
'type' => $type,
......@@ -1688,10 +1741,12 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO
'severity' => $severity,
'link' => $link,
'user' => $user,
'uid' => $user_uid,
'request_uri' => $base_root . request_uri(),
'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '',
'ip' => ip_address(),
'timestamp' => REQUEST_TIME,
// Request time isn't accurate for long processes, use time() instead.
'timestamp' => time(),
);
// Call the logging hooks to log/process the message
......@@ -1706,22 +1761,37 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO
}
/**
* Set a message which reflects the status of the performed operation.
* Sets a message to display to the user.
*
* If the function is called with no arguments, this function returns all set
* messages without clearing them.
* Messages are stored in a session variable and displayed in page.tpl.php via
* the $messages theme variable.
*
* @param $message
* The 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 $type
* The type of the message. One of the following values are possible:
* 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'
* - 'warning'
* - 'error'
* @param $repeat
* If this is FALSE and the message is already set, then the message won't
* be repeated.
* @param bool $repeat
* (optional) If this is FALSE and the message is already set, then the
* message won't be repeated. Defaults to TRUE.
*
* @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.
* Or, if there are no messages set, the function returns NULL.
*
* @see drupal_get_messages()
* @see theme_status_messages()
*/
function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
if ($message) {
......@@ -1742,17 +1812,29 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
}
/**
* Return all messages that have been set.
* Returns all messages that have been set with drupal_set_message().
*
* @param $type
* (optional) Only return messages of this type.
* @param $clear_queue
* (optional) Set to FALSE if you do not want to clear the messages queue
* @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.
* @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.
*
* @return array
* A multidimensional array with keys corresponding to the set message types.
* The indexed array values of each contain the set messages for that type.
* The messages returned are limited to the type specified in the $type
* parameter. If there are no messages of the specified type, an empty array
* is returned.
*
* @see drupal_set_message()
* @see theme_status_messages()
*/
function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
if ($messages = drupal_set_message()) {
......@@ -1775,7 +1857,9 @@ function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
}
/**
* Get the title of the current page, for display on the page and in the title bar.
* Gets the title of the current page.
*
* The title is displayed on the page and in the title bar.
*
* @return
* The current page's title.
......@@ -1792,7 +1876,9 @@ function drupal_get_title() {
}
/**
* Set the title of the current page, for display on the page and in the title bar.
* Sets the title of the current page.
*
* The title is displayed on the page and in the title bar.
*
* @param $title
* Optional string value to assign to the page title; or if set to NULL
......@@ -1817,7 +1903,7 @@ function drupal_set_title($title = NULL, $output = CHECK_PLAIN) {
}
/**
* Check to see if an IP address has been blocked.
* Checks 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
......@@ -1826,6 +1912,7 @@ function drupal_set_title($title = NULL, $output = CHECK_PLAIN) {
*
* @param $ip
* IP address to check.
*
* @return bool
* TRUE if access is denied, FALSE if access is allowed.
*/
......@@ -1851,7 +1938,7 @@ function drupal_is_denied($ip) {
}
/**
* Handle denied users.
* Handles denied users.
*
* @param $ip
* IP address to check. Prints a message and exits if access is denied.
......@@ -1870,14 +1957,15 @@ function drupal_block_denied($ip) {
*
* This function is better than simply calling mt_rand() or any other built-in
* PHP function because it can return a long string of bytes (compared to < 4
* bytes normally from mt_rand()) and uses the best available pseudo-random source.
* bytes normally from mt_rand()) and uses the best available pseudo-random
* source.
*
* @param $count
* The number of characters (bytes) to return in the string.
*/
function drupal_random_bytes($count) {
// $random_state does not use drupal_static as it stores random bytes.
static $random_state, $bytes;
static $random_state, $bytes, $php_compatible;
// Initialize on the first call. The contents of $_SERVER includes a mix of
// user-specific and system information that varies a little with each page.
if (!isset($random_state)) {
......@@ -1889,6 +1977,11 @@ function drupal_random_bytes($count) {
$bytes = '';
}
if (strlen($bytes) < $count) {
// PHP versions prior 5.3.4 experienced openssl_random_pseudo_bytes()
// locking on Windows and rendered it unusable.
if (!isset($php_compatible)) {
$php_compatible = version_compare(PHP_VERSION, '5.3.4', '>=');
}
// /dev/urandom is available on many *nix systems and is considered the
// best commonly available pseudo-random source.
if ($fh = @fopen('/dev/urandom', 'rb')) {
......@@ -1898,6 +1991,11 @@ function drupal_random_bytes($count) {
$bytes .= fread($fh, max(4096, $count));
fclose($fh);
}
// openssl_random_pseudo_bytes() will find entropy in a system-dependent
// way.
elseif ($php_compatible && function_exists('openssl_random_pseudo_bytes')) {
$bytes .= openssl_random_pseudo_bytes($count - strlen($bytes));
}
// If /dev/urandom is not available or returns no bytes, this loop will
// generate a good set of pseudo-random bytes on any system.
// Note that it may be important that our $random_state is passed
......@@ -1917,7 +2015,7 @@ function drupal_random_bytes($count) {
}
/**
* Calculate a base-64 encoded, URL-safe sha-256 hmac.
* Calculates a base-64 encoded, URL-safe sha-256 hmac.
*
* @param $data
* String to be validated with the hmac.
......@@ -1935,7 +2033,7 @@ function drupal_hmac_base64($data, $key) {
}
/**
* Calculate a base-64 encoded, URL-safe sha-256 hash.
* Calculates a base-64 encoded, URL-safe sha-256 hash.
*
* @param $data
* String to be hashed.
......@@ -1978,7 +2076,8 @@ function drupal_hash_base64($data) {
* @see drupal_array_merge_deep_array()
*/
function drupal_array_merge_deep() {
return drupal_array_merge_deep_array(func_get_args());
$args = func_get_args();
return drupal_array_merge_deep_array($args);
}
/**
......@@ -2039,20 +2138,34 @@ function drupal_anonymous_user() {
}
/**
* 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
* phases too. The most important usage is that if you want to access the
* Drupal database from a script without loading anything else, you can
* include bootstrap.inc, and call drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE).
* Ensures Drupal is bootstrapped to the specified phase.
*
* In order to bootstrap Drupal from another PHP script, you can use this code:
* @code
* define('DRUPAL_ROOT', '/path/to/drupal');
* require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
* drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
* @endcode
*
* @param $phase
* A constant. Allowed values are the DRUPAL_BOOTSTRAP_* constants.
* A constant telling which phase to bootstrap to. When you bootstrap to a
* particular phase, all earlier phases are run automatically. Possible
* values:
* - DRUPAL_BOOTSTRAP_CONFIGURATION: Initializes configuration.
* - DRUPAL_BOOTSTRAP_PAGE_CACHE: Tries to serve a cached page.
* - DRUPAL_BOOTSTRAP_DATABASE: Initializes the database layer.
* - DRUPAL_BOOTSTRAP_VARIABLES: Initializes the variable system.
* - DRUPAL_BOOTSTRAP_SESSION: Initializes session handling.
* - DRUPAL_BOOTSTRAP_PAGE_HEADER: Sets up the page header.
* - DRUPAL_BOOTSTRAP_LANGUAGE: Finds out the language of the page.
* - DRUPAL_BOOTSTRAP_FULL: Fully loads Drupal. Validates and fixes input
* data.
* @param $new_phase
* A boolean, set to FALSE if calling drupal_bootstrap from inside a
* function called from drupal_bootstrap (recursion).
*
* @return
* The most recently completed phase.
*
*/
function drupal_bootstrap($phase = NULL, $new_phase = TRUE) {
// Not drupal_static(), because does not depend on any run-time information.
......@@ -2131,7 +2244,7 @@ function drupal_bootstrap($phase = NULL, $new_phase = TRUE) {
}
/**
* Return the time zone of the current user.
* Returns the time zone of the current user.
*/
function drupal_get_user_timezone() {
global $user;
......@@ -2146,7 +2259,7 @@ function drupal_get_user_timezone() {
}
/**
* Custom PHP error handler.
* Provides custom PHP error handling.
*
* @param $error_level
* The level of the error raised.
......@@ -2157,7 +2270,8 @@ function drupal_get_user_timezone() {
* @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.
* 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';
......@@ -2165,7 +2279,7 @@ function _drupal_error_handler($error_level, $message, $filename, $line, $contex
}
/**
* Custom PHP exception handler.
* Provides custom PHP exception handling.
*
* 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
......@@ -2193,7 +2307,7 @@ function _drupal_exception_handler($exception) {
}
/**
* Bootstrap configuration: Setup script environment and load settings.php.
* Sets up the script environment and loads settings.php.
*/
function _drupal_bootstrap_configuration() {
// Set the Drupal custom error handler.
......@@ -2208,7 +2322,7 @@ function _drupal_bootstrap_configuration() {
}
/**
* Bootstrap page cache: Try to serve a page from cache.
* Attempts to serve a page from the cache.
*/
function _drupal_bootstrap_page_cache() {
global $user;
......@@ -2264,7 +2378,7 @@ function _drupal_bootstrap_page_cache() {
}
/**
* Bootstrap database: Initialize database system and register autoload functions.
* Initializes the database system and registers autoload functions.
*/
function _drupal_bootstrap_database() {
// Redirect the user to the installation script if Drupal has not been
......@@ -2316,7 +2430,7 @@ function _drupal_bootstrap_database() {
}
/**
* Bootstrap variables: Load system variables and all enabled bootstrap modules.
* Loads system variables and all enabled bootstrap modules.
*/
function _drupal_bootstrap_variables() {
global $conf;
......@@ -2333,7 +2447,7 @@ function _drupal_bootstrap_variables() {
}
/**
* Bootstrap page header: Invoke hook_boot(), initialize locking system, and send default HTTP headers.
* Invokes hook_boot(), initializes locking system, and sends HTTP headers.
*/
function _drupal_bootstrap_page_header() {
bootstrap_invoke_all('boot');
......@@ -2356,8 +2470,7 @@ function drupal_get_bootstrap_phase() {
}
/**
* Checks the current User-Agent string to see if this is an internal request
* from SimpleTest. If so, returns the test prefix for this test.
* Returns the test prefix if this is an internal request from SimpleTest.
*
* @return
* Either the simpletest prefix (the string "simpletest" followed by any
......@@ -2389,11 +2502,12 @@ function drupal_valid_test_ua() {
}
}
return FALSE;
$test_prefix = FALSE;
return $test_prefix;
}
/**
* Generate a user agent string with a HMAC and timestamp for simpletest.
* Generates a user agent string with a HMAC and timestamp for simpletest.
*/
function drupal_generate_test_ua($prefix) {
global $drupal_hash_salt;
......@@ -2425,7 +2539,35 @@ function drupal_maintenance_theme() {
}
/**
* Return TRUE if a Drupal installation is currently being attempted.
* Returns a simple 404 Not Found page.
*
* If fast 404 pages are enabled, and this is a matching page then print a
* simple 404 page and exit.
*
* This function is called from drupal_deliver_html_page() at the time when a
* a normal 404 page is generated, but it can also optionally be called directly
* from settings.php to prevent a Drupal bootstrap on these pages. See
* documentation in settings.php for the benefits and drawbacks of using this.
*
* Paths to dynamically-generated content, such as image styles, should also be
* accounted for in this function.
*/
function drupal_fast_404() {
$exclude_paths = variable_get('404_fast_paths_exclude', FALSE);
if ($exclude_paths && !preg_match($exclude_paths, $_GET['q'])) {
$fast_paths = variable_get('404_fast_paths', FALSE);
if ($fast_paths && preg_match($fast_paths, $_GET['q'])) {
drupal_add_http_header('Status', '404 Not Found');
$fast_404_html = variable_get('404_fast_html', '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>');
// Replace @path in the variable with the page path.
print strtr($fast_404_html, array('@path' => check_plain(request_uri())));
exit;
}
}
}
/**
* Returns TRUE if a Drupal installation is currently being attempted.
*/
function drupal_installation_attempted() {
return defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install';
......@@ -2443,7 +2585,7 @@ function drupal_installation_attempted() {
* non-installation time, such as while installing the module from the the
* module administration page.
*
* Example useage:
* Example usage:
* @code
* $t = get_t();
* $translated = $t('translate this');
......@@ -2468,7 +2610,7 @@ function get_t() {
}
/**
* Initialize all the defined language types.
* Initializes all the defined language types.
*/
function drupal_language_initialize() {
$types = language_types();
......@@ -2493,7 +2635,7 @@ function drupal_language_initialize() {
}
/**
* The built-in language types.
* Returns a list of the built-in language types.
*
* @return
* An array of key-values pairs where the key is the language type and the
......@@ -2508,7 +2650,10 @@ function drupal_language_types() {
}
/**
* Return true if there is more than one language enabled.
* Returns TRUE if there is more than one language enabled.
*
* @return
* TRUE if more than one language is enabled.
*/
function drupal_multilingual() {
// The "language_count" variable stores the number of enabled languages to
......@@ -2518,16 +2663,29 @@ function drupal_multilingual() {
}
/**
* Return an array of the available language types.
* Returns an array of the available language types.
*
* @return
* An array of all language types where the keys of each are the language type
* name and its value is its configurability (TRUE/FALSE).
*/
function language_types() {
return array_keys(variable_get('language_types', drupal_language_types()));
}
/**
* Get a list of languages set up indexed by the specified key
* Returns a list of installed languages, indexed by the specified key.
*
* @param $field
* (optional) The field to index the list with.
*
* @param $field The field to index the list with.
* @return
* An associative array, keyed on the values of $field.
* - If $field is 'weight' or 'enabled', the array is nested, with the outer
* array's values each being associative arrays with language codes as
* keys and language objects as values.
* - For all other values of $field, the array is only one level deep, and
* the array's values are language objects.
*/
function language_list($field = 'language') {
$languages = &drupal_static(__FUNCTION__);
......@@ -2566,7 +2724,7 @@ function language_list($field = 'language') {
}
/**
* Default language used on the site
* Returns the default language used on the site
*
* @param $property
* Optional property of the language object to return
......@@ -2600,7 +2758,7 @@ function request_path() {
return $path;
}
if (isset($_GET['q'])) {
if (isset($_GET['q']) && is_string($_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
......@@ -2636,16 +2794,16 @@ function request_path() {
}
/**
* Return a component of the current Drupal path.
* Returns a component of the current Drupal path.
*
* When viewing a page at the path "admin/structure/types", for example, arg(0)
* returns "admin", arg(1) returns "structure", and arg(2) returns "types".
*
* Avoid use of this function where possible, as resulting code is hard to read.
* In menu callback functions, attempt to use named arguments. See the explanation
* in menu.inc for how to construct callbacks that take arguments. When attempting
* to use this function to load an element from the current path, e.g. loading the
* node on a node page, please use menu_get_object() instead.
* Avoid use of this function where possible, as resulting code is hard to
* read. In menu callback functions, attempt to use named arguments. See the
* explanation in menu.inc for how to construct callbacks that take arguments.
* When attempting to use this function to load an element from the current
* path, e.g. loading the node on a node page, use menu_get_object() instead.
*
* @param $index
* The index of the component, where each component is separated by a '/'
......@@ -2685,6 +2843,8 @@ function arg($index = NULL, $path = NULL) {
}
/**
* Returns the IP address of the client machine.
*
* If Drupal is behind a reverse proxy, we use the X-Forwarded-For header
* instead of $_SERVER['REMOTE_ADDR'], which would be the IP address of
* the proxy server, and not the client's. The actual header name can be
......@@ -2729,12 +2889,12 @@ function ip_address() {
}
/**
* @ingroup schemaapi
* @addtogroup schemaapi
* @{
*/
/**
* Get the schema definition of a table, or the whole database schema.
* Gets 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().
......@@ -2770,11 +2930,17 @@ function drupal_get_schema($table = NULL, $rebuild = FALSE) {
*/
class SchemaCache extends DrupalCacheArray {
/**
* Constructs a SchemaCache object.
*/
public function __construct() {
// Cache by request method.
parent::__construct('schema:runtime:' . $_SERVER['REQUEST_METHOD'] == 'GET', 'cache');
parent::__construct('schema:runtime:' . ($_SERVER['REQUEST_METHOD'] == 'GET'), 'cache');
}
/**
* Overrides DrupalCacheArray::resolveCacheMiss().
*/
protected function resolveCacheMiss($offset) {
$complete_schema = drupal_get_complete_schema();
$value = isset($complete_schema[$offset]) ? $complete_schema[$offset] : NULL;
......@@ -2785,7 +2951,7 @@ class SchemaCache extends DrupalCacheArray {
}
/**
* Get the whole database schema.
* Gets the whole database schema.
*
* The returned schema will include any modifications made by any
* module that implements hook_schema_alter().
......@@ -2845,23 +3011,24 @@ function drupal_get_complete_schema($rebuild = FALSE) {
}
/**
* @} End of "ingroup schemaapi".
* @} End of "addtogroup schemaapi".
*/
/**
* @ingroup registry
* @addtogroup registry
* @{
*/
/**
* Confirm that an interface is available.
* Confirms that an interface is available.
*
* This function is rarely called directly. Instead, it is registered as an
* spl_autoload() handler, and PHP calls it for us when necessary.
*
* @param $interface
* The name of the interface to check or load.
*
* @return
* TRUE if the interface is currently available, FALSE otherwise.
*/
......@@ -2870,13 +3037,14 @@ function drupal_autoload_interface($interface) {
}
/**
* Confirm that a class is available.
* Confirms that a class is available.
*
* This function is rarely called directly. Instead, it is registered as an
* spl_autoload() handler, and PHP calls it for us when necessary.
*
* @param $class
* The name of the class to check or load.
*
* @return
* TRUE if the class is currently available, FALSE otherwise.
*/
......@@ -2885,7 +3053,7 @@ function drupal_autoload_class($class) {
}
/**
* Helper to check for a resource in the registry.
* Checks for a resource in the registry.
*
* @param $type
* The type of resource we are looking up, or one of the constants
......@@ -2894,6 +3062,7 @@ function drupal_autoload_class($class) {
* @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.
......@@ -2965,7 +3134,7 @@ function _registry_check_code($type, $name = NULL) {
}
/**
* Rescan all enabled modules and rebuild the registry.
* Rescans all enabled modules and rebuilds the registry.
*
* Rescans all code in modules or includes directories, storing the location of
* each interface or class in the database.
......@@ -2976,25 +3145,44 @@ function registry_rebuild() {
}
/**
* Update the registry based on the latest files listed in the database.
* Updates 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.
*
* @return
* TRUE if the registry was rebuilt, FALSE if another thread was rebuilding
* in parallel and the current thread just waited for completion.
*
* @see registry_rebuild()
*/
function registry_update() {
// install_system_module() calls module_enable() which calls into this
// function during initial system installation, so the lock system is neither
// loaded nor does its storage exist yet.
$in_installer = drupal_installation_attempted();
if (!$in_installer && !lock_acquire(__FUNCTION__)) {
// Another request got the lock, wait for it to finish.
lock_wait(__FUNCTION__);
return FALSE;
}
require_once DRUPAL_ROOT . '/includes/registry.inc';
_registry_update();
if (!$in_installer) {
lock_release(__FUNCTION__);
}
return TRUE;
}
/**
* @} End of "ingroup registry".
* @} End of "addtogroup registry".
*/
/**
* Central static variable storage.
* Provides central static variable storage.
*
* 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
......@@ -3145,7 +3333,7 @@ function &drupal_static($name, $default_value = NULL, $reset = FALSE) {
}
/**
* Reset one or all centrally stored static variable(s).
* Resets one or all centrally stored static variable(s).
*
* @param $name
* Name of the static variable to reset. Omit to reset all variables.
......@@ -3155,7 +3343,7 @@ function drupal_static_reset($name = NULL) {
}
/**
* Detect whether the current script is running in a command-line environment.
* Detects 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)));
......@@ -3163,7 +3351,8 @@ function drupal_is_cli() {
/**
* Formats text for emphasized display in a placeholder inside a sentence.
* Used automatically by t().
*
* Used automatically by format_string().
*
* @param $text
* The text to format (plain-text).
......@@ -3176,7 +3365,7 @@ function drupal_placeholder($text) {
}
/**
* Register a function for execution on shutdown.
* Registers a function for execution on shutdown.
*
* Wrapper for register_shutdown_function() that catches thrown exceptions to
* avoid "Exception thrown without a stack frame in Unknown".
......@@ -3211,7 +3400,7 @@ function &drupal_register_shutdown_function($callback = NULL) {
}
/**
* Internal function used to execute registered shutdown functions.
* Executes registered shutdown functions.
*/
function _drupal_shutdown_function() {
$callbacks = &drupal_register_shutdown_function();
......
......@@ -6,7 +6,7 @@
*/
/**
* A stub cache implementation to be used during the installation process.
* Defines a stub cache implementation to be used during installation.
*
* The stub implementation is needed when database access is not yet available.
* Because Drupal's caching system never requires that cached data be present,
......@@ -15,17 +15,30 @@
* normal operations would have a negative impact on performance.
*/
class DrupalFakeCache extends DrupalDatabaseCache implements DrupalCacheInterface {
/**
* Overrides DrupalDatabaseCache::get().
*/
function get($cid) {
return FALSE;
}
/**
* Overrides DrupalDatabaseCache::getMultiple().
*/
function getMultiple(&$cids) {
return array();
}
/**
* Overrides DrupalDatabaseCache::set().
*/
function set($cid, $data, $expire = CACHE_PERMANENT) {
}
/**
* Overrides DrupalDatabaseCache::clear().
*/
function clear($cid = NULL, $wildcard = FALSE) {
// If there is a database cache, attempt to clear it whenever possible. The
// reason for doing this is that the database cache can accumulate data
......@@ -52,6 +65,9 @@ class DrupalFakeCache extends DrupalDatabaseCache implements DrupalCacheInterfac
}
}
/**
* Overrides DrupalDatabaseCache::isEmpty().
*/
function isEmpty() {
return TRUE;
}
......
<?php
/**
* Get the cache object for a cache bin.
* @file
* Functions and interfaces for cache handling.
*/
/**
* Gets the cache object for a cache bin.
*
* By default, this returns an instance of the DrupalDatabaseCache class.
* Classes implementing DrupalCacheInterface can register themselves both as a
* default implementation and for specific bins.
*
* @see DrupalCacheInterface
*
* @param $bin
* The cache bin for which the cache object should be returned.
* @return DrupalCacheInterface
* The cache object associated with the specified bin.
*
* @see DrupalCacheInterface
*/
function _cache_get_object($bin) {
// We do not use drupal_static() here because we do not want to change the
......@@ -29,7 +34,7 @@ function _cache_get_object($bin) {
}
/**
* Return data from the persistent cache
* Returns data from the persistent cache.
*
* Data may be stored as either plain text or as serialized data. cache_get
* will automatically return unserialized objects and arrays.
......@@ -44,19 +49,22 @@ function _cache_get_object($bin) {
*
* @return
* The cache or FALSE on failure.
*
* @see cache_set()
*/
function cache_get($cid, $bin = 'cache') {
return _cache_get_object($bin)->get($cid);
}
/**
* Return data from the persistent cache when given an array of cache IDs.
* Returns data from the persistent cache when given an array of cache IDs.
*
* @param $cids
* An array of cache IDs for the data to retrieve. This is passed by
* reference, and will have the IDs successfully returned from cache removed.
* @param $bin
* The cache bin where the data is stored.
*
* @return
* An array of the items successfully returned from cache indexed by cid.
*/
......@@ -65,50 +73,22 @@ function cache_get_multiple(array &$cids, $bin = 'cache') {
}
/**
* Store data in the persistent cache.
* Stores data in the persistent cache.
*
* The persistent cache is split up into several cache bins. In the default
* cache implementation, each cache bin corresponds to a database table by the
* same name. Other implementations might want to store several bins in data
* structures that get flushed together. While it is not a problem for most
* cache bins if the entries in them are flushed before their expire time, some
* might break functionality or are extremely expensive to recalculate. These
* will be marked with a (*). The other bins expired automatically by core.
* Contributed modules can add additional bins and get them expired
* automatically by implementing hook_flush_caches().
*
* - cache: Generic cache storage bin (used for variables, theme registry,
* locale date, list of simpletest tests etc).
*
* - cache_block: Stores the content of various blocks.
*
* - cache field: Stores the field data belonging to a given object.
*
* - cache_filter: Stores filtered pieces of content.
*
* - cache_form(*): Stores multistep forms. Flushing this bin means that some
* forms displayed to users lose their state and the data already submitted
* to them.
*
* - cache_menu: Stores the structure of visible navigation menus per page.
*
* - cache_page: Stores generated pages for anonymous users. It is flushed
* very often, whenever a page changes, at least for every ode and comment
* submission. This is the only bin affected by the page cache setting on
* the administrator panel.
*
* - cache path: Stores the system paths that have an alias.
*
* - cache update(*): Stores available releases. The update server (for
* example, drupal.org) needs to produce the relevant XML for every project
* installed on the current site. As this is different for (almost) every
* site, it's very expensive to recalculate for the update server.
* might break functionality or are extremely expensive to recalculate. The
* other bins are expired automatically by core. Contributed modules can add
* additional bins and get them expired automatically by implementing
* hook_flush_caches().
*
* The reasons for having several bins are as follows:
*
* - smaller bins mean smaller database tables and allow for faster selects and
* inserts
* - we try to put fast changing cache items and rather static ones into
* - Smaller bins mean smaller database tables and allow for faster selects and
* inserts.
* - We try to put fast changing cache items and rather static ones into
* different bins. The effect is that only the fast changing bins will need a
* lot of writes to disk. The more static bins will also be better cacheable
* with MySQL's query cache.
......@@ -117,13 +97,27 @@ function cache_get_multiple(array &$cids, $bin = 'cache') {
* The cache ID of the data to store.
* @param $data
* The data to store in the cache. Complex data types will be automatically
* serialized before insertion.
* Strings will be stored as plain text and not serialized.
* serialized before insertion. Strings will be stored as plain text and are
* not serialized.
* @param $bin
* The cache bin to store the data in. Valid core values are 'cache_block',
* 'cache_bootstrap', 'cache_field', 'cache_filter', 'cache_form',
* 'cache_menu', 'cache_page', 'cache_update' or 'cache' for the default
* cache.
* The cache bin to store the data in. Valid core values are:
* - cache: (default) Generic cache storage bin (used for theme registry,
* locale date, list of simpletest tests, etc.).
* - cache_block: Stores the content of various blocks.
* - cache_bootstrap: Stores the class registry, the system list of modules,
* the list of which modules implement which hooks, and the Drupal variable
* list.
* - cache_field: Stores the field data belonging to a given object.
* - cache_filter: Stores filtered pieces of content.
* - cache_form: Stores multistep forms. Flushing this bin means that some
* forms displayed to users lose their state and the data already submitted
* to them. This bin should not be flushed before its expired time.
* - cache_menu: Stores the structure of visible navigation menus per page.
* - cache_page: Stores generated pages for anonymous users. It is flushed
* very often, whenever a page changes, at least for every node and comment
* submission. This is the only bin affected by the page cache setting on
* the administrator panel.
* - cache_path: Stores the system paths that have an alias.
* @param $expire
* One of the following values:
* - CACHE_PERMANENT: Indicates that the item should never be removed unless
......@@ -132,13 +126,16 @@ function cache_get_multiple(array &$cids, $bin = 'cache') {
* general cache wipe.
* - A Unix timestamp: Indicates that the item should be kept at least until
* the given time, after which it behaves like CACHE_TEMPORARY.
*
* @see _update_cache_set()
* @see cache_get()
*/
function cache_set($cid, $data, $bin = 'cache', $expire = CACHE_PERMANENT) {
return _cache_get_object($bin)->set($cid, $data, $expire);
}
/**
* Expire data from the cache.
* Expires data from the cache.
*
* If called without arguments, expirable entries will be cleared from the
* cache_page and cache_block bins.
......@@ -146,15 +143,12 @@ function cache_set($cid, $data, $bin = 'cache', $expire = CACHE_PERMANENT) {
* @param $cid
* If set, the cache ID to delete. Otherwise, all cache entries that can
* expire are deleted.
*
* @param $bin
* If set, the bin $bin to delete from. Mandatory
* argument if $cid is set.
*
* If set, the cache bin to delete from. Mandatory argument if $cid is set.
* @param $wildcard
* If $wildcard is TRUE, cache IDs starting with $cid are deleted in
* addition to the exact cache ID specified by $cid. If $wildcard is
* TRUE and $cid is '*' then the entire bin $bin is emptied.
* If TRUE, cache IDs starting with $cid are deleted in addition to the
* exact cache ID specified by $cid. If $wildcard is TRUE and $cid is '*',
* the entire cache bin is emptied.
*/
function cache_clear_all($cid = NULL, $bin = NULL, $wildcard = FALSE) {
if (!isset($cid) && !isset($bin)) {
......@@ -170,13 +164,14 @@ function cache_clear_all($cid = NULL, $bin = NULL, $wildcard = FALSE) {
}
/**
* Check if a cache bin is empty.
* Checks if a cache bin is empty.
*
* A cache bin is considered empty if it does not contain any valid data for any
* cache ID.
*
* @param $bin
* The cache bin to check.
*
* @return
* TRUE if the cache bin specified is empty.
*/
......@@ -185,7 +180,7 @@ function cache_is_empty($bin) {
}
/**
* Interface for cache implementations.
* Defines an interface for cache implementations.
*
* All cache implementations have to implement this interface.
* DrupalDatabaseCache provides the default implementation, which can be
......@@ -223,7 +218,7 @@ function cache_is_empty($bin) {
*/
interface DrupalCacheInterface {
/**
* Constructor.
* Constructs a new cache interface.
*
* @param $bin
* The cache bin for which the object is created.
......@@ -231,31 +226,34 @@ interface DrupalCacheInterface {
function __construct($bin);
/**
* Return data from the persistent cache. Data may be stored as either plain
* text or as serialized data. cache_get will automatically return
* unserialized objects and arrays.
* Returns data from the persistent cache.
*
* Data may be stored as either plain text or as serialized data. cache_get()
* will automatically return unserialized objects and arrays.
*
* @param $cid
* The cache ID of the data to retrieve.
*
* @return
* The cache or FALSE on failure.
*/
function get($cid);
/**
* Return data from the persistent cache when given an array of cache IDs.
* Returns data from the persistent cache when given an array of cache IDs.
*
* @param $cids
* An array of cache IDs for the data to retrieve. This is passed by
* reference, and will have the IDs successfully returned from cache
* removed.
*
* @return
* An array of the items successfully returned from cache indexed by cid.
*/
function getMultiple(&$cids);
/**
* Store data in the persistent cache.
* Stores data in the persistent cache.
*
* @param $cid
* The cache ID of the data to store.
......@@ -276,8 +274,10 @@ interface DrupalCacheInterface {
/**
* Expire data from the cache. If called without arguments, expirable
* entries will be cleared from the cache_page and cache_block bins.
* Expires data from the cache.
*
* If called without arguments, expirable entries will be cleared from the
* cache_page and cache_block bins.
*
* @param $cid
* If set, the cache ID to delete. Otherwise, all cache entries that can
......@@ -290,7 +290,7 @@ interface DrupalCacheInterface {
function clear($cid = NULL, $wildcard = FALSE);
/**
* Check if a cache bin is empty.
* Checks if a cache bin is empty.
*
* A cache bin is considered empty if it does not contain any valid data for
* any cache ID.
......@@ -302,7 +302,7 @@ interface DrupalCacheInterface {
}
/**
* Default cache implementation.
* Defines a default cache implementation.
*
* This is Drupal's default cache implementation. It uses the database to store
* cached data. Each cache bin corresponds to a database table by the same name.
......@@ -310,16 +310,25 @@ interface DrupalCacheInterface {
class DrupalDatabaseCache implements DrupalCacheInterface {
protected $bin;
/**
* Constructs a new DrupalDatabaseCache object.
*/
function __construct($bin) {
$this->bin = $bin;
}
/**
* Implements DrupalCacheInterface::get().
*/
function get($cid) {
$cids = array($cid);
$cache = $this->getMultiple($cids);
return reset($cache);
}
/**
* Implements DrupalCacheInterface::getMultiple().
*/
function getMultiple(&$cids) {
try {
// Garbage collection necessary when enforcing a minimum cache lifetime.
......@@ -357,11 +366,31 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
* The bin being requested.
*/
protected function garbageCollection() {
global $user;
$cache_lifetime = variable_get('cache_lifetime', 0);
// Clean-up the per-user cache expiration session data, so that the session
// handler can properly clean-up the session data for anonymous users.
if (isset($_SESSION['cache_expiration'])) {
$expire = REQUEST_TIME - $cache_lifetime;
foreach ($_SESSION['cache_expiration'] as $bin => $timestamp) {
if ($timestamp < $expire) {
unset($_SESSION['cache_expiration'][$bin]);
}
}
if (!$_SESSION['cache_expiration']) {
unset($_SESSION['cache_expiration']);
}
}
// Garbage collection necessary when enforcing a minimum cache lifetime.
// Garbage collection of temporary items is only necessary when enforcing
// a minimum cache lifetime.
if (!$cache_lifetime) {
return;
}
// When cache lifetime is in force, avoid running garbage collection too
// often since this will remove temporary cache items indiscriminately.
$cache_flush = variable_get('cache_flush_' . $this->bin, 0);
if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= REQUEST_TIME)) {
if ($cache_flush && ($cache_flush + $cache_lifetime <= REQUEST_TIME)) {
// Reset the variable immediately to prevent a meltdown in heavy load situations.
variable_set('cache_flush_' . $this->bin, 0);
// Time to flush old cache data
......@@ -373,13 +402,14 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
}
/**
* Prepare a cached item.
* Prepares a cached item.
*
* Checks that items are either permanent or did not expire, and unserializes
* data as appropriate.
*
* @param $cache
* An item loaded from cache_get() or cache_get_multiple().
*
* @return
* The item with data unserialized as appropriate or FALSE if there is no
* valid item to load.
......@@ -390,17 +420,16 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
if (!isset($cache->data)) {
return FALSE;
}
// If enforcing a minimum cache lifetime, validate that the data is
// currently valid for this user before we return it by making sure the cache
// entry was created before the timestamp in the current session's cache
// timer. The cache variable is loaded into the $user object by _drupal_session_read()
// in session.inc. If the data is permanent or we're not enforcing a minimum
// cache lifetime always return the cached data.
if ($cache->expire != CACHE_PERMANENT && variable_get('cache_lifetime', 0) && $user->cache > $cache->created) {
// This cache data is too old and thus not valid for us, ignore it.
// If the cached data is temporary and subject to a per-user minimum
// lifetime, compare the cache entry timestamp with the user session
// cache_expiration timestamp. If the cache entry is too old, ignore it.
if ($cache->expire != CACHE_PERMANENT && variable_get('cache_lifetime', 0) && isset($_SESSION['cache_expiration'][$this->bin]) && $_SESSION['cache_expiration'][$this->bin] > $cache->created) {
// Ignore cache data that is too old and thus not valid for this user.
return FALSE;
}
// If the data is permanent or not subject to a minimum cache lifetime,
// unserialize and return the cached data.
if ($cache->serialized) {
$cache->data = unserialize($cache->data);
}
......@@ -408,6 +437,9 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
return $cache;
}
/**
* Implements DrupalCacheInterface::set().
*/
function set($cid, $data, $expire = CACHE_PERMANENT) {
$fields = array(
'serialized' => 0,
......@@ -434,16 +466,18 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
}
}
/**
* Implements DrupalCacheInterface::clear().
*/
function clear($cid = NULL, $wildcard = FALSE) {
global $user;
if (empty($cid)) {
if (variable_get('cache_lifetime', 0)) {
// We store the time in the current user's $user->cache variable which
// will be saved into the sessions bin by _drupal_session_write(). We then
// simulate that the cache was flushed for this user by not returning
// cached data that was cached before the timestamp.
$user->cache = REQUEST_TIME;
// We store the time in the current user's session. We then simulate
// that the cache was flushed for this user by not returning cached
// data that was cached before the timestamp.
$_SESSION['cache_expiration'][$this->bin] = REQUEST_TIME;
$cache_flush = variable_get('cache_flush_' . $this->bin, 0);
if ($cache_flush == 0) {
......@@ -496,6 +530,9 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
}
}
/**
* Implements DrupalCacheInterface::isEmpty().
*/
function isEmpty() {
$this->garbageCollection();
$query = db_select($this->bin);
......
......@@ -70,8 +70,7 @@ define('CSS_DEFAULT', 0);
define('CSS_THEME', 100);
/**
* The default group for JavaScript libraries, settings or jQuery plugins added
* to the page.
* The default group for JavaScript and jQuery libraries added to the page.
*/
define('JS_LIBRARY', -100);
......@@ -86,20 +85,27 @@ define('JS_DEFAULT', 0);
define('JS_THEME', 100);
/**
* Error code indicating that the request made by drupal_http_request() exceeded
* the specified timeout.
* Error code indicating that the request exceeded the specified timeout.
*
* @see drupal_http_request()
*/
define('HTTP_REQUEST_TIMEOUT', -1);
/**
* Constants defining cache granularity for blocks and renderable arrays.
*
* Modules specify the caching patterns for their blocks using binary
* combinations of these constants in their hook_block_info():
* $block[delta]['cache'] = DRUPAL_CACHE_PER_ROLE | DRUPAL_CACHE_PER_PAGE;
* DRUPAL_CACHE_PER_ROLE is used as a default when no caching pattern is
* specified. Use DRUPAL_CACHE_CUSTOM to disable standard block cache and
* implement
* @defgroup block_caching Block Caching
* @{
* Constants that define each block's caching state.
*
* Modules specify how their blocks can be cached in their hook_block_info()
* implementations. Caching can be turned off (DRUPAL_NO_CACHE), managed by the
* module declaring the block (DRUPAL_CACHE_CUSTOM), or managed by the core
* Block module. If the Block module is managing the cache, you can specify that
* the block is the same for every page and user (DRUPAL_CACHE_GLOBAL), or that
* it can change depending on the page (DRUPAL_CACHE_PER_PAGE) or by user
* (DRUPAL_CACHE_PER_ROLE or DRUPAL_CACHE_PER_USER). Page and user settings can
* be combined with a bitwise-binary or operator; for example,
* DRUPAL_CACHE_PER_ROLE | DRUPAL_CACHE_PER_PAGE means that the block can change
* depending on the user role or page it is on.
*
* The block cache is cleared in cache_clear_all(), and uses the same clearing
* policy than page cache (node, comment, user, taxonomy added or updated...).
......@@ -110,31 +116,35 @@ define('HTTP_REQUEST_TIMEOUT', -1);
*/
/**
* The block should not get cached. This setting should be used:
* - for simple blocks (notably those that do not perform any db query),
* where querying the db cache would be more expensive than directly generating
* the content.
* - for blocks that change too frequently.
* The block should not get cached.
*
* This setting should be used:
* - For simple blocks (notably those that do not perform any db query), where
* querying the db cache would be more expensive than directly generating the
* content.
* - For blocks that change too frequently.
*/
define('DRUPAL_NO_CACHE', -1);
/**
* The block is handling its own caching in its hook_block_view(). From the
* perspective of the block cache system, this is equivalent to DRUPAL_NO_CACHE.
* Useful when time based expiration is needed or a site uses a node access
* which invalidates standard block cache.
* The block is handling its own caching in its hook_block_view().
*
* This setting is useful when time based expiration is needed or a site uses a
* node access which invalidates standard block cache.
*/
define('DRUPAL_CACHE_CUSTOM', -2);
/**
* The block or element can change depending on the roles the user viewing the
* page belongs to. This is the default setting for blocks, used when the block
* does not specify anything.
* The block or element can change depending on the user's roles.
*
* This is the default setting for blocks, used when the block does not specify
* anything.
*/
define('DRUPAL_CACHE_PER_ROLE', 0x0001);
/**
* The block or element can change depending on the user viewing the page.
* The block or element can change depending on the user.
*
* This setting can be resource-consuming for sites with large number of users,
* and thus should only be used when DRUPAL_CACHE_PER_ROLE is not sufficient.
*/
......@@ -146,12 +156,16 @@ define('DRUPAL_CACHE_PER_USER', 0x0002);
define('DRUPAL_CACHE_PER_PAGE', 0x0004);
/**
* The block or element is the same for every user on every page where it is visible.
* The block or element is the same for every user and page that it is visible.
*/
define('DRUPAL_CACHE_GLOBAL', 0x0008);
/**
* Add content to a specified region.
* @} End of "defgroup block_caching".
*/
/**
* Adds content to a specified region.
*
* @param $region
* Page region the content is added to.
......@@ -168,7 +182,7 @@ function drupal_add_region_content($region = NULL, $data = NULL) {
}
/**
* Get assigned content for a given region.
* Gets assigned content for a given region.
*
* @param $region
* A specified region to fetch content for. If NULL, all regions will be
......@@ -194,16 +208,16 @@ function drupal_get_region_content($region = NULL, $delimiter = ' ') {
}
/**
* Get the name of the currently active install profile.
* 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 standard Drupal systems variable
* table contains the name of the current profile, and we can call variable_get()
* to determine what one is active.
* table contains the name of the current profile, and we can call
* variable_get() to determine what one is active.
*
* @return $profile
* The name of the install profile.
* The name of the installation profile.
*/
function drupal_get_profile() {
global $install_state;
......@@ -220,7 +234,7 @@ function drupal_get_profile() {
/**
* Set the breadcrumb trail for the current page.
* Sets the breadcrumb trail for the current page.
*
* @param $breadcrumb
* Array of links, starting with "home" and proceeding up to but not including
......@@ -236,7 +250,7 @@ function drupal_set_breadcrumb($breadcrumb = NULL) {
}
/**
* Get the breadcrumb trail for the current page.
* Gets the breadcrumb trail for the current page.
*/
function drupal_get_breadcrumb() {
$breadcrumb = drupal_set_breadcrumb();
......@@ -265,9 +279,9 @@ function drupal_get_rdf_namespaces() {
}
/**
* Add output to the head tag of the HTML page.
* Adds output to the HEAD tag of the HTML page.
*
* This function can be called as long the headers aren't sent. Pass no
* This function can be called as long as the headers aren't sent. Pass no
* arguments (or NULL for both) to retrieve the currently stored elements.
*
* @param $data
......@@ -333,7 +347,7 @@ function _drupal_default_html_head() {
}
/**
* Retrieve output to be displayed in the HEAD tag of the HTML page.
* Retrieves output to be displayed in the HEAD tag of the HTML page.
*/
function drupal_get_html_head() {
$elements = drupal_add_html_head();
......@@ -342,7 +356,7 @@ function drupal_get_html_head() {
}
/**
* Add a feed URL for the current page.
* Adds a feed URL for the current page.
*
* This function can be called as long the HTML header hasn't been sent.
*
......@@ -370,7 +384,7 @@ function drupal_add_feed($url = NULL, $title = '') {
}
/**
* Get the feed URLs for the current page.
* Gets the feed URLs for the current page.
*
* @param $delimiter
* A delimiter to split feeds by.
......@@ -387,7 +401,7 @@ function drupal_get_feeds($delimiter = "\n") {
*/
/**
* Process a URL query parameter array to remove unwanted elements.
* Processes a URL query parameter array to remove unwanted elements.
*
* @param $query
* (optional) An array to be processed. Defaults to $_GET.
......@@ -432,13 +446,13 @@ function drupal_get_query_parameters(array $query = NULL, array $exclude = array
}
/**
* Split an URL-encoded query string into an array.
* Splits a URL-encoded query string into an array.
*
* @param $query
* The query string to split.
*
* @return
* An array of url decoded couples $param_name => $value.
* An array of URL decoded couples $param_name => $value.
*/
function drupal_get_query_array($query) {
$result = array();
......@@ -452,7 +466,7 @@ function drupal_get_query_array($query) {
}
/**
* Parse an array into a valid, rawurlencoded query string.
* Parses an array into a valid, rawurlencoded query string.
*
* This differs from http_build_query() as we need to rawurlencode() (instead of
* urlencode()) all query parameters.
......@@ -493,13 +507,19 @@ function drupal_http_build_query(array $query, $parent = '') {
}
/**
* Prepare a 'destination' URL query parameter for use in combination with drupal_goto().
* Prepares a 'destination' URL query parameter for use with drupal_goto().
*
* Used to direct the user back to the referring page after completing a form.
* By default the current URL is returned. If a destination exists in the
* previous request, that destination is returned. As such, a destination can
* persist across multiple pages.
*
* @return
* An associative array containing the key:
* - destination: The path provided via the destination query string or, if
* not available, the current path.
*
* @see current_path()
* @see drupal_goto()
*/
function drupal_get_destination() {
......@@ -524,11 +544,11 @@ function drupal_get_destination() {
}
/**
* Wrapper around parse_url() to parse a system URL string into an associative array, suitable for url().
* Parses a system URL string into an associative array suitable for url().
*
* This function should only be used for URLs that have been generated by the
* system, resp. url(). It should not be used for URLs that come from external
* sources, or URLs that link to external resources.
* system, such as via url(). It should not be used for URLs that come from
* external sources, or URLs that link to external resources.
*
* The returned array contains a 'path' that may be passed separately to url().
* For example:
......@@ -621,7 +641,7 @@ function drupal_encode_path($path) {
}
/**
* Send the user to a different Drupal page.
* Sends the user to a different Drupal page.
*
* This issues an on-site HTTP redirect. The function makes sure the redirected
* URL is formatted correctly.
......@@ -642,20 +662,23 @@ function drupal_encode_path($path) {
* callback.
*
* @param $path
* A Drupal path or a full URL.
* (optional) A Drupal path or a full URL, which will be passed to url() to
* compute the redirect for the URL.
* @param $options
* An associative array of additional URL options to pass to url().
* (optional) An associative array of additional URL options to pass to url().
* @param $http_response_code
* Valid values for an actual "goto" as per RFC 2616 section 10.3 are:
* - 301 Moved Permanently (the recommended value for most redirects)
* - 302 Found (default in Drupal and PHP, sometimes used for spamming search
* engines)
* - 303 See Other
* - 304 Not Modified
* - 305 Use Proxy
* - 307 Temporary Redirect (alternative to "503 Site Down for Maintenance")
* Note: Other values are defined by RFC 2616, but are rarely used and poorly
* supported.
* (optional) The HTTP status code to use for the redirection, defaults to
* 302. The valid values for 3xx redirection status codes are defined in
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3 RFC 2616 @endlink
* and the
* @link http://tools.ietf.org/html/draft-reschke-http-status-308-07 draft for the new HTTP status codes: @endlink
* - 301: Moved Permanently (the recommended value for most redirects).
* - 302: Found (default in Drupal and PHP, sometimes used for spamming search
* engines).
* - 303: See Other.
* - 304: Not Modified.
* - 305: Use Proxy.
* - 307: Temporary Redirect.
*
* @see drupal_get_destination()
* @see url()
......@@ -686,7 +709,7 @@ function drupal_goto($path = '', array $options = array(), $http_response_code =
}
/**
* Deliver a "site is under maintenance" message to the browser.
* Delivers a "site is under maintenance" message to the browser.
*
* Page callback functions wanting to report a "site offline" message should
* return MENU_SITE_OFFLINE instead of calling drupal_site_offline(). However,
......@@ -698,7 +721,7 @@ function drupal_site_offline() {
}
/**
* Deliver a "page not found" error to the browser.
* Delivers a "page not found" error to the browser.
*
* Page callback functions wanting to report a "page not found" message should
* return MENU_NOT_FOUND instead of calling drupal_not_found(). However,
......@@ -710,19 +733,20 @@ function drupal_not_found() {
}
/**
* Deliver a "access denied" error to the browser.
* Delivers an "access denied" error to the browser.
*
* Page callback functions wanting to report an "access denied" message should
* return MENU_ACCESS_DENIED instead of calling drupal_access_denied(). However,
* functions that are invoked in contexts where that return value might not
* bubble up to menu_execute_active_handler() should call drupal_access_denied().
* bubble up to menu_execute_active_handler() should call
* drupal_access_denied().
*/
function drupal_access_denied() {
drupal_deliver_page(MENU_ACCESS_DENIED);
}
/**
* Perform an HTTP request.
* Performs an HTTP request.
*
* This is a flexible and powerful HTTP client implementation. Correctly
* handles GET, POST, PUT or any other HTTP requests. Handles redirects.
......@@ -761,6 +785,13 @@ function drupal_access_denied() {
* - data: A string containing the response body that was received.
*/
function drupal_http_request($url, array $options = array()) {
// Allow an alternate HTTP client library to replace Drupal's default
// implementation.
$override_function = variable_get('drupal_http_request_function', FALSE);
if (!empty($override_function) && function_exists($override_function)) {
return $override_function($url, $options);
}
$result = new stdClass();
// Parse the URL and make sure we can handle the schema.
......@@ -789,10 +820,51 @@ function drupal_http_request($url, array $options = array()) {
'timeout' => 30.0,
'context' => NULL,
);
// Merge the default headers.
$options['headers'] += array(
'User-Agent' => 'Drupal (+http://drupal.org/)',
);
// stream_socket_client() requires timeout to be a float.
$options['timeout'] = (float) $options['timeout'];
// Use a proxy if one is defined and the host is not on the excluded list.
$proxy_server = variable_get('proxy_server', '');
if ($proxy_server && _drupal_http_use_proxy($uri['host'])) {
// Set the scheme so we open a socket to the proxy server.
$uri['scheme'] = 'proxy';
// Set the path to be the full URL.
$uri['path'] = $url;
// Since the URL is passed as the path, we won't use the parsed query.
unset($uri['query']);
// Add in username and password to Proxy-Authorization header if needed.
if ($proxy_username = variable_get('proxy_username', '')) {
$proxy_password = variable_get('proxy_password', '');
$options['headers']['Proxy-Authorization'] = 'Basic ' . base64_encode($proxy_username . (!empty($proxy_password) ? ":" . $proxy_password : ''));
}
// Some proxies reject requests with any User-Agent headers, while others
// require a specific one.
$proxy_user_agent = variable_get('proxy_user_agent', '');
// The default value matches neither condition.
if ($proxy_user_agent === NULL) {
unset($options['headers']['User-Agent']);
}
elseif ($proxy_user_agent) {
$options['headers']['User-Agent'] = $proxy_user_agent;
}
}
switch ($uri['scheme']) {
case 'proxy':
// Make the socket connection to a proxy server.
$socket = 'tcp://' . $proxy_server . ':' . variable_get('proxy_port', 8080);
// The Host header still needs to match the real request.
$options['headers']['Host'] = $uri['host'];
$options['headers']['Host'] .= isset($uri['port']) && $uri['port'] != 80 ? ':' . $uri['port'] : '';
break;
case 'http':
case 'feed':
$port = isset($uri['port']) ? $uri['port'] : 80;
......@@ -802,12 +874,14 @@ function drupal_http_request($url, array $options = array()) {
// checking the host that do not take into account the port number.
$options['headers']['Host'] = $uri['host'] . ($port != 80 ? ':' . $port : '');
break;
case 'https':
// Note: Only works when PHP is compiled with OpenSSL support.
$port = isset($uri['port']) ? $uri['port'] : 443;
$socket = 'ssl://' . $uri['host'] . ':' . $port;
$options['headers']['Host'] = $uri['host'] . ($port != 443 ? ':' . $port : '');
break;
default:
$result->error = 'invalid schema ' . $uri['scheme'];
$result->code = -1003;
......@@ -832,7 +906,7 @@ function drupal_http_request($url, array $options = array()) {
// Mark that this request failed. This will trigger a check of the web
// server's ability to make outgoing HTTP requests the next time that
// requirements checking is performed.
// See system_requirements()
// See system_requirements().
variable_set('drupal_http_request_fails', TRUE);
return $result;
......@@ -844,11 +918,6 @@ function drupal_http_request($url, array $options = array()) {
$path .= '?' . $uri['query'];
}
// Merge the default headers.
$options['headers'] += array(
'User-Agent' => 'Drupal (+http://drupal.org/)',
);
// Only add Content-Length if we actually have any content or if it is a POST
// or PUT request. Some non-standard servers get confused by Content-Length in
// at least HEAD/GET requests, and Squid always requires Content-Length in
......@@ -860,7 +929,7 @@ function drupal_http_request($url, array $options = array()) {
// If the server URL has a user then attempt to use basic authentication.
if (isset($uri['user'])) {
$options['headers']['Authorization'] = 'Basic ' . base64_encode($uri['user'] . (!empty($uri['pass']) ? ":" . $uri['pass'] : ''));
$options['headers']['Authorization'] = 'Basic ' . base64_encode($uri['user'] . (isset($uri['pass']) ? ':' . $uri['pass'] : ''));
}
// If the database prefix is being used by SimpleTest to run the tests in a copied
......@@ -1019,10 +1088,30 @@ function drupal_http_request($url, array $options = array()) {
return $result;
}
/**
* Helper function for determining hosts excluded from needing a proxy.
*
* @return
* TRUE if a proxy should be used for this host.
*/
function _drupal_http_use_proxy($host) {
$proxy_exceptions = variable_get('proxy_exceptions', array('localhost', '127.0.0.1'));
return !in_array(strtolower($host), $proxy_exceptions, TRUE);
}
/**
* @} End of "HTTP handling".
*/
/**
* Strips slashes from a string or array of strings.
*
* Callback for array_walk() within fix_gpx_magic().
*
* @param $item
* An individual string or array of strings from superglobals.
*/
function _fix_gpc_magic(&$item) {
if (is_array($item)) {
array_walk($item, '_fix_gpc_magic');
......@@ -1033,11 +1122,19 @@ function _fix_gpc_magic(&$item) {
}
/**
* Helper function to strip slashes from $_FILES skipping over the tmp_name keys
* since PHP generates single backslashes for file paths on Windows systems.
* Strips slashes from $_FILES items.
*
* Callback for array_walk() within fix_gpc_magic().
*
* The tmp_name key is skipped keys since PHP generates single backslashes for
* file paths on Windows systems.
*
* tmp_name does not have backslashes added see
* http://php.net/manual/en/features.file-upload.php#42280
* @param $item
* An item from $_FILES.
* @param $key
* The key for the item within $_FILES.
*
* @see http://php.net/manual/en/features.file-upload.php#42280
*/
function _fix_gpc_magic_files(&$item, $key) {
if ($key != 'tmp_name') {
......@@ -1051,7 +1148,10 @@ function _fix_gpc_magic_files(&$item, $key) {
}
/**
* Fix double-escaping problems caused by "magic quotes" in some PHP installations.
* Fixes double-escaping caused by "magic quotes" in some PHP installations.
*
* @see _fix_gpc_magic()
* @see _fix_gpc_magic_files()
*/
function fix_gpc_magic() {
static $fixed = FALSE;
......@@ -1072,12 +1172,14 @@ function fix_gpc_magic() {
*/
/**
* Verify the syntax of the given e-mail address.
* Verifies the syntax of the given e-mail address.
*
* Empty e-mail addresses are allowed. See RFC 2822 for details.
* This uses the
* @link http://php.net/manual/filter.filters.validate.php PHP e-mail validation filter. @endlink
*
* @param $mail
* A string containing an e-mail address.
*
* @return
* TRUE if the address is in a valid format.
*/
......@@ -1086,7 +1188,7 @@ function valid_email_address($mail) {
}
/**
* Verify the syntax of the given URL.
* Verifies the syntax of the given URL.
*
* This function should only be used on actual URLs. It should not be used for
* Drupal menu paths, which can contain arbitrary characters.
......@@ -1095,6 +1197,7 @@ function valid_email_address($mail) {
* The URL to verify.
* @param $absolute
* Whether the URL is absolute (beginning with a scheme such as "http:").
*
* @return
* TRUE if the URL is in a valid format.
*/
......@@ -1127,7 +1230,7 @@ function valid_url($url, $absolute = FALSE) {
*/
/**
* Register an event for the current visitor to the flood control mechanism.
* Registers an event for the current visitor to the flood control mechanism.
*
* @param $name
* The name of an event.
......@@ -1154,7 +1257,7 @@ function flood_register_event($name, $window = 3600, $identifier = NULL) {
}
/**
* Make the flood control mechanism forget about an event for the current visitor.
* Makes the flood control mechanism forget an event for the current visitor.
*
* @param $name
* The name of an event.
......@@ -1172,7 +1275,7 @@ function flood_clear_event($name, $identifier = NULL) {
}
/**
* Checks whether user is allowed to proceed with the specified event.
* Checks whether a user is allowed to proceed with the specified event.
*
* Events can have thresholds saying that each user can only do that event
* a certain number of times in a time window. This function verifies that the
......@@ -1266,7 +1369,7 @@ function drupal_strip_dangerous_protocols($uri) {
}
/**
* Strips dangerous protocols (e.g. 'javascript:') from a URI and encodes it for output to an HTML attribute value.
* Strips dangerous protocols from a URI and encodes it for output to HTML.
*
* @param $uri
* A plain-text URI that might contain dangerous protocols.
......@@ -1286,7 +1389,7 @@ function check_url($uri) {
}
/**
* Very permissive XSS/HTML filter for admin-only use.
* Applies a very permissive XSS/HTML filter for admin-only use.
*
* Use only for fields where it is impractical to use the
* whole filter system, but where some (mainly inline) mark-up
......@@ -1300,7 +1403,7 @@ function filter_xss_admin($string) {
}
/**
* Filters an HTML string to prevent cross-site-scripting (XSS) vulnerabilities.
* Filters HTML to prevent cross-site-scripting (XSS) vulnerabilities.
*
* Based on kses by Ulf Harnhammar, see http://sourceforge.net/projects/kses.
* For examples of various XSS attacks, see: http://ha.ckers.org/xss.html.
......@@ -1331,21 +1434,21 @@ function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite',
if (!drupal_validate_utf8($string)) {
return '';
}
// Store the text format
// Store the text format.
_filter_xss_split($allowed_tags, TRUE);
// Remove NULL characters (ignored by some browsers)
// Remove NULL characters (ignored by some browsers).
$string = str_replace(chr(0), '', $string);
// Remove Netscape 4 JS entities
// Remove Netscape 4 JS entities.
$string = preg_replace('%&\s*\{[^}]*(\}\s*;?|$)%', '', $string);
// Defuse all HTML entities
// Defuse all HTML entities.
$string = str_replace('&', '&amp;', $string);
// Change back only well-formed entities in our whitelist
// Decimal numeric entities
// Change back only well-formed entities in our whitelist:
// Decimal numeric entities.
$string = preg_replace('/&amp;#([0-9]+;)/', '&#\1', $string);
// Hexadecimal numeric entities
// Hexadecimal numeric entities.
$string = preg_replace('/&amp;#[Xx]0*((?:[0-9A-Fa-f]{2})+;)/', '&#x\1', $string);
// Named entities
// Named entities.
$string = preg_replace('/&amp;([A-Za-z][A-Za-z0-9]*;)/', '&\1', $string);
return preg_replace_callback('%
......@@ -1369,6 +1472,7 @@ function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite',
* If $store is FALSE then the array has one element, the HTML tag to process.
* @param $store
* Whether to store $m.
*
* @return
* If the element isn't allowed, an empty string. Otherwise, the cleaned up
* version of the HTML element.
......@@ -1384,16 +1488,16 @@ function _filter_xss_split($m, $store = FALSE) {
$string = $m[1];
if (substr($string, 0, 1) != '<') {
// We matched a lone ">" character
// We matched a lone ">" character.
return '&gt;';
}
elseif (strlen($string) == 1) {
// We matched a lone "<" character
// We matched a lone "<" character.
return '&lt;';
}
if (!preg_match('%^<\s*(/\s*)?([a-zA-Z0-9]+)([^>]*)>?|(<!--.*?-->)$%', $string, $matches)) {
// Seriously malformed
// Seriously malformed.
return '';
}
......@@ -1407,7 +1511,7 @@ function _filter_xss_split($m, $store = FALSE) {
}
if (!isset($allowed_html[strtolower($elem)])) {
// Disallowed HTML element
// Disallowed HTML element.
return '';
}
......@@ -1423,7 +1527,7 @@ function _filter_xss_split($m, $store = FALSE) {
$attrlist = preg_replace('%(\s?)/\s*$%', '\1', $attrlist, -1, $count);
$xhtml_slash = $count ? ' /' : '';
// Clean up attributes
// Clean up attributes.
$attr2 = implode(' ', _filter_xss_attributes($attrlist));
$attr2 = preg_replace('/[<>]/', '', $attr2);
$attr2 = strlen($attr2) ? ' ' . $attr2 : '';
......@@ -1448,7 +1552,7 @@ function _filter_xss_attributes($attr) {
switch ($mode) {
case 0:
// Attribute name, href for instance
// Attribute name, href for instance.
if (preg_match('/^([-a-zA-Z]+)/', $attr, $match)) {
$attrname = strtolower($match[1]);
$skip = ($attrname == 'style' || substr($attrname, 0, 2) == 'on');
......@@ -1458,7 +1562,7 @@ function _filter_xss_attributes($attr) {
break;
case 1:
// Equals sign or valueless ("selected")
// Equals sign or valueless ("selected").
if (preg_match('/^\s*=\s*/', $attr)) {
$working = 1; $mode = 2;
$attr = preg_replace('/^\s*=\s*/', '', $attr);
......@@ -1475,7 +1579,7 @@ function _filter_xss_attributes($attr) {
break;
case 2:
// Attribute value, a URL after href= for instance
// Attribute value, a URL after href= for instance.
if (preg_match('/^"([^"]*)"(\s+|$)/', $attr, $match)) {
$thisval = filter_xss_bad_protocol($match[1]);
......@@ -1512,7 +1616,7 @@ function _filter_xss_attributes($attr) {
}
if ($working == 0) {
// not well formed, remove and try again
// Not well formed; remove and try again.
$attr = preg_replace('/
^
(
......@@ -1536,15 +1640,16 @@ function _filter_xss_attributes($attr) {
}
/**
* Processes an HTML attribute value and ensures it does not contain an URL with a disallowed protocol (e.g. javascript:).
* Processes an HTML attribute value and strips dangerous protocols from URLs.
*
* @param $string
* The string with the attribute value.
* @param $decode
* (Deprecated) Whether to decode entities in the $string. Set to FALSE if the
* (deprecated) Whether to decode entities in the $string. Set to FALSE if the
* $string is in plain text, TRUE otherwise. Defaults to TRUE. This parameter
* is deprecated and will be removed in Drupal 8. To process a plain-text URI,
* call drupal_strip_dangerous_protocols() or check_url() instead.
*
* @return
* Cleaned up and HTML-escaped version of $string.
*/
......@@ -1598,7 +1703,7 @@ function format_rss_channel($title, $link, $description, $items, $langcode = NUL
}
/**
* Format a single RSS item.
* Formats a single RSS item.
*
* Arbitrary elements may be added using the $args associative array.
*/
......@@ -1614,7 +1719,7 @@ function format_rss_item($title, $link, $description, $args = array()) {
}
/**
* Format XML elements.
* Formats XML elements.
*
* @param $array
* An array where each item represents an element and is either a:
......@@ -1653,7 +1758,7 @@ function format_xml_elements($array) {
}
/**
* Format a string containing a count of items.
* Formats a string containing a count of items.
*
* This function ensures that the string is pluralized correctly. Since t() is
* called by this function, make sure not to pass already-localized strings to
......@@ -1669,37 +1774,33 @@ function format_xml_elements($array) {
* $output = format_plural($update_count,
* 'Changed the content type of 1 post from %old-type to %new-type.',
* 'Changed the content type of @count posts from %old-type to %new-type.',
* array('%old-type' => $info->old_type, '%new-type' => $info->new_type)));
* array('%old-type' => $info->old_type, '%new-type' => $info->new_type));
* @endcode
*
* @param $count
* The item count to display.
* @param $singular
* The string for the singular case. Please make sure it is clear this is
* singular, to ease translation (e.g. use "1 new comment" instead of "1 new").
* Do not use @count in the singular string.
* The string for the singular case. Make sure it is clear this is singular,
* to ease translation (e.g. use "1 new comment" instead of "1 new"). Do not
* use @count in the singular string.
* @param $plural
* The string for the plural case. Please make sure it is clear this is plural,
* to ease translation. Use @count in place of the item count, as in "@count
* new comments".
* The string for the plural case. Make sure it is clear this is plural, to
* ease translation. Use @count in place of the item count, as in
* "@count new comments".
* @param $args
* An associative array of replacements to make after translation. Incidences
* An associative array of replacements to make after translation. Instances
* 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 + drupal_placeholder)
* Note that you do not need to include @count in this array.
* This replacement is done automatically for the plural case.
* Based on the first character of the key, the value is escaped and/or
* themed. See format_string(). Note that you do not need to include @count
* in this array; this replacement is done automatically for the plural case.
* @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.
* An associative array of additional options. See t() for allowed keys.
*
* @return
* A translated string.
*
* @see t()
* @see format_string()
*/
function format_plural($count, $singular, $plural, array $args = array(), array $options = array()) {
$args['@count'] = $count;
......@@ -1709,7 +1810,8 @@ function format_plural($count, $singular, $plural, array $args = array(), array
// Get the plural index through the gettext formula.
$index = (function_exists('locale_get_plural')) ? locale_get_plural($count, isset($options['langcode']) ? $options['langcode'] : NULL) : -1;
// Backwards compatibility.
// If the index cannot be computed, use the plural as a fallback (which
// allows for most flexiblity with the replaceable @count value).
if ($index < 0) {
return t($plural, $args, $options);
}
......@@ -1728,11 +1830,12 @@ function format_plural($count, $singular, $plural, array $args = array(), array
}
/**
* Parse a given byte count.
* Parses a given byte count.
*
* @param $size
* A size expressed as a number of bytes with optional SI or IEC binary unit
* prefix (e.g. 2, 3K, 5MB, 10G, 6GiB, 8 bytes, 9mbytes).
*
* @return
* An integer representation of the size in bytes.
*/
......@@ -1749,13 +1852,14 @@ function parse_size($size) {
}
/**
* Generate a string representation for the given byte count.
* Generates a string representation for the given byte count.
*
* @param $size
* A size in bytes.
* @param $langcode
* Optional language code to translate to a language other than what is used
* to display the page.
*
* @return
* A translated string representation of the size.
*/
......@@ -1788,19 +1892,20 @@ function format_size($size, $langcode = NULL) {
}
/**
* Format a time interval with the requested granularity.
* Formats a time interval with the requested granularity.
*
* @param $timestamp
* @param $interval
* The length of the interval in seconds.
* @param $granularity
* How many different units to display in the string.
* @param $langcode
* Optional language code to translate to a language other than
* what is used to display the page.
*
* @return
* A translated string representation of the interval.
*/
function format_interval($timestamp, $granularity = 2, $langcode = NULL) {
function format_interval($interval, $granularity = 2, $langcode = NULL) {
$units = array(
'1 year|@count years' => 31536000,
'1 month|@count months' => 2592000,
......@@ -1813,9 +1918,9 @@ function format_interval($timestamp, $granularity = 2, $langcode = NULL) {
$output = '';
foreach ($units as $key => $value) {
$key = explode('|', $key);
if ($timestamp >= $value) {
$output .= ($output ? ' ' : '') . format_plural(floor($timestamp / $value), $key[0], $key[1], array(), array('langcode' => $langcode));
$timestamp %= $value;
if ($interval >= $value) {
$output .= ($output ? ' ' : '') . format_plural(floor($interval / $value), $key[0], $key[1], array(), array('langcode' => $langcode));
$interval %= $value;
$granularity--;
}
......@@ -1928,10 +2033,11 @@ function format_date($timestamp, $type = 'medium', $format = '', $timezone = NUL
/**
* Returns an ISO8601 formatted date based on the given date.
*
* Can be used as a callback for RDF mappings.
* Callback for use within hook_rdf_mapping() implementations.
*
* @param $date
* A UNIX timestamp.
*
* @return string
* An ISO8601 formatted date.
*/
......@@ -1942,7 +2048,9 @@ function date_iso8601($date) {
}
/**
* Callback function for preg_replace_callback().
* Translates a formatted date string.
*
* Callback for preg_replace_callback() within format_date().
*/
function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
// We cache translations to avoid redundant and rather costly calls to t().
......@@ -2009,8 +2117,9 @@ function format_username($account) {
* alternative than url().
*
* @param $path
* The internal path or external URL being linked to, such as "node/34" or
* "http://example.com/foo". A few notes:
* (optional) The internal path or external URL being linked to, such as
* "node/34" or "http://example.com/foo". The default value is equivalent to
* passing in '<front>'. A few notes:
* - If you provide a full URL, it will be considered an external URL.
* - If you provide only the path (e.g. "node/34"), it will be
* considered an internal link. In this case, it should be a system URL,
......@@ -2026,7 +2135,8 @@ function format_username($account) {
* include them in $path, or use $options['query'] to let this function
* URL encode them.
* @param $options
* An associative array of additional options, with the following elements:
* (optional) An associative array of additional options, with the following
* elements:
* - 'query': An array of query key/value-pairs (without any URL-encoding) to
* append to the URL.
* - 'fragment': A fragment identifier (named anchor) to append to the URL.
......@@ -2042,7 +2152,7 @@ function format_username($account) {
* for the URL. If $options['language'] is omitted, the global $language_url
* will be used.
* - 'https': Whether this URL should point to a secure location. If not
* defined, the current scheme is used, so the user stays on http or https
* defined, the current scheme is used, so the user stays on HTTP or HTTPS
* respectively. TRUE enforces HTTPS and FALSE enforces HTTP, but HTTPS can
* only be enforced when the variable 'https' is set to TRUE.
* - 'base_url': Only used internally, to modify the base URL when a language
......@@ -2057,8 +2167,8 @@ function format_username($account) {
* Drupal on a web server that cannot be configured to automatically find
* index.php, then hook_url_outbound_alter() can be implemented to force
* this value to 'index.php'.
* - 'entity_type': The entity type of the object that called url(). Only set if
* url() is invoked by entity_uri().
* - 'entity_type': The entity type of the object that called url(). Only
* set if url() is invoked by entity_uri().
* - 'entity': The entity object (such as a node) for which the URL is being
* generated. Only set if url() is invoked by entity_uri().
*
......@@ -2186,7 +2296,7 @@ function url($path = NULL, array $options = array()) {
}
/**
* Return TRUE if a path is external to Drupal (e.g. http://example.com).
* Returns TRUE if a path is external to Drupal (e.g. http://example.com).
*
* If a path cannot be assessed by Drupal's menu handler, then we must
* treat it as potentially insecure.
......@@ -2194,18 +2304,20 @@ function url($path = NULL, array $options = array()) {
* @param $path
* The internal path or external URL being linked to, such as "node/34" or
* "http://example.com/foo".
*
* @return
* Boolean TRUE or FALSE, where TRUE indicates an external path.
*/
function url_is_external($path) {
$colonpos = strpos($path, ':');
// Only call the slow drupal_strip_dangerous_protocols() if $path contains a
// ':' before any / ? or #.
// Avoid calling drupal_strip_dangerous_protocols() if there is any
// slash (/), hash (#) or question_mark (?) before the colon (:)
// occurrence - if any - as this would clearly mean it is not a URL.
return $colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && drupal_strip_dangerous_protocols($path) == $path;
}
/**
* Format an attribute string for a HTTP header.
* Formats an attribute string for an HTTP header.
*
* @param $attributes
* An associative array of attributes such as 'rel'.
......@@ -2227,7 +2339,7 @@ function drupal_http_header_attributes(array $attributes = array()) {
}
/**
* Converts an associative array to an attribute string for use in XML/HTML tags.
* Converts an associative array to an XML/HTML tag attribute string.
*
* Each array key and its value will be formatted into an attribute string.
* If a value is itself an array, then its elements are concatenated to a single
......@@ -2273,21 +2385,30 @@ function drupal_attributes(array $attributes = array()) {
/**
* Formats an internal or external URL link as an HTML anchor tag.
*
* This function correctly handles aliased paths, and adds an 'active' class
* This function correctly handles aliased paths and adds an 'active' class
* attribute to links that point to the current page (for theming), so all
* internal links output by modules should be generated by this function if
* possible.
*
* @param $text
* The link text for the anchor tag.
* @param $path
* However, for links enclosed in translatable text you should use t() and
* embed the HTML anchor tag directly in the translated string. For example:
* @code
* t('Visit the <a href="@url">settings</a> page', array('@url' => url('admin')));
* @endcode
* This keeps the context of the link title ('settings' in the example) for
* translators.
*
* @param string $text
* The translated link text for the anchor tag.
* @param string $path
* The internal path or external URL being linked to, such as "node/34" or
* "http://example.com/foo". After the url() function is called to construct
* the URL from $path and $options, the resulting URL is passed through
* check_plain() before it is inserted into the HTML anchor tag, to ensure
* well-formed HTML. See url() for more information and notes.
* @param array $options
* An associative array of additional options, with the following elements:
* An associative array of additional options. Defaults to an empty array. It
* may contain the following elements.
* - 'attributes': An associative array of HTML attributes to apply to the
* anchor tag. If element 'class' is included, it must be an array; 'title'
* must be a string; other elements are more flexible, as they just need
......@@ -2303,8 +2424,10 @@ function drupal_attributes(array $attributes = array()) {
* well as the path must match). This element is also used by url().
* - Additional $options elements used by the url() function.
*
* @return
* @return string
* An HTML string containing a link to the given path.
*
* @see url()
*/
function l($text, $path, array $options = array()) {
global $language_url;
......@@ -2340,7 +2463,7 @@ function l($text, $path, array $options = array()) {
// rendering.
if (variable_get('theme_link', TRUE)) {
drupal_theme_initialize();
$registry = theme_get_registry();
$registry = theme_get_registry(FALSE);
// We don't want to duplicate functionality that's in theme(), so any
// hint of a module or theme doing anything at all special with the 'link'
// theme hook should simply result in theme() being called. This includes
......@@ -2448,7 +2571,7 @@ function drupal_deliver_page($page_callback_result, $default_delivery_callback =
}
/**
* Package and send the result of a page callback to the browser as HTML.
* Packages and sends the result of a page callback to the browser as HTML.
*
* @param $page_callback_result
* The result of a page callback. Can be one of:
......@@ -2468,6 +2591,10 @@ function drupal_deliver_html_page($page_callback_result) {
drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
}
// Send appropriate HTTP-Header for browsers and search engines.
global $language;
drupal_add_http_header('Content-Language', $language->language);
// Menu status constants are integers; page content is a string or array.
if (is_int($page_callback_result)) {
// @todo: Break these up into separate functions?
......@@ -2478,6 +2605,9 @@ function drupal_deliver_html_page($page_callback_result) {
watchdog('page not found', check_plain($_GET['q']), NULL, WATCHDOG_WARNING);
// Check for and return a fast 404 page if configured.
drupal_fast_404();
// Keep old path for reference, and to allow forms to redirect to it.
if (!isset($_GET['destination'])) {
$_GET['destination'] = $_GET['q'];
......@@ -2494,7 +2624,7 @@ function drupal_deliver_html_page($page_callback_result) {
if (empty($return) || $return == MENU_NOT_FOUND || $return == MENU_ACCESS_DENIED) {
// Standard 404 handler.
drupal_set_title(t('Page not found'));
$return = t('The requested page could not be found.');
$return = t('The requested page "@path" could not be found.', array('@path' => request_uri()));
}
drupal_set_page_content($return);
......@@ -2550,7 +2680,7 @@ function drupal_deliver_html_page($page_callback_result) {
}
/**
* Perform end-of-request tasks.
* Performs end-of-request tasks.
*
* This function sets the page cache if appropriate, and allows modules to
* react to the closing of the page by calling hook_exit().
......@@ -2577,7 +2707,7 @@ function drupal_page_footer() {
}
/**
* Perform end-of-request tasks.
* Performs end-of-request tasks.
*
* In some cases page requests need to end without calling drupal_page_footer().
* In these cases, call drupal_exit() instead. There should rarely be a reason
......@@ -2599,7 +2729,7 @@ function drupal_exit($destination = NULL) {
}
/**
* Form an associative array from a linear array.
* Forms an associative array from a linear array.
*
* This function walks through the provided array and constructs an associative
* array out of it. The keys of the resulting array will be the values of the
......@@ -2668,17 +2798,17 @@ function drupal_set_time_limit($time_limit) {
* The name of the item for which the path is requested.
*
* @return
* The path to the requested item.
* 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));
}
/**
* Return the base URL path (i.e., directory) of the Drupal installation.
* Returns the base URL path (i.e., directory) of the Drupal installation.
*
* base_path() prefixes and suffixes a "/" onto the returned path if the path is
* not empty. At the very least, this will return "/".
* base_path() adds a "/" to the beginning and end of the returned path if the
* path is not empty. At the very least, this will return "/".
*
* Examples:
* - http://example.com returns "/" because the path is empty.
......@@ -2689,12 +2819,12 @@ function base_path() {
}
/**
* Add a LINK tag with a distinct 'rel' attribute to the page's HEAD.
* Adds a LINK tag with a distinct 'rel' attribute to the page's HEAD.
*
* This function can be called as long the HTML header hasn't been sent,
* which on normal pages is up through the preprocess step of theme('html').
* Adding a link will overwrite a prior link with the exact same 'rel' and
* 'href' attributes.
* This function can be called as long the HTML header hasn't been sent, which
* on normal pages is up through the preprocess step of theme('html'). Adding
* a link will overwrite a prior link with the exact same 'rel' and 'href'
* attributes.
*
* @param $attributes
* Associative array of element attributes including 'href' and 'rel'.
......@@ -2758,8 +2888,8 @@ function drupal_add_html_head_link($attributes, $header = FALSE) {
* See drupal_get_css() where the overrides are performed. Also, if the
* direction of the current language is right-to-left (Hebrew, Arabic,
* etc.), the function will also look for an RTL CSS file and append it to
* the list. The name of this file should have an '-rtl.css' suffix. For
* example a CSS file called 'mymodule-name.css' will have a
* the list. The name of this file should have an '-rtl.css' suffix. For
* example, a CSS file called 'mymodule-name.css' will have a
* 'mymodule-name-rtl.css' file added to the list, if exists in the same
* directory. This CSS file should contain overrides for properties which
* should be reversed or otherwise different in a right-to-left display.
......@@ -2784,7 +2914,7 @@ function drupal_add_html_head_link($attributes, $header = FALSE) {
* - 'group': A number identifying the group in which to add the stylesheet.
* Available constants are:
* - CSS_SYSTEM: Any system-layer CSS.
* - CSS_DEFAULT: Any module-layer CSS.
* - CSS_DEFAULT: (default) Any module-layer CSS.
* - CSS_THEME: Any theme-layer CSS.
* The group number serves as a weight: the markup for loading a stylesheet
* within a lower weight group is output to the page before the markup for
......@@ -2891,7 +3021,7 @@ function drupal_add_css($data = NULL, $options = NULL) {
}
/**
* Returns a themed representation of all stylesheets that should be attached to the page.
* Returns a themed representation of all stylesheets to attach to the page.
*
* It loads the CSS in order, with 'module' first, then 'theme' afterwards.
* This ensures proper cascading of styles so themes can easily override
......@@ -2932,12 +3062,24 @@ function drupal_get_css($css = NULL, $skip_alter = FALSE) {
// Sort CSS items, so that they appear in the correct order.
uasort($css, 'drupal_sort_css_js');
// Provide the page with information about the individual CSS files used,
// information not otherwise available when CSS aggregation is enabled. The
// setting is attached later in this function, but is set here, so that CSS
// files removed below are still considered "used" and prevented from being
// added in a later AJAX request.
// Skip if no files were added to the page or jQuery.extend() will overwrite
// the Drupal.settings.ajaxPageState.css object with an empty array.
if (!empty($css)) {
// Cast the array to an object to be on the safe side even if not empty.
$setting['ajaxPageState']['css'] = (object) array_fill_keys(array_keys($css), 1);
}
// Remove the overridden CSS files. Later CSS files override former ones.
$previous_item = array();
foreach ($css as $key => $item) {
if ($item['type'] == 'file') {
// If defined, force a unique basename for this file.
$basename = isset($item['basename']) ? $item['basename'] : basename($item['data']);
$basename = isset($item['basename']) ? $item['basename'] : drupal_basename($item['data']);
if (isset($previous_item[$basename])) {
// Remove the previous item that shared the same base name.
unset($css[$previous_item[$basename]]);
......@@ -2952,20 +3094,32 @@ function drupal_get_css($css = NULL, $skip_alter = FALSE) {
'#items' => $css,
);
// Provide the page with information about the individual CSS files used,
// information not otherwise available when CSS aggregation is enabled.
$setting['ajaxPageState']['css'] = array_fill_keys(array_keys($css), 1);
$styles['#attached']['js'][] = array('type' => 'setting', 'data' => $setting);
if (!empty($setting)) {
$styles['#attached']['js'][] = array('type' => 'setting', 'data' => $setting);
}
return drupal_render($styles);
}
/**
* Function used by uasort to sort the array structures returned by drupal_add_css() and drupal_add_js().
* Sorts CSS and JavaScript resources.
*
* Callback for uasort() within:
* - drupal_get_css()
* - drupal_get_js()
*
* This sort order helps optimize front-end performance while providing modules
* and themes with the necessary control for ordering the CSS and JavaScript
* appearing on a page.
*
* @param $a
* First item for comparison. The compared items should be associative arrays
* of member items from drupal_add_css() or drupal_add_js().
* @param $b
* Second item for comparison.
*
* @see drupal_add_css()
* @see drupal_add_js()
*/
function drupal_sort_css_js($a, $b) {
// First order by group, so that, for example, all items in the CSS_SYSTEM
......@@ -3018,7 +3172,7 @@ function drupal_sort_css_js($a, $b) {
* are always groupable, and items of the 'external' type are never groupable.
* This function also ensures that the process of grouping items does not change
* their relative order. This requirement may result in multiple groups for the
* same type, media, and browsers, if needed to accomodate other items in
* same type, media, and browsers, if needed to accommodate other items in
* between.
*
* @param $css
......@@ -3032,6 +3186,7 @@ function drupal_sort_css_js($a, $b) {
* 'items' key, which is the subset of items from $css that are in the group.
*
* @see drupal_pre_render_styles()
* @see system_element_info()
*/
function drupal_group_css($css) {
$groups = array();
......@@ -3114,6 +3269,7 @@ function drupal_group_css($css) {
*
* @see drupal_group_css()
* @see drupal_pre_render_styles()
* @see system_element_info()
*/
function drupal_aggregate_css(&$css_groups) {
$preprocess_css = (variable_get('preprocess_css', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
......@@ -3375,8 +3531,8 @@ function drupal_pre_render_styles($elements) {
* in $css while the value is the cache file name. The cache file is generated
* in two cases. First, if there is no file name value for the key, which will
* happen if a new file name has been added to $css or after the lookup
* variable is emptied to force a rebuild of the cache. Second, the cache
* file is generated if it is missing on disk. Old cache files are not deleted
* variable is emptied to force a rebuild of the cache. Second, the cache file
* is generated if it is missing on disk. Old cache files are not deleted
* immediately when the lookup variable is emptied, but are deleted after a set
* period by drupal_delete_file_if_stale(). This ensures that files referenced
* by a cached page will still be available.
......@@ -3391,7 +3547,13 @@ function drupal_build_css_cache($css) {
$data = '';
$uri = '';
$map = variable_get('drupal_css_cache_files', array());
$key = hash('sha256', serialize($css));
// Create a new array so that only the file names are used to create the hash.
// This prevents new aggregates from being created unnecessarily.
$css_data = array();
foreach ($css as $css_file) {
$css_data[] = $css_file['data'];
}
$key = hash('sha256', serialize($css_data));
if (isset($map[$key])) {
$uri = $map[$key];
}
......@@ -3454,9 +3616,7 @@ function drupal_build_css_cache($css) {
}
/**
* Helper function for drupal_build_css_cache().
*
* This function will prefix all paths within a CSS file.
* Prefixes all paths within a CSS file for drupal_build_css_cache().
*/
function _drupal_build_css_path($matches, $base = NULL) {
$_base = &drupal_static(__FUNCTION__);
......@@ -3527,13 +3687,14 @@ function drupal_load_stylesheet($file, $optimize = NULL, $reset_basepath = TRUE)
}
/**
* Process the contents of a stylesheet for aggregation.
* Processes the contents of a stylesheet for aggregation.
*
* @param $contents
* The contents of the stylesheet.
* @param $optimize
* (optional) Boolean whether CSS contents should be minified. Defaults to
* FALSE.
*
* @return
* Contents of the stylesheet including the imported stylesheets.
*/
......@@ -3629,7 +3790,7 @@ function drupal_delete_file_if_stale($uri) {
}
/**
* Prepare a string for use as a valid CSS identifier (element, class or ID name).
* Prepares a string for use as a CSS identifier (element, class, or ID name).
*
* http://www.w3.org/TR/CSS21/syndata.html#characters shows the syntax for valid
* CSS identifiers (including element names, classes, and IDs in selectors.)
......@@ -3638,6 +3799,7 @@ function drupal_delete_file_if_stale($uri) {
* The identifier to clean.
* @param $filter
* An array of string replacements to use on the identifier.
*
* @return
* The cleaned identifier.
*/
......@@ -3659,13 +3821,14 @@ function drupal_clean_css_identifier($identifier, $filter = array(' ' => '-', '_
}
/**
* Prepare a string for use as a valid class name.
* Prepares a string for use as a valid class name.
*
* Do not pass one string containing multiple classes as they will be
* incorrectly concatenated with dashes, i.e. "one two" will become "one-two".
*
* @param $class
* The class name to clean.
*
* @return
* The cleaned class name.
*/
......@@ -3674,7 +3837,7 @@ function drupal_html_class($class) {
}
/**
* Prepare a string for use as a valid HTML ID and guarantee uniqueness.
* Prepares a string for use as a valid HTML ID and guarantees uniqueness.
*
* This function ensures that each passed HTML ID value only exists once on the
* page. By tracking the already returned ids, this function enables forms,
......@@ -3725,7 +3888,16 @@ function drupal_html_id($id) {
// requested id. $_POST['ajax_html_ids'] contains the ids as they were
// returned by this function, potentially with the appended counter, so
// we parse that to reconstruct the $seen_ids array.
foreach ($_POST['ajax_html_ids'] as $seen_id) {
if (is_array($_POST['ajax_html_ids'])) {
$ajax_html_ids = $_POST['ajax_html_ids'];
}
else {
// jquery.form.js may send the server a comma-separated string instead
// of an array (see http://drupal.org/node/1575060), so we need to
// convert it to an array in that case.
$ajax_html_ids = explode(',', $_POST['ajax_html_ids']);
}
foreach ($ajax_html_ids as $seen_id) {
// We rely on '--' being used solely for separating a base id from the
// counter, which this function ensures when returning an id.
$parts = explode('--', $seen_id, 2);
......@@ -3805,7 +3977,7 @@ function drupal_region_class($region) {
* to tell the user that a new message arrived, by opening a pop up, alert
* box, etc.). This should only be used for JavaScript that cannot be executed
* from a file. When adding inline code, make sure that you are not relying on
* $() being the jQuery function. Wrap your code in
* $() being the jQuery function. Wrap your code in
* @code (function ($) {... })(jQuery); @endcode
* or use jQuery() instead of $().
* - Add external JavaScript ('external'): Allows the inclusion of external
......@@ -3851,7 +4023,8 @@ function drupal_region_class($region) {
* actually needed.
*
* @param $data
* (optional) If given, the value depends on the $options parameter:
* (optional) If given, the value depends on the $options parameter, or
* $options['type'] if $options is passed as an associative array:
* - 'file': Path to the file relative to base_path().
* - 'inline': The JavaScript code that should be placed in the given scope.
* - 'external': The absolute path to an external JavaScript file that is not
......@@ -3925,7 +4098,7 @@ function drupal_region_class($region) {
* happened later in the page request gets added to the page after one for
* which drupal_add_js() happened earlier in the page request.
* - defer: If set to TRUE, the defer attribute is set on the &lt;script&gt;
* tag. Defaults to FALSE.
* tag. Defaults to FALSE.
* - cache: If set to FALSE, the JavaScript file is loaded anew on every page
* call; in other words, it is not cached. Used only when 'type' references
* a JavaScript file. Defaults to TRUE.
......@@ -4022,6 +4195,7 @@ function drupal_add_js($data = NULL, $options = NULL) {
*
* @param $data
* (optional) The default data parameter for the JavaScript item array.
*
* @see drupal_get_js()
* @see drupal_add_js()
*/
......@@ -4065,8 +4239,10 @@ function drupal_js_defaults($data = NULL) {
* (optional) If set to TRUE, this function skips calling drupal_alter() on
* $javascript, useful when the calling function passes a $javascript array
* that has already been altered.
*
* @return
* All JavaScript code segments and includes for the scope as HTML tags.
*
* @see drupal_add_js()
* @see locale_js_alter()
* @see drupal_js_defaults()
......@@ -4238,7 +4414,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS
* );
* @endcode
*
* 'js', 'css', and 'library' are types that get special handling. For any
* 'js', 'css', and 'library' are types that get special handling. For any
* other kind of attached data, the array key must be the full name of the
* callback function and each value an array of arguments. For example:
* @code
......@@ -4589,16 +4765,16 @@ function drupal_get_library($module, $name = NULL) {
}
/**
* Assist in adding the tableDrag JavaScript behavior to a themed table.
* Assists in adding the tableDrag JavaScript behavior to a themed table.
*
* Draggable tables should be used wherever an outline or list of sortable items
* needs to be arranged by an end-user. Draggable tables are very flexible and
* can manipulate the value of form elements placed within individual columns.
*
* To set up a table to use drag and drop in place of weight select-lists or
* in place of a form that contains parent relationships, the form must be
* themed into a table. The table must have an id attribute set. If using
* theme_table(), the id may be set as such:
* To set up a table to use drag and drop in place of weight select-lists or in
* place of a form that contains parent relationships, the form must be themed
* into a table. The table must have an ID attribute set. If using
* theme_table(), the ID may be set as follows:
* @code
* $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'my-module-table')));
* return $output;
......@@ -4613,8 +4789,8 @@ function drupal_get_library($module, $name = NULL) {
* $form['my_elements'][$delta]['weight']['#attributes']['class'] = array('my-elements-weight');
* @endcode
*
* Each row of the table must also have a class of "draggable" in order to enable the
* drag handles:
* Each row of the table must also have a class of "draggable" in order to
* enable the drag handles:
* @code
* $row = array(...);
* $rows[] = array(
......@@ -4634,8 +4810,8 @@ function drupal_get_library($module, $name = NULL) {
* @endcode
*
* In a more complex case where there are several groups in one column (such as
* the block regions on the admin/structure/block page), a separate subgroup class
* must also be added to differentiate the groups.
* the block regions on the admin/structure/block page), a separate subgroup
* class must also be added to differentiate the groups.
* @code
* $form['my_elements'][$region][$delta]['weight']['#attributes']['class'] = array('my-elements-weight', 'my-elements-weight-' . $region);
* @endcode
......@@ -4652,14 +4828,14 @@ function drupal_get_library($module, $name = NULL) {
*
* In a situation where tree relationships are present, adding multiple
* subgroups is not necessary, because the table will contain indentations that
* provide enough information about the sibling and parent relationships.
* See theme_menu_overview_form() for an example creating a table containing
* parent relationships.
*
* Please note that this function should be called from the theme layer, such as
* in a .tpl.php file, theme_ function, or in a template_preprocess function,
* not in a form declaration. Though the same JavaScript could be added to the
* page using drupal_add_js() directly, this function helps keep template files
* provide enough information about the sibling and parent relationships. See
* theme_menu_overview_form() for an example creating a table containing parent
* relationships.
*
* Note that this function should be called from the theme layer, such as in a
* .tpl.php file, theme_ function, or in a template_preprocess function, not in
* a form declaration. Though the same JavaScript could be added to the page
* using drupal_add_js() directly, this function helps keep template files
* clean and readable. It also prevents tabledrag.js from being added twice
* accidentally.
*
......@@ -4732,8 +4908,8 @@ function drupal_add_tabledrag($table_id, $action, $relationship, $group, $subgro
* $files while the value is the cache file name. The cache file is generated
* in two cases. First, if there is no file name value for the key, which will
* happen if a new file name has been added to $files or after the lookup
* variable is emptied to force a rebuild of the cache. Second, the cache
* file is generated if it is missing on disk. Old cache files are not deleted
* variable is emptied to force a rebuild of the cache. Second, the cache file
* is generated if it is missing on disk. Old cache files are not deleted
* immediately when the lookup variable is emptied, but are deleted after a set
* period by drupal_delete_file_if_stale(). This ensures that files referenced
* by a cached page will still be available.
......@@ -4748,7 +4924,13 @@ function drupal_build_js_cache($files) {
$contents = '';
$uri = '';
$map = variable_get('drupal_js_cache_files', array());
$key = hash('sha256', serialize($files));
// Create a new array so that only the file names are used to create the hash.
// This prevents new aggregates from being created unnecessarily.
$js_data = array();
foreach ($files as $file) {
$js_data[] = $file['data'];
}
$key = hash('sha256', serialize($js_data));
if (isset($map[$key])) {
$uri = $map[$key];
}
......@@ -4799,14 +4981,29 @@ function drupal_clear_js_cache() {
/**
* Converts a PHP variable into its JavaScript equivalent.
*
* We use HTML-safe strings, i.e. with <, > and & escaped.
* We use HTML-safe strings, with several characters escaped.
*
* @see drupal_json_decode()
* @see drupal_json_encode_helper()
* @ingroup php_wrappers
*/
function drupal_json_encode($var) {
// json_encode() does not escape <, > and &, so we do it with str_replace().
return str_replace(array('<', '>', '&'), array('\u003c', '\u003e', '\u0026'), json_encode($var));
// The PHP version cannot change within a request.
static $php530;
if (!isset($php530)) {
$php530 = version_compare(PHP_VERSION, '5.3.0', '>=');
}
if ($php530) {
// Encode <, >, ', &, and " using the json_encode() options parameter.
return json_encode($var, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT);
}
// json_encode() escapes <, >, ', &, and " using its options parameter, but
// does not support this parameter prior to PHP 5.3.0. Use a helper instead.
include_once DRUPAL_ROOT . '/includes/json-encode.inc';
return drupal_json_encode_helper($var);
}
/**
......@@ -4820,7 +5017,7 @@ function drupal_json_decode($var) {
}
/**
* Return data in JSON format.
* Returns data in JSON format.
*
* This function should be used for JavaScript callback functions returning
* data in JSON format. It sets the header for JavaScript output.
......@@ -4838,7 +5035,7 @@ function drupal_json_output($var = NULL) {
}
/**
* Get a salt useful for hardening against SQL injection.
* Gets a salt useful for hardening against SQL injection.
*
* @return
* A salt based on information in settings.php, not in the database.
......@@ -4851,7 +5048,7 @@ function drupal_get_hash_salt() {
}
/**
* Ensure the private key variable used to generate tokens is set.
* Ensures the private key variable used to generate tokens is set.
*
* @return
* The private key.
......@@ -4865,17 +5062,22 @@ function drupal_get_private_key() {
}
/**
* Generate a token based on $value, the current user session and private key.
* Generates a token based on $value, the user session, and the private key.
*
* @param $value
* An additional value to base the token on.
*
* @return string
* A 43-character URL-safe token for validation, based on the user session ID,
* the global $drupal_hash_salt variable from settings.php, and the
* 'drupal_private_key' configuration variable.
*/
function drupal_get_token($value = '') {
return drupal_hmac_base64($value, session_id() . drupal_get_private_key() . drupal_get_hash_salt());
}
/**
* Validate a token based on $value, the current user session and private key.
* Validates a token based on $value, the user session, and the private key.
*
* @param $token
* The token to be validated.
......@@ -4883,6 +5085,7 @@ function drupal_get_token($value = '') {
* An additional value to base the token on.
* @param $skip_anonymous
* Set to true to skip token validation for anonymous users.
*
* @return
* True for a valid token, false for an invalid token. When $skip_anonymous
* is true, the return value will always be true for anonymous users.
......@@ -4951,7 +5154,7 @@ function _drupal_bootstrap_full() {
}
/**
* Store the current page in the cache.
* Stores the current page in the cache.
*
* If page_compression is enabled, a gzipped version of the page is stored in
* the cache to avoid compressing the output on each request. The cache entry
......@@ -5003,16 +5206,17 @@ function drupal_page_set_cache() {
/**
* Executes a cron run when called.
*
* Do not call this function from test, use $this->cronRun() instead.
* Do not call this function from a test. Use $this->cronRun() instead.
*
* @return
* Returns TRUE if ran successfully
* TRUE if cron ran successfully.
*/
function drupal_cron_run() {
// Allow execution to continue even if the request gets canceled.
@ignore_user_abort(TRUE);
// Prevent session information from being saved while cron is running.
$original_session_saving = drupal_save_session();
drupal_save_session(FALSE);
// Force the current user to anonymous to ensure consistent permissions on
......@@ -5039,7 +5243,7 @@ function drupal_cron_run() {
foreach ($queues as $queue_name => $info) {
DrupalQueue::get($queue_name)->createQueue();
}
// Register shutdown callback
// Register shutdown callback.
drupal_register_shutdown_function('drupal_cron_cleanup');
// Iterate through the modules calling their cron handlers (if any):
......@@ -5053,7 +5257,7 @@ function drupal_cron_run() {
}
}
// Record cron time
// Record cron time.
variable_set('cron_last', REQUEST_TIME);
watchdog('cron', 'Cron run completed.', array(), WATCHDOG_NOTICE);
......@@ -5075,20 +5279,23 @@ function drupal_cron_run() {
}
// Restore the user.
$GLOBALS['user'] = $original_user;
drupal_save_session(TRUE);
drupal_save_session($original_session_saving);
return $return;
}
/**
* Shutdown function for cron cleanup.
* Shutdown function: Performs cron cleanup.
*
* @see drupal_cron_run()
* @see drupal_register_shutdown_function()
*/
function drupal_cron_cleanup() {
// See if the semaphore is still locked.
if (variable_get('cron_semaphore', FALSE)) {
watchdog('cron', 'Cron run exceeded the time limit and was aborted.', array(), WATCHDOG_WARNING);
// Release cron semaphore
// Release cron semaphore.
variable_del('cron_semaphore');
}
}
......@@ -5104,7 +5311,7 @@ function drupal_cron_cleanup() {
* drupal_system_listing("/\.module$/", "modules", 'name', 0);
* @endcode
* this function will search the site-wide modules directory (i.e., /modules/),
* your install profile's directory (i.e.,
* your installation profile's directory (i.e.,
* /profiles/your_site_profile/modules/), the all-sites directory (i.e.,
* /sites/all/modules/), and your site-specific directory (i.e.,
* /sites/your_site_dir/modules/), in that order, and return information about
......@@ -5144,8 +5351,6 @@ function drupal_cron_cleanup() {
function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1) {
$config = conf_path();
$profile = drupal_get_profile();
$searchdir = array($directory);
$files = array();
......@@ -5153,18 +5358,34 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1)
// themes as organized by a distribution. It is pristine in the same way
// that /modules is pristine for core; users should avoid changing anything
// there in favor of sites/all or sites/<domain> directories.
if (file_exists("profiles/$profile/$directory")) {
$searchdir[] = "profiles/$profile/$directory";
$profiles = array();
$profile = drupal_get_profile();
// For SimpleTest to be able to test modules packaged together with a
// distribution we need to include the profile of the parent site (in which
// test runs are triggered).
if (drupal_valid_test_ua()) {
$testing_profile = variable_get('simpletest_parent_profile', FALSE);
if ($testing_profile && $testing_profile != $profile) {
$profiles[] = $testing_profile;
}
}
// In case both profile directories contain the same extension, the actual
// profile always has precedence.
$profiles[] = $profile;
foreach ($profiles as $profile) {
if (file_exists("profiles/$profile/$directory")) {
$searchdir[] = "profiles/$profile/$directory";
}
}
// Always search sites/all/* as well as the global directories
// Always search sites/all/* as well as the global directories.
$searchdir[] = 'sites/all/' . $directory;
if (file_exists("$config/$directory")) {
$searchdir[] = "$config/$directory";
}
// Get current list of items
// Get current list of items.
if (!function_exists('file_scan_directory')) {
require_once DRUPAL_ROOT . '/includes/file.inc';
}
......@@ -5200,7 +5421,7 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1)
}
/**
* Set the main page content value for later use.
* Sets the main page content value for later use.
*
* Given the nature of the Drupal page handling, this will be called once with
* a string or array. We store that and return it later as the block is being
......@@ -5208,6 +5429,7 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1)
*
* @param $content
* A string or renderable array representing the body of the page.
*
* @return
* If called without $content, a renderable array representing the body of
* the page.
......@@ -5379,7 +5601,7 @@ function drupal_pre_render_link($element) {
* @code
* $node->content['links'] = array(
* '#theme' => 'links__node',
* '#pre_render' = array('drupal_pre_render_links'),
* '#pre_render' => array('drupal_pre_render_links'),
* 'comment' => array(
* '#theme' => 'links__node__comment',
* '#links' => array(
......@@ -5458,13 +5680,13 @@ function drupal_pre_render_links($element) {
* Note that if also a #theme is defined for the element, then the result of
* the theme callback will override #children.
*
* @see drupal_render()
*
* @param $elements
* A structured array using the #markup key.
*
* @return
* The passed-in elements, but #markup appended to #children.
*
* @see drupal_render()
*/
function drupal_pre_render_markup($elements) {
$elements['#children'] = $elements['#markup'];
......@@ -5477,8 +5699,10 @@ function drupal_pre_render_markup($elements) {
* @param $page
* A string or array representing the content of a page. The array consists of
* the following keys:
* - #type: Value is always 'page'. This pushes the theming through page.tpl.php (required).
* - #show_messages: Suppress drupal_get_message() items. Used by Batch API (optional).
* - #type: Value is always 'page'. This pushes the theming through
* page.tpl.php (required).
* - #show_messages: Suppress drupal_get_message() items. Used by Batch
* API (optional).
*
* @see hook_page_alter()
* @see element_info()
......@@ -5518,15 +5742,25 @@ function drupal_render_page($page) {
*
* Recursively iterates over each of the array elements, generating HTML code.
*
* HTML generation is controlled by two properties containing theme functions,
* #theme and #theme_wrappers.
* Renderable arrays have two kinds of key/value pairs: properties and
* children. Properties have keys starting with '#' and their values influence
* how the array will be rendered. Children are all elements whose keys do not
* start with a '#'. Their values should be renderable arrays themselves,
* which will be rendered during the rendering of the parent array. The markup
* provided by the children is typically inserted into the markup generated by
* the parent array.
*
* HTML generation for a renderable array, and the treatment of any children,
* is controlled by two properties containing theme functions, #theme and
* #theme_wrappers.
*
* #theme is the theme function called first. If it is set and the element has
* any children, they have to be rendered there. For elements that are not
* allowed to have any children, e.g. buttons or textfields, it can be used to
* render the element itself. If #theme is not present and the element has
* children, they are rendered and concatenated into a string by
* drupal_render_children().
* any children, it is the responsibility of the theme function to render
* these children. For elements that are not allowed to have any children,
* e.g. buttons or textfields, the theme function can be used to render the
* element itself. If #theme is not present and the element has children, each
* child is itself rendered by a call to drupal_render(), and the results are
* concatenated.
*
* The #theme_wrappers property contains an array of theme functions which will
* be called, in order, after #theme has run. These can be used to add further
......@@ -5546,20 +5780,20 @@ function drupal_render_page($page) {
* drupal_render() can optionally cache the rendered output of elements to
* improve performance. To use drupal_render() caching, set the element's #cache
* property to an associative array with one or several of the following keys:
* - 'keys': An array of one or more keys that identify the element. If 'keys'
* is set, the cache ID is created automatically from these keys. See
* drupal_render_cid_create().
* - 'granularity' (optional): Define the cache granularity using binary
* combinations of the cache granularity constants, e.g. DRUPAL_CACHE_PER_USER
* to cache for each user separately or
* DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE to cache separately for each
* page and role. If not specified the element is cached globally for each
* theme and language.
* - 'cid': Specify the cache ID directly. Either 'keys' or 'cid' is required.
* If 'cid' is set, 'keys' and 'granularity' are ignored. Use only if you
* have special requirements.
* - 'expire': Set to one of the cache lifetime constants.
* - 'bin': Specify a cache bin to cache the element in. Defaults to 'cache'.
* - 'keys': An array of one or more keys that identify the element. If 'keys'
* is set, the cache ID is created automatically from these keys. See
* drupal_render_cid_create().
* - 'granularity' (optional): Define the cache granularity using binary
* combinations of the cache granularity constants, e.g.
* DRUPAL_CACHE_PER_USER to cache for each user separately or
* DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE to cache separately for each
* page and role. If not specified the element is cached globally for each
* theme and language.
* - 'cid': Specify the cache ID directly. Either 'keys' or 'cid' is required.
* If 'cid' is set, 'keys' and 'granularity' are ignored. Use only if you
* have special requirements.
* - 'expire': Set to one of the cache lifetime constants.
* - 'bin': Specify a cache bin to cache the element in. Defaults to 'cache'.
*
* This function is usually called from within another function, like
* drupal_get_form() or a theme function. Elements are sorted internally
......@@ -5576,6 +5810,7 @@ function drupal_render_page($page) {
*
* @param $elements
* The structured array describing the data to be rendered.
*
* @return
* The rendered HTML.
*/
......@@ -5591,8 +5826,11 @@ function drupal_render(&$elements) {
}
// Try to fetch the element's markup from cache and return.
if (isset($elements['#cache']) && $cached_output = drupal_render_cache_get($elements)) {
return $cached_output;
if (isset($elements['#cache'])) {
$cached_output = drupal_render_cache_get($elements);
if ($cached_output !== FALSE) {
return $cached_output;
}
}
// If #markup is set, ensure #type is set. This allows to specify just #markup
......@@ -5689,7 +5927,7 @@ function drupal_render(&$elements) {
}
/**
* Render children of an element and concatenate them.
* Renders children of an element and concatenates them.
*
* This renders all children of an element using drupal_render() and then
* joins them together into a single string.
......@@ -5714,7 +5952,7 @@ function drupal_render_children(&$element, $children_keys = NULL) {
}
/**
* Render an element.
* Renders an element.
*
* This function renders an element using drupal_render(). The top level
* element is shown with show() before rendering, so it will always be rendered
......@@ -5743,7 +5981,7 @@ function render(&$element) {
}
/**
* Hide an element from later rendering.
* Hides an element from later rendering.
*
* The first time render() or drupal_render() is called on an element tree,
* as each element in the tree is rendered, it is marked with a #printed flag
......@@ -5769,7 +6007,7 @@ function hide(&$element) {
}
/**
* Show a hidden element for later rendering.
* Shows a hidden element for later rendering.
*
* You can also use render($element), which shows the element while rendering
* it.
......@@ -5798,16 +6036,17 @@ function show(&$element) {
}
/**
* Get the rendered output of a renderable element from cache.
*
* @see drupal_render()
* @see drupal_render_cache_set()
* Gets the rendered output of a renderable element from the cache.
*
* @param $elements
* A renderable array.
*
* @return
* A markup string containing the rendered content of the element, or FALSE
* if no cached copy of the element is available.
*
* @see drupal_render()
* @see drupal_render_cache_set()
*/
function drupal_render_cache_get($elements) {
if (!in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD')) || !$cid = drupal_render_cid_create($elements)) {
......@@ -5828,17 +6067,17 @@ function drupal_render_cache_get($elements) {
}
/**
* Cache the rendered output of a renderable element.
* Caches the rendered output of a renderable element.
*
* This is called by drupal_render() if the #cache property is set on an element.
*
* @see drupal_render()
* @see drupal_render_cache_get()
* This is called by drupal_render() if the #cache property is set on an
* element.
*
* @param $markup
* The rendered output string of $elements.
* @param $elements
* A renderable array.
*
* @see drupal_render_cache_get()
*/
function drupal_render_cache_set(&$markup, $elements) {
// Create the cache ID for the element.
......@@ -5864,7 +6103,7 @@ function drupal_render_cache_set(&$markup, $elements) {
}
/**
* Collect #attached for an element and all child elements into a single array.
* Collects #attached for an element and its children into a single array.
*
* When caching elements, it is necessary to collect all libraries, JavaScript
* and CSS into a single array, from both the element itself and all child
......@@ -5907,9 +6146,10 @@ function drupal_render_collect_attached($elements, $return = FALSE) {
}
/**
* Prepare an element for caching based on a query. This smart caching strategy
* saves Drupal from querying and rendering to HTML when the underlying query is
* unchanged.
* Prepares an element for caching based on a query.
*
* This smart caching strategy saves Drupal from querying and rendering to HTML
* when the underlying query is unchanged.
*
* Expensive queries should use the query builder to create the query and then
* call this function. Executing the query and formatting results should happen
......@@ -5947,12 +6187,15 @@ function drupal_render_cache_by_query($query, $function, $expire = CACHE_TEMPORA
}
/**
* Helper function for building cache ids.
* Returns cache ID parts for building a cache ID.
*
* @param $granularity
* One or more cache granularity constants, e.g. DRUPAL_CACHE_PER_USER to cache
* for each user separately or DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE to
* cache separately for each page and role.
* One or more cache granularity constants. For example, to cache separately
* for each user, use DRUPAL_CACHE_PER_USER. To cache separately for each
* page and role, use the expression:
* @code
* DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_ROLE
* @endcode
*
* @return
* An array of cache ID parts, always containing the active theme. If the
......@@ -5991,7 +6234,7 @@ function drupal_render_cid_parts($granularity = NULL) {
}
/**
* Create the cache ID for a renderable element.
* Creates the cache ID for a renderable element.
*
* This creates the cache ID string, either by returning the #cache['cid']
* property if present or by building the cache ID out of the #cache['keys']
......@@ -6038,7 +6281,7 @@ function element_sort_by_title($a, $b) {
}
/**
* Retrieve the default properties for the defined element type.
* Retrieves the default properties for the defined element type.
*
* @param $type
* An element type as defined by hook_element_info().
......@@ -6064,7 +6307,7 @@ function element_info($type) {
}
/**
* Retrieve a single property for the defined element type.
* Retrieves a single property for the defined element type.
*
* @param $type
* An element type as defined by hook_element_info().
......@@ -6079,7 +6322,19 @@ function element_info_property($type, $property_name, $default = NULL) {
}
/**
* Function used by uasort to sort structured arrays by weight, without the property weight prefix.
* Sorts a structured array by the 'weight' element.
*
* Note that the sorting is by the 'weight' array element, not by the render
* element property '#weight'.
*
* Callback for uasort() used in various functions.
*
* @param $a
* First item for comparison. The compared items should be associative arrays
* that optionally include a 'weight' element. For items without a 'weight'
* element, a default value of 0 will be used.
* @param $b
* Second item for comparison.
*/
function drupal_sort_weight($a, $b) {
$a_weight = (is_array($a) && isset($a['weight'])) ? $a['weight'] : 0;
......@@ -6104,33 +6359,37 @@ function drupal_sort_title($a, $b) {
}
/**
* Check if the key is a property.
* Checks if the key is a property.
*/
function element_property($key) {
return $key[0] == '#';
}
/**
* Get properties of a structured array element. Properties begin with '#'.
* Gets properties of a structured array element (keys beginning with '#').
*/
function element_properties($element) {
return array_filter(array_keys((array) $element), 'element_property');
}
/**
* Check if the key is a child.
* Checks if the key is a child.
*/
function element_child($key) {
return !isset($key[0]) || $key[0] != '#';
}
/**
* Return the children of an element, optionally sorted by weight.
* Identifies the children of an element array, optionally sorted by weight.
*
* The children of a element array are those key/value pairs whose key does
* not start with a '#'. See drupal_render() for details.
*
* @param $elements
* The element to be sorted.
* The element array whose children are to be identified.
* @param $sort
* Boolean to indicate whether the children should be sorted by weight.
*
* @return
* The array keys of the element's children.
*/
......@@ -6166,10 +6425,11 @@ function element_children(&$elements, $sort = FALSE) {
}
/**
* Return the visibile children of an element.
* Returns the visible children of an element.
*
* @param $elements
* The parent element.
*
* @return
* The array keys of the element's visible children.
*/
......@@ -6348,7 +6608,7 @@ function drupal_array_set_nested_value(array &$array, array $parents, $value, $f
*
* @see drupal_array_set_nested_value()
*/
function drupal_array_get_nested_value(array &$array, array $parents, &$key_exists = NULL) {
function &drupal_array_get_nested_value(array &$array, array $parents, &$key_exists = NULL) {
$ref = &$array;
foreach ($parents as $parent) {
if (is_array($ref) && array_key_exists($parent, $ref)) {
......@@ -6356,7 +6616,8 @@ function drupal_array_get_nested_value(array &$array, array $parents, &$key_exis
}
else {
$key_exists = FALSE;
return NULL;
$null = NULL;
return $null;
}
}
$key_exists = TRUE;
......@@ -6364,7 +6625,7 @@ function drupal_array_get_nested_value(array &$array, array $parents, &$key_exis
}
/**
* Determines whether a nested array with variable depth contains all of the requested keys.
* Determines whether a nested array contains the requested keys.
*
* This helper function should be used when the depth of the array element to be
* checked may vary (that is, the number of parent keys is variable). See
......@@ -6400,11 +6661,11 @@ function drupal_array_nested_key_exists(array $array, array $parents) {
}
/**
* Provide theme registration for themes across .inc files.
* Provides theme registration for themes across .inc files.
*/
function drupal_common_theme() {
return array(
// theme.inc
// From theme.inc.
'html' => array(
'render element' => 'page',
'template' => 'html',
......@@ -6480,7 +6741,7 @@ function drupal_common_theme() {
'html_tag' => array(
'render element' => 'element',
),
// from theme.maintenance.inc
// From theme.maintenance.inc.
'maintenance_page' => array(
'variables' => array('content' => NULL, 'show_messages' => TRUE),
'template' => 'maintenance-page',
......@@ -6500,7 +6761,7 @@ function drupal_common_theme() {
'authorize_report' => array(
'variables' => array('messages' => array()),
),
// from pager.inc
// From pager.inc.
'pager' => array(
'variables' => array('tags' => array(), 'element' => 0, 'parameters' => array(), 'quantity' => 9),
),
......@@ -6519,7 +6780,7 @@ function drupal_common_theme() {
'pager_link' => array(
'variables' => array('text' => NULL, 'page_new' => NULL, 'element' => NULL, 'parameters' => array(), 'attributes' => array()),
),
// from menu.inc
// From menu.inc.
'menu_link' => array(
'render element' => 'element',
),
......@@ -6535,7 +6796,7 @@ function drupal_common_theme() {
'menu_local_tasks' => array(
'variables' => array('primary' => array(), 'secondary' => array()),
),
// from form.inc
// From form.inc.
'select' => array(
'render element' => 'element',
),
......@@ -6606,12 +6867,12 @@ function drupal_common_theme() {
}
/**
* @ingroup schemaapi
* @addtogroup schemaapi
* @{
*/
/**
* Creates all tables in a module's hook_schema() implementation.
* Creates all tables defined in a module's hook_schema().
*
* Note: This function does not pass the module's schema through
* hook_schema_alter(). The module's tables will be created exactly as the
......@@ -6630,7 +6891,7 @@ function drupal_install_schema($module) {
}
/**
* Remove all tables that a module defines in its hook_schema().
* Removes all tables defined in a module's hook_schema().
*
* Note: This function does not pass the module's schema through
* hook_schema_alter(). The module's tables will be created exactly as the
......@@ -6638,6 +6899,7 @@ function drupal_install_schema($module) {
*
* @param $module
* The module for which the tables will be removed.
*
* @return
* An array of arrays with the following key/value pairs:
* - success: a boolean indicating whether the query succeeded.
......@@ -6693,7 +6955,7 @@ function drupal_get_schema_unprocessed($module, $table = NULL) {
}
/**
* Fill in required default values for table definitions returned by hook_schema().
* Fills in required default values for table definitions from hook_schema().
*
* @param $schema
* The schema definition array as it was returned by the module's
......@@ -6724,7 +6986,9 @@ function _drupal_schema_initialize(&$schema, $module, $remove_descriptions = TRU
}
/**
* Retrieve a list of fields from a table schema. The list is suitable for use in a SQL query.
* Retrieves a list of fields from a table schema.
*
* The returned list is suitable for use in an SQL query.
*
* @param $table
* The name of the table from which to retrieve fields.
......@@ -6732,7 +6996,7 @@ function _drupal_schema_initialize(&$schema, $module, $remove_descriptions = TRU
* An optional prefix to to all fields.
*
* @return An array of fields.
**/
*/
function drupal_schema_fields_sql($table, $prefix = NULL) {
$schema = drupal_get_schema($table);
$fields = array_keys($schema['fields']);
......@@ -6751,6 +7015,10 @@ function drupal_schema_fields_sql($table, $prefix = NULL) {
/**
* Saves (inserts or updates) a record to the database based upon the schema.
*
* Do not use drupal_write_record() within hook_update_N() functions, since the
* database schema cannot be relied upon when a user is running a series of
* updates. Instead, use db_insert() or db_update() to save the record.
*
* @param $table
* The name of the table; this must be defined by a hook_schema()
* implementation.
......@@ -6906,7 +7174,7 @@ function drupal_write_record($table, &$record, $primary_keys = array()) {
}
/**
* @} End of "ingroup schemaapi".
* @} End of "addtogroup schemaapi".
*/
/**
......@@ -6960,7 +7228,7 @@ function drupal_parse_info_file($filename) {
}
/**
* Parse data in Drupal's .info format.
* Parses data in Drupal's .info format.
*
* Data should be in an .ini-like format to specify values. White-space
* generally doesn't matter, except inside values:
......@@ -6990,6 +7258,7 @@ function drupal_parse_info_file($filename) {
*
* @param $data
* A string to parse.
*
* @return
* The info array.
*
......@@ -7013,19 +7282,19 @@ function drupal_parse_info_format($data) {
)\s*$ # Stop at the next end of a line, ignoring trailing whitespace
@msx', $data, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
// Fetch the key and value string
// Fetch the key and value string.
$i = 0;
foreach (array('key', 'value1', 'value2', 'value3') as $var) {
$$var = isset($match[++$i]) ? $match[$i] : '';
}
$value = stripslashes(substr($value1, 1, -1)) . stripslashes(substr($value2, 1, -1)) . $value3;
// Parse array syntax
// Parse array syntax.
$keys = preg_split('/\]?\[/', rtrim($key, ']'));
$last = array_pop($keys);
$parent = &$info;
// Create nested arrays
// Create nested arrays.
foreach ($keys as $key) {
if ($key == '') {
$key = count($parent);
......@@ -7041,7 +7310,7 @@ function drupal_parse_info_format($data) {
$value = $constants[$value];
}
// Insert actual value
// Insert actual value.
if ($last == '') {
$last = count($parent);
}
......@@ -7053,11 +7322,12 @@ function drupal_parse_info_format($data) {
}
/**
* Severity levels, as defined in RFC 3164: http://www.ietf.org/rfc/rfc3164.txt.
* Returns a list of severity levels, as defined in RFC 3164.
*
* @return
* Array of the possible severity levels for log messages.
*
* @see http://www.ietf.org/rfc/rfc3164.txt
* @see watchdog()
* @ingroup logging_severity_levels
*/
......@@ -7076,7 +7346,7 @@ function watchdog_severity_levels() {
/**
* Explode a string of given tags into an array.
* Explodes a string of tags into an array.
*
* @see drupal_implode_tags()
*/
......@@ -7102,7 +7372,7 @@ function drupal_explode_tags($tags) {
}
/**
* Implode an array of tags into a string.
* Implodes an array of tags into a string.
*
* @see drupal_explode_tags()
*/
......@@ -7120,7 +7390,7 @@ function drupal_implode_tags($tags) {
}
/**
* Flush all cached data on the site.
* Flushes all cached data on the site.
*
* Empties cache tables, rebuilds the menu cache and theme registries, and
* invokes a hook so that other modules' cache data can be cleared as well.
......@@ -7138,6 +7408,7 @@ function drupal_flush_all_caches() {
system_rebuild_theme_data();
drupal_theme_rebuild();
entity_info_cache_clear();
node_types_rebuild();
// node_menu() defines menu items based on node types so it needs to come
// after node types are rebuilt.
......@@ -7161,10 +7432,10 @@ function drupal_flush_all_caches() {
}
/**
* Helper function to change query-strings on css/js files.
* Changes the dummy query string added to all CSS and JavaScript files.
*
* Changes the character added to all css/js files as dummy query-string, so
* that all browsers are forced to reload fresh files.
* Changing the dummy query string appended to CSS and JavaScript files forces
* all browsers to reload fresh files.
*/
function _drupal_flush_css_js() {
// The timestamp is converted to base 36 in order to make it more compact.
......@@ -7172,7 +7443,7 @@ function _drupal_flush_css_js() {
}
/**
* Debug function used for outputting debug information.
* Outputs debug information.
*
* The debug information is passed on to trigger_error() after being converted
* to a string using _drupal_debug_message().
......@@ -7197,10 +7468,11 @@ function debug($data, $label = NULL, $print_r = FALSE) {
}
/**
* Parse a dependency for comparison by drupal_check_incompatibility().
* Parses a dependency for comparison by drupal_check_incompatibility().
*
* @param $dependency
* A dependency string, for example 'foo (>=7.x-4.5-beta5, 3.x)'.
*
* @return
* An associative array with three keys:
* - 'name' includes the name of the thing to depend on (e.g. 'foo').
......@@ -7254,12 +7526,13 @@ function drupal_parse_dependency($dependency) {
}
/**
* Check whether a version is compatible with a given dependency.
* Checks whether a version is compatible with a given dependency.
*
* @param $v
* The parsed dependency structure from drupal_parse_dependency().
* @param $current_version
* The version to check against (like 4.2).
*
* @return
* NULL if compatible, otherwise the original dependency version string that
* caused the incompatibility.
......@@ -7279,12 +7552,12 @@ function drupal_check_incompatibility($v, $current_version) {
/**
* Get the entity info array of an entity type.
*
* @see hook_entity_info()
* @see hook_entity_info_alter()
*
* @param $entity_type
* The entity type, e.g. node, for which the info shall be returned, or NULL
* to return an array with info about all types.
*
* @see hook_entity_info()
* @see hook_entity_info_alter()
*/
function entity_get_info($entity_type = NULL) {
global $language;
......@@ -7372,12 +7645,13 @@ function entity_info_cache_clear() {
* The entity type; e.g. 'node' or 'user'.
* @param $entity
* The entity from which to extract values.
*
* @return
* A numerically indexed array (not a hash table) containing these
* elements:
* 0: primary id of the entity
* 1: revision id of the entity, or NULL if $entity_type is not versioned
* 2: bundle name of the entity
* - 0: Primary ID of the entity.
* - 1: Revision ID of the entity, or NULL if $entity_type is not versioned.
* - 2: Bundle name of the entity, or NULL if $entity_type has no bundles.
*/
function entity_extract_ids($entity_type, $entity) {
$info = entity_get_info($entity_type);
......@@ -7410,13 +7684,12 @@ function entity_extract_ids($entity_type, $entity) {
* @param $entity_type
* The entity type; e.g. 'node' or 'user'.
* @param $ids
* A numerically indexed array, as returned by entity_extract_ids(),
* containing these elements:
* 0: primary id of the entity
* 1: revision id of the entity, or NULL if $entity_type is not versioned
* 2: bundle name of the entity, or NULL if $entity_type has no bundles
* A numerically indexed array, as returned by entity_extract_ids().
*
* @return
* An entity structure, initialized with the ids provided.
*
* @see entity_extract_ids()
*/
function entity_create_stub_entity($entity_type, $ids) {
$entity = new stdClass();
......@@ -7446,11 +7719,6 @@ function entity_create_stub_entity($entity_type, $ids) {
* DrupalDefaultEntityController class. See node_entity_info() and the
* NodeController in node.module as an example.
*
* @see hook_entity_info()
* @see DrupalEntityControllerInterface
* @see DrupalDefaultEntityController
* @see EntityFieldQuery
*
* @param $entity_type
* The entity type to load, e.g. node or user.
* @param $ids
......@@ -7468,6 +7736,11 @@ function entity_create_stub_entity($entity_type, $ids) {
* found, an empty array is returned.
*
* @todo Remove $conditions in Drupal 8.
*
* @see hook_entity_info()
* @see DrupalEntityControllerInterface
* @see DrupalDefaultEntityController
* @see EntityFieldQuery
*/
function entity_load($entity_type, $ids = FALSE, $conditions = array(), $reset = FALSE) {
if ($reset) {
......@@ -7487,7 +7760,7 @@ function entity_load($entity_type, $ids = FALSE, $conditions = array(), $reset =
* @param $entity_type
* The entity type to load, e.g. node or user.
* @param $id
* The id of the entity to load.
* The ID of the entity to load.
*
* @return
* The unchanged entity, or FALSE if the entity cannot be loaded.
......@@ -7524,7 +7797,6 @@ function entity_get_controller($entity_type) {
* recursion. By convention, entity_prepare_view() is called after
* field_attach_prepare_view() to allow entity level hooks to act on content
* loaded by field API.
* @see hook_entity_prepare_view()
*
* @param $entity_type
* The type of entity, i.e. 'node', 'user'.
......@@ -7533,6 +7805,8 @@ function entity_get_controller($entity_type) {
* @param $langcode
* (optional) A language code to be used for rendering. Defaults to the global
* content language of the current request.
*
* @see hook_entity_prepare_view()
*/
function entity_prepare_view($entity_type, $entities, $langcode = NULL) {
if (!isset($langcode)) {
......@@ -7559,56 +7833,42 @@ function entity_prepare_view($entity_type, $entities, $langcode = NULL) {
}
/**
* Returns the uri elements of an entity.
* Returns the URI elements of an entity.
*
* @param $entity_type
* The entity type; e.g. 'node' or 'user'.
* @param $entity
* The entity for which to generate a path.
* @return
* An array containing the 'path' and 'options' keys used to build the uri of
* An array containing the 'path' and 'options' keys used to build the URI of
* the entity, and matching the signature of url(). NULL if the entity has no
* uri of its own.
* URI of its own.
*/
function entity_uri($entity_type, $entity) {
// This check enables the URI of an entity to be easily overridden from what
// the callback for the entity type or bundle would return, and it helps
// minimize performance overhead when entity_uri() is called multiple times
// for the same entity.
if (!isset($entity->uri)) {
$info = entity_get_info($entity_type);
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
// A bundle-specific callback takes precedence over the generic one for the
// entity type.
if (isset($info['bundles'][$bundle]['uri callback'])) {
$uri_callback = $info['bundles'][$bundle]['uri callback'];
}
elseif (isset($info['uri callback'])) {
$uri_callback = $info['uri callback'];
}
else {
$uri_callback = NULL;
}
$info = entity_get_info($entity_type);
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
// Invoke the callback to get the URI. If there is no callback, set the
// entity's 'uri' property to FALSE to indicate that it is known to not have
// a URI.
if (isset($uri_callback) && function_exists($uri_callback)) {
$entity->uri = $uri_callback($entity);
if (!isset($entity->uri['options'])) {
$entity->uri['options'] = array();
}
// Pass the entity data to url() so that alter functions do not need to
// lookup this entity again.
$entity->uri['options']['entity_type'] = $entity_type;
$entity->uri['options']['entity'] = $entity;
}
else {
$entity->uri = FALSE;
}
// A bundle-specific callback takes precedence over the generic one for the
// entity type.
if (isset($info['bundles'][$bundle]['uri callback'])) {
$uri_callback = $info['bundles'][$bundle]['uri callback'];
}
elseif (isset($info['uri callback'])) {
$uri_callback = $info['uri callback'];
}
else {
return NULL;
}
// Invoke the callback to get the URI. If there is no callback, return NULL.
if (isset($uri_callback) && function_exists($uri_callback)) {
$uri = $uri_callback($entity);
// Pass the entity data to url() so that alter functions do not need to
// lookup this entity again.
$uri['options']['entity_type'] = $entity_type;
$uri['options']['entity'] = $entity;
return $uri;
}
return $entity->uri ? $entity->uri : NULL;
}
/**
......@@ -7639,7 +7899,45 @@ function entity_label($entity_type, $entity) {
}
/**
* Helper function for attaching field API validation to entity forms.
* Returns the language of an entity.
*
* @param $entity_type
* The entity type; e.g., 'node' or 'user'.
* @param $entity
* The entity for which to get the language.
*
* @return
* A valid language code or NULL if the entity has no language support.
*/
function entity_language($entity_type, $entity) {
$info = entity_get_info($entity_type);
// Invoke the callback to get the language. If there is no callback, try to
// get it from a property of the entity, otherwise NULL.
if (isset($info['language callback']) && function_exists($info['language callback'])) {
$langcode = $info['language callback']($entity_type, $entity);
}
elseif (!empty($info['entity keys']['language']) && isset($entity->{$info['entity keys']['language']})) {
$langcode = $entity->{$info['entity keys']['language']};
}
else {
// The value returned in D8 would be LANGUAGE_NONE, we cannot use it here to
// preserve backward compatibility. In fact this function has been
// introduced very late in the D7 life cycle, mainly as the proper default
// for field_attach_form(). By returning LANGUAGE_NONE when no language
// information is available, we would introduce a potentially BC-breaking
// API change, since field_attach_form() defaults to the default language
// instead of LANGUAGE_NONE. Moreover this allows us to distinguish between
// entities that have no language specified from ones that do not have
// language support at all.
$langcode = NULL;
}
return $langcode;
}
/**
* Attaches field API validation to entity forms.
*/
function entity_form_field_validate($entity_type, $form, &$form_state) {
// All field attach API functions act on an entity object, but during form
......@@ -7652,7 +7950,7 @@ function entity_form_field_validate($entity_type, $form, &$form_state) {
}
/**
* Helper function for copying submitted values to entity properties for simple entity forms.
* Copies submitted values to entity properties for simple entity forms.
*
* During the submission handling of an entity form's "Save", "Preview", and
* possibly other buttons, the form state's entity needs to be updated with the
......@@ -7778,11 +8076,12 @@ function archiver_get_extensions() {
}
/**
* Create the appropriate archiver for the specified file.
* Creates the appropriate archiver for the specified file.
*
* @param $file
* The full path of the archive file. Note that stream wrapper
* paths are supported, but not remote ones.
* The full path of the archive file. Note that stream wrapper paths are
* supported, but not remote ones.
*
* @return
* A newly created instance of the archiver class appropriate
* for the specified file, already bound to that file.
......@@ -7811,14 +8110,14 @@ function archiver_get_archiver($file) {
}
/**
* Drupal Updater registry.
* Assembles the Drupal Updater registry.
*
* An Updater is a class that knows how to update various parts of the Drupal
* file system, for example to update modules that have newer releases, or to
* install a new theme.
*
* @return
* Returns the Drupal Updater class registry.
* The Drupal Updater class registry.
*
* @see hook_updater_info()
* @see hook_updater_info_alter()
......@@ -7834,10 +8133,10 @@ function drupal_get_updaters() {
}
/**
* Drupal FileTransfer registry.
* Assembles the Drupal FileTransfer registry.
*
* @return
* Returns the Drupal FileTransfer class registry.
* The Drupal FileTransfer class registry.
*
* @see FileTransfer
* @see hook_filetransfer_info()
......
......@@ -41,7 +41,7 @@
* $result = db_query_range('SELECT n.nid, n.title, n.created
* FROM {node} n WHERE n.uid = :uid', 0, 10, array(':uid' => $uid));
* foreach ($result as $record) {
* // Perform operations on $node->title, etc. here.
* // Perform operations on $record->title, etc. here.
* }
* @endcode
* Curly braces are used around "node" to provide table prefixing via
......@@ -152,7 +152,7 @@
* }
*
* // $txn goes out of scope here. Unless the transaction was rolled back, it
* // gets automatically commited here.
* // gets automatically committed here.
* }
*
* function my_other_function($id) {
......@@ -167,7 +167,7 @@
* }
* @endcode
*
* @link http://drupal.org/developing/api/database
* @see http://drupal.org/developing/api/database
*/
......@@ -194,7 +194,7 @@ abstract class DatabaseConnection extends PDO {
/**
* The key representing this connection.
*
*
* The key is a unique string which identifies a database connection. A
* connection can be a single server or a cluster of master and slaves (use
* target to pick between master and slave).
......@@ -303,12 +303,28 @@ abstract class DatabaseConnection extends PDO {
// Call PDO::__construct and PDO::setAttribute.
parent::__construct($dsn, $username, $password, $driver_options);
// Set a specific PDOStatement class if the driver requires that.
// Set a Statement class, unless the driver opted out.
if (!empty($this->statementClass)) {
$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array($this->statementClass, array($this)));
}
}
/**
* Destroys this Connection object.
*
* PHP does not destruct an object if it is still referenced in other
* variables. In case of PDO database connection objects, PHP only closes the
* connection when the PDO object is destructed, so any references to this
* object may cause the number of maximum allowed connections to be exceeded.
*/
public function destroy() {
// Destroy all references to this connection by setting them to NULL.
// The Statement class attribute only accepts a new value that presents a
// proper callable, so we reset it to PDOStatement.
$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('PDOStatement', array()));
$this->schema = NULL;
}
/**
* Returns the default query options for any given query.
*
......@@ -988,6 +1004,9 @@ abstract class DatabaseConnection extends PDO {
* @param $name
* Optional name of the savepoint.
*
* @return DatabaseTransaction
* A DatabaseTransaction object.
*
* @see DatabaseTransaction
*/
public function startTransaction($name = '') {
......@@ -1016,9 +1035,9 @@ abstract class DatabaseConnection extends PDO {
throw new DatabaseTransactionNoActiveException();
}
// A previous rollback to an earlier savepoint may mean that the savepoint
// in question has already been rolled back.
if (!in_array($savepoint_name, $this->transactionLayers)) {
return;
// in question has already been accidentally committed.
if (!isset($this->transactionLayers[$savepoint_name])) {
throw new DatabaseTransactionNoActiveException();
}
// We need to find the point we're rolling back to, all other savepoints
......@@ -1096,8 +1115,12 @@ abstract class DatabaseConnection extends PDO {
if (!$this->supportsTransactions()) {
return;
}
// The transaction has already been committed earlier. There is nothing we
// need to do. If this transaction was part of an earlier out-of-order
// rollback, an exception would already have been thrown by
// Database::rollback().
if (!isset($this->transactionLayers[$name])) {
throw new DatabaseTransactionNoActiveException();
return;
}
// Mark this layer as committable.
......@@ -1426,9 +1449,6 @@ abstract class Database {
/**
* Gets the connection object for the specified database key and target.
*
* Note: do not use the setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE) on the
* returned object because of http://bugs.php.net/bug.php?id=43139.
*
* @param $target
* The database target name.
* @param $key
......@@ -1623,8 +1643,8 @@ abstract class Database {
*/
final public static function removeConnection($key) {
if (isset(self::$databaseInfo[$key])) {
self::closeConnection(NULL, $key);
unset(self::$databaseInfo[$key]);
unset(self::$connections[$key]);
return TRUE;
}
else {
......@@ -1690,11 +1710,24 @@ abstract class Database {
if (!isset($key)) {
$key = self::$activeKey;
}
// To close the connection, we need to unset the static variable.
// To close a connection, it needs to be set to NULL and removed from the
// static variable. In all cases, closeConnection() might be called for a
// connection that was not opened yet, in which case the key is not defined
// yet and we just ensure that the connection key is undefined.
if (isset($target)) {
if (isset(self::$connections[$key][$target])) {
self::$connections[$key][$target]->destroy();
self::$connections[$key][$target] = NULL;
}
unset(self::$connections[$key][$target]);
}
else {
if (isset(self::$connections[$key])) {
foreach (self::$connections[$key] as $target => $connection) {
self::$connections[$key][$target]->destroy();
self::$connections[$key][$target] = NULL;
}
}
unset(self::$connections[$key]);
}
}
......@@ -1848,8 +1881,8 @@ class DatabaseTransaction {
*/
protected $name;
public function __construct(DatabaseConnection &$connection, $name = NULL) {
$this->connection = &$connection;
public function __construct(DatabaseConnection $connection, $name = NULL) {
$this->connection = $connection;
// If there is no transaction depth, then no transaction has started. Name
// the transaction 'drupal_transaction'.
if (!$depth = $connection->transactionDepth()) {
......@@ -1899,21 +1932,19 @@ class DatabaseTransaction {
}
/**
* A prepared statement.
* Represents a prepared statement.
*
* Some methods in that class are purposely commented out. Due to a change in
* Some methods in that class are purposefully commented out. Due to a change in
* how PHP defines PDOStatement, we can't define a signature for those methods
* that will work the same way between versions older than 5.2.6 and later
* versions.
*
* Please refer to http://bugs.php.net/bug.php?id=42452 for more details.
* versions. See http://bugs.php.net/bug.php?id=42452 for more details.
*
* Child implementations should either extend PDOStatement:
* @code
* class DatabaseStatement_oracle extends PDOStatement implements DatabaseStatementInterface {}
* @endcode
* or implement their own class, but in that case they will also have to
* implement the Iterator or IteratorArray interfaces before
* or define their own class. If defining their own class, they will also have
* to implement either the Iterator or IteratorAggregate interface before
* DatabaseStatementInterface:
* @code
* class DatabaseStatement_oracle implements Iterator, DatabaseStatementInterface {}
......@@ -1997,7 +2028,7 @@ interface DatabaseStatementInterface extends Traversable {
* The numeric index of the field to return. Defaults to the first field.
*
* @return
* A single field from the next record.
* A single field from the next record, or FALSE if there is no next record.
*/
public function fetchField($index = 0);
......@@ -2017,7 +2048,7 @@ interface DatabaseStatementInterface extends Traversable {
* helper method, so one is added.
*
* @return
* An associative array.
* An associative array, or FALSE if there is no next row.
*/
public function fetchAssoc();
......@@ -2045,7 +2076,7 @@ interface DatabaseStatementInterface extends Traversable {
* The index of the column number to fetch.
*
* @return
* An indexed array.
* An indexed array, or an empty array if there is no result set.
*/
public function fetchCol($index = 0);
......@@ -2065,7 +2096,7 @@ interface DatabaseStatementInterface extends Traversable {
* The numeric index of the field to use as the array value.
*
* @return
* An associative array.
* An associative array, or an empty array if there is no result set.
*/
public function fetchAllKeyed($key_index = 0, $value_index = 1);
......@@ -2084,7 +2115,7 @@ interface DatabaseStatementInterface extends Traversable {
* set for the query will be used.
*
* @return
* An associative array.
* An associative array, or an empty array if there is no result set.
*/
public function fetchAllAssoc($key, $fetch = NULL);
}
......@@ -2670,7 +2701,7 @@ function db_condition($conjunction) {
/**
* @ingroup schemaapi
* @addtogroup schemaapi
* @{
*/
......@@ -2767,7 +2798,7 @@ function _db_create_keys_sql($spec) {
* Renames a table.
*
* @param $table
* The table to be renamed.
* The current name of the table to be renamed.
* @param $new_name
* The new name for the table.
*/
......@@ -2987,7 +3018,7 @@ function db_change_field($table, $field, $field_new, $spec, $keys_new = array())
}
/**
* @} End of "ingroup schemaapi".
* @} End of "addtogroup schemaapi".
*/
/**
......
......@@ -128,9 +128,10 @@ class DatabaseLog {
* Determine the routine that called this query.
*
* We define "the routine that called this query" as the first entry in
* the call stack that is not inside includes/database. That makes the
* climbing logic very simple, and handles the variable stack depth caused
* by the query builders.
* the call stack that is not inside includes/database and does have a file
* (which excludes call_user_func_array(), anonymous functions and similar).
* That makes the climbing logic very simple, and handles the variable stack
* depth caused by the query builders.
*
* @link http://www.php.net/debug_backtrace
* @return
......@@ -144,7 +145,8 @@ class DatabaseLog {
$stack = debug_backtrace();
$stack_count = count($stack);
for ($i = 0; $i < $stack_count; ++$i) {
if (strpos($stack[$i]['file'], 'includes' . DIRECTORY_SEPARATOR . 'database') === FALSE) {
if (!empty($stack[$i]['file']) && strpos($stack[$i]['file'], 'includes' . DIRECTORY_SEPARATOR . 'database') === FALSE) {
$stack[$i] += array('args' => array());
return array(
'file' => $stack[$i]['file'],
'line' => $stack[$i]['line'],
......
......@@ -6,18 +6,18 @@
*/
/**
* @ingroup database
* @addtogroup database
* @{
*/
class DatabaseConnection_mysql extends DatabaseConnection {
/**
* Flag to indicate if we have registered the nextID cleanup function.
* Flag to indicate if the cleanup function in __destruct() should run.
*
* @var boolean
*/
protected $shutdownRegistered = FALSE;
protected $needsCleanup = FALSE;
public function __construct(array $connection_options = array()) {
// This driver defaults to transaction support, except if explicitly passed FALSE.
......@@ -37,14 +37,18 @@ class DatabaseConnection_mysql extends DatabaseConnection {
$dsn = 'mysql:host=' . $connection_options['host'] . ';port=' . (empty($connection_options['port']) ? 3306 : $connection_options['port']);
}
$dsn .= ';dbname=' . $connection_options['database'];
parent::__construct($dsn, $connection_options['username'], $connection_options['password'], array(
// Allow PDO options to be overridden.
$connection_options += array(
'pdo' => array(),
);
$connection_options['pdo'] += array(
// So we don't have to mess around with cursors and unbuffered queries by default.
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => TRUE,
// Because MySQL's prepared statements skip the query cache, because it's dumb.
PDO::ATTR_EMULATE_PREPARES => TRUE,
// Force column names to lower case.
PDO::ATTR_CASE => PDO::CASE_LOWER,
));
);
parent::__construct($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']);
// Force MySQL to use the UTF-8 character set. Also set the collation, if a
// certain one has been set; otherwise, MySQL defaults to 'utf8_general_ci'
......@@ -56,12 +60,28 @@ class DatabaseConnection_mysql extends DatabaseConnection {
$this->exec('SET NAMES utf8');
}
// Force MySQL's behavior to conform more closely to SQL standards.
// This allows Drupal to run almost seamlessly on many different
// kinds of database systems. These settings force MySQL to behave
// the same as postgresql, or sqlite in regards to syntax interpretation
// and invalid data handling. See http://drupal.org/node/344575 for further discussion.
$this->exec("SET sql_mode='ANSI,TRADITIONAL'");
// Set MySQL init_commands if not already defined. Default Drupal's MySQL
// behavior to conform more closely to SQL standards. This allows Drupal
// to run almost seamlessly on many different kinds of database systems.
// These settings force MySQL to behave the same as postgresql, or sqlite
// in regards to syntax interpretation and invalid data handling. See
// http://drupal.org/node/344575 for further discussion. Also, as MySQL 5.5
// changed the meaning of TRADITIONAL we need to spell out the modes one by
// one.
$connection_options += array(
'init_commands' => array(),
);
$connection_options['init_commands'] += array(
'sql_mode' => "SET sql_mode = 'ANSI,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'",
);
// Set connection options.
$this->exec(implode('; ', $connection_options['init_commands']));
}
public function __destruct() {
if ($this->needsCleanup) {
$this->nextIdDelete();
}
}
public function queryRange($query, $from, $count, array $args = array(), array $options = array()) {
......@@ -101,12 +121,7 @@ class DatabaseConnection_mysql extends DatabaseConnection {
$this->query('INSERT INTO {sequences} (value) VALUES (:value) ON DUPLICATE KEY UPDATE value = value', array(':value' => $existing_id));
$new_id = $this->query('INSERT INTO {sequences} () VALUES ()', array(), array('return' => Database::RETURN_INSERT_ID));
}
if (!$this->shutdownRegistered) {
// Use register_shutdown_function() here to keep the database system
// independent of Drupal.
register_shutdown_function(array($this, 'nextIdDelete'));
$shutdownRegistered = TRUE;
}
$this->needsCleanup = TRUE;
return $new_id;
}
......@@ -167,8 +182,11 @@ class DatabaseConnection_mysql extends DatabaseConnection {
// succeed for MySQL error code 1305 ("SAVEPOINT does not exist").
if ($e->errorInfo[1] == '1305') {
// If one SAVEPOINT was released automatically, then all were.
// Therefore, we keep just the topmost transaction.
$this->transactionLayers = array('drupal_transaction' => 'drupal_transaction');
// Therefore, clean the transaction stack.
$this->transactionLayers = array();
// We also have to explain to PDO that the transaction stack has
// been cleaned-up.
PDO::commit();
}
else {
throw $e;
......@@ -181,5 +199,5 @@ class DatabaseConnection_mysql extends DatabaseConnection {
/**
* @} End of "ingroup database".
* @} End of "addtogroup database".
*/
<?php
/**
* @ingroup database
* @addtogroup database
* @{
*/
......@@ -103,5 +103,5 @@ class TruncateQuery_mysql extends TruncateQuery {
}
/**
* @} End of "ingroup database".
* @} End of "addtogroup database".
*/
......@@ -7,7 +7,7 @@
/**
* @ingroup schemaapi
* @addtogroup schemaapi
* @{
*/
......@@ -131,8 +131,13 @@ class DatabaseSchema_mysql extends DatabaseSchema {
protected function createFieldSql($name, $spec) {
$sql = "`" . $name . "` " . $spec['mysql_type'];
if (in_array($spec['mysql_type'], array('VARCHAR', 'CHAR', 'TINYTEXT', 'MEDIUMTEXT', 'LONGTEXT', 'TEXT')) && isset($spec['length'])) {
$sql .= '(' . $spec['length'] . ')';
if (in_array($spec['mysql_type'], array('VARCHAR', 'CHAR', 'TINYTEXT', 'MEDIUMTEXT', 'LONGTEXT', 'TEXT'))) {
if (isset($spec['length'])) {
$sql .= '(' . $spec['length'] . ')';
}
if (!empty($spec['binary'])) {
$sql .= ' BINARY';
}
}
elseif (isset($spec['precision']) && isset($spec['scale'])) {
$sql .= '(' . $spec['precision'] . ', ' . $spec['scale'] . ')';
......@@ -381,7 +386,7 @@ class DatabaseSchema_mysql extends DatabaseSchema {
// Returns one row for each column in the index. Result is string or FALSE.
// Details at http://dev.mysql.com/doc/refman/5.0/en/show-index.html
$row = $this->connection->query('SHOW INDEX FROM {' . $table . "} WHERE key_name = '$name'")->fetchAssoc();
return isset($row['key_name']);
return isset($row['Key_name']);
}
public function addPrimaryKey($table, $fields) {
......@@ -527,5 +532,5 @@ class DatabaseSchema_mysql extends DatabaseSchema {
}
/**
* @} End of "ingroup schemaapi".
* @} End of "addtogroup schemaapi".
*/
......@@ -6,7 +6,7 @@
*/
/**
* @ingroup database
* @addtogroup database
* @{
*/
......@@ -47,7 +47,12 @@ class DatabaseConnection_pgsql extends DatabaseConnection {
$this->connectionOptions = $connection_options;
$dsn = 'pgsql:host=' . $connection_options['host'] . ' dbname=' . $connection_options['database'] . ' port=' . $connection_options['port'];
parent::__construct($dsn, $connection_options['username'], $connection_options['password'], array(
// Allow PDO options to be overridden.
$connection_options += array(
'pdo' => array(),
);
$connection_options['pdo'] += array(
// Prepared statements are most effective for performance when queries
// are recycled (used several times). However, if they are not re-used,
// prepared statements become ineffecient. Since most of Drupal's
......@@ -57,12 +62,27 @@ class DatabaseConnection_pgsql extends DatabaseConnection {
PDO::ATTR_EMULATE_PREPARES => TRUE,
// Convert numeric values to strings when fetching.
PDO::ATTR_STRINGIFY_FETCHES => TRUE,
// Force column names to lower case.
PDO::ATTR_CASE => PDO::CASE_LOWER,
));
);
parent::__construct($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']);
// Force PostgreSQL to use the UTF-8 character set by default.
$this->exec("SET NAMES 'UTF8'");
// Execute PostgreSQL init_commands.
if (isset($connection_options['init_commands'])) {
$this->exec(implode('; ', $connection_options['init_commands']));
}
}
public function prepareQuery($query) {
// mapConditionOperator converts LIKE operations to ILIKE for consistency
// with MySQL. However, Postgres does not support ILIKE on bytea (blobs)
// fields.
// To make the ILIKE operator work, we type-cast bytea fields into text.
// @todo This workaround only affects bytea fields, but the involved field
// types involved in the query are unknown, so there is no way to
// conditionally execute this for affected queries only.
return parent::prepareQuery(preg_replace('/ ([^ ]+) +(I*LIKE|NOT +I*LIKE) /i', ' ${1}::text ${2} ', $query));
}
public function query($query, array $args = array(), $options = array()) {
......@@ -199,5 +219,5 @@ class DatabaseConnection_pgsql extends DatabaseConnection {
}
/**
* @} End of "ingroup database".
* @} End of "addtogroup database".
*/
......@@ -328,9 +328,9 @@ class DatabaseSchema_pgsql extends DatabaseSchema {
// rename them when renaming the table.
$indexes = $this->connection->query('SELECT indexname FROM pg_indexes WHERE schemaname = :schema AND tablename = :table', array(':schema' => $old_schema, ':table' => $old_table_name));
foreach ($indexes as $index) {
if (preg_match('/^' . preg_quote($old_full_name) . '_(.*)_idx$/', $index->indexname, $matches)) {
if (preg_match('/^' . preg_quote($old_full_name) . '_(.*)$/', $index->indexname, $matches)) {
$index_name = $matches[1];
$this->connection->query('ALTER INDEX ' . $index->indexname . ' RENAME TO {' . $new_name . '}_' . $index_name . '_idx');
$this->connection->query('ALTER INDEX ' . $index->indexname . ' RENAME TO {' . $new_name . '}_' . $index_name);
}
}
......
......@@ -6,7 +6,7 @@
*/
/**
* @ingroup database
* @addtogroup database
* @{
*/
......@@ -103,6 +103,6 @@ class SelectQuery_pgsql extends SelectQuery {
}
/**
* @} End of "ingroup database".
* @} End of "addtogroup database".
*/
......@@ -9,7 +9,7 @@
*/
/**
* @ingroup database
* @addtogroup database
* @{
*/
......@@ -370,7 +370,7 @@ class DatabaseStatementPrefetch implements Iterator, DatabaseStatementInterface
}
}
public function fetchField($index = 0) {
public function fetchColumn($index = 0) {
if (isset($this->currentRow) && isset($this->columnNames[$index])) {
// We grab the value directly from $this->data, and format it.
$return = $this->currentRow[$this->columnNames[$index]];
......@@ -382,6 +382,10 @@ class DatabaseStatementPrefetch implements Iterator, DatabaseStatementInterface
}
}
public function fetchField($index = 0) {
return $this->fetchColumn($index);
}
public function fetchObject($class_name = NULL, $constructor_args = array()) {
if (isset($this->currentRow)) {
if (!isset($class_name)) {
......@@ -498,6 +502,6 @@ class DatabaseStatementPrefetch implements Iterator, DatabaseStatementInterface
}
/**
* @} End of "ingroup database".
* @} End of "addtogroup database".
*/
<?php
/**
* @ingroup database
* @addtogroup database
* @{
*/
......@@ -22,6 +22,9 @@ interface QueryConditionInterface {
* parameters, they are taken as $field and $value with $operator having a
* value of IN if $value is an array and = otherwise.
*
* Do not use this method to test for NULL values. Instead, use
* QueryConditionInterface::isNull() or QueryConditionInterface::isNotNull().
*
* @param $field
* The name of the field to check. If you would like to add a more complex
* condition involving operators or functions, use where().
......@@ -36,6 +39,9 @@ interface QueryConditionInterface {
*
* @return QueryConditionInterface
* The called object.
*
* @see QueryConditionInterface::isNull()
* @see QueryConditionInterface::isNotNull()
*/
public function condition($field, $value = NULL, $operator = NULL);
......@@ -839,7 +845,7 @@ class DeleteQuery extends Query implements QueryConditionInterface {
* Executes the DELETE query.
*
* @return
* The return value is dependant on the database connection.
* The return value is dependent on the database connection.
*/
public function execute() {
$values = array();
......@@ -1892,8 +1898,13 @@ class DatabaseCondition implements QueryConditionInterface, Countable {
function __clone() {
$this->changed = TRUE;
foreach ($this->conditions as $key => $condition) {
if ($condition['field'] instanceOf QueryConditionInterface) {
$this->conditions[$key]['field'] = clone($condition['field']);
if ($key !== '#conjunction') {
if ($condition['field'] instanceOf QueryConditionInterface) {
$this->conditions[$key]['field'] = clone($condition['field']);
}
if ($condition['value'] instanceOf SelectQueryInterface) {
$this->conditions[$key]['value'] = clone($condition['value']);
}
}
}
}
......@@ -1949,5 +1960,5 @@ class DatabaseCondition implements QueryConditionInterface, Countable {
}
/**
* @} End of "ingroup database".
* @} End of "addtogroup database".
*/
......@@ -76,8 +76,13 @@ require_once dirname(__FILE__) . '/query.inc';
* the precision (total number of significant digits) and scale
* (decimal digits right of the decimal point). Both values are
* mandatory. Ignored for other field types.
* - 'binary': A boolean indicating that MySQL should force 'char',
* 'varchar' or 'text' fields to use case-sensitive binary collation.
* This has no effect on other database types for which case sensitivity
* is already the default behavior.
* All parameters apart from 'type' are optional except that type
* 'numeric' columns must specify 'precision' and 'scale'.
* 'numeric' columns must specify 'precision' and 'scale', and type
* 'varchar' must specify the 'length' parameter.
* - 'primary key': An array of one or more key column specifiers (see below)
* that form the primary key.
* - 'unique keys': An associative array of unique keys ('keyname' =>
......
<?php
/**
* @ingroup database
* @addtogroup database
* @{
*/
......@@ -590,11 +590,13 @@ class SelectQueryExtender implements SelectQueryInterface {
}
public function hasAllTags() {
return call_user_func_array(array($this->query, 'hasAllTags'), func_get_args());
$args = func_get_args();
return call_user_func_array(array($this->query, 'hasAllTags'), $args);
}
public function hasAnyTag() {
return call_user_func_array(array($this->query, 'hasAnyTags'), func_get_args());
$args = func_get_args();
return call_user_func_array(array($this->query, 'hasAnyTags'), $args);
}
public function addMetaData($key, $object) {
......@@ -637,16 +639,16 @@ class SelectQueryExtender implements SelectQueryInterface {
/* Implementations of QueryConditionInterface for the HAVING clause. */
public function havingCondition($field, $value = NULL, $operator = '=') {
$this->query->condition($field, $value, $operator, $num_args);
$this->query->havingCondition($field, $value, $operator);
return $this;
}
public function &havingConditions() {
return $this->having->conditions();
return $this->query->havingConditions();
}
public function havingArguments() {
return $this->having->arguments();
return $this->query->havingArguments();
}
public function having($snippet, $args = array()) {
......@@ -790,31 +792,7 @@ class SelectQueryExtender implements SelectQueryInterface {
}
public function countQuery() {
// Create our new query object that we will mutate into a count query.
$count = clone($this);
// Zero-out existing fields and expressions.
$fields =& $count->getFields();
$fields = array();
$expressions =& $count->getExpressions();
$expressions = array();
// Also remove 'all_fields' statements, which are expanded into tablename.*
// when the query is executed.
$tables = &$count->getTables();
foreach ($tables as $alias => &$table) {
unset($table['all_fields']);
}
// Ordering a count query is a waste of cycles, and breaks on some
// databases anyway.
$orders = &$count->getOrderBy();
$orders = array();
// COUNT() is an expression, so we add that back in.
$count->addExpression('COUNT(*)');
return $count;
return $this->query->countQuery();
}
function isNull($field) {
......@@ -836,7 +814,7 @@ class SelectQueryExtender implements SelectQueryInterface {
$this->query->notExists($select);
return $this;
}
public function __toString() {
return (string) $this->query;
}
......@@ -1005,11 +983,13 @@ class SelectQuery extends Query implements SelectQueryInterface {
}
public function hasAllTags() {
return !(boolean)array_diff(func_get_args(), array_keys($this->alterTags));
$args = func_get_args();
return !(boolean)array_diff($args, array_keys($this->alterTags));
}
public function hasAnyTag() {
return (boolean)array_intersect(func_get_args(), array_keys($this->alterTags));
$args = func_get_args();
return (boolean)array_intersect($args, array_keys($this->alterTags));
}
public function addMetaData($key, $object) {
......@@ -1088,7 +1068,7 @@ class SelectQuery extends Query implements SelectQueryInterface {
$this->where->notExists($select);
return $this;
}
public function compile(DatabaseConnection $connection, QueryPlaceholderInterface $queryPlaceholder) {
$this->where->compile($connection, $queryPlaceholder);
$this->having->compile($connection, $queryPlaceholder);
......@@ -1172,17 +1152,17 @@ class SelectQuery extends Query implements SelectQueryInterface {
$this->having->isNotNull($field);
return $this;
}
public function havingExists(SelectQueryInterface $select) {
$this->having->exists($select);
return $this;
}
public function havingNotExists(SelectQueryInterface $select) {
$this->having->notExists($select);
return $this;
}
public function forUpdate($set = TRUE) {
if (isset($set)) {
$this->forUpdate = $set;
......@@ -1451,17 +1431,20 @@ class SelectQuery extends Query implements SelectQueryInterface {
$count = clone($this);
$group_by = $count->getGroupBy();
$having = $count->havingConditions();
if (!$count->distinct) {
if (!$count->distinct && !isset($having[0])) {
// When not executing a distinct query, we can zero-out existing fields
// and expressions that are not used by a GROUP BY. Fields listed in
// the GROUP BY clause need to be present in the query.
// and expressions that are not used by a GROUP BY or HAVING. Fields
// listed in a GROUP BY or HAVING clause need to be present in the
// query.
$fields =& $count->getFields();
foreach (array_keys($fields) as $field) {
if (empty($group_by[$field])) {
unset($fields[$field]);
}
}
$expressions =& $count->getExpressions();
foreach (array_keys($expressions) as $field) {
if (empty($group_by[$field])) {
......@@ -1626,5 +1609,5 @@ class SelectQuery extends Query implements SelectQueryInterface {
}
/**
* @} End of "ingroup database".
* @} End of "addtogroup database".
*/
......@@ -6,7 +6,7 @@
*/
/**
* @ingroup database
* @addtogroup database
* @{
*/
......@@ -37,7 +37,7 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
/**
* All databases attached to the current database. This is used to allow
* prefixes to be safely handled without locking the table
*
*
* @var array
*/
protected $attachedDatabases = array();
......@@ -46,10 +46,10 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
* Whether or not a table has been dropped this request: the destructor will
* only try to get rid of unnecessary databases if there is potential of them
* being empty.
*
*
* This variable is set to public because DatabaseSchema_sqlite needs to
* access it. However, it should not be manually set.
*
*
* @var boolean
*/
var $tableDropped = FALSE;
......@@ -59,16 +59,19 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
$this->statementClass = NULL;
// This driver defaults to transaction support, except if explicitly passed FALSE.
$this->transactionSupport = !isset($connection_options['transactions']) || $connection_options['transactions'] !== FALSE;
$this->transactionSupport = $this->transactionalDDLSupport = !isset($connection_options['transactions']) || $connection_options['transactions'] !== FALSE;
$this->connectionOptions = $connection_options;
parent::__construct('sqlite:' . $connection_options['database'], '', '', array(
// Force column names to lower case.
PDO::ATTR_CASE => PDO::CASE_LOWER,
// Allow PDO options to be overridden.
$connection_options += array(
'pdo' => array(),
);
$connection_options['pdo'] += array(
// Convert numeric values to strings when fetching.
PDO::ATTR_STRINGIFY_FETCHES => TRUE,
));
);
parent::__construct('sqlite:' . $connection_options['database'], '', '', $connection_options['pdo']);
// Attach one database for each registered prefix.
$prefixes = $this->prefixes;
......@@ -103,6 +106,11 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
$this->sqliteCreateFunction('substring', array($this, 'sqlFunctionSubstring'), 3);
$this->sqliteCreateFunction('substring_index', array($this, 'sqlFunctionSubstringIndex'), 3);
$this->sqliteCreateFunction('rand', array($this, 'sqlFunctionRand'));
// Execute sqlite init_commands.
if (isset($connection_options['init_commands'])) {
$this->exec(implode('; ', $connection_options['init_commands']));
}
}
/**
......@@ -119,7 +127,7 @@ class DatabaseConnection_sqlite extends DatabaseConnection {
try {
$count = $this->query('SELECT COUNT(*) FROM ' . $prefix . '.sqlite_master WHERE type = :type AND name NOT LIKE :pattern', array(':type' => 'table', ':pattern' => 'sqlite_%'))->fetchField();
// We can prune the database file if it doens't have any tables.
// We can prune the database file if it doesn't have any tables.
if ($count == 0) {
// Detach the database.
$this->query('DETACH DATABASE :schema', array(':schema' => $prefix));
......@@ -507,5 +515,5 @@ class DatabaseStatement_sqlite extends DatabaseStatementPrefetch implements Iter
}
/**
* @} End of "ingroup database".
* @} End of "addtogroup database".
*/
......@@ -6,7 +6,7 @@
*/
/**
* @ingroup database
* @addtogroup database
* @{
*/
......@@ -57,39 +57,18 @@ class InsertQuery_sqlite extends InsertQuery {
* we don't select those rows.
*
* A query like this one:
* UPDATE test SET name = 'newname' WHERE tid = 1
* UPDATE test SET col1 = 'newcol1', col2 = 'newcol2' WHERE tid = 1
* will become:
* UPDATE test SET name = 'newname' WHERE tid = 1 AND name <> 'newname'
* UPDATE test SET col1 = 'newcol1', col2 = 'newcol2' WHERE tid = 1 AND (col1 <> 'newcol1' OR col2 <> 'newcol2')
*/
class UpdateQuery_sqlite extends UpdateQuery {
/**
* Helper function that removes the fields that are already in a condition.
*
* @param $fields
* The fields.
* @param QueryConditionInterface $condition
* A database condition.
*/
protected function removeFieldsInCondition(&$fields, QueryConditionInterface $condition) {
foreach ($condition->conditions() as $child_condition) {
if ($child_condition['field'] instanceof QueryConditionInterface) {
$this->removeFieldsInCondition($fields, $child_condition['field']);
}
else {
unset($fields[$child_condition['field']]);
}
}
}
public function execute() {
if (!empty($this->queryOptions['sqlite_return_matched_rows'])) {
return parent::execute();
}
// Get the fields used in the update query, and remove those that are already
// in the condition.
// Get the fields used in the update query.
$fields = $this->expressionFields + $this->fields;
$this->removeFieldsInCondition($fields, $this->condition);
// Add the inverse of the fields to the condition.
$condition = new DatabaseCondition('OR');
......@@ -156,5 +135,5 @@ class TruncateQuery_sqlite extends TruncateQuery {
}
/**
* @} End of "ingroup database".
* @} End of "addtogroup database".
*/