Passed
Push — master ( 945062...632d59 )
by Fabien
02:13
created

FileFinder::getDisplayPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Churn\File;
6
7
use Generator;
8
use RecursiveDirectoryIterator;
9
use RecursiveIteratorIterator;
10
use SplFileInfo;
11
12
/**
13
 * @internal
14
 */
15
class FileFinder
16
{
17
18
    /**
19
     * List of file extensions to look for.
20
     *
21
     * @var array<string>
22
     */
23
    private $fileExtensions;
24
25
    /**
26
     * List of regular expressions used to filter files to ignore.
27
     *
28
     * @var array<string>
29
     */
30
    private $filters;
31
32
    /**
33
     * The base path.
34
     *
35
     * @var string
36
     */
37
    private $basePath;
38
39
    /**
40
     * Class constructor.
41
     *
42
     * @param array<string> $fileExtensions List of file extensions to look for.
43
     * @param array<string> $filesToIgnore List of files to ignore.
44
     * @param string $basePath The base path.
45
     */
46
    public function __construct(array $fileExtensions, array $filesToIgnore, string $basePath)
47
    {
48
        $this->fileExtensions = $fileExtensions;
49
        $this->filters = \array_map(function (string $fileToIgnore): string {
50
            return $this->patternToRegex($fileToIgnore);
51
        }, $filesToIgnore);
52
        $this->basePath = $basePath;
53
    }
54
55
    /**
56
     * Recursively finds all files with the .php extension in the provided
57
     * $paths and returns list as array.
58
     *
59
     * @param array<string> $paths Paths in which to look for .php files.
60
     * @return Generator<int, File>
61
     */
62
    public function getPhpFiles(array $paths): Generator
63
    {
64
        foreach ($paths as $path) {
65
            $absolutePath = FileHelper::toAbsolutePath($path, $this->basePath);
66
67
            yield from $this->getPhpFilesFromPath($absolutePath);
68
        }
69
    }
70
71
    /**
72
     * Recursively finds all files with the .php extension in the provided
73
     * $path adds them to $this->files.
74
     *
75
     * @param string $path Path in which to look for .php files.
76
     * @return Generator<int, File>
77
     */
78
    private function getPhpFilesFromPath(string $path): Generator
79
    {
80
        if (\is_file($path)) {
81
            $file = new SplFileInfo($path);
82
83
            yield new File($file->getRealPath(), $this->getDisplayPath($file));
84
85
            return;
86
        }
87
88
        if (!\is_dir($path)) {
89
            // invalid path
90
            return;
91
        }
92
93
        foreach ($this->findPhpFiles($path) as $file) {
94
            yield new File($file->getRealPath(), $this->getDisplayPath($file));
95
        }
96
    }
97
98
    /**
99
     * @param SplFileInfo $file The file object.
100
     * @return string The file path to display.
101
     */
102
    private function getDisplayPath(SplFileInfo $file): string
103
    {
104
        return FileHelper::toRelativePath($file->getPathName(), $this->basePath);
105
    }
106
107
    /**
108
     * Recursively finds all PHP files in a given directory.
109
     *
110
     * @param string $path Path in which to look for .php files.
111
     * @return Generator<int, SplFileInfo>
112
     */
113
    private function findPhpFiles(string $path): Generator
114
    {
115
        foreach ($this->findFiles($path) as $file) {
116
            if (!\in_array($file->getExtension(), $this->fileExtensions, true) || $this->fileShouldBeIgnored($file)) {
117
                continue;
118
            }
119
120
            yield $file;
121
        }
122
    }
123
124
    /**
125
     * Recursively finds all files in a given directory.
126
     *
127
     * @param string $path Path in which to look for .php files.
128
     * @return Generator<int, SplFileInfo>
129
     */
130
    private function findFiles(string $path): Generator
131
    {
132
        $directoryIterator = new RecursiveDirectoryIterator($path);
133
134
        foreach (new RecursiveIteratorIterator($directoryIterator) as $item) {
135
            if ($item->isDir()) {
136
                continue;
137
            }
138
139
            yield $item;
140
        }
141
    }
142
143
    /**
144
     * Determines if a file should be ignored.
145
     *
146
     * @param SplFileInfo $file File.
147
     */
148
    private function fileShouldBeIgnored(SplFileInfo $file): bool
149
    {
150
        foreach ($this->filters as $regex) {
151
            if (\preg_match("#{$regex}#", $file->getRealPath())) {
152
                return true;
153
            }
154
        }
155
156
        return false;
157
    }
158
159
    /**
160
     * Translate file path pattern to regex string.
161
     *
162
     * @param string $filePattern File pattern to be ignored.
163
     */
164
    private function patternToRegex(string $filePattern): string
165
    {
166
        $regex = \preg_replace("#(.*)\*([\w.]*)$#", "$1.+$2$", $filePattern);
167
168
        if ('\\' === \DIRECTORY_SEPARATOR) {
169
            $regex = \str_replace('/', '\\\\', $regex);
170
        }
171
172
        return $regex;
173
    }
174
}
175