Chris@14
|
1 <?php
|
Chris@14
|
2 /*
|
Chris@14
|
3 * This file is part of the php-code-coverage package.
|
Chris@14
|
4 *
|
Chris@14
|
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
|
Chris@14
|
6 *
|
Chris@14
|
7 * For the full copyright and license information, please view the LICENSE
|
Chris@14
|
8 * file that was distributed with this source code.
|
Chris@14
|
9 */
|
Chris@14
|
10
|
Chris@14
|
11 namespace SebastianBergmann\CodeCoverage\Node;
|
Chris@14
|
12
|
Chris@14
|
13 use SebastianBergmann\CodeCoverage\CodeCoverage;
|
Chris@14
|
14
|
Chris@14
|
15 class Builder
|
Chris@14
|
16 {
|
Chris@14
|
17 /**
|
Chris@14
|
18 * @param CodeCoverage $coverage
|
Chris@14
|
19 *
|
Chris@14
|
20 * @return Directory
|
Chris@14
|
21 */
|
Chris@14
|
22 public function build(CodeCoverage $coverage)
|
Chris@14
|
23 {
|
Chris@14
|
24 $files = $coverage->getData();
|
Chris@14
|
25 $commonPath = $this->reducePaths($files);
|
Chris@14
|
26 $root = new Directory(
|
Chris@14
|
27 $commonPath,
|
Chris@14
|
28 null
|
Chris@14
|
29 );
|
Chris@14
|
30
|
Chris@14
|
31 $this->addItems(
|
Chris@14
|
32 $root,
|
Chris@14
|
33 $this->buildDirectoryStructure($files),
|
Chris@14
|
34 $coverage->getTests(),
|
Chris@14
|
35 $coverage->getCacheTokens()
|
Chris@14
|
36 );
|
Chris@14
|
37
|
Chris@14
|
38 return $root;
|
Chris@14
|
39 }
|
Chris@14
|
40
|
Chris@14
|
41 /**
|
Chris@14
|
42 * @param Directory $root
|
Chris@14
|
43 * @param array $items
|
Chris@14
|
44 * @param array $tests
|
Chris@14
|
45 * @param bool $cacheTokens
|
Chris@14
|
46 */
|
Chris@14
|
47 private function addItems(Directory $root, array $items, array $tests, $cacheTokens)
|
Chris@14
|
48 {
|
Chris@14
|
49 foreach ($items as $key => $value) {
|
Chris@14
|
50 if (\substr($key, -2) == '/f') {
|
Chris@14
|
51 $key = \substr($key, 0, -2);
|
Chris@14
|
52
|
Chris@14
|
53 if (\file_exists($root->getPath() . DIRECTORY_SEPARATOR . $key)) {
|
Chris@14
|
54 $root->addFile($key, $value, $tests, $cacheTokens);
|
Chris@14
|
55 }
|
Chris@14
|
56 } else {
|
Chris@14
|
57 $child = $root->addDirectory($key);
|
Chris@14
|
58 $this->addItems($child, $value, $tests, $cacheTokens);
|
Chris@14
|
59 }
|
Chris@14
|
60 }
|
Chris@14
|
61 }
|
Chris@14
|
62
|
Chris@14
|
63 /**
|
Chris@14
|
64 * Builds an array representation of the directory structure.
|
Chris@14
|
65 *
|
Chris@14
|
66 * For instance,
|
Chris@14
|
67 *
|
Chris@14
|
68 * <code>
|
Chris@14
|
69 * Array
|
Chris@14
|
70 * (
|
Chris@14
|
71 * [Money.php] => Array
|
Chris@14
|
72 * (
|
Chris@14
|
73 * ...
|
Chris@14
|
74 * )
|
Chris@14
|
75 *
|
Chris@14
|
76 * [MoneyBag.php] => Array
|
Chris@14
|
77 * (
|
Chris@14
|
78 * ...
|
Chris@14
|
79 * )
|
Chris@14
|
80 * )
|
Chris@14
|
81 * </code>
|
Chris@14
|
82 *
|
Chris@14
|
83 * is transformed into
|
Chris@14
|
84 *
|
Chris@14
|
85 * <code>
|
Chris@14
|
86 * Array
|
Chris@14
|
87 * (
|
Chris@14
|
88 * [.] => Array
|
Chris@14
|
89 * (
|
Chris@14
|
90 * [Money.php] => Array
|
Chris@14
|
91 * (
|
Chris@14
|
92 * ...
|
Chris@14
|
93 * )
|
Chris@14
|
94 *
|
Chris@14
|
95 * [MoneyBag.php] => Array
|
Chris@14
|
96 * (
|
Chris@14
|
97 * ...
|
Chris@14
|
98 * )
|
Chris@14
|
99 * )
|
Chris@14
|
100 * )
|
Chris@14
|
101 * </code>
|
Chris@14
|
102 *
|
Chris@14
|
103 * @param array $files
|
Chris@14
|
104 *
|
Chris@14
|
105 * @return array
|
Chris@14
|
106 */
|
Chris@14
|
107 private function buildDirectoryStructure($files)
|
Chris@14
|
108 {
|
Chris@14
|
109 $result = [];
|
Chris@14
|
110
|
Chris@14
|
111 foreach ($files as $path => $file) {
|
Chris@14
|
112 $path = \explode('/', $path);
|
Chris@14
|
113 $pointer = &$result;
|
Chris@14
|
114 $max = \count($path);
|
Chris@14
|
115
|
Chris@14
|
116 for ($i = 0; $i < $max; $i++) {
|
Chris@14
|
117 if ($i == ($max - 1)) {
|
Chris@14
|
118 $type = '/f';
|
Chris@14
|
119 } else {
|
Chris@14
|
120 $type = '';
|
Chris@14
|
121 }
|
Chris@14
|
122
|
Chris@14
|
123 $pointer = &$pointer[$path[$i] . $type];
|
Chris@14
|
124 }
|
Chris@14
|
125
|
Chris@14
|
126 $pointer = $file;
|
Chris@14
|
127 }
|
Chris@14
|
128
|
Chris@14
|
129 return $result;
|
Chris@14
|
130 }
|
Chris@14
|
131
|
Chris@14
|
132 /**
|
Chris@14
|
133 * Reduces the paths by cutting the longest common start path.
|
Chris@14
|
134 *
|
Chris@14
|
135 * For instance,
|
Chris@14
|
136 *
|
Chris@14
|
137 * <code>
|
Chris@14
|
138 * Array
|
Chris@14
|
139 * (
|
Chris@14
|
140 * [/home/sb/Money/Money.php] => Array
|
Chris@14
|
141 * (
|
Chris@14
|
142 * ...
|
Chris@14
|
143 * )
|
Chris@14
|
144 *
|
Chris@14
|
145 * [/home/sb/Money/MoneyBag.php] => Array
|
Chris@14
|
146 * (
|
Chris@14
|
147 * ...
|
Chris@14
|
148 * )
|
Chris@14
|
149 * )
|
Chris@14
|
150 * </code>
|
Chris@14
|
151 *
|
Chris@14
|
152 * is reduced to
|
Chris@14
|
153 *
|
Chris@14
|
154 * <code>
|
Chris@14
|
155 * Array
|
Chris@14
|
156 * (
|
Chris@14
|
157 * [Money.php] => Array
|
Chris@14
|
158 * (
|
Chris@14
|
159 * ...
|
Chris@14
|
160 * )
|
Chris@14
|
161 *
|
Chris@14
|
162 * [MoneyBag.php] => Array
|
Chris@14
|
163 * (
|
Chris@14
|
164 * ...
|
Chris@14
|
165 * )
|
Chris@14
|
166 * )
|
Chris@14
|
167 * </code>
|
Chris@14
|
168 *
|
Chris@14
|
169 * @param array $files
|
Chris@14
|
170 *
|
Chris@14
|
171 * @return string
|
Chris@14
|
172 */
|
Chris@14
|
173 private function reducePaths(&$files)
|
Chris@14
|
174 {
|
Chris@14
|
175 if (empty($files)) {
|
Chris@14
|
176 return '.';
|
Chris@14
|
177 }
|
Chris@14
|
178
|
Chris@14
|
179 $commonPath = '';
|
Chris@14
|
180 $paths = \array_keys($files);
|
Chris@14
|
181
|
Chris@14
|
182 if (\count($files) == 1) {
|
Chris@14
|
183 $commonPath = \dirname($paths[0]) . '/';
|
Chris@14
|
184 $files[\basename($paths[0])] = $files[$paths[0]];
|
Chris@14
|
185
|
Chris@14
|
186 unset($files[$paths[0]]);
|
Chris@14
|
187
|
Chris@14
|
188 return $commonPath;
|
Chris@14
|
189 }
|
Chris@14
|
190
|
Chris@14
|
191 $max = \count($paths);
|
Chris@14
|
192
|
Chris@14
|
193 for ($i = 0; $i < $max; $i++) {
|
Chris@14
|
194 // strip phar:// prefixes
|
Chris@14
|
195 if (\strpos($paths[$i], 'phar://') === 0) {
|
Chris@14
|
196 $paths[$i] = \substr($paths[$i], 7);
|
Chris@14
|
197 $paths[$i] = \strtr($paths[$i], '/', DIRECTORY_SEPARATOR);
|
Chris@14
|
198 }
|
Chris@14
|
199 $paths[$i] = \explode(DIRECTORY_SEPARATOR, $paths[$i]);
|
Chris@14
|
200
|
Chris@14
|
201 if (empty($paths[$i][0])) {
|
Chris@14
|
202 $paths[$i][0] = DIRECTORY_SEPARATOR;
|
Chris@14
|
203 }
|
Chris@14
|
204 }
|
Chris@14
|
205
|
Chris@14
|
206 $done = false;
|
Chris@14
|
207 $max = \count($paths);
|
Chris@14
|
208
|
Chris@14
|
209 while (!$done) {
|
Chris@14
|
210 for ($i = 0; $i < $max - 1; $i++) {
|
Chris@14
|
211 if (!isset($paths[$i][0]) ||
|
Chris@14
|
212 !isset($paths[$i + 1][0]) ||
|
Chris@14
|
213 $paths[$i][0] != $paths[$i + 1][0]) {
|
Chris@14
|
214 $done = true;
|
Chris@14
|
215
|
Chris@14
|
216 break;
|
Chris@14
|
217 }
|
Chris@14
|
218 }
|
Chris@14
|
219
|
Chris@14
|
220 if (!$done) {
|
Chris@14
|
221 $commonPath .= $paths[0][0];
|
Chris@14
|
222
|
Chris@14
|
223 if ($paths[0][0] != DIRECTORY_SEPARATOR) {
|
Chris@14
|
224 $commonPath .= DIRECTORY_SEPARATOR;
|
Chris@14
|
225 }
|
Chris@14
|
226
|
Chris@14
|
227 for ($i = 0; $i < $max; $i++) {
|
Chris@14
|
228 \array_shift($paths[$i]);
|
Chris@14
|
229 }
|
Chris@14
|
230 }
|
Chris@14
|
231 }
|
Chris@14
|
232
|
Chris@14
|
233 $original = \array_keys($files);
|
Chris@14
|
234 $max = \count($original);
|
Chris@14
|
235
|
Chris@14
|
236 for ($i = 0; $i < $max; $i++) {
|
Chris@14
|
237 $files[\implode('/', $paths[$i])] = $files[$original[$i]];
|
Chris@14
|
238 unset($files[$original[$i]]);
|
Chris@14
|
239 }
|
Chris@14
|
240
|
Chris@14
|
241 \ksort($files);
|
Chris@14
|
242
|
Chris@14
|
243 return \substr($commonPath, 0, -1);
|
Chris@14
|
244 }
|
Chris@14
|
245 }
|