|
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_functionexpects aPostobject, and outputs the author of the post. The base classPostreturns a simple string and outputting a simple string will work just fine. However, the child classBlogPostwhich is a sub-type ofPostinstead decided to return anobject, and is therefore violating the SOLID principles. If aBlogPostwere passed tomy_function, PHP would not complain, but ultimately fail when executing thestrtouppercall in its body.