diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index bf59d6b..1d590c2 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -829,25 +829,21 @@ function drupal_settings_initialize() {
* The filename of the item if it is to be set explicitly rather
* than by consulting the database.
* @param bool $trigger_error
- * Whether to trigger an error when a file is missing / moved. Defaults to
- * TRUE, but we may want to set this to FALSE if we merely want to check
- * whether an item is installed.
+ * Whether to trigger an error when a file is missing or has unexpectedly
+ * moved. This defaults to TRUE, but can be set to FALSE by calling code that
+ * merely wants to check whether an item exists in the filesystem.
*
* @return
* The filename of the requested item or NULL if the item is not found.
*/
function drupal_get_filename($type, $name, $filename = NULL, $trigger_error = TRUE) {
- // The location of files will not change during the request, so do not use
- // drupal_static().
// The $files static variable will hold the locations of all requested files.
// We can be sure that any file listed in this static variable actually
// exists as all additions have gone through a file_exists() check.
+ // The location of files will not change during the request, so do not use
+ // drupal_static().
static $files = array();
- // This flag may hold a string that indicates where the filename was retrieved
- // from.
- $filename_retrieved_from = '';
-
// Profiles are a special case: they have a fixed location and naming.
if ($type == 'profile') {
$profile_filename = "profiles/$name/$name.profile";
@@ -860,34 +856,26 @@ function drupal_get_filename($type, $name, $filename = NULL, $trigger_error = TR
if (!empty($filename) && file_exists($filename)) {
// Prime the static cache with the provided filename.
$files[$type][$name] = $filename;
- $filename_retrieved_from = 'priming';
}
elseif (isset($files[$type][$name])) {
// This item had already been found earlier in the request, either through
- // priming of the static cache (i.e. in system_list()), through a lookup
- // in the {system} table, or through a file scan (cached or not).
- // Do nothing.
+ // priming of the static cache (for example, in system_list()), through a
+ // lookup in the {system} table, or through a file scan (cached or not). Do
+ // nothing.
}
else {
- // Look for the filename listed in the {system} table.
- // Verify that we have an active database connection, before querying
- // the database. This is required because this function is called both
- // before we have a database connection (i.e. during installation) and
- // when a database connection fails.
+ // Look for the filename listed in the {system} table. Verify that we have
+ // an active database connection before doing so, since this function is
+ // called both before we have a database connection (i.e. during
+ // installation) and when a database connection fails.
+ $database_unavailable = TRUE;
try {
- $system_query_success = FALSE;
if (function_exists('db_query')) {
- $file = db_query("SELECT filename FROM {system} WHERE name = :name AND type = :type", array(
- ':name' => $name,
- ':type' => $type
- ))->fetchField();
- if ($file !== FALSE) {
- if (file_exists(DRUPAL_ROOT . '/' . $file)) {
- $files[$type][$name] = $file;
- $filename_retrieved_from = 'system_table';
- }
+ $file = db_query("SELECT filename FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField();
+ if ($file !== FALSE && file_exists(DRUPAL_ROOT . '/' . $file)) {
+ $files[$type][$name] = $file;
}
- $system_query_success = TRUE;
+ $database_unavailable = FALSE;
}
}
catch (Exception $e) {
@@ -896,171 +884,125 @@ function drupal_get_filename($type, $name, $filename = NULL, $trigger_error = TR
// flush while $conf['page_cache_without_database'] = TRUE and
// $conf['page_cache_invoke_hooks'] = TRUE. We have a fallback for these
// cases so we hide the error completely.
- // We also hide the trigger_error() "moved or newly introduced" warning
- // in this case as we can't be sure if it's correct.
- }
- if (!$system_query_success) {
- // We can't tell if the error message will be valid if the query to the
- // {system} table failed, so skip triggering of errors.
- $trigger_error = FALSE;
}
// Fall back to searching the filesystem if the database could not find the
// file or the file does not exist at the path returned by the database.
if (!isset($files[$type][$name])) {
- $files[$type][$name] = _drupal_get_filename_fallback($type, $name, $trigger_error);
+ $files[$type][$name] = _drupal_get_filename_fallback($type, $name, $trigger_error, $database_unavailable);
}
}
if (isset($files[$type][$name])) {
- // We found a file.
- // Get the static file scan list. This list may be incomplete as there
- // may be further files listed in persistent cache, but we skip loading that
- // as a performance optimization, as the persistent cache record will
- // be cleared on every system_rebuild_module_data() anyway.
- $file_scans = &drupal_static('_drupal_get_filename_fallback');
- // If this file had previously been marked as missing, clean up the entry
- // from the file scan cache.
- $missing_reappeared = isset($file_scans[$type][$name]) && $file_scans[$type][$name] === FALSE;
- // If this file had previously been marked as moved, and reappeared
- // at the location listed in the system table or in the primed static,
- // clean up the entry from the file scan cache.
- $moved_reappeared = isset($file_scans[$type][$name]) && is_string($file_scans[$type][$name]) && ($filename_retrieved_from == 'system_table' || $filename_retrieved_from == 'priming');
- if ($missing_reappeared || $moved_reappeared) {
- $file_scans[$type][$name] = NULL;
- $file_scans['#write_cache'] = TRUE;
- }
return $files[$type][$name];
}
}
/**
- * Helper function for drupal_get_filename().
+ * Performs a cached file system scan as a fallback when searching for a file.
*
* This function looks for the requested file by triggering a file scan,
* caching the new location if the file has moved and caching the miss
- * if the file is missing. If a file had been marked as missing or moved
- * in a previous file scan, no new file scan will be performed.
+ * if the file is missing. If a file had been marked as missing in a previous
+ * file scan, or if it has been marked as moved and is still in the last known
+ * location, no new file scan will be performed.
*
* @param string $type
* The type of the item (theme, theme_engine, module, profile).
* @param string $name
* The name of the item for which the filename is requested.
* @param bool $trigger_error
- * Whether to create an error when a file is missing or moved.
+ * Whether to trigger an error when a file is missing or has unexpectedly
+ * moved.
+ * @param bool $database_unavailable
+ * Whether this function is being called because the Drupal database could
+ * not be queried for the file's location.
*
- * @return string
+ * @return
* The filename of the requested item or NULL if the item is not found.
+ *
+ * @see drupal_get_filename()
*/
-function _drupal_get_filename_fallback($type, $name, $trigger_error) {
- // This static variable will hold all missing and moved files, in order
- // to prevent unnecessary file scans. It is an associative array with as
- // keys the type and name of the item, and as value:
- // - the boolean FALSE if the item is missing
- // - a string with location found in a file scan if the module/theme has moved
- // from the location listed in the {system} table.
- $file_scans = &drupal_static('_drupal_get_filename_fallback');
- if (!isset($file_scans)) {
- $file_scans = array();
- }
- // Get the list of files marked as missing and moved during earlier file scans
- // from the persistent cache, if available.
- if (!isset($file_scans['#cache_merge_done'])) {
- try {
- if (function_exists('cache_get')) {
- $cache = cache_get('_drupal_get_filename_fallback', 'cache_bootstrap');
- if (!empty($cache->data)) {
- // Merge the changes to the file scan cache done in the current
- // request (including the setting of previously set records to NULL)
- // with the values that had been stored in the persistent cache.
- $file_scans = drupal_array_merge_deep($cache->data, $file_scans);
- // Set a flag so we remember that we've done a merge with the values
- // stored in the persistent cache.
- }
- $file_scans['#cache_merge_done'] = TRUE;
- }
- }
- catch (Exception $e) {
- // Hide the error.
- }
- }
+function _drupal_get_filename_fallback($type, $name, $trigger_error, $database_unavailable) {
+ $file_scans = &_drupal_file_scan_cache();
+ $filename = NULL;
- // Check whether this file had moved from the path listed in the {system}
- // table during a previous file scan.
- if (isset($file_scans[$type][$name]) && is_string($file_scans[$type][$name]) && file_exists($file_scans[$type][$name])) {
- // The file exists at the location cached in a previous file scan.
- $filename_from_file_scan = $file_scans[$type][$name];
+ // If the cache indicates that the item is missing, or we can verify that the
+ // item exists in the location the cache says it exists in, use that.
+ if (isset($file_scans[$type][$name]) && ($file_scans[$type][$name] === FALSE || file_exists($file_scans[$type][$name]))) {
+ $filename = $file_scans[$type][$name];
}
- // Unless the module had been marked as missing, perform a file scan.
- elseif (!(isset($file_scans[$type][$name]) && $file_scans[$type][$name] === FALSE)) {
- $filename_from_file_scan = _drupal_get_filename_perform_file_scan($type, $name);
+ // Otherwise, perform a new file scan to find the item.
+ else {
+ $filename = _drupal_get_filename_perform_file_scan($type, $name);
+ // Update the static cache, and mark the persistent cache for updating at
+ // the end of the page request. See drupal_file_scan_write_cache().
+ $file_scans[$type][$name] = $filename;
+ $file_scans['#write_cache'] = TRUE;
}
- if (isset($filename_from_file_scan)) {
- // The file has either moved from the location in the {system} table
- // or is not yet listed in the {system} table.
- // Make sure our change to the file scan cache will be written to the
- // persistent cache.
- if (!isset($file_scans[$type][$name]) || isset($file_scans[$type][$name]) && $file_scans[$type][$name] != $filename_from_file_scan) {
- $file_scans[$type][$name] = $filename_from_file_scan;
- if (!$trigger_error) {
- // If we skip error triggering, do not write the found file location to
- // persistent cache.
- $file_scans['#write_cache'] = TRUE;
- }
+ // If requested, trigger a user-level warning about the missing or
+ // unexpectedly moved file. If the database was unavailable, do not trigger a
+ // warning in the latter case, though, since if the {system} table could not
+ // be queried there is no way to know if the location found here was
+ // "unexpected" or not.
+ if ($trigger_error) {
+ $error_type = $filename === FALSE ? 'missing' : 'moved';
+ if ($error_type == 'missing' || !$database_unavailable) {
+ _drupal_get_filename_fallback_trigger_error($type, $name, $error_type);
}
- // Create a user-level warning about the moved / recently introduced file.
- if ($trigger_error) {
- _drupal_get_filename_fallback_trigger_error($type, $name, 'moved');
- }
- return $filename_from_file_scan;
}
- else {
- // Make sure our change to the file scan cache will be written to
- // the persistent cache.
- if (!(isset($file_scans[$type][$name]) && $file_scans[$type][$name] === FALSE)) {
- $file_scans['#write_cache'] = TRUE;
- }
- // Mark the file as missing.
- $file_scans[$type][$name] = FALSE;
- // Create a user-level warning about the missing file.
- if ($trigger_error) {
- _drupal_get_filename_fallback_trigger_error($type, $name, 'missing');
- }
+
+ // The cache stores FALSE for files that aren't found (to be able to
+ // distinguish them from files that have not yet been searched for), but
+ // drupal_get_filename() expects NULL for these instead, so convert to NULL
+ // before returning.
+ if ($filename === FALSE) {
+ $filename = NULL;
}
+ return $filename;
}
/**
- * Helper function for _drupal_get_filename_fallback().
- *
- * Creates a user-level warning when a missing or moved file is detected in
- * _drupal_get_filename_fallback().
+ * Returns the current list of cached file system scan results.
*
- * @param $type
- * The type of the item (theme, theme_engine, module, profile).
- * @param $name
- * The name of the item for which the filename is requested.
- * @param $error_type
- * The type of the error ('missing' or 'moved').
- */
-function _drupal_get_filename_fallback_trigger_error($type, $name, $error_type) {
- // Make sure we only show any missing / moved file error only once per
- // request.
- static $errors_triggered = array();
- if (empty($errors_triggered[$type][$name][$error_type])) {
- if ($error_type == 'missing') {
- trigger_error(format_string('The following @type is missing from the file system: %name. In order to fix this, put the file back in its original location or uninstall the module. For more information, see the documentation page.', array('@type' => $type, '%name' => $name, '@documentation' => 'https://www.drupal.org/node/2487215')), E_USER_WARNING);
+ * @return
+ * An associative array tracking the most recent file scan results for all
+ * files that have had scans performed. The keys are the type and name of the
+ * item that was searched for, and the values can be either:
+ * - Boolean FALSE if the item was not found in the file system.
+ * - A string pointing to the location where the item was found.
+ */
+function &_drupal_file_scan_cache() {
+ $file_scans = &drupal_static(__FUNCTION__, array());
+
+ // The file scan results are stored in a persistent cache (in addition to the
+ // static cache) but because this function can be called before the
+ // persistent cache is available, we must merge any items that were found
+ // earlier in the page request into the results from the persistent cache.
+ if (!isset($file_scans['#cache_merge_done'])) {
+ try {
+ if (function_exists('cache_get')) {
+ $cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
+ if (!empty($cache->data)) {
+ // File scan results from the current request should take precedence
+ // over the results from the persistent cache, since they are newer.
+ $file_scans = drupal_array_merge_deep($cache->data, $file_scans);
+ }
+ // Set a flag to indicate that the persistent cache does not need to be
+ // merged again.
+ $file_scans['#cache_merge_done'] = TRUE;
+ }
}
- elseif ($error_type == 'moved') {
- trigger_error(format_string('The following @type has moved or has recently appeared within the file system: %name. In order to fix this, clear caches or put the file back in its original location. For more information, see the documentation page.', array('@type' => $type, '%name' => $name, '@documentation' => 'https://www.drupal.org/node/2487215')), E_USER_WARNING);
+ catch (Exception $e) {
+ // Hide the error.
}
- $errors_triggered[$type][$name][$error_type] = TRUE;
}
+
+ return $file_scans;
}
/**
- * Helper function for drupal_get_filename().
- *
- * Performs a file scan.
+ * Performs a file system scan to search for a system resource.
*
* @param $type
* The type of the item (theme, theme_engine, module, profile).
@@ -1068,9 +1010,14 @@ function _drupal_get_filename_fallback_trigger_error($type, $name, $error_type)
* The name of the item for which the filename is requested.
*
* @return
- * The filename of the requested item or NULL if the item is not found.
+ * The filename of the requested item or FALSE if the item is not found.
+ *
+ * @see drupal_get_filename()
+ * @see _drupal_get_filename_fallback()
*/
function _drupal_get_filename_perform_file_scan($type, $name) {
+ // The location of files will not change during the request, so do not use
+ // drupal_static().
static $dirs = array(), $files = array();
// We have a consistent directory naming: modules, themes...
@@ -1105,10 +1052,36 @@ function _drupal_get_filename_perform_file_scan($type, $name) {
}
}
- // If current file had been found in a file scan earlier on in this
- // request, use the new location that had been found in the file scan.
- if (isset($files[$type][$name])) {
- return $files[$type][$name];
+ // Return the results of the file system scan, or FALSE to indicate the file
+ // was not found.
+ return isset($files[$type][$name]) ? $files[$type][$name] : FALSE;
+}
+
+/**
+ * Triggers a user-level warning for missing or unexpectedly moved files.
+ *
+ * @param $type
+ * The type of the item (theme, theme_engine, module, profile).
+ * @param $name
+ * The name of the item for which the filename is requested.
+ * @param $error_type
+ * The type of the error ('missing' or 'moved').
+ *
+ * @see drupal_get_filename()
+ * @see _drupal_get_filename_fallback()
+ */
+function _drupal_get_filename_fallback_trigger_error($type, $name, $error_type) {
+ // Make sure we only show any missing or moved file errors only once per
+ // request.
+ static $errors_triggered = array();
+ if (empty($errors_triggered[$type][$name][$error_type])) {
+ if ($error_type == 'missing') {
+ trigger_error(format_string('The following @type is missing from the file system: %name. In order to fix this, put the @type back in its original location. For more information, see the documentation page.', array('@type' => $type, '%name' => $name, '@documentation' => 'https://www.drupal.org/node/2487215')), E_USER_WARNING);
+ }
+ elseif ($error_type == 'moved') {
+ trigger_error(format_string('The following @type has moved within the file system: %name. In order to fix this, clear caches or put the @type back in its original location. For more information, see the documentation page.', array('@type' => $type, '%name' => $name, '@documentation' => 'https://www.drupal.org/node/2487215')), E_USER_WARNING);
+ }
+ $errors_triggered[$type][$name][$error_type] = TRUE;
}
}
@@ -1124,18 +1097,11 @@ function drupal_file_scan_write_cache() {
if (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL) {
return;
}
- $file_scans = &drupal_static('_drupal_get_filename_fallback');
+ $file_scans = &_drupal_file_scan_cache();
if (isset($file_scans['#write_cache'])) {
- // Merge the newly found out missing and moved file data with the previously
- // existing data, if we had not done so yet.
- if (!isset($file_scans['#cache_merge_done'])) {
- $cache = cache_get('_drupal_get_filename_fallback', 'cache_bootstrap');
- if (isset($cache->data)) {
- $file_scans = drupal_array_merge_deep($cache->data, $file_scans);
- }
- }
- $file_scans['#write_cache'] = NULL;
- cache_set('_drupal_get_filename_fallback', $file_scans, 'cache_bootstrap');
+ unset($file_scans['#write_cache']);
+ unset($file_scans['#cache_merge_done']);
+ cache_set('_drupal_file_scan_cache', $file_scans, 'cache_bootstrap');
}
}
diff --git a/includes/module.inc b/includes/module.inc
index 101f5ca..48ea322 100644
--- a/includes/module.inc
+++ b/includes/module.inc
@@ -208,10 +208,9 @@ function system_list($type) {
}
cache_set('system_list', $lists, 'cache_bootstrap');
}
+ // To avoid a separate database lookup for the filepath, prime the
+ // drupal_get_filename() static cache with all enabled modules and themes.
foreach ($lists['filepaths'] as $item) {
- // To avoid a separate database lookup for the file path, prime the
- // drupal_get_filename() static cache with all enabled modules and all
- // themes.
drupal_get_filename($item['type'], $item['name'], $item['filepath']);
}
}
@@ -230,9 +229,8 @@ function system_list_reset() {
cache_clear_all('system_list', 'cache_bootstrap');
// Clean up the bootstrap file scan cache.
- drupal_static_reset('_drupal_get_filename_fallback');
- drupal_static_reset('system_filepaths');
- cache_clear_all('_drupal_get_filename_fallback', 'cache_bootstrap');
+ drupal_static_reset('_drupal_file_scan_cache');
+ cache_clear_all('_drupal_file_scan_cache', 'cache_bootstrap');
}
/**
diff --git a/includes/update.inc b/includes/update.inc
index 4f12db8..ce9319f 100644
--- a/includes/update.inc
+++ b/includes/update.inc
@@ -797,7 +797,7 @@ function update_fix_d7_install_profile() {
// 'Default' profile has been renamed to 'Standard' in D7.
// We change the profile here to prevent a broken record in the system table.
- // See system_update_7049()
+ // See system_update_7049().
if ($profile == 'default') {
$profile = 'standard';
variable_set('install_profile', $profile);
diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module
index a2e6c6e..7c28fba 100644
--- a/modules/simpletest/simpletest.module
+++ b/modules/simpletest/simpletest.module
@@ -374,7 +374,6 @@ function simpletest_test_get_all() {
// Pass FALSE as fourth argument so no error gets created for
// the missing file.
$found_module = drupal_get_filename('module', $module, NULL, FALSE);
-
if (!$found_module) {
continue 2;
}
diff --git a/modules/simpletest/tests/bootstrap.test b/modules/simpletest/tests/bootstrap.test
index 9088023..bdd228c 100644
--- a/modules/simpletest/tests/bootstrap.test
+++ b/modules/simpletest/tests/bootstrap.test
@@ -371,24 +371,22 @@ class HookBootExitTestCase extends DrupalWebTestCase {
/**
* Test drupal_get_filename()'s availability.
*/
-class BootstrapGetFilenameTestCase extends DrupalWebTestCase {
+class BootstrapGetFilenameTestCase extends DrupalUnitTestCase {
public static function getInfo() {
return array(
- 'name' => 'Get filename test',
- 'description' => 'Test that drupal_get_filename() works correctly when the file is not found in the database.',
+ 'name' => 'Get filename test (without the system table)',
+ 'description' => 'Test that drupal_get_filename() works correctly when the database is not available.',
'group' => 'Bootstrap',
);
}
/**
- * Whether the filename test triggered the right error.
+ * The last file-related error message triggered by the filename test.
*
* Used by BootstrapGetFilenameTestCase::testDrupalGetFilename().
- *
- * @var boolean
*/
- protected $getFilenameTestTriggeredError = FALSE;
+ protected $getFilenameTestTriggeredError;
/**
* Test that drupal_get_filename() works correctly when the file is not found in the database.
@@ -420,27 +418,135 @@ class BootstrapGetFilenameTestCase extends DrupalWebTestCase {
// 'module' files in modules.
$this->assertIdentical(drupal_get_filename('script', 'test'), 'scripts/test.script', t('Retrieve test script location.'));
- // Generate a non-existing module name.
+ // When searching for a module that does not exist, drupal_get_filename()
+ // should return NULL and trigger an appropriate error message.
+ $this->getFilenameTestTriggeredError = NULL;
+ set_error_handler(array($this, 'fileNotFoundErrorHandler'));
$non_existing_module = $this->randomName();
+ $this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that does not exist returns NULL.');
+ $this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module is missing from the file system: %name', array('%name' => $non_existing_module))) === 0, 'Searching for an item that does not exist triggers the correct error.');
+ restore_error_handler();
+
+ // Check that the result is stored in the file system scan cache.
+ $file_scans = _drupal_file_scan_cache();
+ $this->assertIdentical($file_scans['module'][$non_existing_module], FALSE, 'Searching for a module that does not exist creates a record in the missing and moved files static variable.');
+
+ // Performing the search again in the same request still should not find
+ // the file, but the error message should not be repeated (therefore we do
+ // not override the error handler here).
+ $this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that does not exist returns NULL during the second search.');
+ }
- // Searching for an item that does not exist returns NULL.
- // Set a custom error handler so we can ignore the file not found error.
+ /**
+ * Skips handling of "file not found" errors.
+ */
+ public function fileNotFoundErrorHandler($error_level, $message, $filename, $line, $context) {
+ // Skip error handling if this is a "file not found" error.
+ if (strpos($message, 'is missing from the file system:') !== FALSE || strpos($message, 'has moved within the file system:') !== FALSE) {
+ $this->getFilenameTestTriggeredError = $message;
+ return;
+ }
+ _drupal_error_handler($error_level, $message, $filename, $line, $context);
+ }
+}
+
+/**
+ * Test drupal_get_filename() in the context of a full Drupal installation.
+ */
+class BootstrapGetFilenameWebTestCase extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Get filename test (full installation)',
+ 'description' => 'Test that drupal_get_filename() works correctly in the context of a full Drupal installation.',
+ 'group' => 'Bootstrap',
+ );
+ }
+
+ /**
+ * The last file-related error message triggered by the filename test.
+ *
+ * Used by BootstrapGetFilenameWebTestCase::testDrupalGetFilename().
+ */
+ protected $getFilenameTestTriggeredError;
+
+ /**
+ * Test that drupal_get_filename() works correctly with a full Drupal site.
+ */
+ function testDrupalGetFilename() {
+ // Search for a module that exists in the file system and the {system}
+ // table and make sure that it is found.
+ $this->assertIdentical(drupal_get_filename('module', 'node'), 'modules/node/node.module', 'Module found at expected location.');
+
+ // Search for a module that does not exist in either the file system or the
+ // {system} table. Make sure that an appropriate error is triggered and
+ // that the module winds up in the static and persistent cache.
+ $this->getFilenameTestTriggeredError = NULL;
+ set_error_handler(array($this, 'fileNotFoundErrorHandler'));
+ $non_existing_module = $this->randomName();
+ $this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that does not exist returns NULL.');
+ $this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module is missing from the file system: %name', array('%name' => $non_existing_module))) === 0, 'Searching for a module that does not exist triggers the correct error.');
+ restore_error_handler();
+ $file_scans = _drupal_file_scan_cache();
+ $this->assertIdentical($file_scans['module'][$non_existing_module], FALSE, 'Searching for a module that does not exist creates a record in the missing and moved files static variable.');
+ drupal_file_scan_write_cache();
+ $cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
+ $this->assertIdentical($cache->data['module'][$non_existing_module], FALSE, 'Searching for a module that does not exist creates a record in the missing and moved files persistent cache.');
+
+ // Simulate moving a module to a location that does not match the location
+ // in the {system} table and perform similar tests as above.
+ db_update('system')
+ ->fields(array('filename' => 'modules/simpletest/tests/fake_location/module_test.module'))
+ ->condition('name', 'module_test')
+ ->condition('type', 'module')
+ ->execute();
+ $this->getFilenameTestTriggeredError = NULL;
set_error_handler(array($this, 'fileNotFoundErrorHandler'));
- $this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for an item that does not exist returns NULL.');
- $this->assertTrue($this->getFilenameTestTriggeredError, 'Searching for an item that does not exist triggers an error.');
- // Restore the original error handler.
+ $this->assertIdentical(drupal_get_filename('module', 'module_test'), 'modules/simpletest/tests/module_test.module', 'Searching for a module that has moved finds the module at its new location.');
+ $this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module has moved within the file system: %name', array('%name' => 'module_test'))) === 0, 'Searching for a module that has moved triggers the correct error.');
restore_error_handler();
+ $file_scans = _drupal_file_scan_cache();
+ $this->assertIdentical($file_scans['module']['module_test'], 'modules/simpletest/tests/module_test.module', 'Searching for a module that has moved creates a record in the missing and moved files static variable.');
+ drupal_file_scan_write_cache();
+ $cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
+ $this->assertIdentical($cache->data['module']['module_test'], 'modules/simpletest/tests/module_test.module', 'Searching for a module that has moved creates a record in the missing and moved files persistent cache.');
- // Get the missing and moved files static variable.
- $file_scans = &drupal_static('_drupal_get_filename_fallback');
-
- // Searching for an item that does not exist creates a record in the static
- // variable.
- $this->assertTrue($file_scans['module'][$non_existing_module] === FALSE, 'Searching for an item that does not exist creates a record in the missing and moved files static variable.');
- drupal_install_schema('system');
+ // Simulate a module that exists in the {system} table but does not exist
+ // in the file system and perform similar tests as above.
+ $non_existing_module = $this->randomName();
+ db_update('system')
+ ->fields(array('name' => $non_existing_module))
+ ->condition('name', 'module_test')
+ ->condition('type', 'module')
+ ->execute();
+ $this->getFilenameTestTriggeredError = NULL;
+ set_error_handler(array($this, 'fileNotFoundErrorHandler'));
+ $this->assertNull(drupal_get_filename('module', $non_existing_module), 'Searching for a module that exists in the system table but not in the file system returns NULL.');
+ $this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module is missing from the file system: %name', array('%name' => $non_existing_module))) === 0, 'Searching for a module that exists in the system table but not in the file system triggers the correct error.');
+ restore_error_handler();
+ $file_scans = _drupal_file_scan_cache();
+ $this->assertIdentical($file_scans['module'][$non_existing_module], FALSE, 'Searching for a module that exists in the system table but not in the file system creates a record in the missing and moved files static variable.');
+ drupal_file_scan_write_cache();
+ $cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
+ $this->assertIdentical($cache->data['module'][$non_existing_module], FALSE, 'Searching for a module that exists in the system table but not in the file system creates a record in the missing and moved files persistent cache.');
+
+ // Simulate a module that exists in the file system but not in the {system}
+ // table and perform similar tests as above.
+ db_delete('system')
+ ->condition('name', 'common_test')
+ ->condition('type', 'module')
+ ->execute();
+ system_list_reset();
+ $this->getFilenameTestTriggeredError = NULL;
+ set_error_handler(array($this, 'fileNotFoundErrorHandler'));
+ $this->assertIdentical(drupal_get_filename('module', 'common_test'), 'modules/simpletest/tests/common_test.module', 'Searching for a module that does not exist in the system table finds the module at its actual location.');
+ $this->assertTrue(strpos($this->getFilenameTestTriggeredError, format_string('The following module has moved within the file system: %name', array('%name' => 'common_test'))) === 0, 'Searching for a module that does not exist in the system table triggers the correct error.');
+ restore_error_handler();
+ $file_scans = _drupal_file_scan_cache();
+ $this->assertIdentical($file_scans['module']['common_test'], 'modules/simpletest/tests/common_test.module', 'Searching for a module that does not exist in the system table creates a record in the missing and moved files static variable.');
drupal_file_scan_write_cache();
- $cache = cache_get('_drupal_get_filename_fallback', 'cache_bootstrap');
- $this->assertTrue($cache->data['module'][$non_existing_module] === FALSE, 'File scan results are correctly saved in persistent cache.');
+ $cache = cache_get('_drupal_file_scan_cache', 'cache_bootstrap');
+ $this->assertIdentical($cache->data['module']['common_test'], 'modules/simpletest/tests/common_test.module', 'Searching for a module that does not exist in the system table creates a record in the missing and moved files persistent cache.');
}
/**
@@ -448,8 +554,8 @@ class BootstrapGetFilenameTestCase extends DrupalWebTestCase {
*/
public function fileNotFoundErrorHandler($error_level, $message, $filename, $line, $context) {
// Skip error handling if this is a "file not found" error.
- if (strstr($message, 'is missing from the file system:')) {
- $this->getFilenameTestTriggeredError = TRUE;
+ if (strpos($message, 'is missing from the file system:') !== FALSE || strpos($message, 'has moved within the file system:') !== FALSE) {
+ $this->getFilenameTestTriggeredError = $message;
return;
}
_drupal_error_handler($error_level, $message, $filename, $line, $context);