Commit c03b4c54 authored by webchick's avatar webchick
Browse files

#255551 by drewish and c960657: Array-itize file_scan_directory()'s parameters (DX improvement).

parent fb7347bc
......@@ -2315,7 +2315,7 @@ function _drupal_load_stylesheet($matches) {
* Delete all cached CSS files.
*/
function drupal_clear_css_cache() {
file_scan_directory(file_create_path('css'), '/.*/', '/(\.\.?|CVS)$/', 'file_unmanaged_delete', TRUE);
file_scan_directory(file_create_path('css'), '/.*/', array('callback' => 'file_unmanaged_delete'));
}
/**
......@@ -2779,7 +2779,7 @@ function drupal_build_js_cache($files, $filename) {
* Delete all cached JS files.
*/
function drupal_clear_js_cache() {
file_scan_directory(file_create_path('js'), '/.*/', '/(\.\.?|CVS)$/', 'file_unmanaged_delete', TRUE);
file_scan_directory(file_create_path('js'), '/.*/', array('callback' => 'file_unmanaged_delete'));
variable_set('javascript_parsed', array());
}
......@@ -3148,7 +3148,7 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1)
// Get current list of items
foreach ($searchdir as $dir) {
$files = array_merge($files, file_scan_directory($dir, $mask, '/(\.\.?|CVS)$/', 0, TRUE, $key, $min_depth));
$files = array_merge($files, file_scan_directory($dir, $mask, array('key' => $key, 'min_depth' => $min_depth)));
}
return $files;
......
......@@ -1382,51 +1382,65 @@ function file_download() {
* The base directory for the scan, without trailing slash.
* @param $mask
* The preg_match() regular expression of the files to find.
* @param $nomask
* The preg_match() regular expression of the files to ignore.
* @param $callback
* The callback function to call for each match.
* @param $recurse
* When TRUE, the directory scan will recurse the entire tree
* starting at the provided directory.
* @param $key
* The key to be used for the returned array of files. Possible
* values are "filename", for the path starting with $dir,
* "basename", for the basename of the file, and "name" for the name
* of the file without an extension.
* @param $min_depth
* Minimum depth of directories to return files from.
* @param $options
* An associative array of additional options, with the following keys:
* - 'nomask'
* The preg_match() regular expression of the files to ignore. Defaults to
* '/(\.\.?|CVS)$/'.
* - 'callback'
* The callback function to call for each match. There is no default
* callback.
* - 'recurse'
* When TRUE, the directory scan will recurse the entire tree starting at
* the provided directory. Defaults to TRUE.
* - 'key'
* The key to be used for the returned array of files. Possible values are
* "filename", for the path starting with $dir, "basename", for the
* basename of the file, and "name" for the name of the file without an
* extension. Defaults to 'filename'.
* - 'min_depth'
* Minimum depth of directories to return files from. Defaults to 0.
* @param $depth
* Current depth of recursion. This parameter is only used internally and
* should not be passed.
* @return
* An associative array (keyed on the provided key) of objects with
* "path", "basename", and "name" members corresponding to the
* "filename", "basename", and "name" members corresponding to the
* matching files.
*/
function file_scan_directory($dir, $mask, $nomask = '/(\.\.?|CVS)$/', $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth = 0, $depth = 0) {
$key = (in_array($key, array('filename', 'basename', 'name')) ? $key : 'filename');
function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
// Merge in defaults.
$options += array(
'nomask' => '/(\.\.?|CVS)$/',
'callback' => 0,
'recurse' => TRUE,
'key' => 'filename',
'min_depth' => 0,
);
$options['key'] = (in_array($options['key'], array('filename', 'basename', 'name')) ? $options['key'] : 'filename');
$files = array();
if (is_dir($dir) && $handle = opendir($dir)) {
while (FALSE !== ($file = readdir($handle))) {
if (!preg_match($nomask, $file) && $file[0] != '.') {
if (is_dir("$dir/$file") && $recurse) {
if (!preg_match($options['nomask'], $file) && $file[0] != '.') {
if (is_dir("$dir/$file") && $options['recurse']) {
// Give priority to files in this folder by merging them in after any subdirectory files.
$files = array_merge(file_scan_directory("$dir/$file", $mask, $nomask, $callback, $recurse, $key, $min_depth, $depth + 1), $files);
$files = array_merge(file_scan_directory("$dir/$file", $mask, $options, $depth + 1), $files);
}
elseif ($depth >= $min_depth && preg_match($mask, $file)) {
elseif ($depth >= $options['min_depth'] && preg_match($mask, $file)) {
// Always use this match over anything already set in $files with the
// same $$key.
// same $$options['key'].
$filename = "$dir/$file";
$basename = basename($file);
$name = substr($basename, 0, strrpos($basename, '.'));
$files[$$key] = new stdClass();
$files[$$key]->filename = $filename;
$files[$$key]->basename = $basename;
$files[$$key]->name = $name;
if ($callback) {
$callback($filename);
$files[${$options['key']}] = (object) array(
'filename' => $filename,
'basename' => $basename,
'name' => $name,
);
if ($options['callback']) {
$options['callback']($filename);
}
}
}
......
......@@ -215,7 +215,7 @@ function drupal_detect_database_types() {
// Because we have no registry yet, we need to also include the install.inc
// file for the driver explicitly.
foreach (file_scan_directory(DRUPAL_ROOT . '/includes/database', '/^[a-z]*$/i', '/(\.\.?|CVS)$/', 0, FALSE) as $file) {
foreach (file_scan_directory(DRUPAL_ROOT . '/includes/database', '/^[a-z]*$/i', array('recurse' => FALSE)) as $file) {
include_once "{$file->filename}/install.inc";
include_once "{$file->filename}/database.inc";
$drivers[$file->basename] = $file->filename;
......
......@@ -2617,7 +2617,7 @@ function locale_batch_by_language($langcode, $finished = NULL, $skip = array())
// with names ending with $langcode.po. This allows for filenames
// like node-module.de.po to let translators use small files and
// be able to import in smaller chunks.
$files = array_merge($files, file_scan_directory(dirname($component->filename) . '/translations', '/(^|\.)' . $langcode . '\.po$/', '/(\.\.?|CVS)$/', 0, FALSE));
$files = array_merge($files, file_scan_directory(dirname($component->filename) . '/translations', '/(^|\.)' . $langcode . '\.po$/', array('recurse' => FALSE)));
$components[] = $component->name;
}
......@@ -2649,7 +2649,7 @@ function locale_batch_by_component($components, $finished = '_locale_batch_syste
// as $langcode.po or with names ending with $langcode.po. This allows
// for filenames like node-module.de.po to let translators use small
// files and be able to import in smaller chunks.
$files = array_merge($files, file_scan_directory(dirname($component->filename) . '/translations', '/(^|\.)(' . $language_list . ')\.po$/', '/(\.\.?|CVS)$/', 0, FALSE));
$files = array_merge($files, file_scan_directory(dirname($component->filename) . '/translations', '/(^|\.)(' . $language_list . ')\.po$/', array('recurse' => FALSE)));
}
}
return _locale_batch_build($files, $finished);
......
......@@ -404,7 +404,7 @@ function install_settings_form_submit($form, &$form_state) {
* Find all .profile files.
*/
function install_find_profiles() {
return file_scan_directory('./profiles', '/\.profile$/', '/(\.\.?|CVS)$/', 0, TRUE, 'name', 0);
return file_scan_directory('./profiles', '/\.profile$/', array('key' => 'name'));
}
/**
......@@ -490,7 +490,7 @@ function install_select_profile_form(&$form_state, $profile_files) {
* Find all .po files for the current profile.
*/
function install_find_locales($profilename) {
$locales = file_scan_directory('./profiles/' . $profilename . '/translations', '/\.po$/', '/(\.\.?|CVS)$/', 0, FALSE);
$locales = file_scan_directory('./profiles/' . $profilename . '/translations', '/\.po$/', array('recurse' => FALSE));
array_unshift($locales, (object) array('name' => 'en'));
return $locales;
}
......
......@@ -14,6 +14,32 @@ function file_test_validator($file, $errors) {
return $errors;
}
/**
* Helper function for testing file_scan_directory().
*
* Each time the function is called the file is stored in a static variable.
* When the function is called with $reset parameter TRUE the cache is cleared
* and the results returned.
*
* @param $file
* File object
* @param $reset
* Boolean indicating that the stored files should be removed and returned.
* @return
* An array of all previous $file parameters since $reset was last called.
*/
function file_test_file_scan_callback($file, $reset = FALSE) {
static $files = array();
if ($reset) {
$ret = $files;
$files = array();
return $ret;
}
$files[] = $file;
}
/**
* Base class for file tests that adds some additional file specific
* assertions and helper functions.
......@@ -786,20 +812,119 @@ class FileScanDirectoryTest extends FileTestCase {
);
}
function setUp() {
parent::setUp();
$this->path = $this->originalFileDirectory . '/simpletest';
}
/**
* Check that the no-mask parameter is honored.
* Check the format of the returned values.
*/
function testNoMask() {
$path = $this->originalFileDirectory . '/simpletest';
function testReturn() {
// Grab a listing of all the JavaSscript files and check that they're
// passed to the callback.
$all_files = file_scan_directory($this->path, '/^javascript-/');
ksort($all_files);
$this->assertEqual(2, count($all_files), t('Found two, expected javascript files.'));
// Check the first file.
$file = reset($all_files);
$this->assertEqual(key($all_files), $file->filename, t('Correct array key was used for the first returned file.'));
$this->assertEqual($file->filename, $this->path . '/javascript-1.txt', t('First file name was set correctly.'));
$this->assertEqual($file->basename, 'javascript-1.txt', t('First basename was set correctly'));
$this->assertEqual($file->name, 'javascript-1', t('First name was set correctly.'));
// Grab a listing of all the JS files.
$all_files = file_scan_directory($path, '/javascript*/');
// Check the second file.
$file = next($all_files);
$this->assertEqual(key($all_files), $file->filename, t('Correct array key was used for the second returned file.'));
$this->assertEqual($file->filename, $this->path . '/javascript-2.script', t('Second file name was set correctly.'));
$this->assertEqual($file->basename, 'javascript-2.script', t('Second basename was set correctly'));
$this->assertEqual($file->name, 'javascript-2', t('Second name was set correctly.'));
}
/**
* Check that the callback function is called correctly.
*/
function testOptionCallback() {
// When nothing is matched nothing should be passed to the callback.
$all_files = file_scan_directory($this->path, '/^NONEXISTINGFILENAME/', array('callback' => 'file_test_file_scan_callback'));
$this->assertEqual(0, count($all_files), t('No files were found.'));
$results = file_test_file_scan_callback(NULL, TRUE);
$this->assertEqual(0, count($results), t('No files were passed to the callback.'));
// Grab a listing of all the JavaSscript files and check that they're
// passed to the callback.
$all_files = file_scan_directory($this->path, '/^javascript-/', array('callback' => 'file_test_file_scan_callback'));
$this->assertEqual(2, count($all_files), t('Found two, expected javascript files.'));
$results = file_test_file_scan_callback(NULL, TRUE);
$this->assertEqual(2, count($results), t('Files were passed to the callback.'));
}
/**
* Check that the no-mask parameter is honored.
*/
function testOptionNoMask() {
// Grab a listing of all the JavaSscript files.
$all_files = file_scan_directory($this->path, '/^javascript-/');
$this->assertEqual(2, count($all_files), t('Found two, expected javascript files.'));
// Now use the nomast parameter to filter out the .script file.
$filtered_files = file_scan_directory($path, '/javascript*/', '/.script$/');
$filtered_files = file_scan_directory($this->path, '/^javascript-/', array('nomask' => '/.script$/'));
$this->assertEqual(1, count($filtered_files), t('Filtered correctly.'));
}
/**
* Check that key parameter sets the return value's key.
*/
function testOptionKey() {
// "filename", for the path starting with $dir.
$expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script');
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'filename')));
sort($actual);
$this->assertEqual($expected, $actual, t('Returned the correct values for the filename key.'));
// "basename", for the basename of the file.
$expected = array('javascript-1.txt', 'javascript-2.script');
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'basename')));
sort($actual);
$this->assertEqual($expected, $actual, t('Returned the correct values for the basename key.'));
// "name" for the name of the file without an extension.
$expected = array('javascript-1', 'javascript-2');
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'name')));
sort($actual);
$this->assertEqual($expected, $actual, t('Returned the correct values for the name key.'));
// Invalid option that should default back to "filename".
$expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script');
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'INVALID')));
sort($actual);
$this->assertEqual($expected, $actual, t('An invalid key defaulted back to the default.'));
}
/**
* Check that the recurse option decends into subdirectories.
*/
function testOptionRecurse() {
$files = file_scan_directory($this->originalFileDirectory, '/^javascript-/', array('recurse' => FALSE));
$this->assertTrue(empty($files), t("Without recursion couldn't find javascript files."));
$files = file_scan_directory($this->originalFileDirectory, '/^javascript-/', array('recurse' => TRUE));
$this->assertEqual(2, count($files), t('With recursion we found the expected javascript files.'));
}
/**
* Check that the min_depth options lets us ignore files in the starting
* directory.
*/
function testOptionMinDepth() {
$files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 0));
$this->assertEqual(2, count($files), t('No minimum-depth gets files in current directory.'));
$files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 1));
$this->assertTrue(empty($files), t("Minimum-depth of 1 successfully excludes files from current directory."));
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment