diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index eac2f97cb1b7..5577be475741 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -938,7 +938,7 @@ function drupal_get_filename($type, $name, $filename = NULL) { // extension, not just the file we are currently looking for. This // prevents unnecessary scans from being repeated when this function is // called more than once in the same page request. - $matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0); + $matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir); foreach ($matches as $matched_name => $file) { $files[$type][$matched_name] = $file->uri; } diff --git a/core/includes/common.inc b/core/includes/common.inc index 540f42bcb940..fdfa9e241fcb 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -5119,17 +5119,17 @@ function drupal_cron_cleanup() { * depending on what type of object you are looking for. For instance, if you * are looking for modules and call: * @code - * drupal_system_listing("/\.module$/", "modules", 'name', 0); + * drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules'); * @endcode - * the function will search the following directories in this order: - * - /core/modules. - * - /core/profiles/testing/modules (only if a test is being run). - * - /core/profiles/active_profile/modules or /profiles/active_profile/modules. - * - /modules (standard directory for your contrib/custom modules). - * - /sites/all/modules (legacy contrib/custom modules directory, see above). - * - /sites/your_site_dir/modules (for multisite installations). - * and return information about all of the files ending in .module in those - * directories. + * this function will search: + * - the core modules directory; i.e., /core/modules + * - the testing profile directory; e.g., /profiles/testing/modules + * - your installation profile's directory; e.g., /profiles/standard/modules + * - the site-wide modules directory; i.e., /modules + * - the all-sites directory; i.e., /sites/all/modules + * - the site-specific directory; i.e., /sites/example.com/modules + * in that order, and return information about all of the files ending in + * .module in those directories. * * The information is returned in an associative array, which can be keyed on * the file name ($key = 'filename'), the file name without the extension ($key @@ -5140,21 +5140,28 @@ function drupal_cron_cleanup() { * files found. * * @param string $mask - * The preg_match() regular expression for the files to find. + * The preg_match() regular expression for the files to find. The expression + * must be anchored and use DRUPAL_PHP_FUNCTION_PATTERN for the file name part + * before the extension, since the results could contain matches that do not + * present valid Drupal extensions otherwise. * @param string $directory * The subdirectory name in which the files are found. For example, - * 'core/modules' will search in sub-directories of the /core/modules - * directory, sub-directories of /modules/, etc. + * 'modules' will search all 'modules' directories and their sub-directories + * as explained above. * @param string $key - * The key to be used for the associative array returned. Possible values are - * 'uri', for the file's URI; 'filename', for the basename of the file; and - * 'name' for the name of the file without the extension. If you choose 'name' - * or 'filename', only the highest-precedence file will be returned. + * (optional) The key to be used for the associative array returned. Possible + * values are: + * - 'uri' for the file's URI. + * - 'filename' for the basename of the file. + * - 'name' for the name of the file without the extension. + * For 'name' and 'filename' only the highest-precedence file is returned. + * Defaults to 'name'. * @param int $min_depth - * Minimum depth of directories to return files from, relative to each - * directory searched. For instance, a minimum depth of 2 would find modules - * inside /core/modules/node/tests, but not modules directly in - * /core/modules/node. + * (optional) Minimum depth of directories to return files from, relative to + * each directory searched. For instance, a directory of 'modules' and a + * minimum depth of 2 would find modules inside /modules/node/tests, but not + * modules directly in /modules/node. Defaults to 1. The default is sufficient + * and suitable for all extension types known by Drupal core. * * @return array * An associative array of file objects, keyed on the chosen key. Each element @@ -5204,7 +5211,14 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1) require_once DRUPAL_ROOT . '/core/includes/file.inc'; } foreach ($searchdir as $dir) { - $files_to_add = file_scan_directory($dir, $mask, array('key' => $key, 'min_depth' => $min_depth)); + $files_to_add = file_scan_directory($dir, $mask, array( + 'key' => $key, + 'min_depth' => $min_depth, + // Do not recurse into ./lib directories; they cannot contain extensions. + // We also skip templates, css, and js directories. + // @todo Find a way to skip ./config directories (but not modules/config). + 'nomask' => '/^(CVS|lib|templates|css|js)$/', + )); // Duplicate files found in later search directories take precedence over // earlier ones, so we want them to overwrite keys in our resulting @@ -5216,7 +5230,7 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1) foreach (array_intersect_key($files_to_add, $files) as $file_key => $file) { // If it has no info file, then we just behave liberally and accept the // new resource on the list for merging. - if (file_exists($info_file = dirname($file->uri) . '/' . $file->name . '.info')) { + if (file_exists($info_file = DRUPAL_ROOT . '/' . dirname($file->uri) . '/' . $file->name . '.info')) { // Get the .info file for the module or theme this file belongs to. $info = drupal_parse_info_file($info_file); diff --git a/core/includes/file.inc b/core/includes/file.inc index 16ed6192f711..5d4a2bc25308 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -1432,12 +1432,17 @@ function file_download() { function file_scan_directory($dir, $mask, $options = array(), $depth = 0) { // Merge in defaults. $options += array( - 'nomask' => '/(\.\.?|CVS)$/', + 'nomask' => '/^CVS$/', 'callback' => 0, 'recurse' => TRUE, 'key' => 'uri', 'min_depth' => 0, ); + // Normalize $dir only once. + if ($depth == 0) { + $dir = file_stream_wrapper_uri_normalize($dir); + $dir_has_slash = (substr($dir, -1) === '/'); + } $options['key'] = in_array($options['key'], array('uri', 'filename', 'name')) ? $options['key'] : 'uri'; $files = array(); @@ -1446,10 +1451,15 @@ function file_scan_directory($dir, $mask, $options = array(), $depth = 0) { if (is_dir($dir)) { if($handle = @opendir($dir)) { while (FALSE !== ($filename = readdir($handle))) { - if (!preg_match($options['nomask'], $filename) && $filename[0] != '.') { - $uri = "$dir/$filename"; - $uri = file_stream_wrapper_uri_normalize($uri); - if (is_dir($uri) && $options['recurse']) { + // Skip this file if it matches the nomask or starts with a dot. + if ($filename[0] != '.' && !preg_match($options['nomask'], $filename)) { + if ($depth == 0 && $dir_has_slash) { + $uri = "$dir$filename"; + } + else { + $uri = "$dir/$filename"; + } + if ($options['recurse'] && is_dir($uri)) { // Give priority to files in this folder by merging them in after // any subdirectory files. $files = array_merge(file_scan_directory($uri, $mask, $options, $depth + 1), $files); diff --git a/core/includes/install.inc b/core/includes/install.inc index ee501ba3a2ce..a2666aec7307 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -384,7 +384,7 @@ function drupal_verify_profile($install_state) { // Get a list of modules that exist in Drupal's assorted subdirectories. $present_modules = array(); - foreach (drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0) as $present_module) { + foreach (drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules') as $present_module) { $present_modules[] = $present_module->name; } diff --git a/core/includes/module.inc b/core/includes/module.inc index 2db6e9990433..0c1a30b4890c 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -1006,7 +1006,7 @@ function module_invoke_all($hook) { * Returns an array of modules required by core. */ function drupal_required_modules() { - $files = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info$/', 'modules', 'name', 0); + $files = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info$/', 'modules'); $required = array(); // An installation profile is required and one must always be loaded. diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index 2388601ad395..0acc8b3537c3 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -385,10 +385,20 @@ function simpletest_test_get_all() { * Registers namespaces for disabled modules. */ function simpletest_classloader_register() { - $all_data = system_rebuild_module_data(); - $all_data += system_rebuild_theme_data(); - foreach ($all_data as $name => $data) { - drupal_classloader_register($name, dirname($data->filename)); + // @see drupal_get_filename() + $types = array( + 'theme_engine' => array('dir' => 'themes/engines', 'extension' => 'engine'), + 'module' => array('dir' => 'modules', 'extension' => 'module'), + 'theme' => array('dir' => 'themes', 'extension' => 'info'), + 'profile' => array('dir' => 'profiles', 'extension' => 'profile'), + ); + foreach ($types as $type => $info) { + $matches = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.' . $info['extension'] . '$/', $info['dir']); + foreach ($matches as $name => $file) { + drupal_classloader_register($name, dirname($file->uri)); + // While being there, prime drupal_get_filename(). + drupal_get_filename($type, $name, $file->uri); + } } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Bootstrap/GetFilenameUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Bootstrap/GetFilenameUnitTest.php index ae9a1628626f..32705bf374ec 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Bootstrap/GetFilenameUnitTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Bootstrap/GetFilenameUnitTest.php @@ -22,14 +22,24 @@ class GetFilenameUnitTest extends UnitTestBase { ); } + function setUp() { + parent::setUp(); + + // Remove the keyvalue service definition, since this test wants to verify + // the filesystem scan fallback of drupal_get_filename(). + $this->keyvalue_definition = $this->container->getDefinition('keyvalue'); + $this->container->removeDefinition('keyvalue'); + } + + function tearDown() { + $this->container->setDefinition('keyvalue', $this->keyvalue_definition); + parent::tearDown(); + } + /** * Tests that drupal_get_filename() works when the file is not in database. */ function testDrupalGetFilename() { - // Reset the static cache so we can test the "db is not active" code of - // drupal_get_filename(). - drupal_static_reset('drupal_get_filename'); - // Retrieving the location of a module. $this->assertIdentical(drupal_get_filename('module', 'php'), 'core/modules/php/php.module', 'Retrieve module location.'); @@ -50,6 +60,6 @@ class GetFilenameUnitTest extends UnitTestBase { // Since there is already a core/scripts directory, drupal_get_filename() // will automatically check there for 'script' files, just as it does // for (e.g.) 'module' files in core/modules. - $this->assertIdentical(drupal_get_filename('script', 'test'), 'core/scripts/test.script', 'Retrieve test script location.'); + $this->assertIdentical(drupal_get_filename('script', 'test'), 'core/scripts/test/test.script'); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/SystemListingTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/SystemListingTest.php index 523a20bee691..ee1f12b70b06 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/SystemListingTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/SystemListingTest.php @@ -55,7 +55,7 @@ class SystemListingTest extends WebTestBase { // Now scan the directories and check that the files take precedence as // expected. - $files = drupal_system_listing('/\.module$/', 'modules', 'name', 1); + $files = drupal_system_listing('/\.module$/', 'modules'); foreach ($expected_directories as $module => $directories) { $expected_directory = array_shift($directories); $expected_filename = "$expected_directory/$module/$module.module"; diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 4670f8819471..0e0077fb2492 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -2776,10 +2776,10 @@ function system_get_module_info($property) { */ function _system_rebuild_module_data() { // Find modules - $modules = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0); + $modules = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules'); // Find installation profiles. - $profiles = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.profile$/', 'profiles', 'name', 0); + $profiles = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.profile$/', 'profiles'); // Include the installation profile in modules that are loaded. $profile = drupal_get_profile(); diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh index 5aa8b22d0175..5029d13359c9 100755 --- a/core/scripts/run-tests.sh +++ b/core/scripts/run-tests.sh @@ -28,7 +28,7 @@ else { } // Bootstrap to perform initial validation or other operations. -drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); +drupal_bootstrap(DRUPAL_BOOTSTRAP_CODE); if (!module_exists('simpletest')) { simpletest_script_print_error("The simpletest module must be enabled before this script can run."); exit; diff --git a/core/scripts/test.script b/core/scripts/test/test.script similarity index 100% rename from core/scripts/test.script rename to core/scripts/test/test.script