1 file.inc file_scan_directory($dir, $mask, $options = array(), $depth = 0)

Finds all files that match a given mask in a given directory.

Directories and files beginning with a period are excluded; this prevents hidden files and directories (such as SVN working directories) from being scanned.

Parameters

$dir: The base directory or URI to scan, without trailing slash.

$mask: The preg_match() regular expression of the files to find.

$options: An associative array of additional options, with the following elements:

  • 'nomask': The preg_match() regular expression of the files to ignore. Defaults to '/^(\..*)|(CVS)$/'. This default ignores all hidden files (those that start with a period) and items named "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 associative array of files. 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. Defaults to 'uri'.
  • 'min_depth': Minimum depth of directories to return files from. Defaults to 0.

$depth: Current depth of recursion. This parameter is only used internally and should not be passed in.

Return value

An associative array (keyed on the chosen key) of objects with 'uri',: 'filename', and 'name' members corresponding to the matching files.

Related topics

File

core/includes/file.inc, line 2250
API for handling file uploads and server file management.

Code

function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
  // By default, do not check for files in common special-purpose directories.
  $ignore_directories = array(
    'node_modules',
    'bower_components',
  );
  $no_mask = '/^((\..*)|' . implode('|', $ignore_directories) . ')$/';

  // Merge in defaults.
  $options += array(
    'nomask' => $no_mask,
    'callback' => 0,
    'recurse' => TRUE,
    'key' => 'uri',
    'min_depth' => 0,
  );

  $options['key'] = in_array($options['key'], array('uri', 'filename', 'name')) ? $options['key'] : 'uri';
  $files = array();
  if (is_dir($dir) && $handle = opendir($dir)) {
    while (FALSE !== ($filename = readdir($handle))) {
      if (!preg_match($options['nomask'], $filename) && $filename !== '.' && $filename !== '..') {
        $uri = "$dir/$filename";
        $uri = file_stream_wrapper_uri_normalize($uri);
        if (is_dir($uri) && $options['recurse']) {
          // 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);
        }
        elseif ($depth >= $options['min_depth'] && preg_match($mask, $filename)) {
          // Always use this match over anything already set in $files with the
          // same $$options['key'].
          $file = new stdClass();
          $file->uri = $uri;
          $file->filename = $filename;
          $file->name = pathinfo($filename, PATHINFO_FILENAME);
          $key = $options['key'];
          $files[$file->$key] = $file;
          if ($options['callback']) {
            $options['callback']($uri);
          }
        }
      }
    }

    closedir($handle);
  }

  return $files;
}