diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index 847bfc3..c5ad9d2 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -873,6 +873,9 @@ function drupal_get_filename($type, $name, $filename = NULL) { // Fallback to searching the filesystem if the database could not find the // file or the file returned by the database is not found. if (!isset($files[$type][$name])) { + // We use drupal_static() for the missing records so we can test it. + $missing = &drupal_static('drupal_get_filename:missing'); + // We have a consistent directory naming: modules, themes... $dir = $type . 's'; if ($type == 'theme_engine') { @@ -886,7 +889,23 @@ function drupal_get_filename($type, $name, $filename = NULL) { $extension = $type; } - if (!isset($dirs[$dir][$extension])) { + // See if we have a cached list of files missing from the file system. + if (!isset($missing)) { + $missing = array(); + try { + if (function_exists('cache_get')) { + $cache = cache_get('drupal_get_filename:missing'); + if (!empty($cache->data)) { + $missing = $cache->data; + } + } + } + catch (Exception $e) { + // Hide the error. + } + } + + if (!isset($dirs[$dir][$extension]) && !isset($missing[$type][$name])) { $dirs[$dir][$extension] = TRUE; if (!function_exists('drupal_system_listing')) { require_once DRUPAL_ROOT . '/includes/common.inc'; @@ -906,6 +925,23 @@ function drupal_get_filename($type, $name, $filename = NULL) { if (isset($files[$type][$name])) { return $files[$type][$name]; } + else { + if (!isset($missing[$type][$name])) { + // Add the missing file to a temporary cache and throw an alert. + // This cache will be cleared on cron runs as well as when visiting the + // module and theme list pages. + $missing[$type][$name] = TRUE; + try { + if (function_exists('cache_set')) { + cache_set('drupal_get_filename:missing', $missing, 'cache'); + } + } + catch (Exception $e) { + // Hide the error. + } + } + trigger_error(format_string('The following @type is missing from the file system: @name. For more information, see the documentation page.', array('@type' => $type, '@name' => $name, '@documentation' => 'https://www.drupal.org/node/2487215')), E_USER_WARNING); + } } /** diff --git a/includes/update.inc b/includes/update.inc index a17161c..c23519d 100644 --- a/includes/update.inc +++ b/includes/update.inc @@ -795,6 +795,14 @@ function update_fix_d7_requirements() { function update_fix_d7_install_profile() { $profile = drupal_get_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() + if ($profile == 'default') { + $profile = 'standard'; + variable_set('install_profile', $profile); + } + $results = db_select('system', 's') ->fields('s', array('name', 'schema_version')) ->condition('name', $profile) diff --git a/modules/simpletest/simpletest.module b/modules/simpletest/simpletest.module index 91f0f90..4241e2e 100644 --- a/modules/simpletest/simpletest.module +++ b/modules/simpletest/simpletest.module @@ -371,7 +371,24 @@ function simpletest_test_get_all() { // If this test class requires a non-existing module, skip it. if (!empty($info['dependencies'])) { foreach ($info['dependencies'] as $module) { - if (!drupal_get_filename('module', $module)) { + + // Searching for an item that does not exist triggers an PHP + // error. Set a custom error handler so we can ignore the file + // not found error. + set_error_handler(function($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:')) { + return; + } + _drupal_error_handler($error_level, $message, $filename, $line, $context); + }); + + $found_module = drupal_get_filename('module', $module); + + // Restore the original error handler. + restore_error_handler(); + + if (!$found_module) { continue 2; } } diff --git a/modules/simpletest/tests/bootstrap.test b/modules/simpletest/tests/bootstrap.test index ece1cd9..3f8fc4b 100644 --- a/modules/simpletest/tests/bootstrap.test +++ b/modules/simpletest/tests/bootstrap.test @@ -382,6 +382,11 @@ class BootstrapGetFilenameTestCase extends DrupalUnitTestCase { } /** + * @var bool + */ + protected $getFilenameTestTriggeredError = FALSE; + + /** * Test that drupal_get_filename() works correctly when the file is not found in the database. */ function testDrupalGetFilename() { @@ -410,6 +415,37 @@ class BootstrapGetFilenameTestCase extends DrupalUnitTestCase { // automatically check there for 'script' files, just as it does for (e.g.) // '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. + $non_existing_module = $this->randomName(); + + // We save that the error handler is triggered by drupal_get_filename(). + // This variable is inherited in the error handler with 'use'. + $get_filename_test_triggered_error = FALSE; + + // Searching for an item that does not exist returns NULL. + // Set a custom error handler so we can ignore the file not found error. + 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. + restore_error_handler(); + + // Get the missing records static from drupal_get_filename(). + $missing = &drupal_static('drupal_get_filename:missing'); + + // Searching for an item that does not exist creates a static record in + // drupal_get_filename(). + $this->assertTrue($missing['module'][$non_existing_module], 'Searching for an item that does not exist creates a static record in drupal_get_filename().'); + } + + 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; + return; + } + _drupal_error_handler($error_level, $message, $filename, $line, $context); } } diff --git a/modules/system/system.module b/modules/system/system.module index 8fc517f..eb454b7 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -2365,6 +2365,10 @@ function system_get_info($type, $name = NULL) { * An associative array of module information. */ function _system_rebuild_module_data() { + // Clean up the bootstrap "missing files" cache when rebuilding theme data. + drupal_static_reset('drupal_get_filename:missing'); + cache_clear_all('drupal_get_filename:missing', 'cache'); + // Find modules $modules = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0); @@ -2501,6 +2505,10 @@ function _system_update_bootstrap_status() { * An associative array of themes information. */ function _system_rebuild_theme_data() { + // Clean up the bootstrap "missing files" cache when rebuilding theme data. + drupal_static_reset('drupal_get_filename:missing'); + cache_clear_all('drupal_get_filename:missing', 'cache'); + // Find themes $themes = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info$/', 'themes'); // Allow modules to add further themes.