Issue #3035312 by kim.pepper, andypost, martin107, yogeshmpawar, naveenvalecha, pguillard, Berdir, dww, claudiu.cristea, jibran, Mile23: Move file_scan_directory() to file_system service

merge-requests/55/head
catch 2019-07-24 11:29:08 +01:00
parent 5d68f9fc21
commit dd907896b1
28 changed files with 315 additions and 136 deletions

View File

@ -992,81 +992,23 @@ function file_unmanaged_save_data($data, $destination = NULL, $replace = FILE_EX
* @return * @return
* An associative array (keyed on the chosen key) of objects with 'uri', * An associative array (keyed on the chosen key) of objects with 'uri',
* 'filename', and 'name' properties corresponding to the matched files. * 'filename', and 'name' properties corresponding to the matched files.
*
* @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0.
* Use \Drupal\Core\File\FileSystemInterface::scanDirectory() instead.
*
* @see https://www.drupal.org/node/3038437
*/ */
function file_scan_directory($dir, $mask, $options = [], $depth = 0) { function file_scan_directory($dir, $mask, $options = [], $depth = 0) {
// Merge in defaults. @trigger_error('file_scan_directory() is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\File\FileSystemInterface::scanDirectory() instead. See https://www.drupal.org/node/3038437', E_USER_DEPRECATED);
$options += [
'callback' => 0,
'recurse' => TRUE,
'key' => 'uri',
'min_depth' => 0,
];
// Normalize $dir only once.
if ($depth == 0) {
/** @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager */
$stream_wrapper_manager = \Drupal::service('stream_wrapper_manager');
$dir = $stream_wrapper_manager->normalizeUri($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 = []; $files = [];
// Avoid warnings when opendir does not have the permissions to open a try {
// directory. if (is_dir($dir)) {
if (is_dir($dir)) { $files = \Drupal::service('file_system')->scanDirectory($dir, $mask, $options);
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]);
} }
} }
catch (FileException $e) {
// Ignore and return empty array for BC.
}
return $files; return $files;
} }

View File

@ -444,7 +444,9 @@ function install_begin_request($class_loader, &$install_state) {
else { else {
$directory = $site_path . '/files/translations'; $directory = $site_path . '/files/translations';
} }
$container->set('string_translator.file_translation', new FileTranslation($directory)); /** @var \Drupal\Core\File\FileSystemInterface $file_system */
$file_system = $container->get('file_system');
$container->set('string_translator.file_translation', new FileTranslation($directory, $file_system));
$container->get('string_translation') $container->get('string_translation')
->addTranslator($container->get('string_translator.file_translation')); ->addTranslator($container->get('string_translator.file_translation'));
@ -1324,9 +1326,9 @@ function _install_select_profile(&$install_state) {
* *
* @return * @return
* An associative array of file URIs keyed by language code. URIs as * An associative array of file URIs keyed by language code. URIs as
* returned by file_scan_directory(). * returned by FileSystemInterface::scanDirectory().
* *
* @see file_scan_directory() * @see \Drupal\Core\File\FileSystemInterface::scanDirectory()
*/ */
function install_find_translations() { function install_find_translations() {
$translations = []; $translations = [];

View File

@ -5,15 +5,15 @@
* API functions for installing modules and themes. * API functions for installing modules and themes.
*/ */
use Drupal\Core\Extension\Dependency;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\File\FileSystemInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\Component\Utility\Crypt; use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\OpCodeCache; use Drupal\Component\Utility\OpCodeCache;
use Drupal\Component\Utility\Unicode;
use Drupal\Component\Utility\UrlHelper; use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Extension\Dependency;
use Drupal\Core\Extension\ExtensionDiscovery; use Drupal\Core\Extension\ExtensionDiscovery;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Site\Settings; use Drupal\Core\Site\Settings;
use Symfony\Component\HttpFoundation\RedirectResponse;
/** /**
* Requirement severity -- Informational message only. * Requirement severity -- Informational message only.
@ -166,9 +166,11 @@ function drupal_get_database_types() {
// The internal database driver name is any valid PHP identifier. // The internal database driver name is any valid PHP identifier.
$mask = '/^' . DRUPAL_PHP_FUNCTION_PATTERN . '$/'; $mask = '/^' . DRUPAL_PHP_FUNCTION_PATTERN . '$/';
$files = file_scan_directory(DRUPAL_ROOT . '/core/lib/Drupal/Core/Database/Driver', $mask, ['recurse' => FALSE]); /** @var \Drupal\Core\File\FileSystemInterface $file_system */
$file_system = \Drupal::service('file_system');
$files = $file_system->scanDirectory(DRUPAL_ROOT . '/core/lib/Drupal/Core/Database/Driver', $mask, ['recurse' => FALSE]);
if (is_dir(DRUPAL_ROOT . '/drivers/lib/Drupal/Driver/Database')) { if (is_dir(DRUPAL_ROOT . '/drivers/lib/Drupal/Driver/Database')) {
$files += file_scan_directory(DRUPAL_ROOT . '/drivers/lib/Drupal/Driver/Database/', $mask, ['recurse' => FALSE]); $files += $file_system->scanDirectory(DRUPAL_ROOT . '/drivers/lib/Drupal/Driver/Database/', $mask, ['recurse' => FALSE]);
} }
foreach ($files as $file) { foreach ($files as $file) {
if (file_exists($file->uri . '/Install/Tasks.php')) { if (file_exists($file->uri . '/Install/Tasks.php')) {

View File

@ -216,7 +216,10 @@ function drupal_find_theme_templates($cache, $extension, $path) {
// Escape the periods in the extension. // Escape the periods in the extension.
$regex = '/' . str_replace('.', '\.', $extension) . '$/'; $regex = '/' . str_replace('.', '\.', $extension) . '$/';
// Get a listing of all template files in the path to search. // Get a listing of all template files in the path to search.
$files = file_scan_directory($path, $regex, ['key' => 'filename']); $files = [];
if (is_dir($path)) {
$files = \Drupal::service('file_system')->scanDirectory($path, $regex, ['key' => 'filename']);
}
// Find templates that implement registered theme hooks and include that in // Find templates that implement registered theme hooks and include that in
// what is returned so that the registry knows that the theme has this // what is returned so that the registry knows that the theme has this

View File

@ -196,7 +196,9 @@ class CssCollectionOptimizer implements AssetCollectionOptimizerInterface {
$this->fileSystem->delete($uri); $this->fileSystem->delete($uri);
} }
}; };
file_scan_directory('public://css', '/.*/', ['callback' => $delete_stale]); if (is_dir('public://css')) {
$this->fileSystem->scanDirectory('public://css', '/.*/', ['callback' => $delete_stale]);
}
} }
} }

View File

@ -198,7 +198,9 @@ class JsCollectionOptimizer implements AssetCollectionOptimizerInterface {
$this->fileSystem->delete($uri); $this->fileSystem->delete($uri);
} }
}; };
file_scan_directory('public://js', '/.*/', ['callback' => $delete_stale]); if (is_dir('public://js')) {
$this->fileSystem->scanDirectory('public://js', '/.*/', ['callback' => $delete_stale]);
}
} }
} }

