Skip to content

Commit ebc3cb7

Browse files
committed
Add ** glob support for recursive directory matching in excludePath()
Fixes #405. The `*` wildcard now correctly matches within a single directory level only (not across `/`), while the new `**` wildcard matches across any number of directory levels. This enables patterns like `**/Tests/` to exclude test directories at any depth. https://claude.ai/code/session_01SMWs4ae2xSVr15EwiNcnsH
1 parent 7c59b0f commit ebc3cb7

File tree

3 files changed

+45
-9
lines changed

3 files changed

+45
-9
lines changed

src/Glob.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ class Glob
88
public static function toRegex(string $glob): string
99
{
1010
$regexp = strtr(preg_quote($glob, '/'), [
11-
'\*' => '.*',
12-
'\?' => '.',
13-
'\[' => '[',
14-
'\]' => ']',
11+
'\*\*' => '.*',
12+
'\*' => '[^\/]*',
13+
'\?' => '[^\/]',
14+
'\[' => '[',
15+
'\]' => ']',
1516
'\[\!' => '',
1617
]);
1718

tests/Unit/ClassSetTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,38 @@ public function test_can_exclude_glob_patterns(): void
8686
self::assertEquals($expected, $actual);
8787
}
8888

89+
public function test_can_exclude_using_double_wildcard_glob_pattern(): void
90+
{
91+
$structure = [
92+
'Domain' => [
93+
'Product' => [
94+
'Tests' => [
95+
'ProductTest.php' => '',
96+
],
97+
'Product.php' => '',
98+
],
99+
'User' => [
100+
'Tests' => [
101+
'UserTest.php' => '',
102+
],
103+
'User.php' => '',
104+
],
105+
],
106+
];
107+
108+
$path = vfsStream::setup('root', null, $structure)->url();
109+
$set = ClassSet::fromDir($path)->excludePath('**/Tests/');
110+
111+
$expected = [
112+
'Domain/Product/Product.php',
113+
'Domain/User/User.php',
114+
];
115+
116+
$actual = array_values(array_map(static fn ($item) => $item->getRelativePathname(), iterator_to_array($set)));
117+
118+
self::assertEquals($expected, $actual);
119+
}
120+
89121
protected function createMvcProjectStructure(): string
90122
{
91123
$structure = [

tests/Unit/GlobTest.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,22 @@ class GlobTest extends TestCase
1010
{
1111
public function test_can_exclude_using_glob_pattern(): void
1212
{
13-
// * - Matches zero or more characters.
14-
self::assertEquals('/.*Catalog.*/', Glob::toRegex('*Catalog*'));
13+
// * - Matches zero or more characters except directory separator.
14+
self::assertEquals('/[^\/]*Catalog[^\/]*/', Glob::toRegex('*Catalog*'));
1515

16-
// * - Matches zero or more characters.
16+
// * - Matches zero or more characters except directory separator.
1717
self::assertEquals('/Cata\.log/', Glob::toRegex('Cata.log'));
1818

19-
// ? - Matches exactly one character (any character).
20-
self::assertEquals('/C.talog/', Glob::toRegex('C?talog'));
19+
// ? - Matches exactly one character (any character except directory separator).
20+
self::assertEquals('/C[^\/]talog/', Glob::toRegex('C?talog'));
2121

2222
// [...] - Matches one character from a group of characters. If the first character is !, matches any character not in the group.
2323
self::assertEquals('/prova[123]/', Glob::toRegex('prova[123]'));
2424

2525
// [...] - Matches one character from a group of characters. If the first character is !, matches any character not in the group.
2626
self::assertEquals('/prova[ˆ123]/', Glob::toRegex('prova[!123]'));
27+
28+
// ** - Matches zero or more characters including directory separator (recursive).
29+
self::assertEquals('/.*\/Tests\//', Glob::toRegex('**/Tests/'));
2730
}
2831
}

0 commit comments

Comments
 (0)