diff --git a/config.xsd b/config.xsd index 46840b0a3a9..28ba0edfbc4 100644 --- a/config.xsd +++ b/config.xsd @@ -118,6 +118,7 @@ + diff --git a/src/Psalm/Config/FileFilter.php b/src/Psalm/Config/FileFilter.php index 90c7606212d..2db9bdaaee8 100644 --- a/src/Psalm/Config/FileFilter.php +++ b/src/Psalm/Config/FileFilter.php @@ -2,6 +2,7 @@ namespace Psalm\Config; +use FilesystemIterator; use Psalm\Exception\ConfigException; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; @@ -112,6 +113,7 @@ public static function loadFromArray( foreach ($config['directory'] as $directory) { $directory_path = (string) ($directory['name'] ?? ''); $ignore_type_stats = (bool) ($directory['ignoreTypeStats'] ?? false); + $resolve_symlinks = (bool) ($directory['resolveSymlinks'] ?? false); $declare_strict_types = (bool) ($directory['useStrictTypes'] ?? false); if ($directory_path[0] === '/' && DIRECTORY_SEPARATOR === '/') { @@ -182,27 +184,33 @@ public static function loadFromArray( ); } - /** @var RecursiveDirectoryIterator */ - $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory_path)); - $iterator->rewind(); + if ($resolve_symlinks) { + /** @var RecursiveDirectoryIterator */ + $iterator = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($directory_path, FilesystemIterator::SKIP_DOTS) + ); + $iterator->rewind(); - while ($iterator->valid()) { - if (!$iterator->isDot() && $iterator->isLink()) { - $linked_path = readlink($iterator->getPathname()); + while ($iterator->valid()) { + if ($iterator->isLink()) { + $linked_path = readlink($iterator->getPathname()); - if (stripos($linked_path, $directory_path) !== 0) { - if ($ignore_type_stats && $filter instanceof ProjectFileFilter) { - $filter->ignore_type_stats[$directory_path] = true; - } + if (stripos($linked_path, $directory_path) !== 0) { + if ($ignore_type_stats && $filter instanceof ProjectFileFilter) { + $filter->ignore_type_stats[$directory_path] = true; + } - if ($declare_strict_types && $filter instanceof ProjectFileFilter) { - $filter->declare_strict_types[$directory_path] = true; - } + if ($declare_strict_types && $filter instanceof ProjectFileFilter) { + $filter->declare_strict_types[$directory_path] = true; + } - if (is_dir($linked_path)) { - $filter->addDirectory($linked_path); + if (is_dir($linked_path)) { + $filter->addDirectory($linked_path); + } } } + + $iterator->next(); } $iterator->next(); @@ -346,6 +354,7 @@ public static function loadFromXMLElement( $config['directory'][] = [ 'name' => (string) $directory['name'], 'ignoreTypeStats' => strtolower((string) ($directory['ignoreTypeStats'] ?? '')) === 'true', + 'resolveSymlinks' => strtolower((string) ($directory['resolveSymlinks'] ?? '')) === 'true', 'useStrictTypes' => strtolower((string) ($directory['useStrictTypes'] ?? '')) === 'true', ]; } diff --git a/tests/Config/ConfigTest.php b/tests/Config/ConfigTest.php index d8b352bc447..b6be450a72c 100644 --- a/tests/Config/ConfigTest.php +++ b/tests/Config/ConfigTest.php @@ -188,7 +188,7 @@ public function testIgnoreSymlinkedProjectDirectory(): void - + '