View File

@ -0,0 +1,8 @@
<?php
namespace Drupal\Core\File\Exception;
/**
* Exception thrown when a target is not a regular directory (e.g. a file).
*/
class NotRegularDirectoryException extends FileException {}

View File

@ -8,6 +8,7 @@ use Drupal\Core\File\Exception\FileException;
use Drupal\Core\File\Exception\FileExistsException; use Drupal\Core\File\Exception\FileExistsException;
use Drupal\Core\File\Exception\FileNotExistsException; use Drupal\Core\File\Exception\FileNotExistsException;
use Drupal\Core\File\Exception\FileWriteException; use Drupal\Core\File\Exception\FileWriteException;
use Drupal\Core\File\Exception\NotRegularDirectoryException;
use Drupal\Core\File\Exception\NotRegularFileException; use Drupal\Core\File\Exception\NotRegularFileException;
use Drupal\Core\Site\Settings; use Drupal\Core\Site\Settings;
use Drupal\Core\StreamWrapper\StreamWrapperManager; use Drupal\Core\StreamWrapper\StreamWrapperManager;
@ -625,4 +626,97 @@ class FileSystem implements FileSystemInterface {
return $destination; return $destination;
} }
/**
* {@inheritdoc}
*/
public function scanDirectory($dir, $mask, array $options = []) {
// Merge in defaults.
$options += [
'callback' => 0,
'recurse' => TRUE,
'key' => 'uri',
'min_depth' => 0,
];
$dir = $this->streamWrapperManager->normalizeUri($dir);
if (!is_dir($dir)) {
throw new NotRegularDirectoryException("$dir is not a directory.");
}
// 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 = $this->settings->get('file_scan_ignore_directories', []);
array_walk($ignore_directories, function (&$value) {
$value = preg_quote($value, '/');
});
$options['nomask'] = '/^' . implode('|', $ignore_directories) . '$/';
}
$options['key'] = in_array($options['key'], ['uri', 'filename', 'name']) ? $options['key'] : 'uri';
return $this->doScanDirectory($dir, $mask, $options);
}
/**
* Internal function to handle directory scanning with recursion.
*
* @param string $dir
* The base directory or URI to scan, without trailing slash.
* @param string $mask
* The preg_match() regular expression for files to be included.
* @param array $options
* The options as per ::scanDirectory().
* @param int $depth
* The current depth of recursion.
*
* @return array
* An associative array as per ::scanDirectory().
*
* @throws \Drupal\Core\File\Exception\NotRegularDirectoryException
* If the directory does not exist.
*
* @see \Drupal\Core\File\FileSystemInterface::scanDirectory()
*/
protected function doScanDirectory($dir, $mask, array $options = [], $depth = 0) {
$files = [];
// Avoid warnings when opendir does not have the permissions to open a
// directory.
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] != '.' && !(preg_match($options['nomask'], $filename))) {
if (substr($dir, -1) == '/') {
$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($this->doScanDirectory($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 {
$this->logger->error('@dir can not be opened', ['@dir' => $dir]);
}
return $files;
}
} }

View File

@ -472,4 +472,43 @@ interface FileSystemInterface {
*/ */
public function getDestinationFilename($destination, $replace); public function getDestinationFilename($destination, $replace);
/**
* 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.
*
* @param string $dir
* The base directory or URI to scan, without trailing slash.
* @param string $mask
* The preg_match() regular expression for files to be included.
* @param array $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.
*
* @return array
* An associative array (keyed on the chosen key) of objects with 'uri',
* 'filename', and 'name' properties corresponding to the matched files.
*
* @throws \Drupal\Core\File\Exception\NotRegularDirectoryException
* If the directory does not exist.
*/
public function scanDirectory($dir, $mask, array $options = []);
} }

