1
|
|
|
<?php |
2
|
|
|
/* |
3
|
|
|
* Go! AOP framework |
4
|
|
|
* |
5
|
|
|
* @copyright Copyright 2015, Lisachenko Alexander <[email protected]> |
6
|
|
|
* |
7
|
|
|
* This source file is subject to the license that is bundled |
8
|
|
|
* with this source code in the file LICENSE. |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace Go\Instrument\FileSystem; |
12
|
|
|
|
13
|
|
|
use CallbackFilterIterator; |
14
|
|
|
use InvalidArgumentException; |
15
|
|
|
use LogicException; |
16
|
|
|
use RecursiveIteratorIterator; |
17
|
|
|
use SplFileInfo; |
18
|
|
|
use Symfony\Component\Finder\Finder; |
19
|
|
|
use UnexpectedValueException; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* Enumerates files in the concrete directory, applying filtration logic |
23
|
|
|
*/ |
24
|
|
|
class Enumerator |
25
|
|
|
{ |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Path to the root directory, where enumeration should start |
29
|
|
|
* |
30
|
|
|
* @var string |
31
|
|
|
*/ |
32
|
|
|
private $rootDirectory; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* List of additional include paths, should be below rootDirectory |
36
|
|
|
* |
37
|
|
|
* @var array |
38
|
|
|
*/ |
39
|
|
|
private $includePaths; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* List of additional exclude paths, should be below rootDirectory |
43
|
|
|
* |
44
|
|
|
* @var array |
45
|
|
|
*/ |
46
|
|
|
private $excludePaths; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Initializes an enumerator |
50
|
|
|
* |
51
|
|
|
* @param string $rootDirectory Path to the root directory |
52
|
|
|
* @param array $includePaths List of additional include paths |
53
|
|
|
* @param array $excludePaths List of additional exclude paths |
54
|
|
|
*/ |
55
|
7 |
|
public function __construct($rootDirectory, array $includePaths = [], array $excludePaths = []) |
56
|
|
|
{ |
57
|
7 |
|
$this->rootDirectory = $rootDirectory; |
58
|
7 |
|
$this->includePaths = $includePaths; |
59
|
7 |
|
$this->excludePaths = $excludePaths; |
60
|
7 |
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Returns an enumerator for files |
64
|
|
|
* |
65
|
|
|
* @return CallbackFilterIterator|RecursiveIteratorIterator|\IteratorIterator|SplFileInfo[] |
66
|
|
|
* @throws UnexpectedValueException |
67
|
|
|
* @throws InvalidArgumentException |
68
|
|
|
* @throws LogicException |
69
|
|
|
*/ |
70
|
7 |
|
public function enumerate() |
71
|
|
|
{ |
72
|
7 |
|
$finder = new Finder(); |
73
|
7 |
|
$finder->files() |
74
|
7 |
|
->name('*.php') |
75
|
7 |
|
->in($this->getInPaths()); |
76
|
|
|
|
77
|
7 |
|
foreach ($this->getExcludePaths() as $path) { |
78
|
4 |
|
$finder->notPath($path); |
79
|
|
|
} |
80
|
|
|
|
81
|
7 |
|
$iterator = new \RecursiveIteratorIterator( |
82
|
7 |
|
new \RecursiveDirectoryIterator( |
83
|
7 |
|
$this->rootDirectory, |
84
|
7 |
|
\FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS |
85
|
|
|
) |
86
|
|
|
); |
87
|
|
|
|
88
|
7 |
|
$callback = $this->getFilter(); |
89
|
7 |
|
$iterator = new \CallbackFilterIterator($iterator, $callback); |
90
|
|
|
|
91
|
7 |
|
return $iterator; |
92
|
|
|
|
93
|
|
|
return $finder->getIterator(); |
|
|
|
|
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* @return array |
98
|
|
|
* @throws UnexpectedValueException |
99
|
|
|
*/ |
100
|
7 |
|
private function getInPaths() |
101
|
|
|
{ |
102
|
7 |
|
$inPaths = []; |
103
|
|
|
|
104
|
7 |
|
foreach ($this->includePaths as $path) { |
105
|
1 |
|
if (strpos($path, $this->rootDirectory, 0) === false) { |
106
|
|
|
throw new UnexpectedValueException(sprintf('Path %s is not in %s', $path, $this->rootDirectory)); |
107
|
|
|
} |
108
|
|
|
|
109
|
1 |
|
$path = str_replace('*', '', $path); |
110
|
1 |
|
$inPaths[] = $path; |
111
|
|
|
} |
112
|
|
|
|
113
|
7 |
|
if (empty($inPaths)) { |
114
|
6 |
|
$inPaths[] = $this->rootDirectory; |
115
|
|
|
} |
116
|
|
|
|
117
|
7 |
|
return $inPaths; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* @return array |
122
|
|
|
*/ |
123
|
7 |
|
private function getExcludePaths() |
124
|
|
|
{ |
125
|
7 |
|
$excludePaths = []; |
126
|
|
|
|
127
|
7 |
|
foreach ($this->excludePaths as $path) { |
128
|
4 |
|
$path = str_replace('*', '.*', $path); |
129
|
4 |
|
$excludePaths[] = '#' . str_replace($this->rootDirectory . '/', '', $path) . '#'; |
130
|
|
|
} |
131
|
|
|
|
132
|
7 |
|
return $excludePaths; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* Returns a filter callback for enumerating files |
137
|
|
|
* |
138
|
|
|
* @return \Closure |
139
|
|
|
*/ |
140
|
7 |
|
public function getFilter() |
141
|
|
|
{ |
142
|
7 |
|
$rootDirectory = $this->rootDirectory; |
143
|
7 |
|
$includePaths = $this->includePaths; |
144
|
7 |
|
$excludePaths = $this->excludePaths; |
145
|
|
|
|
146
|
7 |
|
return function (SplFileInfo $file) use ($rootDirectory, $includePaths, $excludePaths) { |
147
|
|
|
|
148
|
7 |
|
if ($file->getExtension() !== 'php') { |
149
|
|
|
return false; |
150
|
|
|
} |
151
|
|
|
|
152
|
7 |
|
$fullPath = $this->getFileFullPath($file); |
153
|
|
|
// Do not touch files that not under rootDirectory |
154
|
7 |
|
if (strpos($fullPath, $rootDirectory) !== 0) { |
155
|
|
|
return false; |
156
|
|
|
} |
157
|
|
|
|
158
|
7 |
|
if (!empty($includePaths)) { |
159
|
1 |
|
$found = false; |
160
|
1 |
|
foreach ($includePaths as $includePattern) { |
161
|
1 |
|
if (fnmatch("{$includePattern}*", $fullPath, FNM_NOESCAPE)) { |
162
|
1 |
|
$found = true; |
163
|
1 |
|
break; |
164
|
|
|
} |
165
|
|
|
} |
166
|
1 |
|
if (!$found) { |
167
|
|
|
return false; |
168
|
|
|
} |
169
|
|
|
} |
170
|
|
|
|
171
|
7 |
|
foreach ($excludePaths as $excludePattern) { |
172
|
4 |
|
if (fnmatch("{$excludePattern}*", $fullPath, FNM_NOESCAPE)) { |
173
|
4 |
|
return false; |
174
|
|
|
} |
175
|
|
|
} |
176
|
|
|
|
177
|
5 |
|
return true; |
178
|
7 |
|
}; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* Return the real path of the given file |
183
|
|
|
* |
184
|
|
|
* This is used for testing purpose with virtual file system. |
185
|
|
|
* In a vfs the 'realPath' methode will always return false. |
186
|
|
|
* So we have a chance to mock this single function to return different path. |
187
|
|
|
* |
188
|
|
|
* @param SplFileInfo $file |
189
|
|
|
* |
190
|
|
|
* @return string |
191
|
|
|
*/ |
192
|
1 |
|
protected function getFileFullPath(SplFileInfo $file) |
193
|
|
|
{ |
194
|
1 |
|
return $file->getRealPath(); |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
} |
198
|
|
|
|
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return
,die
orexit
statements that have been added for debug purposes.In the above example, the last
return false
will never be executed, because a return statement has already been met in every possible execution path.