summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/actions.inc17
-rw-r--r--includes/ajax.inc50
-rw-r--r--includes/archiver.inc24
-rw-r--r--includes/authorize.inc26
-rw-r--r--includes/batch.inc31
-rw-r--r--includes/batch.queue.inc35
-rw-r--r--includes/bootstrap.inc284
-rw-r--r--includes/cache-install.inc18
-rw-r--r--includes/cache.inc86
-rw-r--r--includes/common.inc613
-rw-r--r--includes/database/database.inc12
-rw-r--r--includes/database/mysql/database.inc41
-rw-r--r--includes/database/pgsql/database.inc15
-rw-r--r--includes/database/query.inc6
-rw-r--r--includes/database/select.inc63
-rw-r--r--includes/database/sqlite/database.inc16
-rw-r--r--includes/file.inc63
-rw-r--r--includes/filetransfer/filetransfer.inc6
-rw-r--r--includes/form.inc39
-rw-r--r--includes/graph.inc1
-rw-r--r--includes/image.inc42
-rw-r--r--includes/install.core.inc41
-rw-r--r--includes/install.inc63
-rw-r--r--includes/iso.inc1
-rw-r--r--includes/json-encode.inc102
-rw-r--r--includes/locale.inc131
-rw-r--r--includes/mail.inc46
-rw-r--r--includes/menu.inc85
-rw-r--r--includes/module.inc20
-rw-r--r--includes/password.inc1
-rw-r--r--includes/path.inc10
-rw-r--r--includes/registry.inc1
-rw-r--r--includes/stream_wrappers.inc4
-rw-r--r--includes/theme.inc189
-rw-r--r--includes/token.inc7
-rw-r--r--includes/update.inc2
-rw-r--r--includes/updater.inc4
-rw-r--r--includes/utility.inc7
-rw-r--r--includes/xmlrpc.inc1
-rw-r--r--includes/xmlrpcs.inc1
40 files changed, 1486 insertions, 718 deletions
diff --git a/includes/actions.inc b/includes/actions.inc
index 760de8300..ed43af4fd 100644
--- a/includes/actions.inc
+++ b/includes/actions.inc
@@ -22,7 +22,7 @@
* - $a1, $a2: Optional additional information, which can be passed into
* actions_do() and will be passed along to the action function.
*
- * @} End of "defgroup actions".
+ * @}
*/
/**
@@ -48,6 +48,7 @@
* Passed along to the callback.
* @param $a2
* Passed along to the callback.
+ *
* @return
* An associative array containing the results of the functions that
* perform the actions, keyed on action ID.
@@ -149,6 +150,7 @@ function actions_do($action_ids, $object = NULL, $context = NULL, $a1 = NULL, $a
*
* @param $reset
* Reset the action info static cache.
+ *
* @return
* An associative array keyed on action function name, with the same format
* as the return value of hook_action_info(), containing all
@@ -176,9 +178,9 @@ function actions_list($reset = FALSE) {
* function and the actions returned by actions_list() are partially
* synchronized. Non-configurable actions from hook_action_info()
* implementations are put into the database when actions_synchronize() is
- * called, which happens when admin/config/system/actions is visited. Configurable
- * actions are not added to the database until they are configured in the
- * user interface, in which case a database row is created for each
+ * called, which happens when admin/config/system/actions is visited.
+ * Configurable actions are not added to the database until they are configured
+ * in the user interface, in which case a database row is created for each
* configuration of each action.
*
* @return
@@ -205,6 +207,7 @@ function actions_get_all_actions() {
* An associative array with function names or action IDs as keys
* and associative arrays with keys 'label', 'type', etc. as values.
* This is usually the output of actions_list() or actions_get_all_actions().
+ *
* @return
* An associative array whose keys are hashes of the input array keys, and
* whose corresponding values are associative arrays with components
@@ -223,7 +226,7 @@ function actions_actions_map($actions) {
}
/**
- * Given a hash of an action array key, returns the key (function or ID).
+ * Returns an action array key (function or ID), given its hash.
*
* Faster than actions_actions_map() when you only need the function name or ID.
*
@@ -231,6 +234,7 @@ function actions_actions_map($actions) {
* Hash of a function name or action ID array key. The array key
* is a key into the return value of actions_list() (array key is the action
* function name) or actions_get_all_actions() (array key is the action ID).
+ *
* @return
* The corresponding array key, or FALSE if no match is found.
*/
@@ -332,6 +336,7 @@ function actions_synchronize($delete_orphans = FALSE) {
* to Jim'.
* @param $aid
* The ID of this action. If omitted, a new action is created.
+ *
* @return
* The ID of the action.
*/
@@ -361,6 +366,7 @@ function actions_save($function, $type, $params, $label, $aid = NULL) {
*
* @param $aid
* The ID of the action to retrieve.
+ *
* @return
* The appropriate action row from the database as an object.
*/
@@ -380,4 +386,3 @@ function actions_delete($aid) {
->execute();
module_invoke_all('actions_delete', $aid);
}
-
diff --git a/includes/ajax.inc b/includes/ajax.inc
index d70808efe..fb07477d6 100644
--- a/includes/ajax.inc
+++ b/includes/ajax.inc
@@ -24,7 +24,8 @@
* ajax_form_callback() and a defined #ajax['callback'] function.
* However, you may optionally specify a different path to request or a
* different callback function to invoke, which can return updated HTML or can
- * also return a richer set of @link ajax_commands Ajax framework commands @endlink.
+ * also return a richer set of
+ * @link ajax_commands Ajax framework commands @endlink.
*
* Standard form handling is as follows:
* - A form element has a #ajax property that includes #ajax['callback'] and
@@ -101,7 +102,7 @@
* In the above example, the 'changethis' element is Ajax-enabled. The default
* #ajax['event'] is 'change', so when the 'changethis' element changes,
* an Ajax call is made. The form is submitted and reprocessed, and then the
- * callback is called. In this case, the form has been automatically
+ * callback is called. In this case, the form has been automatically
* built changing $form['replace_textfield']['#description'], so the callback
* just returns that part of the form.
*
@@ -188,11 +189,11 @@
* be converted to a JSON object and returned to the client, which will then
* iterate over the array and process it like a macro language.
*
- * Each command item is an associative array which will be converted to a command
- * object on the JavaScript side. $command_item['command'] is the type of
- * command, e.g. 'alert' or 'replace', and will correspond to a method in the
- * Drupal.ajax[command] space. The command array may contain any other data
- * that the command needs to process, e.g. 'method', 'selector', 'settings', etc.
+ * Each command item is an associative array which will be converted to a
+ * command object on the JavaScript side. $command_item['command'] is the type
+ * of command, e.g. 'alert' or 'replace', and will correspond to a method in the
+ * Drupal.ajax[command] space. The command array may contain any other data that
+ * the command needs to process, e.g. 'method', 'selector', 'settings', etc.
*
* Commands are usually created with a couple of helper functions, so they
* look like this:
@@ -222,7 +223,7 @@
*/
/**
- * Render a commands array into JSON.
+ * Renders a commands array into JSON.
*
* @param $commands
* A list of macro commands generated by the use of ajax_command_*()
@@ -262,19 +263,13 @@ function ajax_render($commands = array()) {
}
}
- // Settings are handled separately, later in this function, so that changes to
- // the ajaxPageState setting that occur during drupal_get_css() and
- // drupal_get_js() get included, and because the jQuery.extend() code produced
- // by drupal_get_js() for adding settings isn't appropriate during an Ajax
- // response, because it does not pass TRUE for the "deep" parameter, and
- // therefore, can clobber existing settings on the page.
+ // Render the HTML to load these files, and add AJAX commands to insert this
+ // HTML in the page. We pass TRUE as the $skip_alter argument to prevent the
+ // data from being altered again, as we already altered it above. Settings are
+ // handled separately, afterwards.
if (isset($items['js']['settings'])) {
unset($items['js']['settings']);
}
-
- // Render the HTML to load these files, and add Ajax commands to insert this
- // HTML in the page. We pass TRUE as the $skip_alter argument to prevent the
- // data from being altered again, as we already altered it above.
$styles = drupal_get_css($items['css'], TRUE);
$scripts_footer = drupal_get_js('footer', $items['js'], TRUE);
$scripts_header = drupal_get_js('header', $items['js'], TRUE);
@@ -293,11 +288,10 @@ function ajax_render($commands = array()) {
$commands = array_merge($extra_commands, $commands);
}
+ // Now add a command to merge changes and additions to Drupal.settings.
$scripts = drupal_add_js();
if (!empty($scripts['settings'])) {
$settings = $scripts['settings'];
- // Automatically extract any settings added via drupal_add_js() and make
- // them the first command.
array_unshift($commands, ajax_command_settings(call_user_func_array('array_merge_recursive', $settings['data']), TRUE));
}
@@ -308,7 +302,7 @@ function ajax_render($commands = array()) {
}
/**
- * Get a form submitted via #ajax during an Ajax callback.
+ * Gets a form submitted via #ajax during an Ajax callback.
*
* This will load a form from the form cache used during Ajax operations. It
* pulls the form info from $_POST.
@@ -368,6 +362,8 @@ function ajax_get_form() {
* #ajax['path']. If processing is required that cannot be accomplished with
* a callback, re-implement this function and set #ajax['path'] to the
* enhanced function.
+ *
+ * @see system_menu()
*/
function ajax_form_callback() {
list($form, $form_state) = ajax_get_form();
@@ -403,6 +399,9 @@ function ajax_form_callback() {
* of the page. Therefore, system_menu() sets the 'theme callback' for
* 'system/ajax' to this function, and it is recommended that modules
* implementing other generic Ajax paths do the same.
+ *
+ * @see system_menu()
+ * @see file_menu()
*/
function ajax_base_page_theme() {
if (!empty($_POST['ajax_page_state']['theme']) && !empty($_POST['ajax_page_state']['theme_token'])) {
@@ -421,7 +420,7 @@ function ajax_base_page_theme() {
}
/**
- * Package and send the result of a page callback to the browser as an Ajax response.
+ * Packages and sends the result of a page callback as an Ajax response.
*
* This function is the equivalent of drupal_deliver_html_page(), but for Ajax
* requests. Like that function, it:
@@ -554,7 +553,7 @@ function ajax_prepare_response($page_callback_result) {
}
/**
- * Perform end-of-Ajax-request tasks.
+ * Performs end-of-Ajax-request tasks.
*
* This function is the equivalent of drupal_page_footer(), but for Ajax
* requests.
@@ -577,7 +576,7 @@ function ajax_footer() {
}
/**
- * Form element process callback to handle #ajax.
+ * Form element processing handler for the #ajax form property.
*
* @param $element
* An associative array containing the properties of the element.
@@ -596,7 +595,7 @@ function ajax_process_form($element, &$form_state) {
}
/**
- * Add Ajax information about an element to the page to communicate with JavaScript.
+ * Adds Ajax information about an element to communicate with JavaScript.
*
* If #ajax['path'] is set on an element, this additional JavaScript is added
* to the page header to attach the Ajax behaviors. See ajax.js for more
@@ -1210,4 +1209,3 @@ function ajax_command_restripe($selector) {
'selector' => $selector,
);
}
-
diff --git a/includes/archiver.inc b/includes/archiver.inc
index fec053be6..3ce117390 100644
--- a/includes/archiver.inc
+++ b/includes/archiver.inc
@@ -6,61 +6,63 @@
*/
/**
- * Common interface for all Archiver classes.
+ * Defines the common interface for all Archiver classes.
*/
interface ArchiverInterface {
/**
- * Constructor for a new archiver instance.
+ * Constructs a new archiver instance.
*
* @param $file_path
- * The full system path of the archive to manipulate. Only local files
- * are supported. If the file does not yet exist, it will be created if
+ * The full system path of the archive to manipulate. Only local files
+ * are supported. If the file does not yet exist, it will be created if
* appropriate.
*/
public function __construct($file_path);
/**
- * Add the specified file or directory to the archive.
+ * Adds the specified file or directory to the archive.
*
* @param $file_path
* The full system path of the file or directory to add. Only local files
* and directories are supported.
+ *
* @return ArchiverInterface
* The called object.
*/
public function add($file_path);
/**
- * Remove the specified file from the archive.
+ * Removes the specified file from the archive.
*
* @param $path
* The file name relative to the root of the archive to remove.
+ *
* @return ArchiverInterface
* The called object.
*/
public function remove($path);
/**
- * Extract multiple files in the archive to the specified path.
+ * Extracts multiple files in the archive to the specified path.
*
* @param $path
* A full system path of the directory to which to extract files.
* @param $files
* Optionally specify a list of files to be extracted. Files are
* relative to the root of the archive. If not specified, all files
- * in the archive will be extracted
+ * in the archive will be extracted.
+ *
* @return ArchiverInterface
* The called object.
*/
- public function extract($path, Array $files = array());
+ public function extract($path, array $files = array());
/**
- * List all files in the archive.
+ * Lists all files in the archive.
*
* @return
* An array of file names relative to the root of the archive.
*/
public function listContents();
}
-
diff --git a/includes/authorize.inc b/includes/authorize.inc
index 852860413..da6918ca7 100644
--- a/includes/authorize.inc
+++ b/includes/authorize.inc
@@ -6,7 +6,13 @@
*/
/**
- * Build the form for choosing a FileTransfer type and supplying credentials.
+ * Form constructor for the file transfer authorization form.
+ *
+ * Allows the user to choose a FileTransfer type and supply credentials.
+ *
+ * @see authorize_filetransfer_form_validate()
+ * @see authorize_filetransfer_form_submit()
+ * @ingroup forms
*/
function authorize_filetransfer_form($form, &$form_state) {
global $base_url, $is_https;
@@ -127,10 +133,11 @@ function authorize_filetransfer_form($form, &$form_state) {
}
/**
- * Generate the Form API array for the settings for a given connection backend.
+ * Generates the Form API array for a given connection backend's settings.
*
* @param $backend
* The name of the backend (e.g. 'ftp', 'ssh', etc).
+ *
* @return
* Form API array of connection settings for the given backend.
*
@@ -151,7 +158,7 @@ function _authorize_filetransfer_connection_settings($backend) {
}
/**
- * Recursively fill in the default settings on a file transfer connection form.
+ * Sets the default settings on a file transfer connection form recursively.
*
* The default settings for the file transfer connection forms are saved in
* the database. The settings are stored as a nested array in the case of a
@@ -165,8 +172,6 @@ function _authorize_filetransfer_connection_settings($backend) {
* The key for our current form element, if any.
* @param array $defaults
* The default settings for the file transfer backend we're operating on.
- * @return
- * Nothing, this function just sets $element['#default_value'] if needed.
*/
function _authorize_filetransfer_connection_settings_set_defaults(&$element, $key, array $defaults) {
// If we're operating on a form element which isn't a fieldset, and we have
@@ -186,9 +191,10 @@ function _authorize_filetransfer_connection_settings_set_defaults(&$element, $ke
}
/**
- * Validate callback for the filetransfer authorization form.
+ * Form validation handler for authorize_filetransfer_form().
*
* @see authorize_filetransfer_form()
+ * @see authorize_filetransfer_submit()
*/
function authorize_filetransfer_form_validate($form, &$form_state) {
// Only validate the form if we have collected all of the user input and are
@@ -218,9 +224,10 @@ function authorize_filetransfer_form_validate($form, &$form_state) {
}
/**
- * Submit callback when a file transfer is being authorized.
+ * Form submission handler for authorize_filetransfer_form().
*
* @see authorize_filetransfer_form()
+ * @see authorize_filetransfer_validate()
*/
function authorize_filetransfer_form_submit($form, &$form_state) {
global $base_url;
@@ -280,7 +287,7 @@ function authorize_filetransfer_form_submit($form, &$form_state) {
}
/**
- * Run the operation specified in $_SESSION['authorize_operation']
+ * Runs the operation specified in $_SESSION['authorize_operation'].
*
* @param $filetransfer
* The FileTransfer object to use for running the operation.
@@ -298,12 +305,13 @@ function authorize_run_operation($filetransfer) {
}
/**
- * Get a FileTransfer class for a specific transfer method and settings.
+ * Gets a FileTransfer class for a specific transfer method and settings.
*
* @param $backend
* The FileTransfer backend to get the class for.
* @param $settings
* Array of settings for the FileTransfer.
+ *
* @return
* An instantiated FileTransfer object for the requested method and settings,
* or FALSE if there was an error finding or instantiating it.
diff --git a/includes/batch.inc b/includes/batch.inc
index 727c62560..061acd4c6 100644
--- a/includes/batch.inc
+++ b/includes/batch.inc
@@ -1,6 +1,5 @@
<?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();
}
}
-
diff --git a/includes/batch.queue.inc b/includes/batch.queue.inc
index 846483698..ed290ee70 100644
--- a/includes/batch.queue.inc
+++ b/includes/batch.queue.inc
@@ -1,24 +1,30 @@
<?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();
diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index 292b8bb87..c32c05d5f 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -8,7 +8,7 @@
/**
* The current system version.
*/
-define('VERSION', '7.11');
+define('VERSION', '7.12');
/**
* Core API compatibility.
@@ -43,9 +43,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."
*
@@ -137,8 +137,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 +152,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 +191,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']);
@@ -285,12 +289,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 +307,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 +323,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 +342,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 +391,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,14 +424,16 @@ 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.
+ *
+ * If you start and stop the same timer multiple times, the measured intervals
+ * will be accumulated.
*
* @param $name
* The name of the timer.
@@ -429,7 +446,7 @@ function timer_start($name) {
}
/**
- * Read the current timer value without stopping the timer.
+ * Reads the current timer value without stopping the timer.
*
* @param $name
* The name of the timer.
@@ -453,7 +470,7 @@ function timer_read($name) {
}
/**
- * Stop the timer with the specified name.
+ * Stops the timer with the specified name.
*
* @param $name
* The name of the timer.
@@ -578,7 +595,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.
@@ -640,7 +657,7 @@ function drupal_override_server_variables($variables = array()) {
}
/**
- * Initialize PHP environment.
+ * Initializes the PHP environment.
*/
function drupal_environment_initialize() {
if (!isset($_SERVER['HTTP_REFERER'])) {
@@ -699,7 +716,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.
@@ -709,8 +726,7 @@ 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;
@@ -796,9 +812,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
@@ -828,6 +845,11 @@ function drupal_get_filename($type, $name, $filename = NULL) {
// drupal_static().
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();
}
@@ -895,11 +917,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
@@ -1006,7 +1028,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
@@ -1038,7 +1060,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.
@@ -1057,7 +1079,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.
@@ -1079,8 +1101,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).
@@ -1112,7 +1135,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).
@@ -1148,11 +1171,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.
@@ -1169,6 +1193,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).
*/
@@ -1182,9 +1208,10 @@ 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.
@@ -1219,7 +1246,7 @@ function drupal_send_headers($default_headers = array(), $only_default = FALSE)
}
/**
- * 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
@@ -1262,7 +1289,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
@@ -1365,7 +1392,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');
@@ -1418,10 +1445,10 @@ 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.) 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
@@ -1430,8 +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.
- * See format_string().
+ * 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
@@ -1444,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()) {
@@ -1482,7 +1511,7 @@ function t($string, array $args = array(), array $options = array()) {
}
/**
- * Replace placeholders with sanitized values in a string.
+ * Replaces placeholders with sanitized values in a string.
*
* @param $string
* A string containing placeholders.
@@ -1524,7 +1553,7 @@ function format_string($string, array $args = array()) {
}
/**
- * 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.
@@ -1563,6 +1592,7 @@ function check_plain($text) {
*
* @param $text
* The text to check.
+ *
* @return
* TRUE if the text is valid UTF-8, FALSE if not.
*/
@@ -1604,7 +1634,7 @@ function request_uri() {
}
/**
- * Log an exception.
+ * Logs an exception.
*
* This is a wrapper function for watchdog() which automatically decodes an
* exception.
@@ -1645,7 +1675,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
@@ -1705,7 +1735,7 @@ function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NO
}
/**
- * Set a message which reflects the status of the performed operation.
+ * Sets a message which reflects the status of the performed operation.
*
* If the function is called with no arguments, this function returns all set
* messages without clearing them.
@@ -1741,12 +1771,13 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
}
/**
- * Return all messages that have been set.
+ * Returns all messages that have been set.
*
* @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,
@@ -1774,7 +1805,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.
@@ -1791,7 +1824,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
@@ -1816,7 +1851,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
@@ -1825,6 +1860,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.
*/
@@ -1850,7 +1886,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.
@@ -1869,7 +1905,8 @@ 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.
@@ -1916,7 +1953,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.
@@ -1934,7 +1971,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.
@@ -1977,7 +2014,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);
}
/**
@@ -2038,20 +2076,22 @@ 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.
+ *
+ * The bootstrap phase is an integer constant identifying a phase of Drupal
+ * to load. Each phase adds to the previous one, so invoking a later phase
+ * automatically runs the earlier phases as well. To access the Drupal
+ * database from a script without loading anything else, include bootstrap.inc
+ * and call drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE).
*
* @param $phase
* A constant. Allowed values are the DRUPAL_BOOTSTRAP_* constants.
* @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.
@@ -2130,7 +2170,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;
@@ -2145,7 +2185,7 @@ function drupal_get_user_timezone() {
}
/**
- * Custom PHP error handler.
+ * Provides custom PHP error handling.
*
* @param $error_level
* The level of the error raised.
@@ -2156,7 +2196,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';
@@ -2164,7 +2205,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
@@ -2192,7 +2233,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.
@@ -2207,7 +2248,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;
@@ -2263,7 +2304,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
@@ -2315,7 +2356,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;
@@ -2332,7 +2373,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');
@@ -2355,8 +2396,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
@@ -2392,7 +2432,7 @@ function drupal_valid_test_ua() {
}
/**
- * 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;
@@ -2452,7 +2492,7 @@ function drupal_fast_404() {
}
/**
- * Return TRUE if a Drupal installation is currently being attempted.
+ * Returns TRUE if a Drupal installation is currently being attempted.
*/
function drupal_installation_attempted() {
return defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install';
@@ -2495,10 +2535,9 @@ function get_t() {
}
/**
- * Initialize all the defined language types.
+ * Initializes all the defined language types.
*/
function drupal_language_initialize() {
- global $language;
$types = language_types();
// Ensure the language is correctly returned, even without multilanguage
@@ -2518,13 +2557,10 @@ function drupal_language_initialize() {
// environments.
bootstrap_invoke_all('language_init');
}
-
- // Send appropriate HTTP-Header for browsers and search engines.
- header('Content-Language: ' . $language->language);
}
/**
- * 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
@@ -2539,7 +2575,7 @@ function drupal_language_types() {
}
/**
- * Return true if there is more than one language enabled.
+ * Returns TRUE if there is more than one language enabled.
*/
function drupal_multilingual() {
// The "language_count" variable stores the number of enabled languages to
@@ -2549,7 +2585,7 @@ function drupal_multilingual() {
}
/**
- * Return an array of the available language types.
+ * Returns an array of the available language types.
*/
function language_types() {
return array_keys(variable_get('language_types', drupal_language_types()));
@@ -2606,7 +2642,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
@@ -2676,16 +2712,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 '/'
@@ -2725,6 +2761,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
@@ -2774,7 +2812,7 @@ function ip_address() {
*/
/**
- * 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().
@@ -2810,11 +2848,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');
}
+ /**
+ * Overrides DrupalCacheArray::resolveCacheMiss().
+ */
protected function resolveCacheMiss($offset) {
$complete_schema = drupal_get_complete_schema();
$value = isset($complete_schema[$offset]) ? $complete_schema[$offset] : NULL;
@@ -2825,7 +2869,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().
@@ -2895,13 +2939,14 @@ function drupal_get_complete_schema($rebuild = FALSE) {
*/
/**
- * 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.
*/
@@ -2910,13 +2955,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.
*/
@@ -2925,7 +2971,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
@@ -2934,6 +2980,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.
@@ -3005,7 +3052,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.
@@ -3016,7 +3063,7 @@ 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
@@ -3034,7 +3081,7 @@ function registry_update() {
*/
/**
- * 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
@@ -3185,7 +3232,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.
@@ -3195,7 +3242,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)));
@@ -3203,7 +3250,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).
@@ -3216,7 +3264,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".
@@ -3251,7 +3299,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();
diff --git a/includes/cache-install.inc b/includes/cache-install.inc
index d9bb0f92e..9e0dd01de 100644
--- a/includes/cache-install.inc
+++ b/includes/cache-install.inc
@@ -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;
}
diff --git a/includes/cache.inc b/includes/cache.inc
index 8666874ac..eb7f09040 100644
--- a/includes/cache.inc
+++ b/includes/cache.inc
@@ -1,18 +1,23 @@
<?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,7 +73,7 @@ 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
@@ -132,13 +140,15 @@ 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 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 +156,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 +177,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 +193,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 +231,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 +239,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 +287,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 +303,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 +315,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 +323,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.
@@ -373,13 +395,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.
@@ -408,6 +431,9 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
return $cache;
}
+ /**
+ * Implements DrupalCacheInterface::set().
+ */
function set($cid, $data, $expire = CACHE_PERMANENT) {
$fields = array(
'serialized' => 0,
@@ -434,6 +460,9 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
}
}
+ /**
+ * Implements DrupalCacheInterface::clear().
+ */
function clear($cid = NULL, $wildcard = FALSE) {
global $user;
@@ -496,6 +525,9 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
}
}
+ /**
+ * Implements DrupalCacheInterface::isEmpty().
+ */
function isEmpty() {
$this->garbageCollection();
$query = db_select($this->bin);
diff --git a/includes/common.inc b/includes/common.inc
index 3a3d27473..43e211813 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -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,8 +85,9 @@ 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);
@@ -110,31 +110,36 @@ 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().
+ *
+ * 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.
*/
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 +151,12 @@ 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.
+ * Adds content to a specified region.
*
* @param $region
* Page region the content is added to.
@@ -168,7 +173,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,13 +199,13 @@ function drupal_get_region_content($region = NULL, $delimiter = ' ') {
}
/**
- * Get the name of the currently active install profile.
+ * Gets the name of the currently active install 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.
@@ -220,7 +225,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 +241,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,7 +270,7 @@ 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
* arguments (or NULL for both) to retrieve the currently stored elements.
@@ -333,7 +338,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 +347,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 +375,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 +392,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,7 +437,7 @@ 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.
@@ -452,7 +457,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,7 +498,7 @@ 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
@@ -524,7 +529,7 @@ 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
@@ -621,7 +626,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.
@@ -686,7 +691,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 +703,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 +715,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.
@@ -832,7 +838,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;
@@ -1023,6 +1029,14 @@ function drupal_http_request($url, array $options = array()) {
* @} 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 +1047,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().
*
- * tmp_name does not have backslashes added see
- * http://php.net/manual/en/features.file-upload.php#42280
+ * The tmp_name key is skipped keys since PHP generates single backslashes for
+ * file paths on Windows systems.
+ *
+ * @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 +1073,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 +1097,13 @@ 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.
*
* @param $mail
* A string containing an e-mail address.
+ *
* @return
* TRUE if the address is in a valid format.
*/
@@ -1086,7 +1112,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 +1121,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 +1154,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 +1181,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 +1199,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 +1293,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 +1313,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 +1327,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 +1358,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 +1396,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 +1412,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 +1435,7 @@ function _filter_xss_split($m, $store = FALSE) {
}
if (!isset($allowed_html[strtolower($elem)])) {
- // Disallowed HTML element
+ // Disallowed HTML element.
return '';
}
@@ -1423,7 +1451,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 +1476,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 +1486,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 +1503,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 +1540,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 +1564,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 +1627,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 +1643,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 +1682,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
@@ -1675,31 +1704,27 @@ function format_xml_elements($array) {
* @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;
@@ -1728,11 +1753,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 +1775,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 +1815,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 +1841,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 +1956,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 +1971,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().
@@ -2057,8 +2088,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().
*
@@ -2183,7 +2214,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.
@@ -2191,6 +2222,7 @@ 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.
*/
@@ -2203,7 +2235,7 @@ function url_is_external($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'.
@@ -2225,7 +2257,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
@@ -2338,7 +2370,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
@@ -2446,7 +2478,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:
@@ -2466,6 +2498,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?
@@ -2551,7 +2587,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().
@@ -2578,7 +2614,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
@@ -2600,7 +2636,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
@@ -2676,10 +2712,10 @@ function drupal_get_path($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.
@@ -2690,12 +2726,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'.
@@ -2759,8 +2795,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.
@@ -2892,7 +2928,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
@@ -2938,7 +2974,7 @@ function drupal_get_css($css = NULL, $skip_alter = FALSE) {
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]]);
@@ -2962,11 +2998,24 @@ function drupal_get_css($css = NULL, $skip_alter = FALSE) {
}
/**
- * 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
@@ -3033,6 +3082,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();
@@ -3115,6 +3165,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'));
@@ -3376,8 +3427,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.
@@ -3455,9 +3506,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__);
@@ -3528,13 +3577,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.
*/
@@ -3630,7 +3680,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.)
@@ -3639,6 +3689,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.
*/
@@ -3660,13 +3711,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.
*/
@@ -3675,7 +3727,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,
@@ -3806,7 +3858,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
@@ -3926,7 +3978,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.
@@ -4023,6 +4075,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()
*/
@@ -4066,8 +4119,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()
@@ -4239,7 +4294,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
@@ -4590,16 +4645,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;
@@ -4614,8 +4669,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(
@@ -4635,8 +4690,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
@@ -4653,14 +4708,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.
*
@@ -4733,8 +4788,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.
@@ -4800,14 +4855,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);
}
/**
@@ -4821,7 +4891,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.
@@ -4839,7 +4909,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.
@@ -4852,7 +4922,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.
@@ -4866,7 +4936,7 @@ 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.
@@ -4876,7 +4946,7 @@ function drupal_get_token($value = '') {
}
/**
- * 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.
@@ -4884,6 +4954,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.
@@ -4952,7 +5023,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
@@ -5004,10 +5075,10 @@ 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.
@@ -5040,7 +5111,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):
@@ -5054,7 +5125,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);
@@ -5082,14 +5153,17 @@ function drupal_cron_run() {
}
/**
- * 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');
}
}
@@ -5158,14 +5232,14 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1)
$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';
}
@@ -5201,7 +5275,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
@@ -5209,6 +5283,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.
@@ -5459,13 +5534,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'];
@@ -5478,8 +5553,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()
@@ -5556,20 +5633,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
@@ -5586,6 +5663,7 @@ function drupal_render_page($page) {
*
* @param $elements
* The structured array describing the data to be rendered.
+ *
* @return
* The rendered HTML.
*/
@@ -5601,8 +5679,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
@@ -5699,7 +5780,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.
@@ -5724,7 +5805,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
@@ -5753,7 +5834,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
@@ -5779,7 +5860,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.
@@ -5808,16 +5889,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)) {
@@ -5838,17 +5920,17 @@ function drupal_render_cache_get($elements) {
}
/**
- * Cache the rendered output of a renderable element.
- *
- * This is called by drupal_render() if the #cache property is set on an element.
+ * Caches the rendered output of a renderable 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.
@@ -5874,7 +5956,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
@@ -5917,9 +5999,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
@@ -5957,12 +6040,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
@@ -6001,7 +6087,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']
@@ -6048,7 +6134,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().
@@ -6074,7 +6160,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().
@@ -6114,21 +6200,21 @@ 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] != '#';
@@ -6144,6 +6230,7 @@ function element_child($key) {
* 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.
*/
@@ -6183,6 +6270,7 @@ function element_children(&$elements, $sort = FALSE) {
*
* @param $elements
* The parent element.
+ *
* @return
* The array keys of the element's visible children.
*/
@@ -6377,7 +6465,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
@@ -6413,11 +6501,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',
@@ -6493,7 +6581,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',
@@ -6513,7 +6601,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),
),
@@ -6532,7 +6620,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',
),
@@ -6548,7 +6636,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',
),
@@ -6624,7 +6712,7 @@ function drupal_common_theme() {
*/
/**
- * 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
@@ -6643,7 +6731,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
@@ -6651,6 +6739,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.
@@ -6706,7 +6795,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
@@ -6737,7 +6826,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.
@@ -6745,7 +6836,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']);
@@ -6973,7 +7064,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:
@@ -7003,6 +7094,7 @@ function drupal_parse_info_file($filename) {
*
* @param $data
* A string to parse.
+ *
* @return
* The info array.
*
@@ -7026,19 +7118,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);
@@ -7054,7 +7146,7 @@ function drupal_parse_info_format($data) {
$value = $constants[$value];
}
- // Insert actual value
+ // Insert actual value.
if ($last == '') {
$last = count($parent);
}
@@ -7066,11 +7158,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
*/
@@ -7089,7 +7182,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()
*/
@@ -7115,7 +7208,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()
*/
@@ -7133,7 +7226,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.
@@ -7151,6 +7244,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.
@@ -7174,10 +7268,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.
@@ -7185,7 +7279,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().
@@ -7210,10 +7304,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').
@@ -7267,12 +7362,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.
@@ -7777,11 +7873,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.
@@ -7810,14 +7907,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()
@@ -7833,10 +7930,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()
diff --git a/includes/database/database.inc b/includes/database/database.inc
index 33000aa70..6e40b2765 100644
--- a/includes/database/database.inc
+++ b/includes/database/database.inc
@@ -1016,9 +1016,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 +1096,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.
diff --git a/includes/database/mysql/database.inc b/includes/database/mysql/database.inc
index 7d5d85998..e024a7f39 100644
--- a/includes/database/mysql/database.inc
+++ b/includes/database/mysql/database.inc
@@ -37,14 +37,20 @@ 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,14 +62,22 @@ 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. Also, as MySQL 5.5 changed the meaning of
- // TRADITIONAL we need to spell out the modes one by one.
- $this->exec("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 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 queryRange($query, $from, $count, array $args = array(), array $options = array()) {
@@ -169,8 +183,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;
diff --git a/includes/database/pgsql/database.inc b/includes/database/pgsql/database.inc
index 39b4e9b69..d42a1cc3c 100644
--- a/includes/database/pgsql/database.inc
+++ b/includes/database/pgsql/database.inc
@@ -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
@@ -59,10 +64,16 @@ class DatabaseConnection_pgsql extends DatabaseConnection {
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 query($query, array $args = array(), $options = array()) {
diff --git a/includes/database/query.inc b/includes/database/query.inc
index c77968767..6020b0ea5 100644
--- a/includes/database/query.inc
+++ b/includes/database/query.inc
@@ -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);
diff --git a/includes/database/select.inc b/includes/database/select.inc
index 9b587aebe..7e2af85e7 100644
--- a/includes/database/select.inc
+++ b/includes/database/select.inc
@@ -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])) {
diff --git a/includes/database/sqlite/database.inc b/includes/database/sqlite/database.inc
index 3e2490b00..b4e41b527 100644
--- a/includes/database/sqlite/database.inc
+++ b/includes/database/sqlite/database.inc
@@ -59,16 +59,21 @@ 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(
+ // Allow PDO options to be overridden.
+ $connection_options += array(
+ 'pdo' => array(),
+ );
+ $connection_options['pdo'] += array(
// Force column names to lower case.
PDO::ATTR_CASE => PDO::CASE_LOWER,
// 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 +108,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']));
+ }
}
/**
diff --git a/includes/file.inc b/includes/file.inc
index 40e8349a0..7fd6c71c9 100644
--- a/includes/file.inc
+++ b/includes/file.inc
@@ -770,7 +770,7 @@ function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS
$file = clone $source;
$file->fid = NULL;
$file->uri = $uri;
- $file->filename = basename($uri);
+ $file->filename = drupal_basename($uri);
// If we are replacing an existing file re-use its database record.
if ($replace == FILE_EXISTS_REPLACE) {
$existing_files = file_load_multiple(array(), array('uri' => $uri));
@@ -783,7 +783,7 @@ function file_copy(stdClass $source, $destination = NULL, $replace = FILE_EXISTS
// If we are renaming around an existing file (rather than a directory),
// use its basename for the filename.
elseif ($replace == FILE_EXISTS_RENAME && is_file($destination)) {
- $file->filename = basename($destination);
+ $file->filename = drupal_basename($destination);
}
$file = file_save($file);
@@ -828,6 +828,10 @@ function file_valid_uri($uri) {
* is reported.
* - If file already exists in $destination either the call will error out,
* replace the file or rename the file based on the $replace parameter.
+ * - Provides a fallback using realpaths if the move fails using stream
+ * wrappers. This can occur because PHP's copy() function does not properly
+ * support streams if safe_mode or open_basedir are enabled. See
+ * https://bugs.php.net/bug.php?id=60456
*
* @param $source
* A string specifying the filepath or URI of the source file.
@@ -867,14 +871,14 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST
// Build a destination URI if necessary.
if (!isset($destination)) {
- $destination = file_build_uri(basename($source));
+ $destination = file_build_uri(drupal_basename($source));
}
// Prepare the destination directory.
if (file_prepare_directory($destination)) {
// The destination is already a directory, so append the source basename.
- $destination = file_stream_wrapper_uri_normalize($destination . '/' . basename($source));
+ $destination = file_stream_wrapper_uri_normalize($destination . '/' . drupal_basename($source));
}
else {
// Perhaps $destination is a dir/file?
@@ -907,8 +911,12 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST
file_ensure_htaccess();
// Perform the copy operation.
if (!@copy($source, $destination)) {
- watchdog('file', 'The specified file %file could not be copied to %destination.', array('%file' => $source, '%destination' => $destination), WATCHDOG_ERROR);
- return FALSE;
+ // If the copy failed and realpaths exist, retry the operation using them
+ // instead.
+ if ($real_source === FALSE || $real_destination === FALSE || !@copy($real_source, $real_destination)) {
+ watchdog('file', 'The specified file %file could not be copied to %destination.', array('%file' => $source, '%destination' => $destination), WATCHDOG_ERROR);
+ return FALSE;
+ }
}
// Set the permissions on the new file.
@@ -950,7 +958,7 @@ function file_destination($destination, $replace) {
break;
case FILE_EXISTS_RENAME:
- $basename = basename($destination);
+ $basename = drupal_basename($destination);
$directory = drupal_dirname($destination);
$destination = file_create_filename($basename, $directory);
break;
@@ -1025,7 +1033,7 @@ function file_move(stdClass $source, $destination = NULL, $replace = FILE_EXISTS
// If we are renaming around an existing file (rather than a directory),
// use its basename for the filename.
elseif ($replace == FILE_EXISTS_RENAME && is_file($destination)) {
- $file->filename = basename($destination);
+ $file->filename = drupal_basename($destination);
}
$file = file_save($file);
@@ -1138,7 +1146,7 @@ function file_munge_filename($filename, $extensions, $alerts = TRUE) {
}
/**
- * Undo the effect of upload_munge_filename().
+ * Undo the effect of file_munge_filename().
*
* @param $filename
* String with the filename to be unmunged.
@@ -1443,7 +1451,7 @@ function file_save_upload($source, $validators = array(), $destination = FALSE,
$file = new stdClass();
$file->uid = $user->uid;
$file->status = 0;
- $file->filename = trim(basename($_FILES['files']['name'][$source]), '.');
+ $file->filename = trim(drupal_basename($_FILES['files']['name'][$source]), '.');
$file->uri = $_FILES['files']['tmp_name'][$source];
$file->filemime = file_get_mimetype($file->filename);
$file->filesize = $_FILES['files']['size'][$source];
@@ -1841,7 +1849,7 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM
$file = new stdClass();
$file->fid = NULL;
$file->uri = $uri;
- $file->filename = basename($uri);
+ $file->filename = drupal_basename($uri);
$file->filemime = file_get_mimetype($file->uri);
$file->uid = $user->uid;
$file->status = FILE_STATUS_PERMANENT;
@@ -1857,7 +1865,7 @@ function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAM
// If we are renaming around an existing file (rather than a directory),
// use its basename for the filename.
elseif ($replace == FILE_EXISTS_RENAME && is_file($destination)) {
- $file->filename = basename($destination);
+ $file->filename = drupal_basename($destination);
}
return file_save($file);
@@ -2267,6 +2275,35 @@ function drupal_dirname($uri) {
}
/**
+ * Gets the filename from a given path.
+ *
+ * PHP's basename() does not properly support streams or filenames beginning
+ * with a non-US-ASCII character.
+ *
+ * @see http://bugs.php.net/bug.php?id=37738
+ * @see basename()
+ *
+ * @ingroup php_wrappers
+ */
+function drupal_basename($uri, $suffix = NULL) {
+ $separators = '/';
+ if (DIRECTORY_SEPARATOR != '/') {
+ // For Windows OS add special separator.
+ $separators .= DIRECTORY_SEPARATOR;
+ }
+ // Remove right-most slashes when $uri points to directory.
+ $uri = rtrim($uri, $separators);
+ // Returns the trailing part of the $uri starting after one of the directory
+ // separators.
+ $filename = preg_match('@[^' . preg_quote($separators, '@') . ']+$@', $uri, $matches) ? $matches[0] : '';
+ // Cuts off a suffix from the filename.
+ if ($suffix) {
+ $filename = preg_replace('@' . preg_quote($suffix, '@') . '$@', '', $filename);
+ }
+ return $filename;
+}
+
+/**
* Creates a directory using Drupal's default mode.
*
* PHP's mkdir() does not respect Drupal's default permissions mode. If a mode
@@ -2362,7 +2399,7 @@ function drupal_tempnam($directory, $prefix) {
$wrapper = file_stream_wrapper_get_instance_by_scheme($scheme);
if ($filename = tempnam($wrapper->getDirectoryPath(), $prefix)) {
- return $scheme . '://' . basename($filename);
+ return $scheme . '://' . drupal_basename($filename);
}
else {
return FALSE;
diff --git a/includes/filetransfer/filetransfer.inc b/includes/filetransfer/filetransfer.inc
index 2083da9d3..bd2057cdd 100644
--- a/includes/filetransfer/filetransfer.inc
+++ b/includes/filetransfer/filetransfer.inc
@@ -211,7 +211,7 @@ abstract class FileTransfer {
*/
protected function copyDirectoryJailed($source, $destination) {
if ($this->isDirectory($destination)) {
- $destination = $destination . '/' . basename($source);
+ $destination = $destination . '/' . drupal_basename($source);
}
$this->createDirectory($destination);
foreach (new RecursiveIteratorIterator(new SkipDotsRecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST) as $filename => $file) {
@@ -302,9 +302,9 @@ abstract class FileTransfer {
$chroot = '';
while (count($parts)) {
$check = implode($parts, '/');
- if ($this->isFile($check . '/' . basename(__FILE__))) {
+ if ($this->isFile($check . '/' . drupal_basename(__FILE__))) {
// Remove the trailing slash.
- return substr($chroot,0,-1);
+ return substr($chroot, 0, -1);
}
$chroot .= array_shift($parts) . '/';
}
diff --git a/includes/form.inc b/includes/form.inc
index e0bc9cba0..3d5f6f22e 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -16,7 +16,7 @@
* \@see user_pass_validate().
* \@see user_pass_submit().
*
- * @} End of "defgroup forms".
+ * @}
*/
/**
@@ -156,6 +156,8 @@ function drupal_get_form($form_id) {
* automatically loaded by form_get_cache(). By default the current menu
* router item's 'file' definition is added, if any. Use
* form_load_include() to add include files from a form constructor.
+ * - base_form_id: Identification for a base form, as declared in a
+ * hook_forms() implementation.
* - rebuild_info: Internal. Similar to 'build_info', but pertaining to
* drupal_rebuild_form().
* - rebuild: Normally, after the entire form processing is completed and
@@ -3349,6 +3351,13 @@ function form_process_machine_name($element, &$form_state) {
'replace' => '_',
);
+ // By default, machine names are restricted to Latin alphanumeric characters.
+ // So, default to LTR directionality.
+ if (!isset($element['#attributes'])) {
+ $element['#attributes'] = array();
+ }
+ $element['#attributes'] += array('dir' => 'ltr');
+
// The source element defaults to array('name'), but may have been overidden.
if (empty($element['#machine_name']['source'])) {
return $element;
@@ -3786,13 +3795,27 @@ function theme_password($variables) {
* Expand weight elements into selects.
*/
function form_process_weight($element) {
- for ($n = (-1 * $element['#delta']); $n <= $element['#delta']; $n++) {
- $weights[$n] = $n;
- }
- $element['#options'] = $weights;
- $element['#type'] = 'select';
$element['#is_weight'] = TRUE;
- $element += element_info('select');
+
+ // If the number of options is small enough, use a select field.
+ $max_elements = variable_get('drupal_weight_select_max', DRUPAL_WEIGHT_SELECT_MAX);
+ if ($element['#delta'] <= $max_elements) {
+ $element['#type'] = 'select';
+ for ($n = (-1 * $element['#delta']); $n <= $element['#delta']; $n++) {
+ $weights[$n] = $n;
+ }
+ $element['#options'] = $weights;
+ $element += element_info('select');
+ }
+ // Otherwise, use a text field.
+ else {
+ $element['#type'] = 'textfield';
+ // Use a field big enough to fit most weights.
+ $element['#size'] = 10;
+ $element['#element_validate'] = array('element_validate_integer');
+ $element += element_info('textfield');
+ }
+
return $element;
}
@@ -3976,7 +3999,7 @@ function theme_form_element_label($variables) {
$t = get_t();
// If title and required marker are both empty, output no label.
- if (empty($element['#title']) && empty($element['#required'])) {
+ if ((!isset($element['#title']) || $element['#title'] === '') && empty($element['#required'])) {
return '';
}
diff --git a/includes/graph.inc b/includes/graph.inc
index 416fad6df..7fcc57a45 100644
--- a/includes/graph.inc
+++ b/includes/graph.inc
@@ -143,4 +143,3 @@ function _drupal_depth_first_search(&$graph, &$state, $start, &$component = NULL
// topological order if the graph is acyclic.
$state['last_visit_order'][] = $start;
}
-
diff --git a/includes/image.inc b/includes/image.inc
index 8dc36b995..f6ae7f19b 100644
--- a/includes/image.inc
+++ b/includes/image.inc
@@ -186,14 +186,14 @@ function image_scale_and_crop(stdClass $image, $width, $height) {
* Dimensions to be modified - an array with components width and height, in
* pixels.
* @param $width
- * The target width, in pixels. This value is omitted then the scaling will
+ * The target width, in pixels. If this value is NULL then the scaling will be
* based only on the height value.
* @param $height
- * The target height, in pixels. This value is omitted then the scaling will
- * based only on the width value.
+ * The target height, in pixels. If this value is NULL then the scaling will
+ * be based only on the width value.
* @param $upscale
- * Boolean indicating that files smaller than the dimensions will be scaled
- * up. This generally results in a low quality image.
+ * Boolean indicating that images smaller than the target dimensions will be
+ * scaled up. This generally results in a low quality image.
*
* @return
* TRUE if $dimensions was modified, FALSE otherwise.
@@ -203,31 +203,25 @@ function image_scale_and_crop(stdClass $image, $width, $height) {
function image_dimensions_scale(array &$dimensions, $width = NULL, $height = NULL, $upscale = FALSE) {
$aspect = $dimensions['height'] / $dimensions['width'];
- if ($upscale) {
- // Set width/height according to aspect ratio if either is empty.
- $width = !empty($width) ? $width : $height / $aspect;
- $height = !empty($height) ? $height : $width / $aspect;
+ // Calculate one of the dimensions from the other target dimension,
+ // ensuring the same aspect ratio as the source dimensions. If one of the
+ // target dimensions is missing, that is the one that is calculated. If both
+ // are specified then the dimension calculated is the one that would not be
+ // calculated to be bigger than its target.
+ if (($width && !$height) || ($width && $height && $aspect < $height / $width)) {
+ $height = (int) round($width * $aspect);
}
else {
- // Set impossibly large values if the width and height aren't set.
- $width = !empty($width) ? $width : 9999999;
- $height = !empty($height) ? $height : 9999999;
-
- // Don't scale up.
- if (round($width) >= $dimensions['width'] && round($height) >= $dimensions['height']) {
- return FALSE;
- }
+ $width = (int) round($height / $aspect);
}
- if ($aspect < $height / $width) {
- $dimensions['width'] = $width;
- $dimensions['height'] = (int) round($width * $aspect);
- }
- else {
- $dimensions['width'] = (int) round($height / $aspect);
- $dimensions['height'] = $height;
+ // Don't upscale if the option isn't enabled.
+ if (!$upscale && ($width >= $dimensions['width'] || $height >= $dimensions['height'])) {
+ return FALSE;
}
+ $dimensions['width'] = $width;
+ $dimensions['height'] = $height;
return TRUE;
}
diff --git a/includes/install.core.inc b/includes/install.core.inc
index a74dfdf0f..ec3a8539b 100644
--- a/includes/install.core.inc
+++ b/includes/install.core.inc
@@ -570,6 +570,12 @@ function install_tasks($install_state) {
// Now add any tasks defined by the installation profile.
if (!empty($install_state['parameters']['profile'])) {
+ // Load the profile install file, because it is not always loaded when
+ // hook_install_tasks() is invoked (e.g. batch processing).
+ $profile_install_file = DRUPAL_ROOT . '/profiles/' . $install_state['parameters']['profile'] . '/' . $install_state['parameters']['profile'] . '.install';
+ if (file_exists($profile_install_file)) {
+ include_once $profile_install_file;
+ }
$function = $install_state['parameters']['profile'] . '_install_tasks';
if (function_exists($function)) {
$result = $function($install_state);
@@ -595,7 +601,7 @@ function install_tasks($install_state) {
// Allow the installation profile to modify the full list of tasks.
if (!empty($install_state['parameters']['profile'])) {
$profile_file = DRUPAL_ROOT . '/profiles/' . $install_state['parameters']['profile'] . '/' . $install_state['parameters']['profile'] . '.profile';
- if (is_file($profile_file)) {
+ if (file_exists($profile_file)) {
include_once $profile_file;
$function = $install_state['parameters']['profile'] . '_install_tasks_alter';
if (function_exists($function)) {
@@ -710,8 +716,10 @@ function install_display_output($output, $install_state) {
*
* @return
* A themed status report, or an exception if there are requirement errors.
- * Otherwise, no output is returned, so that the next task can be run
- * in the same page request.
+ * If there are only requirement warnings, a themed status report is shown
+ * initially, but the user is allowed to bypass it by providing 'continue=1'
+ * in the URL. Otherwise, no output is returned, so that the next task can be
+ * run in the same page request.
*/
function install_verify_requirements(&$install_state) {
// Check the installation requirements for Drupal and this profile.
@@ -723,22 +731,30 @@ function install_verify_requirements(&$install_state) {
// Check the severity of the requirements reported.
$severity = drupal_requirements_severity($requirements);
- if ($severity == REQUIREMENT_ERROR) {
+ // If there are errors, always display them. If there are only warnings, skip
+ // them if the user has provided a URL parameter acknowledging the warnings
+ // and indicating a desire to continue anyway. See drupal_requirements_url().
+ if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && empty($install_state['parameters']['continue']))) {
if ($install_state['interactive']) {
drupal_set_title(st('Requirements problem'));
$status_report = theme('status_report', array('requirements' => $requirements));
- $status_report .= st('Check the error messages and <a href="!url">proceed with the installation</a>.', array('!url' => check_url(request_uri())));
+ $status_report .= st('Check the error messages and <a href="!url">proceed with the installation</a>.', array('!url' => check_url(drupal_requirements_url($severity))));
return $status_report;
}
else {
- // Throw an exception showing all unmet requirements.
+ // Throw an exception showing any unmet requirements.
$failures = array();
foreach ($requirements as $requirement) {
+ // Skip warnings altogether for non-interactive installations; these
+ // proceed in a single request so there is no good opportunity (and no
+ // good method) to warn the user anyway.
if (isset($requirement['severity']) && $requirement['severity'] == REQUIREMENT_ERROR) {
$failures[] = $requirement['title'] . ': ' . $requirement['value'] . "\n\n" . $requirement['description'];
}
}
- throw new Exception(implode("\n\n", $failures));
+ if (!empty($failures)) {
+ throw new Exception(implode("\n\n", $failures));
+ }
}
}
}
@@ -1290,7 +1306,7 @@ function install_already_done_error() {
*/
function install_load_profile(&$install_state) {
$profile_file = DRUPAL_ROOT . '/profiles/' . $install_state['parameters']['profile'] . '/' . $install_state['parameters']['profile'] . '.profile';
- if (is_file($profile_file)) {
+ if (file_exists($profile_file)) {
include_once $profile_file;
$install_state['profile_info'] = install_profile_info($install_state['parameters']['profile'], $install_state['parameters']['locale']);
}
@@ -1408,13 +1424,6 @@ function install_import_locales(&$install_state) {
* The form API definition for the site configuration form.
*/
function install_configure_form($form, &$form_state, &$install_state) {
- if (variable_get('site_name', FALSE) || variable_get('site_mail', FALSE)) {
- // Site already configured: This should never happen, means re-running the
- // installer, possibly by an attacker after the 'install_task' variable got
- // accidentally blown somewhere. Stop it now.
- throw new Exception(install_already_done_error());
- }
-
drupal_set_title(st('Configure site'));
// Warn about settings.php permissions risk
@@ -1816,7 +1825,7 @@ function install_configure_form_submit($form, &$form_state) {
// We precreated user 1 with placeholder values. Let's save the real values.
$account = user_load(1);
- $merge_data = array('init' => $form_state['values']['account']['mail'], 'roles' => !empty($account->roles) ? $account->roles : array(), 'status' => 1);
+ $merge_data = array('init' => $form_state['values']['account']['mail'], 'roles' => !empty($account->roles) ? $account->roles : array(), 'status' => 1, 'timezone' => $form_state['values']['date_default_timezone']);
user_save($account, array_merge($form_state['values']['account'], $merge_data));
// Load global $user and perform final login tasks.
$user = user_load(1);
diff --git a/includes/install.inc b/includes/install.inc
index 516e14618..6411f8f19 100644
--- a/includes/install.inc
+++ b/includes/install.inc
@@ -999,7 +999,6 @@ function drupal_install_fix_file($file, $mask, $message = TRUE) {
}
}
-
/**
* Send the user to a different installer page.
*
@@ -1017,6 +1016,68 @@ function install_goto($path) {
}
/**
+ * Returns the URL of the current script, with modified query parameters.
+ *
+ * This function can be called by low-level scripts (such as install.php and
+ * update.php) and returns the URL of the current script. Existing query
+ * parameters are preserved by default, but new ones can optionally be merged
+ * in.
+ *
+ * This function is used when the script must maintain certain query parameters
+ * over multiple page requests in order to work correctly. In such cases (for
+ * example, update.php, which requires the 'continue=1' parameter to remain in
+ * the URL throughout the update process if there are any requirement warnings
+ * that need to be bypassed), using this function to generate the URL for links
+ * to the next steps of the script ensures that the links will work correctly.
+ *
+ * @param $query
+ * (optional) An array of query parameters to merge in to the existing ones.
+ *
+ * @return
+ * The URL of the current script, with query parameters modified by the
+ * passed-in $query. The URL is not sanitized, so it still needs to be run
+ * through check_url() if it will be used as an HTML attribute value.
+ *
+ * @see drupal_requirements_url()
+ */
+function drupal_current_script_url($query = array()) {
+ $uri = $_SERVER['SCRIPT_NAME'];
+ $query = array_merge(drupal_get_query_parameters(), $query);
+ if (!empty($query)) {
+ $uri .= '?' . drupal_http_build_query($query);
+ }
+ return $uri;
+}
+
+/**
+ * Returns a URL for proceeding to the next page after a requirements problem.
+ *
+ * This function can be called by low-level scripts (such as install.php and
+ * update.php) and returns a URL that can be used to attempt to proceed to the
+ * next step of the script.
+ *
+ * @param $severity
+ * The severity of the requirements problem, as returned by
+ * drupal_requirements_severity().
+ *
+ * @return
+ * A URL for attempting to proceed to the next step of the script. The URL is
+ * not sanitized, so it still needs to be run through check_url() if it will
+ * be used as an HTML attribute value.
+ *
+ * @see drupal_current_script_url()
+ */
+function drupal_requirements_url($severity) {
+ $query = array();
+ // If there are no errors, only warnings, append 'continue=1' to the URL so
+ // the user can bypass this screen on the next page load.
+ if ($severity == REQUIREMENT_WARNING) {
+ $query['continue'] = 1;
+ }
+ return drupal_current_script_url($query);
+}
+
+/**
* Functional equivalent of t(), used when some systems are not available.
*
* Used during the install process, when database, theme, and localization
diff --git a/includes/iso.inc b/includes/iso.inc
index dabbefdd5..a88de57e9 100644
--- a/includes/iso.inc
+++ b/includes/iso.inc
@@ -74,6 +74,7 @@ function _country_get_predefined_list() {
'CO' => $t('Colombia'),
'CR' => $t('Costa Rica'),
'CU' => $t('Cuba'),
+ 'CW' => $t('Curaçao'),
'CV' => $t('Cape Verde'),
'CX' => $t('Christmas Island'),
'CY' => $t('Cyprus'),
diff --git a/includes/json-encode.inc b/includes/json-encode.inc
new file mode 100644
index 000000000..1efd6ddbe
--- /dev/null
+++ b/includes/json-encode.inc
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * @file
+ * Provides a helper to properly encode HTML-safe JSON prior to PHP 5.3.0.
+ */
+
+/**
+ * Encodes a PHP variable to HTML-safe JSON for PHP versions below 5.3.0.
+ *
+ * @see drupal_json_encode()
+ */
+function drupal_json_encode_helper($var) {
+ switch (gettype($var)) {
+ case 'boolean':
+ return $var ? 'true' : 'false'; // Lowercase necessary!
+
+ case 'integer':
+ case 'double':
+ return $var;
+
+ case 'resource':
+ case 'string':
+ // Always use Unicode escape sequences (\u0022) over JSON escape
+ // sequences (\") to prevent browsers interpreting these as
+ // special characters.
+ $replace_pairs = array(
+ // ", \ and U+0000 - U+001F must be escaped according to RFC 4627.
+ '\\' => '\u005C',
+ '"' => '\u0022',
+ "\x00" => '\u0000',
+ "\x01" => '\u0001',
+ "\x02" => '\u0002',
+ "\x03" => '\u0003',
+ "\x04" => '\u0004',
+ "\x05" => '\u0005',
+ "\x06" => '\u0006',
+ "\x07" => '\u0007',
+ "\x08" => '\u0008',
+ "\x09" => '\u0009',
+ "\x0a" => '\u000A',
+ "\x0b" => '\u000B',
+ "\x0c" => '\u000C',
+ "\x0d" => '\u000D',
+ "\x0e" => '\u000E',
+ "\x0f" => '\u000F',
+ "\x10" => '\u0010',
+ "\x11" => '\u0011',
+ "\x12" => '\u0012',
+ "\x13" => '\u0013',
+ "\x14" => '\u0014',
+ "\x15" => '\u0015',
+ "\x16" => '\u0016',
+ "\x17" => '\u0017',
+ "\x18" => '\u0018',
+ "\x19" => '\u0019',
+ "\x1a" => '\u001A',
+ "\x1b" => '\u001B',
+ "\x1c" => '\u001C',
+ "\x1d" => '\u001D',
+ "\x1e" => '\u001E',
+ "\x1f" => '\u001F',
+ // Prevent browsers from interpreting these as as special.
+ "'" => '\u0027',
+ '<' => '\u003C',
+ '>' => '\u003E',
+ '&' => '\u0026',
+ // Prevent browsers from interpreting the solidus as special and
+ // non-compliant JSON parsers from interpreting // as a comment.
+ '/' => '\u002F',
+ // While these are allowed unescaped according to ECMA-262, section
+ // 15.12.2, they cause problems in some JSON parsers.
+ "\xe2\x80\xa8" => '\u2028', // U+2028, Line Separator.
+ "\xe2\x80\xa9" => '\u2029', // U+2029, Paragraph Separator.
+ );
+
+ return '"' . strtr($var, $replace_pairs) . '"';
+
+ case 'array':
+ // Arrays in JSON can't be associative. If the array is empty or if it
+ // has sequential whole number keys starting with 0, it's not associative
+ // so we can go ahead and convert it as an array.
+ if (empty($var) || array_keys($var) === range(0, sizeof($var) - 1)) {
+ $output = array();
+ foreach ($var as $v) {
+ $output[] = drupal_json_encode_helper($v);
+ }
+ return '[ ' . implode(', ', $output) . ' ]';
+ }
+ // Otherwise, fall through to convert the array as an object.
+
+ case 'object':
+ $output = array();
+ foreach ($var as $k => $v) {
+ $output[] = drupal_json_encode_helper(strval($k)) . ':' . drupal_json_encode_helper($v);
+ }
+ return '{' . implode(', ', $output) . '}';
+
+ default:
+ return 'null';
+ }
+}
diff --git a/includes/locale.inc b/includes/locale.inc
index 6ebb8972c..0db4d4a07 100644
--- a/includes/locale.inc
+++ b/includes/locale.inc
@@ -43,6 +43,36 @@ define('LOCALE_LANGUAGE_NEGOTIATION_SESSION', 'locale-session');
define('LOCALE_JS_STRING', '(?:(?:\'(?:\\\\\'|[^\'])*\'|"(?:\\\\"|[^"])*")(?:\s*\+\s*)?)+');
/**
+ * Regular expression pattern used to match simple JS object literal.
+ *
+ * This pattern matches a basic JS object, but will fail on an object with
+ * nested objects. Used in JS file parsing for string arg processing.
+ */
+define('LOCALE_JS_OBJECT', '\{.*?\}');
+
+/**
+ * Regular expression to match an object containing a key 'context'.
+ *
+ * Pattern to match a JS object containing a 'context key' with a string value,
+ * which is captured. Will fail if there are nested objects.
+ */
+define('LOCALE_JS_OBJECT_CONTEXT', '
+ \{ # match object literal start
+ .*? # match anything, non-greedy
+ (?: # match a form of "context"
+ \'context\'
+ |
+ "context"
+ |
+ context
+ )
+ \s*:\s* # match key-value separator ":"
+ (' . LOCALE_JS_STRING . ') # match context string
+ .*? # match anything, non-greedy
+ \} # match end of object literal
+');
+
+/**
* Translation import mode overwriting all existing translations
* if new translated version available.
*/
@@ -1447,6 +1477,9 @@ function _locale_parse_js_file($filepath) {
[^\w]Drupal\s*\.\s*t\s* # match "Drupal.t" with whitespace
\(\s* # match "(" argument list start
(' . LOCALE_JS_STRING . ')\s* # capture string argument
+ (?:,\s*' . LOCALE_JS_OBJECT . '\s* # optionally capture str args
+ (?:,\s*' . LOCALE_JS_OBJECT_CONTEXT . '\s*) # optionally capture context
+ ?)? # close optional args
[,\)] # match ")" or "," to finish
~sx', $file, $t_matches);
@@ -1474,55 +1507,73 @@ function _locale_parse_js_file($filepath) {
(?:\s*\+\s*)? # match "+" with possible whitespace, for str concat
)+ # match multiple because we supports concatenating strs
)\s* # end capturing of plural string argument
+ (?:,\s*' . LOCALE_JS_OBJECT . '\s* # optionally capture string args
+ (?:,\s*' . LOCALE_JS_OBJECT_CONTEXT . '\s*)? # optionally capture context
+ )?
[,\)]
~sx', $file, $plural_matches);
+ $matches = array();
- // Loop through all matches and process them.
- $all_matches = array_merge($plural_matches[1], $t_matches[1]);
- foreach ($all_matches as $key => $string) {
- $strings = array($string);
+ // Add strings from Drupal.t().
+ foreach ($t_matches[1] as $key => $string) {
+ $matches[] = array(
+ 'string' => $string,
+ 'context' => $t_matches[2][$key],
+ );
+ }
+
+ // Add string from Drupal.formatPlural().
+ foreach ($plural_matches[1] as $key => $string) {
+ $matches[] = array(
+ 'string' => $string,
+ 'context' => $plural_matches[3][$key],
+ );
// If there is also a plural version of this string, add it to the strings array.
if (isset($plural_matches[2][$key])) {
- $strings[] = $plural_matches[2][$key];
- }
-
- foreach ($strings as $key => $string) {
- // Remove the quotes and string concatenations from the string.
- $string = implode('', preg_split('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s', substr($string, 1, -1)));
-
- $source = db_query("SELECT lid, location FROM {locales_source} WHERE source = :source AND textgroup = 'default'", array(':source' => $string))->fetchObject();
- if ($source) {
- // We already have this source string and now have to add the location
- // to the location column, if this file is not yet present in there.
- $locations = preg_split('~\s*;\s*~', $source->location);
-
- if (!in_array($filepath, $locations)) {
- $locations[] = $filepath;
- $locations = implode('; ', $locations);
-
- // Save the new locations string to the database.
- db_update('locales_source')
- ->fields(array(
- 'location' => $locations,
- ))
- ->condition('lid', $source->lid)
- ->execute();
- }
- }
- else {
- // We don't have the source string yet, thus we insert it into the database.
- db_insert('locales_source')
+ $matches[] = array(
+ 'string' => $plural_matches[2][$key],
+ 'context' => $plural_matches[3][$key],
+ );
+ }
+ }
+
+ foreach ($matches as $key => $match) {
+ // Remove the quotes and string concatenations from the string.
+ $string = implode('', preg_split('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s', substr($match['string'], 1, -1)));
+ $context = implode('', preg_split('~(?<!\\\\)[\'"]\s*\+\s*[\'"]~s', substr($match['context'], 1, -1)));
+
+ $source = db_query("SELECT lid, location FROM {locales_source} WHERE source = :source AND context = :context AND textgroup = 'default'", array(':source' => $string, ':context' => $context))->fetchObject();
+ if ($source) {
+ // We already have this source string and now have to add the location
+ // to the location column, if this file is not yet present in there.
+ $locations = preg_split('~\s*;\s*~', $source->location);
+
+ if (!in_array($filepath, $locations)) {
+ $locations[] = $filepath;
+ $locations = implode('; ', $locations);
+
+ // Save the new locations string to the database.
+ db_update('locales_source')
->fields(array(
- 'location' => $filepath,
- 'source' => $string,
- 'context' => '',
- 'textgroup' => 'default',
+ 'location' => $locations,
))
+ ->condition('lid', $source->lid)
->execute();
}
}
+ else {
+ // We don't have the source string yet, thus we insert it into the database.
+ db_insert('locales_source')
+ ->fields(array(
+ 'location' => $filepath,
+ 'source' => $string,
+ 'context' => $context,
+ 'textgroup' => 'default',
+ ))
+ ->execute();
+ }
}
}
@@ -1942,11 +1993,11 @@ function _locale_rebuild_js($langcode = NULL) {
// Construct the array for JavaScript translations.
// Only add strings with a translation to the translations array.
- $result = db_query("SELECT s.lid, s.source, t.translation FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.location LIKE '%.js%' AND s.textgroup = :textgroup", array(':language' => $language->language, ':textgroup' => 'default'));
+ $result = db_query("SELECT s.lid, s.source, s.context, t.translation FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid AND t.language = :language WHERE s.location LIKE '%.js%' AND s.textgroup = :textgroup", array(':language' => $language->language, ':textgroup' => 'default'));
$translations = array();
foreach ($result as $data) {
- $translations[$data->source] = $data->translation;
+ $translations[$data->context][$data->source] = $data->translation;
}
// Construct the JavaScript file, if there are translations.
@@ -2242,7 +2293,7 @@ function _locale_batch_import($filepath, &$context) {
// The filename is either {langcode}.po or {prefix}.{langcode}.po, so
// we can extract the language code to use for the import from the end.
if (preg_match('!(/|\.)([^\./]+)\.po$!', $filepath, $langcode)) {
- $file = (object) array('filename' => basename($filepath), 'uri' => $filepath);
+ $file = (object) array('filename' => drupal_basename($filepath), 'uri' => $filepath);
_locale_import_read_po('db-store', $file, LOCALE_IMPORT_KEEP, $langcode[2]);
$context['results'][] = $filepath;
}
diff --git a/includes/mail.inc b/includes/mail.inc
index 7272df972..13a6f4643 100644
--- a/includes/mail.inc
+++ b/includes/mail.inc
@@ -57,6 +57,12 @@ define('MAIL_LINE_ENDINGS', isset($_SERVER['WINDIR']) || strpos($_SERVER['SERVER
* user_mail_tokens($variables, $data, $options);
* switch($key) {
* case 'notice':
+ * // If the recipient can receive such notices by instant-message, do
+ * // not send by email.
+ * if (example_im_send($key, $message, $params)) {
+ * $message['send'] = FALSE;
+ * break;
+ * }
* $langcode = $message['language']->language;
* $message['subject'] = t('Notification from !site', $variables, array('langcode' => $langcode));
* $message['body'][] = t("Dear !username\n\nThere is new content available on the site.", $variables, array('langcode' => $langcode));
@@ -65,6 +71,19 @@ define('MAIL_LINE_ENDINGS', isset($_SERVER['WINDIR']) || strpos($_SERVER['SERVER
* }
* @endcode
*
+ * Another example, which uses drupal_mail() to format a message for sending
+ * later:
+ *
+ * @code
+ * $params = array('current_conditions' => $data);
+ * $to = 'user@example.com';
+ * $message = drupal_mail('example', 'notice', $to, $language, $params, FALSE);
+ * // Only add to the spool if sending was not canceled.
+ * if ($message['send']) {
+ * example_spool_message($message);
+ * }
+ * @endcode
+ *
* @param $module
* A module name to invoke hook_mail() on. The {$module}_mail() hook will be
* called to complete the $message structure which will already contain common
@@ -86,8 +105,10 @@ define('MAIL_LINE_ENDINGS', isset($_SERVER['WINDIR']) || strpos($_SERVER['SERVER
* @param $from
* Sets From to this value, if given.
* @param $send
- * Send the message directly, without calling drupal_mail_system()->mail()
- * manually.
+ * If TRUE, drupal_mail() will call drupal_mail_system()->mail() to deliver
+ * the message, and store the result in $message['result']. Modules
+ * implementing hook_mail_alter() may cancel sending by setting
+ * $message['send'] to FALSE.
*
* @return
* The $message array structure containing all details of the
@@ -108,6 +129,7 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N
'from' => isset($from) ? $from : $default_from,
'language' => $language,
'params' => $params,
+ 'send' => TRUE,
'subject' => '',
'body' => array()
);
@@ -148,12 +170,20 @@ function drupal_mail($module, $key, $to, $language, $params = array(), $from = N
// Optionally send e-mail.
if ($send) {
- $message['result'] = $system->mail($message);
-
- // Log errors
- if (!$message['result']) {
- watchdog('mail', 'Error sending e-mail (from %from to %to).', array('%from' => $message['from'], '%to' => $message['to']), WATCHDOG_ERROR);
- drupal_set_message(t('Unable to send e-mail. Contact the site administrator if the problem persists.'), 'error');
+ // The original caller requested sending. Sending was canceled by one or
+ // more hook_mail_alter() implementations. We set 'result' to NULL, because
+ // FALSE indicates an error in sending.
+ if (empty($message['send'])) {
+ $message['result'] = NULL;
+ }
+ // Sending was originally requested and was not canceled.
+ else {
+ $message['result'] = $system->mail($message);
+ // Log errors.
+ if (!$message['result']) {
+ watchdog('mail', 'Error sending e-mail (from %from to %to).', array('%from' => $message['from'], '%to' => $message['to']), WATCHDOG_ERROR);
+ drupal_set_message(t('Unable to send e-mail. Contact the site administrator if the problem persists.'), 'error');
+ }
}
}
diff --git a/includes/menu.inc b/includes/menu.inc
index dad65d727..25a87af12 100644
--- a/includes/menu.inc
+++ b/includes/menu.inc
@@ -275,6 +275,20 @@ define('MENU_MAX_DEPTH', 9);
*/
/**
+ * Reserved key to identify the most specific menu link for a given path.
+ *
+ * The value of this constant is a hash of the constant name. We use the hash
+ * so that the reserved key is over 32 characters in length and will not
+ * collide with allowed menu names:
+ * @code
+ * sha1('MENU_PREFERRED_LINK') = 1cf698d64d1aa4b83907cf6ed55db3a7f8e92c91
+ * @endcode
+ *
+ * @see menu_link_get_preferred()
+ */
+define('MENU_PREFERRED_LINK', '1cf698d64d1aa4b83907cf6ed55db3a7f8e92c91');
+
+/**
* Returns the ancestors (and relevant placeholders) for any given path.
*
* For example, the ancestors of node/12345/edit are:
@@ -1241,7 +1255,7 @@ function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail =
if ($item['access']) {
// Find a menu link corresponding to the current path. If $active_path
// is NULL, let menu_link_get_preferred() determine the path.
- if ($active_link = menu_link_get_preferred($active_path)) {
+ if ($active_link = menu_link_get_preferred($active_path, $menu_name)) {
// The active link may only be taken into account to build the
// active trail, if it resides in the requested menu. Otherwise,
// we'd needlessly re-run _menu_build_tree() queries for every menu
@@ -1325,6 +1339,8 @@ function menu_tree_page_data($menu_name, $max_depth = NULL, $only_active_trail =
* Defaults to 1, which is the default to build a whole tree for a menu, i.e.
* excluding menu container itself.
* - max_depth: The maximum depth of menu links in the resulting tree.
+ * - conditions: An associative array of custom database select query
+ * condition key/value pairs; see _menu_build_tree() for the actual query.
*
* @return
* A fully built menu tree.
@@ -1408,6 +1424,12 @@ function _menu_build_tree($menu_name, array $parameters = array()) {
if (isset($parameters['max_depth'])) {
$query->condition('ml.depth', $parameters['max_depth'], '<=');
}
+ // Add custom query conditions, if any were passed.
+ if (isset($parameters['conditions'])) {
+ foreach ($parameters['conditions'] as $column => $value) {
+ $query->condition($column, $value);
+ }
+ }
// Build an ordered array of links using the query result object.
$links = array();
@@ -2260,6 +2282,13 @@ function theme_menu_local_tasks(&$variables) {
/**
* Set (or get) the active menu for the current page - determines the active trail.
+ *
+ * @return
+ * An array of menu machine names, in order of preference. The
+ * 'menu_default_active_menus' variable may be used to assert a menu order
+ * different from the order of creation, or to prevent a particular menu from
+ * being used at all in the active trail.
+ * E.g., $conf['menu_default_active_menus'] = array('navigation', 'main-menu')
*/
function menu_set_active_menu_names($menu_names = NULL) {
$active = &drupal_static(__FUNCTION__);
@@ -2390,23 +2419,30 @@ function menu_set_active_trail($new_trail = NULL) {
* @param $path
* The path, for example 'node/5'. The function will find the corresponding
* menu link ('node/5' if it exists, or fallback to 'node/%').
+ * @param $selected_menu
+ * The name of a menu used to restrict the search for a preferred menu link.
+ * If not specified, all the menus returned by menu_get_active_menu_names()
+ * will be used.
*
* @return
- * A fully translated menu link, or NULL if no matching menu link was
+ * A fully translated menu link, or FALSE if no matching menu link was
* found. The most specific menu link ('node/5' preferred over 'node/%') in
* the most preferred menu (as defined by menu_get_active_menu_names()) is
* returned.
*/
-function menu_link_get_preferred($path = NULL) {
+function menu_link_get_preferred($path = NULL, $selected_menu = NULL) {
$preferred_links = &drupal_static(__FUNCTION__);
if (!isset($path)) {
$path = $_GET['q'];
}
- if (!isset($preferred_links[$path])) {
- $preferred_links[$path] = FALSE;
+ if (empty($selected_menu)) {
+ // Use an illegal menu name as the key for the preferred menu link.
+ $selected_menu = MENU_PREFERRED_LINK;
+ }
+ if (!isset($preferred_links[$path])) {
// Look for the correct menu link by building a list of candidate paths,
// which are ordered by priority (translated hrefs are preferred over
// untranslated paths). Afterwards, the most relevant path is picked from
@@ -2428,6 +2464,8 @@ function menu_link_get_preferred($path = NULL) {
// Retrieve a list of menu names, ordered by preference.
$menu_names = menu_get_active_menu_names();
+ // Put the selected menu at the front of the list.
+ array_unshift($menu_names, $selected_menu);
$query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
$query->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
@@ -2435,7 +2473,6 @@ function menu_link_get_preferred($path = NULL) {
// Weight must be taken from {menu_links}, not {menu_router}.
$query->addField('ml', 'weight', 'link_weight');
$query->fields('m');
- $query->condition('ml.menu_name', $menu_names, 'IN');
$query->condition('ml.link_path', $path_candidates, 'IN');
// Sort candidates by link path and menu name.
@@ -2443,29 +2480,35 @@ function menu_link_get_preferred($path = NULL) {
foreach ($query->execute() as $candidate) {
$candidate['weight'] = $candidate['link_weight'];
$candidates[$candidate['link_path']][$candidate['menu_name']] = $candidate;
+ // Add any menus not already in the menu name search list.
+ if (!in_array($candidate['menu_name'], $menu_names)) {
+ $menu_names[] = $candidate['menu_name'];
+ }
}
- // Pick the most specific link, in the most preferred menu.
+ // Store the most specific link for each menu. Also save the most specific
+ // link of the most preferred menu in $preferred_link.
foreach ($path_candidates as $link_path) {
- if (!isset($candidates[$link_path])) {
- continue;
- }
- foreach ($menu_names as $menu_name) {
- if (!isset($candidates[$link_path][$menu_name])) {
- continue;
- }
- $candidate_item = $candidates[$link_path][$menu_name];
- $map = explode('/', $path);
- _menu_translate($candidate_item, $map);
- if ($candidate_item['access']) {
- $preferred_links[$path] = $candidate_item;
+ if (isset($candidates[$link_path])) {
+ foreach ($menu_names as $menu_name) {
+ if (empty($preferred_links[$path][$menu_name]) && isset($candidates[$link_path][$menu_name])) {
+ $candidate_item = $candidates[$link_path][$menu_name];
+ $map = explode('/', $path);
+ _menu_translate($candidate_item, $map);
+ if ($candidate_item['access']) {
+ $preferred_links[$path][$menu_name] = $candidate_item;
+ if (empty($preferred_links[$path][MENU_PREFERRED_LINK])) {
+ // Store the most specific link.
+ $preferred_links[$path][MENU_PREFERRED_LINK] = $candidate_item;
+ }
+ }
+ }
}
- break 2;
}
}
}
- return $preferred_links[$path];
+ return isset($preferred_links[$path][$selected_menu]) ? $preferred_links[$path][$selected_menu] : FALSE;
}
/**
diff --git a/includes/module.inc b/includes/module.inc
index 66c77f577..77e35b7b0 100644
--- a/includes/module.inc
+++ b/includes/module.inc
@@ -539,6 +539,7 @@ function module_disable($module_list, $disable_dependents = TRUE) {
system_list_reset();
module_list(TRUE);
module_implements('', FALSE, TRUE);
+ entity_info_cache_clear();
// Invoke hook_modules_disabled before disabling modules,
// so we can still call module hooks to get information.
module_invoke_all('modules_disabled', $invoke_modules);
@@ -952,10 +953,24 @@ function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
}
// If any modules implement one of the extra hooks that do not implement
// the primary hook, we need to add them to the $modules array in their
- // appropriate order.
+ // appropriate order. module_implements() can only return ordered
+ // implementations of a single hook. To get the ordered implementations
+ // of multiple hooks, we mimic the module_implements() logic of first
+ // ordering by module_list(), and then calling
+ // drupal_alter('module_implements').
if (array_diff($extra_modules, $modules)) {
- // Order the modules by the order returned by module_list().
+ // Merge the arrays and order by module_list().
$modules = array_intersect(module_list(), array_merge($modules, $extra_modules));
+ // Since module_implements() already took care of loading the necessary
+ // include files, we can safely pass FALSE for the array values.
+ $implementations = array_fill_keys($modules, FALSE);
+ // Let modules adjust the order solely based on the primary hook. This
+ // ensures the same module order regardless of whether this if block
+ // runs. Calling drupal_alter() recursively in this way does not result
+ // in an infinite loop, because this call is for a single $type, so we
+ // won't end up in this code block again.
+ drupal_alter('module_implements', $implementations, $hook);
+ $modules = array_keys($implementations);
}
foreach ($modules as $module) {
// Since $modules is a merged array, for any given module, we do not
@@ -1003,4 +1018,3 @@ function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) {
$function($data, $context1, $context2);
}
}
-
diff --git a/includes/password.inc b/includes/password.inc
index c0761da69..d4f5f738a 100644
--- a/includes/password.inc
+++ b/includes/password.inc
@@ -285,4 +285,3 @@ function user_needs_new_hash($account) {
// Check whether the iteration count used differs from the standard number.
return (_password_get_count_log2($account->pass) !== $count_log2);
}
-
diff --git a/includes/path.inc b/includes/path.inc
index db605370c..ed5b639fb 100644
--- a/includes/path.inc
+++ b/includes/path.inc
@@ -13,12 +13,12 @@
* Initialize the $_GET['q'] variable to the proper normal path.
*/
function drupal_path_initialize() {
- if (!empty($_GET['q'])) {
- $_GET['q'] = drupal_get_normal_path($_GET['q']);
- }
- else {
- $_GET['q'] = drupal_get_normal_path(variable_get('site_frontpage', 'node'));
+ // Ensure $_GET['q'] is set before calling drupal_normal_path(), to support
+ // path caching with hook_url_inbound_alter().
+ if (empty($_GET['q'])) {
+ $_GET['q'] = variable_get('site_frontpage', 'node');
}
+ $_GET['q'] = drupal_get_normal_path($_GET['q']);
}
/**
diff --git a/includes/registry.inc b/includes/registry.inc
index 3fb14fb31..c7b8702c4 100644
--- a/includes/registry.inc
+++ b/includes/registry.inc
@@ -183,4 +183,3 @@ function _registry_parse_file($filename, $contents, $module = '', $weight = 0) {
/**
* @} End of "defgroup registry".
*/
-
diff --git a/includes/stream_wrappers.inc b/includes/stream_wrappers.inc
index e47668e3a..2af8c9e91 100644
--- a/includes/stream_wrappers.inc
+++ b/includes/stream_wrappers.inc
@@ -317,7 +317,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
}
$extension = '';
- $file_parts = explode('.', basename($uri));
+ $file_parts = explode('.', drupal_basename($uri));
// Remove the first part: a full filename should not match an extension.
array_shift($file_parts);
@@ -377,7 +377,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
$realpath = realpath($path);
if (!$realpath) {
// This file does not yet exist.
- $realpath = realpath(dirname($path)) . '/' . basename($path);
+ $realpath = realpath(dirname($path)) . '/' . drupal_basename($path);
}
$directory = realpath($this->getDirectoryPath());
if (!$realpath || !$directory || strpos($realpath, $directory) !== 0) {
diff --git a/includes/theme.inc b/includes/theme.inc
index 252eec081..da4200e56 100644
--- a/includes/theme.inc
+++ b/includes/theme.inc
@@ -237,18 +237,33 @@ function _drupal_theme_initialize($theme, $base_theme = array(), $registry_callb
/**
* Get the theme registry.
*
+ * @param $complete
+ * Optional boolean to indicate whether to return the complete theme registry
+ * array or an instance of the ThemeRegistry class. If TRUE, the complete
+ * theme registry array will be returned. This is useful if you want to
+ * foreach over the whole registry, use array_* functions or inspect it in a
+ * debugger. If FALSE, an instance of the ThemeRegistry class will be
+ * returned, this provides an ArrayObject which allows it to be accessed
+ * with array syntax and isset(), and should be more lightweight
+ * than the full registry. Defaults to TRUE.
+ *
* @return
- * The theme registry array if it has been stored in memory, NULL otherwise.
+ * The complete theme registry array, or an instance of the ThemeRegistry
+ * class.
*/
-function theme_get_registry() {
- static $theme_registry = NULL;
+function theme_get_registry($complete = TRUE) {
+ static $theme_registry = array();
+ $key = (int) $complete;
- if (!isset($theme_registry)) {
+ if (!isset($theme_registry[$key])) {
list($callback, $arguments) = _theme_registry_callback();
- $theme_registry = call_user_func_array($callback, $arguments);
+ if (!$complete) {
+ $arguments[] = FALSE;
+ }
+ $theme_registry[$key] = call_user_func_array($callback, $arguments);
}
- return $theme_registry;
+ return $theme_registry[$key];
}
/**
@@ -268,7 +283,7 @@ function _theme_registry_callback($callback = NULL, array $arguments = array())
}
/**
- * Get the theme_registry cache from the database; if it doesn't exist, build it.
+ * Get the theme_registry cache; if it doesn't exist, build it.
*
* @param $theme
* The loaded $theme object as returned by list_themes().
@@ -277,23 +292,34 @@ function _theme_registry_callback($callback = NULL, array $arguments = array())
* oldest first order.
* @param $theme_engine
* The name of the theme engine.
+ * @param $complete
+ * Whether to load the complete theme registry or an instance of the
+ * ThemeRegistry class.
+ *
+ * @return
+ * The theme registry array, or an instance of the ThemeRegistry class.
*/
-function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL) {
- // Check the theme registry cache; if it exists, use it.
- $cache = cache_get("theme_registry:$theme->name", 'cache');
- if (isset($cache->data)) {
- $registry = $cache->data;
+function _theme_load_registry($theme, $base_theme = NULL, $theme_engine = NULL, $complete = TRUE) {
+ if ($complete) {
+ // Check the theme registry cache; if it exists, use it.
+ $cached = cache_get("theme_registry:$theme->name");
+ if (isset($cached->data)) {
+ $registry = $cached->data;
+ }
+ else {
+ // If not, build one and cache it.
+ $registry = _theme_build_registry($theme, $base_theme, $theme_engine);
+ // Only persist this registry if all modules are loaded. This assures a
+ // complete set of theme hooks.
+ if (module_load_all(NULL)) {
+ _theme_save_registry($theme, $registry);
+ }
+ }
+ return $registry;
}
else {
- // If not, build one and cache it.
- $registry = _theme_build_registry($theme, $base_theme, $theme_engine);
- // Only persist this registry if all modules are loaded. This assures a
- // complete set of theme hooks.
- if (module_load_all(NULL)) {
- _theme_save_registry($theme, $registry);
- }
+ return new ThemeRegistry('theme_registry:runtime:' . $theme->name, 'cache');
}
- return $registry;
}
/**
@@ -313,6 +339,116 @@ function drupal_theme_rebuild() {
}
/**
+ * Builds the run-time theme registry.
+ *
+ * Extends DrupalCacheArray to allow the theme registry to be accessed as a
+ * complete registry, while internally caching only the parts of the registry
+ * that are actually in use on the site. On cache misses the complete
+ * theme registry is loaded and used to update the run-time cache.
+ */
+class ThemeRegistry Extends DrupalCacheArray {
+
+ /**
+ * Whether the partial registry can be persisted to the cache.
+ *
+ * This is only allowed if all modules and the request method is GET. theme()
+ * should be very rarely called on POST requests and this avoids polluting
+ * the runtime cache.
+ */
+ protected $persistable;
+
+ /**
+ * The complete theme registry array.
+ */
+ protected $completeRegistry;
+
+ function __construct($cid, $bin) {
+ $this->cid = $cid;
+ $this->bin = $bin;
+ $this->persistable = module_load_all(NULL) && $_SERVER['REQUEST_METHOD'] == 'GET';
+
+ $data = array();
+ if ($this->persistable && $cached = cache_get($this->cid, $this->bin)) {
+ $data = $cached->data;
+ }
+ else {
+ // If there is no runtime cache stored, fetch the full theme registry,
+ // but then initialize each value to NULL. This allows offsetExists()
+ // to function correctly on non-registered theme hooks without triggering
+ // a call to resolveCacheMiss().
+ $data = $this->initializeRegistry();
+ if ($this->persistable) {
+ $this->set($data);
+ }
+ }
+ $this->storage = $data;
+ }
+
+ /**
+ * Initializes the full theme registry.
+ *
+ * @return
+ * An array with the keys of the full theme registry, but the values
+ * initialized to NULL.
+ */
+ function initializeRegistry() {
+ $this->completeRegistry = theme_get_registry();
+
+ return array_fill_keys(array_keys($this->completeRegistry), NULL);
+ }
+
+ public function offsetExists($offset) {
+ // Since the theme registry allows for theme hooks to be requested that
+ // are not registered, just check the existence of the key in the registry.
+ // Use array_key_exists() here since a NULL value indicates that the theme
+ // hook exists but has not yet been requested.
+ return array_key_exists($offset, $this->storage);
+ }
+
+ public function offsetGet($offset) {
+ // If the offset is set but empty, it is a registered theme hook that has
+ // not yet been requested. Offsets that do not exist at all were not
+ // registered in hook_theme().
+ if (isset($this->storage[$offset])) {
+ return $this->storage[$offset];
+ }
+ elseif (array_key_exists($offset, $this->storage)) {
+ return $this->resolveCacheMiss($offset);
+ }
+ }
+
+ public function resolveCacheMiss($offset) {
+ if (!isset($this->completeRegistry)) {
+ $this->completeRegistry = theme_get_registry();
+ }
+ $this->storage[$offset] = $this->completeRegistry[$offset];
+ if ($this->persistable) {
+ $this->persist($offset);
+ }
+ return $this->storage[$offset];
+ }
+
+ public function set($data, $lock = TRUE) {
+ $lock_name = $this->cid . ':' . $this->bin;
+ if (!$lock || lock_acquire($lock_name)) {
+ if ($cached = cache_get($this->cid, $this->bin)) {
+ // Use array merge instead of union so that filled in values in $data
+ // overwrite empty values in the current cache.
+ $data = array_merge($cached->data, $data);
+ }
+ else {
+ $registry = $this->initializeRegistry();
+ $data = array_merge($registry, $data);
+ }
+ cache_set($this->cid, $data, $this->bin);
+ if ($lock) {
+ lock_release($lock_name);
+ }
+ }
+ }
+}
+
+/**
* Process a single implementation of hook_theme().
*
* @param $cache
@@ -657,8 +793,9 @@ function list_themes($refresh = FALSE) {
* Generates themed output.
*
* All requests for themed output must go through this function. It examines
- * the request and routes it to the appropriate theme function or template, by
- * checking the theme registry.
+ * the request and routes it to the appropriate
+ * @link themeable theme function or template @endlink, by checking the theme
+ * registry.
*
* The first argument to this function is the name of the theme hook. For
* instance, to theme a table, the theme hook name is 'table'. By default, this
@@ -758,6 +895,8 @@ function list_themes($refresh = FALSE) {
*
* @return
* An HTML string representing the themed output.
+ *
+ * @see themeable
*/
function theme($hook, $variables = array()) {
static $hooks = NULL;
@@ -771,7 +910,7 @@ function theme($hook, $variables = array()) {
if (!isset($hooks)) {
drupal_theme_initialize();
- $hooks = theme_get_registry();
+ $hooks = theme_get_registry(FALSE);
}
// If an array of hook candidates were passed, use the first one that has an
@@ -1391,7 +1530,7 @@ function theme_link($variables) {
* @param $variables
* An associative array containing:
* - links: An associative array of links to be themed. The key for each link
- * is used as its css class. Each link should be itself an array, with the
+ * is used as its CSS class. Each link should be itself an array, with the
* following elements:
* - title: The link text.
* - href: The link URL. If omitted, the 'title' is shown as a plain text
@@ -1981,6 +2120,8 @@ function theme_username($variables) {
/**
* Returns HTML for a progress bar.
*
+ * Note that the core Batch API uses this only for non-JavaScript batch jobs.
+ *
* @param $variables
* An associative array containing:
* - percent: The percentage of the progress.
diff --git a/includes/token.inc b/includes/token.inc
index edc8a962f..0b05c68f4 100644
--- a/includes/token.inc
+++ b/includes/token.inc
@@ -77,8 +77,13 @@
* Text with tokens replaced.
*/
function token_replace($text, array $data = array(), array $options = array()) {
+ $text_tokens = token_scan($text);
+ if (empty($text_tokens)) {
+ return $text;
+ }
+
$replacements = array();
- foreach (token_scan($text) as $type => $tokens) {
+ foreach ($text_tokens as $type => $tokens) {
$replacements += token_generate($type, $tokens, $data, $options);
if (!empty($options['clear'])) {
$replacements += array_fill_keys($tokens, '');
diff --git a/includes/update.inc b/includes/update.inc
index 3a131083d..41f33f402 100644
--- a/includes/update.inc
+++ b/includes/update.inc
@@ -746,7 +746,7 @@ function update_fix_d7_requirements() {
// Rename 'site_offline_message' variable to 'maintenance_mode_message'
// and 'site_offline' variable to 'maintenance_mode'.
// Old variable is removed in update for system.module, see
- // system_update_7036().
+ // system_update_7072().
if ($message = variable_get('site_offline_message', NULL)) {
variable_set('maintenance_mode_message', $message);
}
diff --git a/includes/updater.inc b/includes/updater.inc
index 363c6ebff..9801629b1 100644
--- a/includes/updater.inc
+++ b/includes/updater.inc
@@ -141,7 +141,7 @@ class Updater {
return FALSE;
}
foreach ($info_files as $info_file) {
- if (drupal_substr($info_file->filename, 0, -5) == basename($directory)) {
+ if (drupal_substr($info_file->filename, 0, -5) == drupal_basename($directory)) {
// Info file Has the same name as the directory, return it.
return $info_file->uri;
}
@@ -163,7 +163,7 @@ class Updater {
* The name of the project.
*/
public static function getProjectName($directory) {
- return basename($directory);
+ return drupal_basename($directory);
}
/**
diff --git a/includes/utility.inc b/includes/utility.inc
index df6c48fb9..d195bff74 100644
--- a/includes/utility.inc
+++ b/includes/utility.inc
@@ -46,6 +46,13 @@ function drupal_var_export($var, $prefix = '') {
$output = "'" . $var . "'";
}
}
+ elseif (is_object($var) && get_class($var) === 'stdClass') {
+ // var_export() will export stdClass objects using an undefined
+ // magic method __set_state() leaving the export broken. This
+ // workaround avoids this by casting the object as an array for
+ // export and casting it back to an object when evaluated.
+ $output .= '(object) ' . drupal_var_export((array) $var, $prefix);
+ }
else {
$output = var_export($var, TRUE);
}
diff --git a/includes/xmlrpc.inc b/includes/xmlrpc.inc
index 92e5d14f0..b1c6f39c6 100644
--- a/includes/xmlrpc.inc
+++ b/includes/xmlrpc.inc
@@ -622,4 +622,3 @@ function xmlrpc_error_msg() {
function xmlrpc_clear_error() {
xmlrpc_error(NULL, NULL, TRUE);
}
-
diff --git a/includes/xmlrpcs.inc b/includes/xmlrpcs.inc
index 70c7cdac3..118f652d2 100644
--- a/includes/xmlrpcs.inc
+++ b/includes/xmlrpcs.inc
@@ -382,4 +382,3 @@ function xmlrpc_server_method_help($method) {
$xmlrpc_server = xmlrpc_server_get();
return $xmlrpc_server->help[$method];
}
-