diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 18e1718..84def90 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,6 +6,9 @@ on:
name: "CI"
+env:
+ COMPOSER_ROOT_VERSION: "4.0-dev"
+
permissions:
contents: read
@@ -22,7 +25,7 @@ jobs:
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
- php-version: 8.1
+ php-version: 8.2
coverage: none
- name: Run PHP-CS-Fixer
@@ -40,7 +43,7 @@ jobs:
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
- php-version: 8.1
+ php-version: 8.2
coverage: none
- name: Install dependencies with Composer
@@ -72,13 +75,6 @@ jobs:
php-version: "${{ matrix.php-version }}"
coverage: "pcov"
- - name: "Cache dependencies installed with Composer"
- uses: "actions/cache@v2"
- with:
- path: "~/.composer/cache"
- key: "php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.json') }}"
- restore-keys: "php${{ matrix.php-version }}-composer-${{ matrix.dependencies }}-"
-
- name: "Install dependencies with Composer"
run: "./tools/composer update --no-ansi --no-interaction --no-progress"
diff --git a/.phive/phars.xml b/.phive/phars.xml
index c3faedd..b13bf49 100644
--- a/.phive/phars.xml
+++ b/.phive/phars.xml
@@ -1,6 +1,6 @@
-
-
-
+
+
+
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
index 79f0c62..1a225c8 100644
--- a/.php-cs-fixer.dist.php
+++ b/.php-cs-fixer.dist.php
@@ -33,6 +33,7 @@
'blank_line_before_statement' => [
'statements' => [
'break',
+ 'case',
'continue',
'declare',
'default',
@@ -44,6 +45,7 @@
'if',
'include',
'include_once',
+ 'phpdoc',
'require',
'require_once',
'return',
@@ -52,15 +54,13 @@
'try',
'while',
'yield',
+ 'yield_from',
],
],
- 'braces' => [
- 'position_after_anonymous_constructs' => 'next',
- ],
'cast_spaces' => true,
'class_attributes_separation' => [
'elements' => [
- 'const' => 'one',
+ 'const' => 'none',
'method' => 'one',
'property' => 'only_if_meta'
]
@@ -73,7 +73,14 @@
'compact_nullable_typehint' => true,
'concat_space' => ['spacing' => 'one'],
'constant_case' => true,
+ 'control_structure_braces' => true,
+ 'control_structure_continuation_position' => true,
+ 'curly_braces_position' => [
+ 'anonymous_functions_opening_brace' => 'next_line_unless_newline_at_signature_end',
+ 'anonymous_classes_opening_brace' => 'next_line_unless_newline_at_signature_end',
+ ],
'declare_equal_normalize' => ['space' => 'none'],
+ 'declare_parentheses' => true,
'declare_strict_types' => true,
'dir_constant' => true,
'echo_tag_syntax' => true,
@@ -147,6 +154,7 @@
'no_leading_import_slash' => true,
'no_leading_namespace_whitespace' => true,
'no_mixed_echo_print' => ['use' => 'print'],
+ 'no_multiple_statements_per_line' => true,
'no_multiline_whitespace_around_double_arrow' => true,
'no_null_property_initialization' => true,
'no_php4_constructor' => true,
@@ -272,11 +280,12 @@
'single_import_per_statement' => true,
'single_line_after_imports' => true,
'single_quote' => true,
- 'single_space_after_construct' => true,
+ 'single_space_around_construct' => true,
'single_trait_insert_per_statement' => true,
'space_after_semicolon' => true,
'standardize_increment' => true,
'standardize_not_equals' => true,
+ 'statement_indentation' => true,
'static_lambda' => true,
'strict_param' => true,
'string_line_ending' => true,
diff --git a/.psalm/baseline.xml b/.psalm/baseline.xml
index fc0c624..09f04de 100644
--- a/.psalm/baseline.xml
+++ b/.psalm/baseline.xml
@@ -1,2 +1,8 @@
-
+
+
+
+ ExcludeIterator
+
+
+
diff --git a/.psalm/config.xml b/.psalm/config.xml
index 518ab43..343cc8e 100644
--- a/.psalm/config.xml
+++ b/.psalm/config.xml
@@ -6,6 +6,7 @@
resolveFromConfigFile="false"
errorBaseline=".psalm/baseline.xml"
findUnusedBaselineEntry="true"
+ findUnusedCode="false"
>
diff --git a/ChangeLog.md b/ChangeLog.md
index bf349d7..2d6d9d5 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -2,6 +2,18 @@
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
+## [4.0.2] - 2023-05-07
+
+### Fixed
+
+* [#80](https://github.com/sebastianbergmann/php-file-iterator/pull/80): Ignore unresolvable symbolic link
+
+## [4.0.1] - 2023-02-10
+
+### Fixed
+
+* [#67](https://github.com/sebastianbergmann/php-file-iterator/issues/61): Excluded directories are traversed unnecessarily
+
## [4.0.0] - 2023-02-03
### Removed
@@ -132,6 +144,8 @@ No changes
* [#23](https://github.com/sebastianbergmann/php-file-iterator/pull/23): Added support for wildcards (glob) in exclude
+[4.0.2]: https://github.com/sebastianbergmann/php-file-iterator/compare/4.0.1...4.0.2
+[4.0.1]: https://github.com/sebastianbergmann/php-file-iterator/compare/4.0.0...4.0.1
[4.0.0]: https://github.com/sebastianbergmann/php-file-iterator/compare/3.0.6...4.0.0
[3.0.6]: https://github.com/sebastianbergmann/php-file-iterator/compare/3.0.5...3.0.6
[3.0.5]: https://github.com/sebastianbergmann/php-file-iterator/compare/3.0.4...3.0.5
diff --git a/SECURITY.md b/SECURITY.md
index d4ee967..d88ff00 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -1,9 +1,30 @@
# Security Policy
-This library is intended to be used in development environments only. For instance, it is used by the testing framework PHPUnit. There is no reason why this library should be installed on a webserver.
+If you believe you have found a security vulnerability in the library that is developed in this repository, please report it to us through coordinated disclosure.
-**If you upload this library to a webserver then your deployment process is broken. On a more general note, if your `vendor` directory is publicly accessible on your webserver then your deployment process is also broken.**
+**Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.**
-## Security Contact Information
+Instead, please email `sebastian@phpunit.de`.
+
+Please include as much of the information listed below as you can to help us better understand and resolve the issue:
+
+* The type of issue
+* Full paths of source file(s) related to the manifestation of the issue
+* The location of the affected source code (tag/branch/commit or direct URL)
+* Any special configuration required to reproduce the issue
+* Step-by-step instructions to reproduce the issue
+* Proof-of-concept or exploit code (if possible)
+* Impact of the issue, including how an attacker might exploit the issue
+
+This information will help us triage your report more quickly.
+
+## Web Context
+
+The library that is developed in this repository was either extracted from [PHPUnit](https://github.com/sebastianbergmann/phpunit) or developed specifically as a dependency for PHPUnit.
+
+The library is developed with a focus on development environments and the command-line. No specific testing or hardening with regard to using the library in an HTTP or web context or with untrusted input data is performed. The library might also contain functionality that intentionally exposes internal application data for debugging purposes.
+
+If the library is used in a web application, the application developer is responsible for filtering inputs or escaping outputs as necessary and for verifying that the used functionality is safe for use within the intended context.
+
+Vulnerabilities specific to the use outside a development context will be fixed as applicable, provided that the fix does not have an averse effect on the primary use case for development purposes.
-After the above, if you still would like to report a security vulnerability, please email `sebastian@phpunit.de`.
diff --git a/composer.json b/composer.json
index e2bef2a..3f0089c 100644
--- a/composer.json
+++ b/composer.json
@@ -16,7 +16,8 @@
}
],
"support": {
- "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues"
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy"
},
"config": {
"platform": {
diff --git a/phpunit.xml b/phpunit.xml
index ec50a87..c567f14 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,6 +1,6 @@
-
+
- src
+ src
-
+
diff --git a/src/ExcludeIterator.php b/src/ExcludeIterator.php
new file mode 100644
index 0000000..a928b4b
--- /dev/null
+++ b/src/ExcludeIterator.php
@@ -0,0 +1,80 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+namespace SebastianBergmann\FileIterator;
+
+use function assert;
+use function str_starts_with;
+use RecursiveDirectoryIterator;
+use RecursiveFilterIterator;
+use SplFileInfo;
+
+/**
+ * @internal This class is not covered by the backward compatibility promise for phpunit/php-file-iterator
+ */
+final class ExcludeIterator extends RecursiveFilterIterator
+{
+ /**
+ * @psalm-var list
+ */
+ private array $exclude;
+
+ /**
+ * @psalm-param list $exclude
+ */
+ public function __construct(RecursiveDirectoryIterator $iterator, array $exclude)
+ {
+ parent::__construct($iterator);
+
+ $this->exclude = $exclude;
+ }
+
+ public function accept(): bool
+ {
+ $current = $this->current();
+
+ assert($current instanceof SplFileInfo);
+
+ $path = $current->getRealPath();
+
+ if ($path === false) {
+ return false;
+ }
+
+ foreach ($this->exclude as $exclude) {
+ if (str_starts_with($path, $exclude)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function hasChildren(): bool
+ {
+ return $this->getInnerIterator()->hasChildren();
+ }
+
+ public function getChildren(): self
+ {
+ return new self(
+ $this->getInnerIterator()->getChildren(),
+ $this->exclude
+ );
+ }
+
+ public function getInnerIterator(): RecursiveDirectoryIterator
+ {
+ $innerIterator = parent::getInnerIterator();
+
+ assert($innerIterator instanceof RecursiveDirectoryIterator);
+
+ return $innerIterator;
+ }
+}
diff --git a/src/Factory.php b/src/Factory.php
index b6599d8..db4edef 100644
--- a/src/Factory.php
+++ b/src/Factory.php
@@ -67,11 +67,13 @@ public function getFileIterator(array|string $paths, array|string $suffixes = ''
new Iterator(
$path,
new RecursiveIteratorIterator(
- new RecursiveDirectoryIterator($path, FilesystemIterator::FOLLOW_SYMLINKS | FilesystemIterator::SKIP_DOTS)
+ new ExcludeIterator(
+ new RecursiveDirectoryIterator($path, FilesystemIterator::FOLLOW_SYMLINKS | FilesystemIterator::SKIP_DOTS),
+ $exclude,
+ ),
),
$suffixes,
$prefixes,
- $exclude
)
);
}
diff --git a/src/Iterator.php b/src/Iterator.php
index f35af13..d071205 100644
--- a/src/Iterator.php
+++ b/src/Iterator.php
@@ -9,9 +9,6 @@
*/
namespace SebastianBergmann\FileIterator;
-use function array_filter;
-use function array_map;
-use function array_values;
use function assert;
use function preg_match;
use function realpath;
@@ -30,7 +27,6 @@
final class Iterator extends FilterIterator
{
public const PREFIX = 0;
-
public const SUFFIX = 1;
private string|false $basePath;
@@ -44,22 +40,15 @@ final class Iterator extends FilterIterator
*/
private array $prefixes;
- /**
- * @psalm-var list
- */
- private array $exclude;
-
/**
* @psalm-param list $suffixes
* @psalm-param list $prefixes
- * @psalm-param list $exclude
*/
- public function __construct(string $basePath, \Iterator $iterator, array $suffixes = [], array $prefixes = [], array $exclude = [])
+ public function __construct(string $basePath, \Iterator $iterator, array $suffixes = [], array $prefixes = [])
{
$this->basePath = realpath($basePath);
$this->prefixes = $prefixes;
$this->suffixes = $suffixes;
- $this->exclude = array_values(array_filter(array_map('realpath', $exclude)));
parent::__construct($iterator);
}
@@ -91,12 +80,6 @@ private function acceptPath(string $path): bool
return false;
}
- foreach ($this->exclude as $exclude) {
- if (str_starts_with($path, $exclude)) {
- return false;
- }
- }
-
return true;
}
diff --git a/tests/unit/FacadeTest.php b/tests/unit/FacadeTest.php
index 422a24f..0b820da 100644
--- a/tests/unit/FacadeTest.php
+++ b/tests/unit/FacadeTest.php
@@ -10,11 +10,14 @@
namespace SebastianBergmann\FileIterator;
use function realpath;
+use function symlink;
+use function unlink;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Small;
use PHPUnit\Framework\TestCase;
+#[CoversClass(ExcludeIterator::class)]
#[CoversClass(Facade::class)]
#[CoversClass(Factory::class)]
#[CoversClass(Iterator::class)]
@@ -23,7 +26,7 @@ final class FacadeTest extends TestCase
{
public static function provider(): array
{
- $fixtureDirectoryRealpath = realpath(__DIR__ . '/../fixture');
+ $fixtureDirectoryRealpath = self::fixtureDirectoryRealpath();
return [
'filter prefix: no, filter suffix: no, excludes: none' => [
@@ -118,6 +121,21 @@ public static function provider(): array
];
}
+ protected function setUp(): void
+ {
+ $fixtureDirectoryRealpath = self::fixtureDirectoryRealpath();
+
+ symlink(
+ $fixtureDirectoryRealpath . '/a/DoesNotExist.php',
+ $fixtureDirectoryRealpath . '/a/DoesNotExist.php',
+ );
+ }
+
+ protected function tearDown(): void
+ {
+ unlink(self::fixtureDirectoryRealpath() . '/a/DoesNotExist.php');
+ }
+
#[DataProvider('provider')]
public function testSomething(array $expected, array|string $paths, array|string $suffixes, array|string $prefixes, array $exclude): void
{
@@ -126,4 +144,9 @@ public function testSomething(array $expected, array|string $paths, array|string
(new Facade)->getFilesAsArray($paths, $suffixes, $prefixes, $exclude)
);
}
+
+ private static function fixtureDirectoryRealpath(): string|false
+ {
+ return realpath(__DIR__ . '/../fixture');
+ }
}
diff --git a/tools/composer b/tools/composer
index decd087..4d2e00e 100755
Binary files a/tools/composer and b/tools/composer differ
diff --git a/tools/php-cs-fixer b/tools/php-cs-fixer
index 7f455dc..924bbc0 100755
Binary files a/tools/php-cs-fixer and b/tools/php-cs-fixer differ
diff --git a/tools/psalm b/tools/psalm
index f78dc3f..63664da 100755
Binary files a/tools/psalm and b/tools/psalm differ