View File

@ -4,6 +4,7 @@ namespace Drupal\Core\StringTranslation\Translator;
use Drupal\Component\Gettext\PoStreamReader; use Drupal\Component\Gettext\PoStreamReader;
use Drupal\Component\Gettext\PoMemoryWriter; use Drupal\Component\Gettext\PoMemoryWriter;
use Drupal\Core\File\FileSystemInterface;
/** /**
* File based string translation. * File based string translation.
@ -22,15 +23,29 @@ class FileTranslation extends StaticTranslation {
*/ */
protected $directory; protected $directory;
/**
* The file system.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/** /**
* Constructs a StaticTranslation object. * Constructs a StaticTranslation object.
* *
* @param string $directory * @param string $directory
* The directory to retrieve file translations from. * The directory to retrieve file translations from.
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file system service.
*/ */
public function __construct($directory) { public function __construct($directory, FileSystemInterface $file_system = NULL) {
parent::__construct(); parent::__construct();
$this->directory = $directory; $this->directory = $directory;
if (!isset($file_system)) {
@trigger_error('Calling FileTranslation::__construct() without the $file_system argument is deprecated in drupal:8.8.0. The $file_system argument will be required in drupal:9.0.0. See https://www.drupal.org/node/3038437', E_USER_DEPRECATED);
$file_system = \Drupal::service('file_system');
}
$this->fileSystem = $file_system;
} }
/** /**
@ -65,12 +80,15 @@ class FileTranslation extends StaticTranslation {
* *
* @return array * @return array
* An associative array of file information objects keyed by file URIs as * An associative array of file information objects keyed by file URIs as
* returned by file_scan_directory(). * returned by FileSystemInterface::scanDirectory().
* *
* @see file_scan_directory() * @see \Drupal\Core\File\FileSystemInterface::scanDirectory()
*/ */
public function findTranslationFiles($langcode = NULL) { public function findTranslationFiles($langcode = NULL) {
$files = file_scan_directory($this->directory, $this->getTranslationFilesPattern($langcode), ['recurse' => FALSE]); $files = [];
if (is_dir($this->directory)) {
$files = $this->fileSystem->scanDirectory($this->directory, $this->getTranslationFilesPattern($langcode), ['recurse' => FALSE]);
}
return $files; return $files;
} }

View File

