Completed
Push — master ( d372d6...3eec25 )
by Christian
02:37
created

FileList   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 342
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 85.71%

Importance

Changes 0
Metric Value
wmc 30
lcom 1
cbo 9
dl 0
loc 342
ccs 114
cts 133
cp 0.8571
rs 10
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A name() 0 13 1
A path() 0 13 1
A notPath() 0 13 1
A files() 0 12 1
A directories() 0 12 1
B depth() 0 37 6
A date() 0 12 1
A contains() 0 13 1
A notContains() 0 13 1
A filter() 0 12 1
A toArray() 0 13 2
A getIterator() 0 4 1
A count() 0 4 1
D getSourceIterator() 0 32 9
A notName() 0 13 1
1
<?php
2
3
namespace uuf6429\ElderBrother\Change;
4
5
use Symfony\Component\Finder\Comparator;
6
use Symfony\Component\Finder\Iterator;
7
use Symfony\Component\Finder\SplFileInfo as SfyFileInfo;
8
9
class FileList implements \IteratorAggregate, \Countable
10
{
11
    /** @var string */
12
    protected $cacheKey;
13
14
    /** @var callable */
15
    protected $source;
16
17
    /** @var array */
18
    protected static $cache;
19
20
    /** @var \Iterator */
21
    protected $sourceResult;
22
23
    /**
24
     * @param string   $cacheKey Unique key to identify this collection of files
25
     * @param callable $source   Callable that return an iterator or array of SplFileInfo
26
     */
27 33
    public function __construct($cacheKey, callable $source)
28
    {
29 33
        $this->cacheKey = $cacheKey;
30 33
        $this->source = $source;
31 33
    }
32
33
    /**
34
     * Search file names (excluding path) by pattern.
35
     *
36
     * @param string $pattern Pattern to look for in file name (regexp, glob, or string)
37
     *
38
     * @return static
39
     */
40 5
    public function name($pattern)
41
    {
42 5
        return new self(
43 5
            $this->cacheKey . '->' . __FUNCTION__ . '(' . $pattern . ')',
44
            function () use ($pattern) {
45 5
                return new Iterator\FilenameFilterIterator(
46 5
                    $this->getSourceIterator(),
47 5
                    [$pattern],
48 5
                    []
49
                );
50 5
            }
51
        );
52
    }
53
54
    /**
55
     * Search file names (excluding path) by pattern.
56
     *
57
     * @param string $pattern Pattern to exclude files (regexp, glob, or string)
58
     *
59
     * @return static
60
     */
61 1
    public function notName($pattern)
62
    {
63 1
        return new self(
64 1
            $this->cacheKey . '->' . __FUNCTION__ . '(' . $pattern . ')',
65
            function () use ($pattern) {
66 1
                return new Iterator\FilenameFilterIterator(
67 1
                    $this->getSourceIterator(),
68 1
                    [],
69 1
                    [$pattern]
70
                );
71 1
            }
72
        );
73
    }
74
75
    /**
76
     * Search path names by pattern.
77
     *
78
     * @param string $pattern Pattern to look for in path (regexp, glob, or string)
79
     *
80
     * @return static
81
     */
82 1
    public function path($pattern)
83
    {
84 1
        return new self(
85 1
            $this->cacheKey . '->' . __FUNCTION__ . '(' . $pattern . ')',
86
            function () use ($pattern) {
87 1
                return new Iterator\PathFilterIterator(
88 1
                    $this->getSourceIterator(),
89 1
                    [$pattern],
90 1
                    []
91
                );
92 1
            }
93
        );
94
    }
95
96
    /**
97
     * Search path names by pattern.
98
     *
99
     * @param string $pattern Pattern to exclude paths (regexp, glob, or string)
100
     *
101
     * @return static
102
     */
103 1
    public function notPath($pattern)
104
    {
105 1
        return new self(
106 1
            $this->cacheKey . '->' . __FUNCTION__ . '(' . $pattern . ')',
107
            function () use ($pattern) {
108 1
                return new Iterator\PathFilterIterator(
109 1
                    $this->getSourceIterator(),
110 1
                    [],
111 1
                    [$pattern]
112
                );
113 1
            }
114
        );
115
    }
116
117
    /**
118
     * Filters out anything that is not a file.
119
     *
120
     * @return static
121
     */
122 3
    public function files()
123
    {
124 3
        return new self(
125 3
            $this->cacheKey . '->' . __FUNCTION__ . '()',
126
            function () {
127 3
                return new Iterator\FileTypeFilterIterator(
128 3
                    $this->getSourceIterator(),
129 3
                    Iterator\FileTypeFilterIterator::ONLY_FILES
130
                );
131 3
            }
132
        );
133
    }
134
135
    /**
136
     * Filters out anything that is not a directory.
137
     *
138
     * @return static
139
     */
140 2
    public function directories()
141
    {
142 2
        return new self(
143 2
            $this->cacheKey . '->' . __FUNCTION__ . '()',
144
            function () {
145 2
                return new Iterator\FileTypeFilterIterator(
146 2
                    $this->getSourceIterator(),
147 2
                    Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES
148
                );
149 2
            }
150
        );
151
    }
152
153
    /**
154
     * Filters out items that do not match the specified level.
155
     *
156
     * @param string $level The depth expression (for example '< 1')
157
     *
158
     * @return static
159
     */
160 3
    public function depth($level)
161
    {
162 3
        $minDepth = 0;
163 3
        $maxDepth = PHP_INT_MAX;
164 3
        $comparator = new Comparator\NumberComparator($level);
165 3
        $comparatorTarget = intval($comparator->getTarget());
166
167 3
        switch ($comparator->getOperator()) {
168 3
            case '>':
169 1
                $minDepth = $comparatorTarget + 1;
170 1
                break;
171
172 2
            case '>=':
173
                $minDepth = $comparatorTarget;
174
                break;
175
176 2
            case '<':
177 2
                $maxDepth = $comparatorTarget - 1;
178 2
                break;
179
180
            case '<=':
181
                $maxDepth = $comparatorTarget;
182
                break;
183
184
            default:
185
                $minDepth = $maxDepth = $comparatorTarget;
186
                break;
187
        }
188
189 3
        return $this->filter(
190
            function (SfyFileInfo $file) use ($minDepth, $maxDepth) {
191 3
                $depth = count(explode('/', str_replace('\\', '/', $file->getRelativePathname()))) - 1;
192
193 3
                return $depth >= $minDepth && $depth <= $maxDepth;
194 3
            }
195
        );
196
    }
197
198
    /**
199
     * Filters out items whose last modified do not match expression.
200
     *
201
     * @param string $date A date range string that can be parsed by `strtotime()``
202
     *
203
     * @return static
204
     */
205
    public function date($date)
206
    {
207
        return new self(
208
            $this->cacheKey . '->' . __FUNCTION__ . '(' . $date . ')',
209
            function () use ($date) {
210
                return new Iterator\DateRangeFilterIterator(
211
                    $this->getSourceIterator(),
212
                    [new Comparator\DateComparator($date)]
213
                );
214
            }
215
        );
216
    }
217
218
    /**
219
     * Filters out files not matching a string or regexp.
220
     *
221
     * @param string $pattern A pattern (string or regexp)
222
     *
223
     * @return static
224
     */
225 2
    public function contains($pattern)
226
    {
227 2
        return new self(
228 2
            $this->cacheKey . '->' . __FUNCTION__ . '(' . $pattern . ')',
229
            function () use ($pattern) {
230 2
                return new Iterator\FilecontentFilterIterator(
231 2
                    $this->getSourceIterator(),
232 2
                    [$pattern],
233 2
                    []
234
                );
235 2
            }
236
        );
237
    }
238
239
    /**
240
     * Filters out files matching a string or regexp.
241
     *
242
     * @param string $pattern A pattern (string or regexp)
243
     *
244
     * @return static
245
     */
246 1
    public function notContains($pattern)
247
    {
248 1
        return new self(
249 1
            $this->cacheKey . '->' . __FUNCTION__ . '(' . $pattern . ')',
250
            function () use ($pattern) {
251 1
                return new Iterator\FilecontentFilterIterator(
252 1
                    $this->getSourceIterator(),
253 1
                    [],
254 1
                    [$pattern]
255
                );
256 1
            }
257
        );
258
    }
259
260
    /**
261
     * Filters using an anonymous function. Function receives a \SplFileInfo and must return false to filter it out.
262
     *
263
     * @param \Closure $closure An anonymous function
264
     *
265
     * @return static
266
     */
267 4
    public function filter(\Closure $closure)
268
    {
269 4
        return new self(
270 4
            $this->cacheKey . '->' . __FUNCTION__ . '(' . spl_object_hash($closure) . ')',
271
            function () use ($closure) {
272 4
                return new Iterator\CustomFilterIterator(
273 4
                    $this->getSourceIterator(),
274 4
                    [$closure]
275
                );
276 4
            }
277
        );
278
    }
279
280
    /**
281
     * Returns array of file paths.
282
     *
283
     * @return string[]
284
     */
285 32
    public function toArray()
286
    {
287 32
        if (!isset(self::$cache[$this->cacheKey])) {
288 32
            self::$cache[$this->cacheKey] = array_map(
289 32
                function (\SplFileInfo $file) {
290 27
                    return $file->getPathname();
291 32
                },
292 32
                array_values(iterator_to_array($this->getSourceIterator()))
293
            );
294
        }
295
296 32
        return self::$cache[$this->cacheKey];
297
    }
298
299
    /**
300
     * @return \Iterator
301
     */
302
    public function getIterator()
303
    {
304
        return new \ArrayIterator($this->toArray());
305
    }
306
307
    /**
308
     * @return \Iterator
309
     */
310 32
    public function getSourceIterator()
311
    {
312 32
        if (!$this->sourceResult) {
313 32
            $source = $this->source;
314 32
            $result = $source();
315
316 32
            if ($result instanceof \IteratorAggregate) {
317
                $this->sourceResult = $result->getIterator();
318
            } elseif ($result instanceof \Iterator) {
319 14
                $this->sourceResult = $result;
320 18
            } elseif ($result instanceof \Traversable || is_array($result)) {
321 18
                $iterator = new \ArrayIterator();
322 18
                foreach ($result as $file) {
323 13
                    $iterator->append(
324
                        $file instanceof \SplFileInfo
325 2
                            ? $file
326 13
                            : new SfyFileInfo($file, getcwd(), $file)
327
                    );
328
                }
329 18
                $this->sourceResult = $iterator;
330
            } else {
331
                throw new \RuntimeException(
332
                    sprintf(
333
                        'Iterator or array was expected instead of %s.',
334
                        is_object($result) ? get_class($result) : gettype($result)
335
                    )
336
                );
337
            }
338
        }
339
340 32
        return $this->sourceResult;
341
    }
342
343
    /**
344
     * @return int
345
     */
346 3
    public function count()
347
    {
348 3
        return count($this->toArray());
349
    }
350
}
351