Completed
Pull Request — 2.x (#402)
by
unknown
02:17
created

Enumerator::getInPaths()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.016

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 9
cts 10
cp 0.9
rs 9.6333
c 0
b 0
f 0
cc 4
nc 5
nop 0
crap 4.016
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 ArrayIterator;
14
use CallbackFilterIterator;
15
use InvalidArgumentException;
16
use LogicException;
17
use RecursiveIteratorIterator;
18
use SplFileInfo;
19
use Symfony\Component\Finder\Finder;
20
use UnexpectedValueException;
21
22
/**
23
 * Enumerates files in the concrete directory, applying filtration logic
24
 */
25
class Enumerator
26
{
27
28
    /**
29
     * Path to the root directory, where enumeration should start
30
     *
31
     * @var string
32
     */
33
    private $rootDirectory;
34
35
    /**
36
     * List of additional include paths, should be below rootDirectory
37
     *
38
     * @var array
39
     */
40
    private $includePaths;
41
42
    /**
43
     * List of additional exclude paths, should be below rootDirectory
44
     *
45
     * @var array
46
     */
47
    private $excludePaths;
48
49
    /**
50
     * Initializes an enumerator
51
     *
52
     * @param string $rootDirectory Path to the root directory
53
     * @param array  $includePaths  List of additional include paths
54
     * @param array  $excludePaths  List of additional exclude paths
55
     */
56 7
    public function __construct($rootDirectory, array $includePaths = [], array $excludePaths = [])
57
    {
58 7
        $this->rootDirectory = $rootDirectory;
59 7
        $this->includePaths = $includePaths;
60 7
        $this->excludePaths = $excludePaths;
61 7
    }
62
63
    /**
64
     * Returns an enumerator for files
65
     *
66
     * @return \Iterator|SplFileInfo[]
67
     * @throws UnexpectedValueException
68
     * @throws InvalidArgumentException
69
     * @throws LogicException
70
     */
71 7
    public function enumerate()
72
    {
73 7
        $finder = new Finder();
74 7
        $finder->files()
75 7
            ->name('*.php')
76 7
            ->in($this->getInPaths());
77
78 7
        foreach ($this->getExcludePaths() as $path) {
79 4
            $finder->notPath($path);
80
        }
81
82 7
        $iterator = $finder->getIterator();
83
84
        // on Windows platrofrm the default iterator is unable to rewind, not sure why
85 7
        if (strpos(PHP_OS, 'WIN') === 0) {
86
            $iterator = new ArrayIterator(iterator_to_array($iterator));
87
        }
88
89 7
        return $iterator;
90
    }
91
92
    /**
93
     * @return array
94
     * @throws UnexpectedValueException
95
     */
96 7
    private function getInPaths()
97
    {
98 7
        $inPaths = [];
99
100 7
        foreach ($this->includePaths as $path) {
101 1
            if (strpos($path, $this->rootDirectory, 0) === false) {
102
                throw new UnexpectedValueException(sprintf('Path %s is not in %s', $path, $this->rootDirectory));
103
            }
104
105 1
            $path = str_replace('*', '', $path);
106 1
            $inPaths[] = $path;
107
        }
108
109 7
        if (empty($inPaths)) {
110 6
            $inPaths[] = $this->rootDirectory;
111
        }
112
113 7
        return $inPaths;
114
    }
115
116
    /**
117
     * @return array
118
     */
119 7
    private function getExcludePaths()
120
    {
121 7
        $excludePaths = [];
122
123 7
        foreach ($this->excludePaths as $path) {
124 4
            $path = str_replace('*', '.*', $path);
125 4
            $excludePaths[] = '#' . str_replace($this->rootDirectory . '/', '', $path) . '#';
126
        }
127
128 7
        return $excludePaths;
129
    }
130
131
    /**
132
     * Returns a filter callback for enumerating files
133
     *
134
     * @return \Closure
135
     */
136
    public function getFilter()
137
    {
138
        $rootDirectory = $this->rootDirectory;
139
        $includePaths = $this->includePaths;
140
        $excludePaths = $this->excludePaths;
141
142
        return function (SplFileInfo $file) use ($rootDirectory, $includePaths, $excludePaths) {
143
144
            if ($file->getExtension() !== 'php') {
145
                return false;
146
            }
147
148
            $fullPath = $this->getFileFullPath($file);
149
            // Do not touch files that not under rootDirectory
150
            if (strpos($fullPath, $rootDirectory) !== 0) {
151
                return false;
152
            }
153
154
            if (!empty($includePaths)) {
155
                $found = false;
156
                foreach ($includePaths as $includePattern) {
157
                    if (fnmatch("{$includePattern}*", $fullPath, FNM_NOESCAPE)) {
158
                        $found = true;
159
                        break;
160
                    }
161
                }
162
                if (!$found) {
163
                    return false;
164
                }
165
            }
166
167
            foreach ($excludePaths as $excludePattern) {
168
                if (fnmatch("{$excludePattern}*", $fullPath, FNM_NOESCAPE)) {
169
                    return false;
170
                }
171
            }
172
173
            return true;
174
        };
175
    }
176
177
    /**
178
     * Return the real path of the given file
179
     *
180
     * This is used for testing purpose with virtual file system.
181
     * In a vfs the 'realPath' methode will always return false.
182
     * So we have a chance to mock this single function to return different path.
183
     *
184
     * @param SplFileInfo $file
185
     *
186
     * @return string
187
     */
188
    protected function getFileFullPath(SplFileInfo $file)
189
    {
190
        return $file->getRealPath();
191
    }
192
193
}
194