@ -106,12 +106,17 @@ class Updater {
* Path to the info file. * Path to the info file.
*/ */
public static function findInfoFile($directory) { public static function findInfoFile($directory) {
$info_files = file_scan_directory($directory, '/.*\.info.yml$/'); /** @var \Drupal\Core\File\FileSystemInterface $file_system */
$file_system = \Drupal::service('file_system');
$info_files = [];
if (is_dir($directory)) {
$info_files = $file_system->scanDirectory($directory, '/.*\.info.yml$/');
}
if (!$info_files) { if (!$info_files) {
return FALSE; return FALSE;
} }
foreach ($info_files as $info_file) { foreach ($info_files as $info_file) {
if (mb_substr($info_file->filename, 0, -9) == \Drupal::service('file_system')->basename($directory)) { if (mb_substr($info_file->filename, 0, -9) == $file_system->basename($directory)) {
// Info file Has the same name as the directory, return it. // Info file Has the same name as the directory, return it.
return $info_file->uri; return $info_file->uri;
} }

View File

@ -314,7 +314,7 @@ function file_test_validator(File $file, $errors) {
} }
/** /**
* Helper function for testing file_scan_directory(). * Helper function for testing FileSystemInterface::scanDirectory().
* *
* Each time the function is called the file is stored in a static variable. * Each time the function is called the file is stored in a static variable.
* When the function is called with no $filepath parameter, the results are * When the function is called with no $filepath parameter, the results are

View File

@ -44,7 +44,11 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
* Count the number of images currently create for a style. * Count the number of images currently create for a style.
*/ */
public function getImageCount(ImageStyleInterface $style) { public function getImageCount(ImageStyleInterface $style) {
return count(file_scan_directory('public://styles/' . $style->id(), '/.*/')); $count = 0;
if (is_dir('public://styles/' . $style->id())) {
$count = count(\Drupal::service('file_system')->scanDirectory('public://styles/' . $style->id(), '/.*/'));
}
return $count;
} }
/** /**

View File

@ -40,7 +40,11 @@ class ImageFieldValidateTest extends ImageFieldTestBase {
$this->drupalPostForm(NULL, [], t('Save')); $this->drupalPostForm(NULL, [], t('Save'));
// Get invalid image test files from simpletest. // Get invalid image test files from simpletest.
$files = file_scan_directory(drupal_get_path('module', 'simpletest') . '/files', '/invalid-img-.*/'); $dir = drupal_get_path('module', 'simpletest') . '/files';
$files = [];
if (is_dir($dir)) {
$files = $file_system->scanDirectory($dir, '/invalid-img-.*/');
}
$invalid_image_files = []; $invalid_image_files = [];
foreach ($files as $file) { foreach ($files as $file) {
$invalid_image_files[$file->filename] = $file; $invalid_image_files[$file->filename] = $file;

View File

@ -42,7 +42,11 @@ class ImageStyleFlushTest extends ImageFieldTestBase {
* Count the number of images currently created for a style in a wrapper. * Count the number of images currently created for a style in a wrapper.
*/ */
public function getImageCount($style, $wrapper) { public function getImageCount($style, $wrapper) {
return count(file_scan_directory($wrapper . '://styles/' . $style->id(), '/.*/')); $count = 0;
if (is_dir($wrapper . '://styles/' . $style->id())) {
$count = count(\Drupal::service('file_system')->scanDirectory($wrapper . '://styles/' . $style->id(), '/.*/'));
}
return $count;
} }
/** /**

View File

@ -97,7 +97,10 @@ function locale_translate_get_interface_translation_files(array $projects = [],
// {project}-{version}.{langcode}.po. // {project}-{version}.{langcode}.po.
// Only files of known projects and languages will be returned. // Only files of known projects and languages will be returned.
$directory = \Drupal::config('locale.settings')->get('translation.path'); $directory = \Drupal::config('locale.settings')->get('translation.path');
$result = file_scan_directory($directory, '![a-z_]+(\-[0-9a-z\.\-\+]+|)\.[^\./]+\.po$!', ['recurse' => FALSE]); $result = [];
if (is_dir($directory)) {
$result = \Drupal::service('file_system')->scanDirectory($directory, '![a-z_]+(\-[0-9a-z\.\-\+]+|)\.[^\./]+\.po$!', ['recurse' => FALSE]);
}
foreach ($result as $file) { foreach ($result as $file) {
// Update the file object with project name and version from the file name. // Update the file object with project name and version from the file name.

View File

@ -32,10 +32,12 @@ function locale_uninstall() {
if (is_dir($locale_js_directory)) { if (is_dir($locale_js_directory)) {
$locale_javascripts = \Drupal::state()->get('locale.translation.javascript') ?: []; $locale_javascripts = \Drupal::state()->get('locale.translation.javascript') ?: [];
/** @var \Drupal\Core\File\FileSystemInterface $file_system */
$file_system = \Drupal::service('file_system');
foreach ($locale_javascripts as $langcode => $file_suffix) { foreach ($locale_javascripts as $langcode => $file_suffix) {
if (!empty($file_suffix)) { if (!empty($file_suffix)) {
try { try {
\Drupal::service('file_system')->delete($locale_js_directory . '/' . $langcode . '_' . $file_suffix . '.js'); $file_system->delete($locale_js_directory . '/' . $langcode . '_' . $file_suffix . '.js');
} }
catch (FileException $e) { catch (FileException $e) {
// Ignore and continue. // Ignore and continue.
@ -43,8 +45,10 @@ function locale_uninstall() {
} }
} }
// Delete the JavaScript translations directory if empty. // Delete the JavaScript translations directory if empty.
if (!file_scan_directory($locale_js_directory, '/.*/')) { if (is_dir($locale_js_directory)) {
\Drupal::service('file_system')->rmdir($locale_js_directory); if (!$file_system->scanDirectory($locale_js_directory, '/.*/')) {
$file_system->rmdir($locale_js_directory);
}
} }
} }

View File

@ -175,11 +175,13 @@ function locale_translation_source_check_file($source) {
$directory = $source_file->directory; $directory = $source_file->directory;
$filename = '/' . preg_quote($source_file->filename) . '$/'; $filename = '/' . preg_quote($source_file->filename) . '$/';
if ($files = file_scan_directory($directory, $filename, ['key' => 'name', 'recurse' => FALSE])) { if (is_dir($directory)) {
$file = current($files); if ($files = \Drupal::service('file_system')->scanDirectory($directory, $filename, ['key' => 'name', 'recurse' => FALSE])) {
$source_file->uri = $file->uri; $file = current($files);
$source_file->timestamp = filemtime($file->uri); $source_file->uri = $file->uri;
return $source_file; $source_file->timestamp = filemtime($file->uri);
return $source_file;
}
} }
} }
return FALSE; return FALSE;

View File

@ -24,7 +24,7 @@ function media_install() {
$file_system = \Drupal::service('file_system'); $file_system = \Drupal::service('file_system');
$file_system->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS); $file_system->prepareDirectory($destination, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
$files = file_scan_directory($source, '/.*\.(svg|png|jpg|jpeg|gif)$/'); $files = $file_system->scanDirectory($source, '/.*\.(svg|png|jpg|jpeg|gif)$/');
foreach ($files as $file) { foreach ($files as $file) {
// When reinstalling the media module we don't want to copy the icons when // When reinstalling the media module we don't want to copy the icons when
// they already exist. The icons could be replaced (by a contrib module or // they already exist. The icons could be replaced (by a contrib module or

View File

@ -1,16 +1,21 @@
<?php <?php
namespace Drupal\Tests\system\Unit\Installer; namespace Drupal\Tests\system\Kernel\Installer;
use Drupal\Core\StringTranslation\Translator\FileTranslation; use Drupal\Core\StringTranslation\Translator\FileTranslation;
use Drupal\Tests\UnitTestCase; use Drupal\KernelTests\KernelTestBase;
/** /**
* Tests for installer language support. * Tests for installer language support.
* *
* @group Installer * @group Installer
*/ */
class InstallTranslationFilePatternTest extends UnitTestCase { class InstallTranslationFilePatternTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['system'];
/** /**
* @var \Drupal\Core\StringTranslation\Translator\FileTranslation * @var \Drupal\Core\StringTranslation\Translator\FileTranslation
@ -27,7 +32,7 @@ class InstallTranslationFilePatternTest extends UnitTestCase {
*/ */
protected function setup() { protected function setup() {
parent::setUp(); parent::setUp();
$this->fileTranslation = new FileTranslation('filename'); $this->fileTranslation = new FileTranslation('filename', $this->container->get('file_system'));
$method = new \ReflectionMethod('\Drupal\Core\StringTranslation\Translator\FileTranslation', 'getTranslationFilesPattern'); $method = new \ReflectionMethod('\Drupal\Core\StringTranslation\Translator\FileTranslation', 'getTranslationFilesPattern');
$method->setAccessible(TRUE); $method->setAccessible(TRUE);
$this->filePatternMethod = $method; $this->filePatternMethod = $method;

View File

@ -690,7 +690,9 @@ function update_verify_update_archive($project, $archive_file, $directory) {
// functionality). // functionality).
$compatible_project = FALSE; $compatible_project = FALSE;
$incompatible = []; $incompatible = [];
$files = file_scan_directory("$directory/$project", '/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info.yml$/', ['key' => 'name', 'min_depth' => 0]); /** @var \Drupal\Core\File\FileSystemInterface $file_system */
$file_system = \Drupal::service('file_system');
$files = $file_system->scanDirectory("$directory/$project", '/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info.yml$/', ['key' => 'name', 'min_depth' => 0]);
foreach ($files as $file) { foreach ($files as $file) {
// Get the .info.yml file for the module or theme this file belongs to. // Get the .info.yml file for the module or theme this file belongs to.
$info = \Drupal::service('info_parser')->parse($file->uri); $info = \Drupal::service('info_parser')->parse($file->uri);
@ -705,8 +707,6 @@ function update_verify_update_archive($project, $archive_file, $directory) {
} }
} }
/** @var \Drupal\Core\File\FileSystemInterface $file_system */
$file_system = \Drupal::service('file_system');
if (empty($files)) { if (empty($files)) {
$errors[] = t('%archive_file does not contain any .info.yml files.', ['%archive_file' => $file_system->basename($archive_file)]); $errors[] = t('%archive_file does not contain any .info.yml files.', ['%archive_file' => $file_system->basename($archive_file)]);
} }
@ -805,7 +805,7 @@ function update_clear_update_disk_cache() {
// Search for files and directories in base folder only without recursion. // Search for files and directories in base folder only without recursion.
foreach ($directories as $directory) { foreach ($directories as $directory) {
file_scan_directory($directory, '/.*/', ['callback' => 'update_delete_file_if_stale', 'recurse' => FALSE]); \Drupal::service('file_system')->scanDirectory($directory, '/.*/', ['callback' => 'update_delete_file_if_stale', 'recurse' => FALSE]);
} }
} }

View File

@ -1084,7 +1084,7 @@ function simpletest_script_get_test_list() {
else { else {
$directory = DRUPAL_ROOT . "/" . $args['directory']; $directory = DRUPAL_ROOT . "/" . $args['directory'];
} }
foreach (file_scan_directory($directory, '/\.php$/', $ignore) as $file) { foreach (\Drupal::service('file_system')->scanDirectory($directory, '/\.php$/', $ignore) as $file) {
// '/Tests/' can be contained anywhere in the file's path (there can be // '/Tests/' can be contained anywhere in the file's path (there can be
// sub-directories below /Tests), but must be contained literally. // sub-directories below /Tests), but must be contained literally.
// Case-insensitive to match all Simpletest and PHPUnit tests: // Case-insensitive to match all Simpletest and PHPUnit tests:

View File

@ -259,4 +259,11 @@ class FileSystemDeprecationTest extends KernelTestBase {
$this->assertNotEmpty(drupal_realpath('public://')); $this->assertNotEmpty(drupal_realpath('public://'));
} }
/**
* @expectedDeprecation file_scan_directory() is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\File\FileSystemInterface::scanDirectory() instead. See https://www.drupal.org/node/3038437
*/
public function testDeprecatedScanDirectory() {
$this->assertNotNull(file_scan_directory('temporary://', '/^NONEXISTINGFILENAME/'));
}
} }

View File

@ -3,8 +3,9 @@
namespace Drupal\KernelTests\Core\File; namespace Drupal\KernelTests\Core\File;
/** /**
* Tests the file_scan_directory() function. * Tests \Drupal\Core\File\FileSystemInterface::scanDirectory().
* *
* @coversDefaultClass \Drupal\Core\File\FileSystem
* @group File * @group File
*/ */
class RemoteFileScanDirectoryTest extends ScanDirectoryTest { class RemoteFileScanDirectoryTest extends ScanDirectoryTest {

View File

@ -3,8 +3,9 @@
namespace Drupal\KernelTests\Core\File; namespace Drupal\KernelTests\Core\File;
/** /**
* Tests the file_scan_directory() function. * Tests \Drupal\Core\File\FileSystem::scanDirectory.
* *
* @coversDefaultClass \Drupal\Core\File\FileSystem
* @group File * @group File
*/ */
class ScanDirectoryTest extends FileTestBase { class ScanDirectoryTest extends FileTestBase {
@ -21,6 +22,16 @@ class ScanDirectoryTest extends FileTestBase {
*/ */
protected $path; protected $path;
/**
* The file system.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* {@inheritdoc}
*/
protected function setUp() { protected function setUp() {
parent::setUp(); parent::setUp();
// Hardcode the location of the simpletest files as it is already known // Hardcode the location of the simpletest files as it is already known
@ -28,15 +39,18 @@ class ScanDirectoryTest extends FileTestBase {
// location from drupal_get_filename() in a cached way. // location from drupal_get_filename() in a cached way.
// @todo Remove as part of https://www.drupal.org/node/2186491 // @todo Remove as part of https://www.drupal.org/node/2186491
$this->path = 'core/modules/simpletest/files'; $this->path = 'core/modules/simpletest/files';
$this->fileSystem = $this->container->get('file_system');
} }
/** /**
* Check the format of the returned values. * Check the format of the returned values.
*
* @covers ::scanDirectory
*/ */
public function testReturn() { public function testReturn() {
// Grab a listing of all the JavaScript files and check that they're // Grab a listing of all the JavaScript files and check that they're
// passed to the callback. // passed to the callback.
$all_files = file_scan_directory($this->path, '/^javascript-/'); $all_files = $this->fileSystem->scanDirectory($this->path, '/^javascript-/');
ksort($all_files); ksort($all_files);
$this->assertEqual(2, count($all_files), 'Found two, expected javascript files.'); $this->assertEqual(2, count($all_files), 'Found two, expected javascript files.');
@ -57,11 +71,13 @@ class ScanDirectoryTest extends FileTestBase {
/** /**
* Check that the callback function is called correctly. * Check that the callback function is called correctly.
*
* @covers ::scanDirectory
*/ */
public function testOptionCallback() { public function testOptionCallback() {
// When nothing is matched nothing should be passed to the callback. // When nothing is matched nothing should be passed to the callback.
$all_files = file_scan_directory($this->path, '/^NONEXISTINGFILENAME/', ['callback' => 'file_test_file_scan_callback']); $all_files = $this->fileSystem->scanDirectory($this->path, '/^NONEXISTINGFILENAME/', ['callback' => 'file_test_file_scan_callback']);
$this->assertEqual(0, count($all_files), 'No files were found.'); $this->assertEqual(0, count($all_files), 'No files were found.');
$results = file_test_file_scan_callback(); $results = file_test_file_scan_callback();
file_test_file_scan_callback_reset(); file_test_file_scan_callback_reset();
@ -69,7 +85,7 @@ class ScanDirectoryTest extends FileTestBase {
// Grab a listing of all the JavaScript files and check that they're // Grab a listing of all the JavaScript files and check that they're
// passed to the callback. // passed to the callback.
$all_files = file_scan_directory($this->path, '/^javascript-/', ['callback' => 'file_test_file_scan_callback']); $all_files = $this->fileSystem->scanDirectory($this->path, '/^javascript-/', ['callback' => 'file_test_file_scan_callback']);
$this->assertEqual(2, count($all_files), 'Found two, expected javascript files.'); $this->assertEqual(2, count($all_files), 'Found two, expected javascript files.');
$results = file_test_file_scan_callback(); $results = file_test_file_scan_callback();
file_test_file_scan_callback_reset(); file_test_file_scan_callback_reset();
@ -78,87 +94,97 @@ class ScanDirectoryTest extends FileTestBase {
/** /**
* Check that the no-mask parameter is honored. * Check that the no-mask parameter is honored.
*
* @covers ::scanDirectory
*/ */
public function testOptionNoMask() { public function testOptionNoMask() {
// Grab a listing of all the JavaScript files. // Grab a listing of all the JavaScript files.
$all_files = file_scan_directory($this->path, '/^javascript-/'); $all_files = $this->fileSystem->scanDirectory($this->path, '/^javascript-/');
$this->assertEqual(2, count($all_files), 'Found two, expected javascript files.'); $this->assertEqual(2, count($all_files), 'Found two, expected javascript files.');
// Now use the nomask parameter to filter out the .script file. // Now use the nomask parameter to filter out the .script file.
$filtered_files = file_scan_directory($this->path, '/^javascript-/', ['nomask' => '/.script$/']); $filtered_files = $this->fileSystem->scanDirectory($this->path, '/^javascript-/', ['nomask' => '/.script$/']);
$this->assertEqual(1, count($filtered_files), 'Filtered correctly.'); $this->assertEqual(1, count($filtered_files), 'Filtered correctly.');
} }
/** /**
* Check that key parameter sets the return value's key. * Check that key parameter sets the return value's key.
*
* @covers ::scanDirectory
*/ */
public function testOptionKey() { public function testOptionKey() {
// "filename", for the path starting with $dir. // "filename", for the path starting with $dir.
$expected = [$this->path . '/javascript-1.txt', $this->path . '/javascript-2.script']; $expected = [$this->path . '/javascript-1.txt', $this->path . '/javascript-2.script'];
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', ['key' => 'filepath'])); $actual = array_keys($this->fileSystem->scanDirectory($this->path, '/^javascript-/', ['key' => 'filepath']));
sort($actual); sort($actual);
$this->assertEqual($expected, $actual, 'Returned the correct values for the filename key.'); $this->assertEqual($expected, $actual, 'Returned the correct values for the filename key.');
// "basename", for the basename of the file. // "basename", for the basename of the file.
$expected = ['javascript-1.txt', 'javascript-2.script']; $expected = ['javascript-1.txt', 'javascript-2.script'];
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', ['key' => 'filename'])); $actual = array_keys($this->fileSystem->scanDirectory($this->path, '/^javascript-/', ['key' => 'filename']));
sort($actual); sort($actual);
$this->assertEqual($expected, $actual, 'Returned the correct values for the basename key.'); $this->assertEqual($expected, $actual, 'Returned the correct values for the basename key.');
// "name" for the name of the file without an extension. // "name" for the name of the file without an extension.
$expected = ['javascript-1', 'javascript-2']; $expected = ['javascript-1', 'javascript-2'];
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', ['key' => 'name'])); $actual = array_keys($this->fileSystem->scanDirectory($this->path, '/^javascript-/', ['key' => 'name']));
sort($actual); sort($actual);
$this->assertEqual($expected, $actual, 'Returned the correct values for the name key.'); $this->assertEqual($expected, $actual, 'Returned the correct values for the name key.');
// Invalid option that should default back to "filename". // Invalid option that should default back to "filename".
$expected = [$this->path . '/javascript-1.txt', $this->path . '/javascript-2.script']; $expected = [$this->path . '/javascript-1.txt', $this->path . '/javascript-2.script'];
$actual = array_keys(file_scan_directory($this->path, '/^javascript-/', ['key' => 'INVALID'])); $actual = array_keys($this->fileSystem->scanDirectory($this->path, '/^javascript-/', ['key' => 'INVALID']));
sort($actual); sort($actual);
$this->assertEqual($expected, $actual, 'An invalid key defaulted back to the default.'); $this->assertEqual($expected, $actual, 'An invalid key defaulted back to the default.');
} }
/** /**
* Check that the recurse option descends into subdirectories. * Check that the recurse option descends into subdirectories.
*
* @covers ::scanDirectory
*/ */
public function testOptionRecurse() { public function testOptionRecurse() {
$files = file_scan_directory($this->path . '/..', '/^javascript-/', ['recurse' => FALSE]); $files = $this->fileSystem->scanDirectory($this->path . '/..', '/^javascript-/', ['recurse' => FALSE]);
$this->assertTrue(empty($files), "Without recursion couldn't find javascript files."); $this->assertTrue(empty($files), "Without recursion couldn't find javascript files.");
$files = file_scan_directory($this->path . '/..', '/^javascript-/', ['recurse' => TRUE]); $files = $this->fileSystem->scanDirectory($this->path . '/..', '/^javascript-/', ['recurse' => TRUE]);
$this->assertEqual(2, count($files), 'With recursion we found the expected javascript files.'); $this->assertEqual(2, count($files), 'With recursion we found the expected javascript files.');
} }
/** /**
* Check that the min_depth options lets us ignore files in the starting * Check that the min_depth options lets us ignore files in the starting
* directory. * directory.
*
* @covers ::scanDirectory
*/ */
public function testOptionMinDepth() { public function testOptionMinDepth() {
$files = file_scan_directory($this->path, '/^javascript-/', ['min_depth' => 0]); $files = $this->fileSystem->scanDirectory($this->path, '/^javascript-/', ['min_depth' => 0]);
$this->assertEqual(2, count($files), 'No minimum-depth gets files in current directory.'); $this->assertEqual(2, count($files), 'No minimum-depth gets files in current directory.');
$files = file_scan_directory($this->path, '/^javascript-/', ['min_depth' => 1]); $files = $this->fileSystem->scanDirectory($this->path, '/^javascript-/', ['min_depth' => 1]);
$this->assertTrue(empty($files), 'Minimum-depth of 1 successfully excludes files from current directory.'); $this->assertTrue(empty($files), 'Minimum-depth of 1 successfully excludes files from current directory.');
} }
/** /**
* Tests file_scan_directory() obeys 'file_scan_ignore_directories' setting. * Tests ::scanDirectory obeys 'file_scan_ignore_directories' setting.
*
* @covers ::scanDirectory
*/ */
public function testIgnoreDirectories() { public function testIgnoreDirectories() {
$files = file_scan_directory('core/modules/system/tests/fixtures/IgnoreDirectories', '/\.txt$/'); $files = $this->fileSystem->scanDirectory('core/modules/system/tests/fixtures/IgnoreDirectories', '/\.txt$/');
$this->assertCount(2, $files, '2 text files found when not ignoring directories.'); $this->assertCount(2, $files, '2 text files found when not ignoring directories.');
$this->setSetting('file_scan_ignore_directories', ['frontend_framework']); $this->setSetting('file_scan_ignore_directories', ['frontend_framework']);
$files = file_scan_directory('core/modules/system/tests/fixtures/IgnoreDirectories', '/\.txt$/'); $files = $this->fileSystem->scanDirectory('core/modules/system/tests/fixtures/IgnoreDirectories', '/\.txt$/');
$this->assertCount(1, $files, '1 text files found when ignoring directories called "frontend_framework".'); $this->assertCount(1, $files, '1 text files found when ignoring directories called "frontend_framework".');
// Ensure that the directories in file_scan_ignore_directories are escaped // Ensure that the directories in file_scan_ignore_directories are escaped
// using preg_quote. // using preg_quote.
$this->setSetting('file_scan_ignore_directories', ['frontend.*']); $this->setSetting('file_scan_ignore_directories', ['frontend.*']);
$files = file_scan_directory('core/modules/system/tests/fixtures/IgnoreDirectories', '/\.txt$/'); $files = $this->fileSystem->scanDirectory('core/modules/system/tests/fixtures/IgnoreDirectories', '/\.txt$/');
$this->assertCount(2, $files, '2 text files found when ignoring a directory that is not there.'); $this->assertCount(2, $files, '2 text files found when ignoring a directory that is not there.');
$files = file_scan_directory('core/modules/system/tests/fixtures/IgnoreDirectories', '/\.txt$/', ['nomask' => '/^something_thing_else$/']); $files = $this->fileSystem->scanDirectory('core/modules/system/tests/fixtures/IgnoreDirectories', '/\.txt$/', ['nomask' => '/^something_thing_else$/']);
$this->assertCount(2, $files, '2 text files found when an "nomask" option is passed in.'); $this->assertCount(2, $files, '2 text files found when an "nomask" option is passed in.');
} }

View File

@ -28,7 +28,7 @@ class InstallerLanguageTest extends KernelTestBase {
// Hardcode the simpletest module location as we don't yet know where it is. // Hardcode the simpletest module location as we don't yet know where it is.
// @todo Remove as part of https://www.drupal.org/node/2186491 // @todo Remove as part of https://www.drupal.org/node/2186491
$file_translation = new FileTranslation('core/modules/simpletest/files/translations'); $file_translation = new FileTranslation('core/modules/simpletest/files/translations', $this->container->get('file_system'));
foreach ($expected_translation_files as $langcode => $files_expected) { foreach ($expected_translation_files as $langcode => $files_expected) {
$files_found = $file_translation->findTranslationFiles($langcode); $files_found = $file_translation->findTranslationFiles($langcode);
$this->assertTrue(count($files_found) == count($files_expected), new FormattableMarkup('@count installer languages found.', ['@count' => count($files_expected)])); $this->assertTrue(count($files_found) == count($files_expected), new FormattableMarkup('@count installer languages found.', ['@count' => count($files_expected)]));

View File

@ -52,6 +52,8 @@ trait TestFileCreationTrait {
* List of files in public:// that match the filter(s). * List of files in public:// that match the filter(s).
*/ */
protected function getTestFiles($type, $size = NULL) { protected function getTestFiles($type, $size = NULL) {
/** @var \Drupal\Core\File\FileSystemInterface $file_system */
$file_system = \Drupal::service('file_system');
if (empty($this->generatedTestFiles)) { if (empty($this->generatedTestFiles)) {
// Generate binary test files. // Generate binary test files.
$lines = [64, 1024]; $lines = [64, 1024];
@ -69,9 +71,9 @@ trait TestFileCreationTrait {
// Copy other test files from simpletest. // Copy other test files from simpletest.
$original = drupal_get_path('module', 'simpletest') . '/files'; $original = drupal_get_path('module', 'simpletest') . '/files';
$files = file_scan_directory($original, '/(html|image|javascript|php|sql)-.*/'); $files = $file_system->scanDirectory($original, '/(html|image|javascript|php|sql)-.*/');
foreach ($files as $file) { foreach ($files as $file) {
\Drupal::service('file_system')->copy($file->uri, PublicStream::basePath()); $file_system->copy($file->uri, PublicStream::basePath());
} }
$this->generatedTestFiles = TRUE; $this->generatedTestFiles = TRUE;
@ -80,7 +82,7 @@ trait TestFileCreationTrait {
$files = []; $files = [];
// Make sure type is valid. // Make sure type is valid.
if (in_array($type, ['binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'])) { if (in_array($type, ['binary', 'html', 'image', 'javascript', 'php', 'sql', 'text'])) {
$files = file_scan_directory('public://', '/' . $type . '\-.*/'); $files = $file_system->scanDirectory('public://', '/' . $type . '\-.*/');
// If size is set then remove any files that are not of that size. // If size is set then remove any files that are not of that size.
if ($size !== NULL) { if ($size !== NULL) {

View File

@ -703,7 +703,7 @@ $settings['container_yamls'][] = $app_root . '/' . $site_path . '/services.yml';
* with common frontend tools and recursive scanning of directories looking for * with common frontend tools and recursive scanning of directories looking for
* extensions. * extensions.
* *
* @see file_scan_directory() * @see \Drupal\Core\File\FileSystemInterface::scanDirectory()
* @see \Drupal\Core\Extension\ExtensionDiscovery::scanDirectory() * @see \Drupal\Core\Extension\ExtensionDiscovery::scanDirectory()
*/ */
$settings['file_scan_ignore_directories'] = [ $settings['file_scan_ignore_directories'] = [