summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathaniel Catchpole2016-07-20 21:06:16 (GMT)
committerNathaniel Catchpole2016-07-20 21:06:16 (GMT)
commit68a543a315925bcf2c83faa0883914fef68875ff (patch)
tree506238f6b90dd63b014771df9800ddab1db06dcc
parent066437b85d60c9047fc002246dba16ca1ff004f7 (diff)
Issue #2329453 by alexpott, joelpittet, hussainweb, kaidjohnson, andriyun, JohnAlbin, mirie, steveoliver, quicksketch, geerlingguy, RobLoach, jenlampton, SebCorbin, pablo.guerino, afoster, markcarver, danbohea, andypost, catch, donquixote, marcvangend, David_Rothstein, LewisNyman, neclimdul: Ignore front end vendor folders to improve directory search performance
-rw-r--r--core/includes/file.inc20
-rw-r--r--core/lib/Drupal/Core/Extension/Discovery/RecursiveExtensionFilterIterator.php16
-rw-r--r--core/lib/Drupal/Core/Extension/ExtensionDiscovery.php8
-rw-r--r--core/modules/system/src/Tests/Common/SystemListingTest.php22
-rw-r--r--core/modules/system/tests/fixtures/IgnoreDirectories/a.txt0
-rw-r--r--core/modules/system/tests/fixtures/IgnoreDirectories/frontend_framework/b.txt0
-rw-r--r--core/tests/Drupal/KernelTests/Core/File/ScanDirectoryTest.php21
-rw-r--r--sites/default/default.settings.php15
8 files changed, 99 insertions, 3 deletions
diff --git a/core/includes/file.inc b/core/includes/file.inc
index 017a3d6..fe4bf54 100644
--- a/core/includes/file.inc
+++ b/core/includes/file.inc
@@ -10,6 +10,7 @@ use Drupal\Component\Utility\UrlHelper;
use Drupal\Component\PhpStorage\FileStorage;
use Drupal\Component\Utility\Bytes;
use Drupal\Core\File\FileSystem;
+use Drupal\Core\Site\Settings;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\Core\StreamWrapper\PrivateStream;
@@ -978,7 +979,7 @@ function file_unmanaged_save_data($data, $destination = NULL, $replace = FILE_EX
* @param $options
* An associative array of additional options, with the following elements:
* - 'nomask': The preg_match() regular expression for files to be excluded.
- * There is no default.
+ * 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
@@ -1011,6 +1012,18 @@ function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
$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'], array('uri', 'filename', 'name')) ? $options['key'] : 'uri';
$files = array();
// Avoid warnings when opendir does not have the permissions to open a
@@ -1019,7 +1032,10 @@ function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
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))) {
+ 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";
}
diff --git a/core/lib/Drupal/Core/Extension/Discovery/RecursiveExtensionFilterIterator.php b/core/lib/Drupal/Core/Extension/Discovery/RecursiveExtensionFilterIterator.php
index 554ff28..5c3ebc3 100644
--- a/core/lib/Drupal/Core/Extension/Discovery/RecursiveExtensionFilterIterator.php
+++ b/core/lib/Drupal/Core/Extension/Discovery/RecursiveExtensionFilterIterator.php
@@ -82,6 +82,20 @@ class RecursiveExtensionFilterIterator extends \RecursiveFilterIterator {
protected $acceptTests = FALSE;
/**
+ * Construct a RecursiveExtensionFilterIterator.
+ *
+ * @param \RecursiveIterator $iterator
+ * The iterator to filter.
+ * @param array $blacklist
+ * (optional) Add to the blacklist of directories that should be filtered
+ * out during the iteration.
+ */
+ public function __construct(\RecursiveIterator $iterator, array $blacklist = []) {
+ parent::__construct($iterator);
+ $this->blacklist = array_merge($this->blacklist, $blacklist);
+ }
+
+ /**
* Controls whether test directories will be scanned.
*
* @param bool $flag
@@ -102,6 +116,8 @@ class RecursiveExtensionFilterIterator extends \RecursiveFilterIterator {
*/
public function getChildren() {
$filter = parent::getChildren();
+ // Pass on the blacklist.
+ $filter->blacklist = $this->blacklist;
// Pass the $acceptTests flag forward to child iterators.
$filter->acceptTests($this->acceptTests);
return $filter;
diff --git a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php
index 337629b..0d9283c 100644
--- a/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php
+++ b/core/lib/Drupal/Core/Extension/ExtensionDiscovery.php
@@ -427,11 +427,17 @@ class ExtensionDiscovery {
$flags |= \FilesystemIterator::CURRENT_AS_SELF;
$directory_iterator = new \RecursiveDirectoryIterator($absolute_dir, $flags);
+ // 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.
+ $ignore_directories = Settings::get('file_scan_ignore_directories', []);
+
// Filter the recursive scan to discover extensions only.
// Important: Without a RecursiveFilterIterator, RecursiveDirectoryIterator
// would recurse into the entire filesystem directory tree without any kind
// of limitations.
- $filter = new RecursiveExtensionFilterIterator($directory_iterator);
+ $filter = new RecursiveExtensionFilterIterator($directory_iterator, $ignore_directories);
$filter->acceptTests($include_tests);
// The actual recursive filesystem scan is only invoked by instantiating the
diff --git a/core/modules/system/src/Tests/Common/SystemListingTest.php b/core/modules/system/src/Tests/Common/SystemListingTest.php
index cf29be4..9d4e2fc 100644
--- a/core/modules/system/src/Tests/Common/SystemListingTest.php
+++ b/core/modules/system/src/Tests/Common/SystemListingTest.php
@@ -51,4 +51,26 @@ class SystemListingTest extends KernelTestBase {
}
}
+ /**
+ * Tests that directories matching file_scan_ignore_directories are ignored
+ */
+ public function testFileScanIgnoreDirectory() {
+ $listing = new ExtensionDiscovery(\Drupal::root(), FALSE);
+ $listing->setProfileDirectories(array('core/profiles/testing'));
+ $files = $listing->scan('module');
+ $this->assertArrayHasKey('drupal_system_listing_compatible_test', $files);
+
+ // Reset the static to force a rescan of the directories.
+ $reflected_class = new \ReflectionClass(ExtensionDiscovery::class);
+ $reflected_property = $reflected_class->getProperty('files');
+ $reflected_property->setAccessible(TRUE);
+ $reflected_property->setValue($reflected_class, []);
+
+ $this->setSetting('file_scan_ignore_directories', ['drupal_system_listing_compatible_test']);
+ $listing = new ExtensionDiscovery(\Drupal::root(), FALSE);
+ $listing->setProfileDirectories(array('core/profiles/testing'));
+ $files = $listing->scan('module');
+ $this->assertArrayNotHasKey('drupal_system_listing_compatible_test', $files);
+ }
+
}
diff --git a/core/modules/system/tests/fixtures/IgnoreDirectories/a.txt b/core/modules/system/tests/fixtures/IgnoreDirectories/a.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/core/modules/system/tests/fixtures/IgnoreDirectories/a.txt
diff --git a/core/modules/system/tests/fixtures/IgnoreDirectories/frontend_framework/b.txt b/core/modules/system/tests/fixtures/IgnoreDirectories/frontend_framework/b.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/core/modules/system/tests/fixtures/IgnoreDirectories/frontend_framework/b.txt
diff --git a/core/tests/Drupal/KernelTests/Core/File/ScanDirectoryTest.php b/core/tests/Drupal/KernelTests/Core/File/ScanDirectoryTest.php
index 2e753f6..8362c04 100644
--- a/core/tests/Drupal/KernelTests/Core/File/ScanDirectoryTest.php
+++ b/core/tests/Drupal/KernelTests/Core/File/ScanDirectoryTest.php
@@ -142,4 +142,25 @@ class ScanDirectoryTest extends FileTestBase {
$this->assertTrue(empty($files), 'Minimum-depth of 1 successfully excludes files from current directory.');
}
+ /**
+ * Tests file_scan_directory() obeys 'file_scan_ignore_directories' setting.
+ */
+ public function testIgnoreDirectories() {
+ $files = file_scan_directory('core/modules/system/tests/fixtures/IgnoreDirectories', '/\.txt$/');
+ $this->assertCount(2, $files, '2 text files found when not ignoring directories.');
+
+ $this->setSetting('file_scan_ignore_directories', ['frontend_framework']);
+ $files = file_scan_directory('core/modules/system/tests/fixtures/IgnoreDirectories', '/\.txt$/');
+ $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
+ // using preg_quote.
+ $this->setSetting('file_scan_ignore_directories', ['frontend.*']);
+ $files = file_scan_directory('core/modules/system/tests/fixtures/IgnoreDirectories', '/\.txt$/');
+ $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$/']);
+ $this->assertCount(2, $files, '2 text files found when an "nomask" option is passed in.');
+ }
+
}
diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php
index d6d130e..67f1104 100644
--- a/sites/default/default.settings.php
+++ b/sites/default/default.settings.php
@@ -716,6 +716,21 @@ $settings['container_yamls'][] = __DIR__ . '/services.yml';
*/
/**
+ * The default list of directories that will be ignored by Drupal's file API.
+ *
+ * By default ignore node_modules and bower_components folders to avoid issues
+ * with common frontend tools and recursive scanning of directories looking for
+ * extensions.
+ *
+ * @see file_scan_directory()
+ * @see \Drupal\Core\Extension\ExtensionDiscovery::scanDirectory()
+ */
+$settings['file_scan_ignore_directories'] = [
+ 'node_modules',
+ 'bower_components',
+];
+
+/**
* Load local development override configuration, if available.
*
* Use settings.local.php to override variables on secondary (staging,