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 |
|
$arr = []; |
82
|
7 |
|
foreach ($finder->getIterator() as $x) { |
83
|
7 |
|
$arr[] = $x; |
84
|
|
|
} |
85
|
|
|
|
86
|
7 |
|
return new \ArrayIterator($arr); |
|
|
|
|
87
|
|
|
|
88
|
|
|
$this->dumpForAppVeyor($finder->getIterator()); |
|
|
|
|
89
|
|
|
$iterator = new \RecursiveIteratorIterator( |
90
|
|
|
new \RecursiveDirectoryIterator( |
91
|
|
|
$this->rootDirectory, |
92
|
|
|
\FilesystemIterator::SKIP_DOTS | \FilesystemIterator::UNIX_PATHS |
93
|
|
|
) |
94
|
|
|
); |
95
|
|
|
|
96
|
|
|
$callback = $this->getFilter(); |
97
|
|
|
$iterator = new \CallbackFilterIterator($iterator, $callback); |
98
|
|
|
|
99
|
|
|
$this->dumpForAppVeyor($iterator); |
100
|
|
|
|
101
|
|
|
return $finder->getIterator(); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
private function dumpForAppVeyor($iterator) |
|
|
|
|
105
|
|
|
{ |
106
|
|
|
echo 'start' . PHP_EOL . PHP_EOL; |
107
|
|
|
|
108
|
|
|
foreach ($iterator as $thing) { |
109
|
|
|
var_dump($thing->getRealPath()); |
|
|
|
|
110
|
|
|
} |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* @return array |
115
|
|
|
* @throws UnexpectedValueException |
116
|
|
|
*/ |
117
|
7 |
|
private function getInPaths() |
118
|
|
|
{ |
119
|
7 |
|
$inPaths = []; |
120
|
|
|
|
121
|
7 |
|
foreach ($this->includePaths as $path) { |
122
|
1 |
|
if (strpos($path, $this->rootDirectory, 0) === false) { |
123
|
|
|
throw new UnexpectedValueException(sprintf('Path %s is not in %s', $path, $this->rootDirectory)); |
124
|
|
|
} |
125
|
|
|
|
126
|
1 |
|
$path = str_replace('*', '', $path); |
127
|
1 |
|
$inPaths[] = $path; |
128
|
|
|
} |
129
|
|
|
|
130
|
7 |
|
if (empty($inPaths)) { |
131
|
6 |
|
$inPaths[] = $this->rootDirectory; |
132
|
|
|
} |
133
|
|
|
|
134
|
7 |
|
return $inPaths; |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* @return array |
139
|
|
|
*/ |
140
|
7 |
|
private function getExcludePaths() |
141
|
|
|
{ |
142
|
7 |
|
$excludePaths = []; |
143
|
|
|
|
144
|
7 |
|
foreach ($this->excludePaths as $path) { |
145
|
4 |
|
$path = str_replace('*', '.*', $path); |
146
|
4 |
|
$excludePaths[] = '#' . str_replace($this->rootDirectory . '/', '', $path) . '#'; |
147
|
|
|
} |
148
|
|
|
|
149
|
7 |
|
return $excludePaths; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* Returns a filter callback for enumerating files |
154
|
|
|
* |
155
|
|
|
* @return \Closure |
156
|
|
|
*/ |
157
|
|
|
public function getFilter() |
158
|
|
|
{ |
159
|
|
|
$rootDirectory = $this->rootDirectory; |
160
|
|
|
$includePaths = $this->includePaths; |
161
|
|
|
$excludePaths = $this->excludePaths; |
162
|
|
|
|
163
|
|
|
return function (SplFileInfo $file) use ($rootDirectory, $includePaths, $excludePaths) { |
164
|
|
|
|
165
|
|
|
if ($file->getExtension() !== 'php') { |
166
|
|
|
return false; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
$fullPath = $this->getFileFullPath($file); |
170
|
|
|
// Do not touch files that not under rootDirectory |
171
|
|
|
if (strpos($fullPath, $rootDirectory) !== 0) { |
172
|
|
|
return false; |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
if (!empty($includePaths)) { |
176
|
|
|
$found = false; |
177
|
|
|
foreach ($includePaths as $includePattern) { |
178
|
|
|
if (fnmatch("{$includePattern}*", $fullPath, FNM_NOESCAPE)) { |
179
|
|
|
$found = true; |
180
|
|
|
break; |
181
|
|
|
} |
182
|
|
|
} |
183
|
|
|
if (!$found) { |
184
|
|
|
return false; |
185
|
|
|
} |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
foreach ($excludePaths as $excludePattern) { |
189
|
|
|
if (fnmatch("{$excludePattern}*", $fullPath, FNM_NOESCAPE)) { |
190
|
|
|
return false; |
191
|
|
|
} |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
return true; |
195
|
|
|
}; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* Return the real path of the given file |
200
|
|
|
* |
201
|
|
|
* This is used for testing purpose with virtual file system. |
202
|
|
|
* In a vfs the 'realPath' methode will always return false. |
203
|
|
|
* So we have a chance to mock this single function to return different path. |
204
|
|
|
* |
205
|
|
|
* @param SplFileInfo $file |
206
|
|
|
* |
207
|
|
|
* @return string |
208
|
|
|
*/ |
209
|
|
|
protected function getFileFullPath(SplFileInfo $file) |
210
|
|
|
{ |
211
|
|
|
return $file->getRealPath(); |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
} |
215
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.