Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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