-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFileFinder.php
More file actions
122 lines (105 loc) · 2.85 KB
/
FileFinder.php
File metadata and controls
122 lines (105 loc) · 2.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<?php
declare(strict_types=1);
namespace LaraMint\PhpSecurityScanner\Discovery;
use Symfony\Component\Finder\Finder;
final class FileFinder
{
/**
* Directories skipped unconditionally. The scanner doesn't analyze
* third-party code or generated/cache trees — they explode memory
* and rarely surface actionable findings.
*/
public const DEFAULT_SKIP_DIRS = [
// Third-party / package-manager trees
'vendor',
'node_modules',
'bower_components',
'jspm_packages',
'Pods',
'.bundle',
// VCS metadata
'.git',
'.svn',
'.hg',
// Editor / IDE state
'.idea',
'.vscode',
'.fleet',
'.zed',
// Framework runtime + cache trees (Laravel/Symfony idioms)
'storage',
'bootstrap/cache',
'var/cache',
'var/log',
// Build artefacts
'build',
'dist',
'out',
'target',
'public/build',
'public/hot',
'coverage',
// Generic cache + tmp
'cache',
'.cache',
'tmp',
'.tmp',
'.phpunit.cache',
'.phpstan.cache',
'.pint.cache',
'.psalm.cache',
'.rector.cache',
'.phpactor',
];
/**
* @param string[] $paths
* @param string[] $excludes glob patterns relative to scan root
* @return iterable<string> absolute file paths
*/
public function find(array $paths, array $excludes = []): iterable
{
foreach ($paths as $path) {
if (is_file($path)) {
if ($this->isPhpFile($path) && ! $this->isExcluded($path, $excludes)) {
yield $path;
}
continue;
}
if (! is_dir($path)) {
continue;
}
$finder = new Finder;
$finder->files()
->in($path)
->name('*.php')
->ignoreVCSIgnored(true)
->exclude(self::DEFAULT_SKIP_DIRS);
foreach ($excludes as $pattern) {
if (strpos($pattern, '/') === false) {
$finder->notName($pattern);
} else {
$finder->notPath($pattern);
}
}
foreach ($finder as $file) {
yield $file->getPathname();
}
}
}
private function isPhpFile(string $path): bool
{
return substr($path, -4) === '.php';
}
/**
* @param string[] $excludes
*/
private function isExcluded(string $path, array $excludes): bool
{
foreach ($excludes as $pattern) {
if (fnmatch($pattern, $path) || fnmatch($pattern, basename($path))) {
return true;
}
}
return false;
}
}