Completed
Pull Request — master (#463)
by Alexander
30:17 queued 05:15
created

Enumerator   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 155
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 91.07%

Importance

Changes 0
Metric Value
wmc 20
lcom 1
cbo 1
dl 0
loc 155
ccs 51
cts 56
cp 0.9107
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types = 1);
4
/*
5
 * Go! AOP framework
6
 *
7
 * @copyright Copyright 2015, Lisachenko Alexander <[email protected]>
8
 *
9
 * This source file is subject to the license that is bundled
10
 * with this source code in the file LICENSE.
11
 */
12
13
namespace Go\Instrument\FileSystem;
14
15
use ArrayIterator;
16
use Closure;
17
use InvalidArgumentException;
18
use Iterator;
19
use LogicException;
20
use SplFileInfo;
21
use Symfony\Component\Finder\Finder;
22
use UnexpectedValueException;
23
24
/**
25
 * Enumerates files in the concrete directory, applying filtration logic
26
 */
27
class Enumerator
28
{
29
    /**
30
     * Path to the root directory, where enumeration should start
31
     */
32
    private string $rootDirectory;
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_STRING, expecting T_FUNCTION or T_CONST
Loading history...
33
34
    /**
35
     * List of additional include paths, should be below rootDirectory
36
     */
37
    private array $includePaths;
38
39
    /**
40
     * List of additional exclude paths, should be below rootDirectory
41
     */
42
    private array $excludePaths;
43
44
    /**
45
     * Initializes an enumerator
46
     *
47
     * @param string $rootDirectory Path to the root directory
48
     * @param array  $includePaths  List of additional include paths
49
     * @param array  $excludePaths  List of additional exclude paths
50 8
     */
51
    public function __construct(string $rootDirectory, array $includePaths = [], array $excludePaths = [])
52 8
    {
53 8
        $this->rootDirectory = $rootDirectory;
54 8
        $this->includePaths  = $includePaths;
55 8
        $this->excludePaths  = $excludePaths;
56
    }
57
58
    /**
59
     * Returns an enumerator for files
60
     *
61
     * @return Iterator|SplFileInfo[]
62
     * @throws UnexpectedValueException
63
     * @throws InvalidArgumentException
64
     * @throws LogicException
65 7
     */
66
    public function enumerate(): Iterator
67 7
    {
68 7
        $finder = new Finder();
69 7
        $finder->files()
70 7
            ->name('*.php')
71
            ->in($this->getInPaths());
72 7
73 4
        foreach ($this->getExcludePaths() as $path) {
74
            $finder->notPath($path);
75
        }
76 7
77
        $iterator = $finder->getIterator();
78
79 7
        // on Windows platform the default iterator is unable to rewind, not sure why
80
        if (strpos(PHP_OS, 'WIN') === 0) {
81
            $iterator = new ArrayIterator(iterator_to_array($iterator));
82
        }
83 7
84
        return $iterator;
85
    }
86
87
    /**
88
     * Returns a filter callback for enumerating files
89 18
     */
90
    public function getFilter(): Closure
91 1
    {
92 1
        $rootDirectory = $this->rootDirectory;
93 1
        $includePaths = $this->includePaths;
94
        $excludePaths = $this->excludePaths;
95
96
        return function (SplFileInfo $file) use ($rootDirectory, $includePaths, $excludePaths) {
97 18
98
            if ($file->getExtension() !== 'php') {
99
                return false;
100
            }
101 18
102
            $fullPath = $this->getFileFullPath($file);
103 18
            // Do not touch files that not under rootDirectory
104 18
            if (strpos($fullPath, $rootDirectory) !== 0) {
105
                return false;
106
            }
107 1
108 1
            if (!empty($includePaths)) {
109 1
                $found = false;
110 1
                foreach ($includePaths as $includePattern) {
111 1
                    if (fnmatch("{$includePattern}*", $fullPath, FNM_NOESCAPE)) {
112 1
                        $found = true;
113
                        break;
114
                    }
115 1
                }
116
                if (!$found) {
117
                    return false;
118
                }
119
            }
120 1
121 1
            foreach ($excludePaths as $excludePattern) {
122
                if (fnmatch("{$excludePattern}*", $fullPath, FNM_NOESCAPE)) {
123
                    return false;
124
                }
125
            }
126 1
127 1
            return true;
128
        };
129
    }
130
131
    /**
132
     * Return the real path of the given file
133
     *
134
     * This is used for testing purpose with virtual file system.
135
     * In a vfs the 'realPath' method will always return false.
136
     * So we have a chance to mock this single function to return different path.
137 18
     */
138
    protected function getFileFullPath(SplFileInfo $file): string
139 18
    {
140
        return $file->getRealPath();
141
    }
142
143
    /**
144
     * Returns collection of directories to look at
145
     *
146
     * @throws UnexpectedValueException if directory not under the root
147 7
     */
148
    private function getInPaths(): array
149 7
    {
150
        $inPaths = [];
151 7
152 1
        foreach ($this->includePaths as $path) {
153
            if (strpos($path, $this->rootDirectory, 0) === false) {
154
                throw new UnexpectedValueException(sprintf('Path %s is not in %s', $path, $this->rootDirectory));
155
            }
156 1
157 1
            $inPaths[] = $path;
158
        }
159
160 7
        if (empty($inPaths)) {
161 6
            $inPaths[] = $this->rootDirectory;
162
        }
163
164 7
        return $inPaths;
165
    }
166
167
    /**
168
     * Returns the list of excluded paths
169
     */
170 7
    private function getExcludePaths(): array
171
    {
172 7
        $excludePaths = [];
173
174 7
        foreach ($this->excludePaths as $path) {
175 4
            $path = str_replace('*', '.*', $path);
176 4
            $excludePaths[] = '#' . str_replace($this->rootDirectory . '/', '', $path) . '#';
177
        }
178
179 7
        return $excludePaths;
180
    }
181
}
182