8.5.x file.inc file_scan_directory($dir, $mask, $options = [], $depth = 0)
8.0.x file.inc file_scan_directory($dir, $mask, $options = array(), $depth = 0)
8.1.x file.inc file_scan_directory($dir, $mask, $options = array(), $depth = 0)
8.2.x file.inc file_scan_directory($dir, $mask, $options = array(), $depth = 0)
8.3.x file.inc file_scan_directory($dir, $mask, $options = [], $depth = 0)
8.4.x file.inc file_scan_directory($dir, $mask, $options = [], $depth = 0)
8.6.x file.inc file_scan_directory($dir, $mask, $options = [], $depth = 0)
4.6.x file.inc file_scan_directory($dir, $mask, $nomask = array('.', '..', 'CVS'), $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth = 0, $depth = 0)
4.7.x file.inc file_scan_directory($dir, $mask, $nomask = array('.', '..', 'CVS'), $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth = 0, $depth = 0)
5.x file.inc file_scan_directory($dir, $mask, $nomask = array('.', '..', 'CVS'), $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth = 0, $depth = 0)
6.x file.inc file_scan_directory($dir, $mask, $nomask = array('.', '..', 'CVS'), $callback = 0, $recurse = TRUE, $key = 'filename', $min_depth = 0, $depth = 0)
7.x 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 dot are excluded; this prevents hidden files and directories (such as SVN working directories) from being scanned. Use the umask option to skip configuration directories to eliminate the possibility of accidentally exposing configuration information. Also, you can use the base directory, recurse, and min_depth options to improve performance by limiting how much of the filesystem has to be traversed.

Parameters

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

$mask: The preg_match() regular expression for files to be included.

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

  • 'nomask': The preg_match() regular expression for files to be excluded. Defaults to the 'file_scan_ignore_directories' setting.
  • '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: The 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' properties corresponding to the matched files.

Related topics

20 calls to file_scan_directory()
CssCollectionOptimizer::deleteAll in core/lib/Drupal/Core/Asset/CssCollectionOptimizer.php
Deletes all optimized asset collections assets.
drupal_find_theme_templates in core/includes/theme.inc
Allows themes and/or theme engines to easily discover overridden templates.
drupal_get_database_types in core/includes/install.inc
Returns all supported database driver installer objects.
FileTranslation::findTranslationFiles in core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php
Finds installer translations either for a specific or all languages.
ImageAdminStylesTest::getImageCount in core/modules/image/src/Tests/ImageAdminStylesTest.php
Count the number of images currently create for a style.

... See full list

File

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

Code

function file_scan_directory($dir, $mask, $options = [], $depth = 0) {

  // Merge in defaults.
  $options += [
    '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) === '/';
  }

  // Allow directories specified in settings.php to be ignored. You can use this
  // to not check for files in common special-purpose directories. For example,
  // node_modules and bower_components. Ignoring irrelevant directories is a
  // performance boost.
  if (!isset($options['nomask'])) {
    $ignore_directories = Settings::get('file_scan_ignore_directories', []);
    array_walk($ignore_directories, function (&$value) {
      $value = preg_quote($value, '/');
    });
    $default_nomask = '/^' . implode('|', $ignore_directories) . '$/';
  }
  $options['key'] = in_array($options['key'], [
    'uri',
    'filename',
    'name',
  ]) ? $options['key'] : 'uri';
  $files = [];

  // Avoid warnings when opendir does not have the permissions to open a
  // directory.
  if (is_dir($dir)) {
    if ($handle = @opendir($dir)) {
      while (FALSE !== ($filename = readdir($handle))) {

        // Skip this file if it matches the nomask or starts with a dot.
        if ($filename[0] != '.' && !(isset($options['nomask']) && preg_match($options['nomask'], $filename)) && !(!empty($default_nomask) && preg_match($default_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);
          }
          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);
    }
    else {
      \Drupal::logger('file')
        ->error('@dir can not be opened', [
        '@dir' => $dir,
      ]);
    }
  }
  return $files;
}