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\Report\Xml;
11
 
12
use const DIRECTORY_SEPARATOR;
13
use const PHP_EOL;
14
use function count;
15
use function dirname;
16
use function file_get_contents;
17
use function file_put_contents;
18
use function is_array;
19
use function is_dir;
20
use function is_file;
21
use function is_writable;
22
use function libxml_clear_errors;
23
use function libxml_get_errors;
24
use function libxml_use_internal_errors;
25
use function sprintf;
26
use function strlen;
27
use function substr;
28
use DateTimeImmutable;
29
use DOMDocument;
30
use SebastianBergmann\CodeCoverage\CodeCoverage;
31
use SebastianBergmann\CodeCoverage\Driver\PathExistsButIsNotDirectoryException;
32
use SebastianBergmann\CodeCoverage\Driver\WriteOperationFailedException;
33
use SebastianBergmann\CodeCoverage\Node\AbstractNode;
34
use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
35
use SebastianBergmann\CodeCoverage\Node\File as FileNode;
36
use SebastianBergmann\CodeCoverage\Util\Filesystem as DirectoryUtil;
37
use SebastianBergmann\CodeCoverage\Version;
38
use SebastianBergmann\CodeCoverage\XmlException;
39
use SebastianBergmann\Environment\Runtime;
40
 
41
final class Facade
42
{
43
    /**
44
     * @var string
45
     */
46
    private $target;
47
 
48
    /**
49
     * @var Project
50
     */
51
    private $project;
52
 
53
    /**
54
     * @var string
55
     */
56
    private $phpUnitVersion;
57
 
58
    public function __construct(string $version)
59
    {
60
        $this->phpUnitVersion = $version;
61
    }
62
 
63
    /**
64
     * @throws XmlException
65
     */
66
    public function process(CodeCoverage $coverage, string $target): void
67
    {
68
        if (substr($target, -1, 1) !== DIRECTORY_SEPARATOR) {
69
            $target .= DIRECTORY_SEPARATOR;
70
        }
71
 
72
        $this->target = $target;
73
        $this->initTargetDirectory($target);
74
 
75
        $report = $coverage->getReport();
76
 
77
        $this->project = new Project(
78
            $coverage->getReport()->name()
79
        );
80
 
81
        $this->setBuildInformation();
82
        $this->processTests($coverage->getTests());
83
        $this->processDirectory($report, $this->project);
84
 
85
        $this->saveDocument($this->project->asDom(), 'index');
86
    }
87
 
88
    private function setBuildInformation(): void
89
    {
90
        $buildNode = $this->project->buildInformation();
91
        $buildNode->setRuntimeInformation(new Runtime);
92
        $buildNode->setBuildTime(new DateTimeImmutable);
93
        $buildNode->setGeneratorVersions($this->phpUnitVersion, Version::id());
94
    }
95
 
96
    /**
97
     * @throws PathExistsButIsNotDirectoryException
98
     * @throws WriteOperationFailedException
99
     */
100
    private function initTargetDirectory(string $directory): void
101
    {
102
        if (is_file($directory)) {
103
            if (!is_dir($directory)) {
104
                throw new PathExistsButIsNotDirectoryException($directory);
105
            }
106
 
107
            if (!is_writable($directory)) {
108
                throw new WriteOperationFailedException($directory);
109
            }
110
        }
111
 
112
        DirectoryUtil::createDirectory($directory);
113
    }
114
 
115
    /**
116
     * @throws XmlException
117
     */
118
    private function processDirectory(DirectoryNode $directory, Node $context): void
119
    {
120
        $directoryName = $directory->name();
121
 
122
        if ($this->project->projectSourceDirectory() === $directoryName) {
123
            $directoryName = '/';
124
        }
125
 
126
        $directoryObject = $context->addDirectory($directoryName);
127
 
128
        $this->setTotals($directory, $directoryObject->totals());
129
 
130
        foreach ($directory->directories() as $node) {
131
            $this->processDirectory($node, $directoryObject);
132
        }
133
 
134
        foreach ($directory->files() as $node) {
135
            $this->processFile($node, $directoryObject);
136
        }
137
    }
138
 
139
    /**
140
     * @throws XmlException
141
     */
142
    private function processFile(FileNode $file, Directory $context): void
143
    {
144
        $fileObject = $context->addFile(
145
            $file->name(),
146
            $file->id() . '.xml'
147
        );
148
 
149
        $this->setTotals($file, $fileObject->totals());
150
 
151
        $path = substr(
152
            $file->pathAsString(),
153
            strlen($this->project->projectSourceDirectory())
154
        );
155
 
156
        $fileReport = new Report($path);
157
 
158
        $this->setTotals($file, $fileReport->totals());
159
 
160
        foreach ($file->classesAndTraits() as $unit) {
161
            $this->processUnit($unit, $fileReport);
162
        }
163
 
164
        foreach ($file->functions() as $function) {
165
            $this->processFunction($function, $fileReport);
166
        }
167
 
168
        foreach ($file->lineCoverageData() as $line => $tests) {
169
            if (!is_array($tests) || count($tests) === 0) {
170
                continue;
171
            }
172
 
173
            $coverage = $fileReport->lineCoverage((string) $line);
174
 
175
            foreach ($tests as $test) {
176
                $coverage->addTest($test);
177
            }
178
 
179
            $coverage->finalize();
180
        }
181
 
182
        $fileReport->source()->setSourceCode(
183
            file_get_contents($file->pathAsString())
184
        );
185
 
186
        $this->saveDocument($fileReport->asDom(), $file->id());
187
    }
188
 
189
    private function processUnit(array $unit, Report $report): void
190
    {
191
        if (isset($unit['className'])) {
192
            $unitObject = $report->classObject($unit['className']);
193
        } else {
194
            $unitObject = $report->traitObject($unit['traitName']);
195
        }
196
 
197
        $unitObject->setLines(
198
            $unit['startLine'],
199
            $unit['executableLines'],
200
            $unit['executedLines']
201
        );
202
 
203
        $unitObject->setCrap((float) $unit['crap']);
204
        $unitObject->setNamespace($unit['namespace']);
205
 
206
        foreach ($unit['methods'] as $method) {
207
            $methodObject = $unitObject->addMethod($method['methodName']);
208
            $methodObject->setSignature($method['signature']);
209
            $methodObject->setLines((string) $method['startLine'], (string) $method['endLine']);
210
            $methodObject->setCrap($method['crap']);
211
            $methodObject->setTotals(
212
                (string) $method['executableLines'],
213
                (string) $method['executedLines'],
214
                (string) $method['coverage']
215
            );
216
        }
217
    }
218
 
219
    private function processFunction(array $function, Report $report): void
220
    {
221
        $functionObject = $report->functionObject($function['functionName']);
222
 
223
        $functionObject->setSignature($function['signature']);
224
        $functionObject->setLines((string) $function['startLine']);
225
        $functionObject->setCrap($function['crap']);
226
        $functionObject->setTotals((string) $function['executableLines'], (string) $function['executedLines'], (string) $function['coverage']);
227
    }
228
 
229
    private function processTests(array $tests): void
230
    {
231
        $testsObject = $this->project->tests();
232
 
233
        foreach ($tests as $test => $result) {
234
            $testsObject->addTest($test, $result);
235
        }
236
    }
237
 
238
    private function setTotals(AbstractNode $node, Totals $totals): void
239
    {
240
        $loc = $node->linesOfCode();
241
 
242
        $totals->setNumLines(
243
            $loc['linesOfCode'],
244
            $loc['commentLinesOfCode'],
245
            $loc['nonCommentLinesOfCode'],
246
            $node->numberOfExecutableLines(),
247
            $node->numberOfExecutedLines()
248
        );
249
 
250
        $totals->setNumClasses(
251
            $node->numberOfClasses(),
252
            $node->numberOfTestedClasses()
253
        );
254
 
255
        $totals->setNumTraits(
256
            $node->numberOfTraits(),
257
            $node->numberOfTestedTraits()
258
        );
259
 
260
        $totals->setNumMethods(
261
            $node->numberOfMethods(),
262
            $node->numberOfTestedMethods()
263
        );
264
 
265
        $totals->setNumFunctions(
266
            $node->numberOfFunctions(),
267
            $node->numberOfTestedFunctions()
268
        );
269
    }
270
 
271
    private function targetDirectory(): string
272
    {
273
        return $this->target;
274
    }
275
 
276
    /**
277
     * @throws XmlException
278
     */
279
    private function saveDocument(DOMDocument $document, string $name): void
280
    {
281
        $filename = sprintf('%s/%s.xml', $this->targetDirectory(), $name);
282
 
283
        $document->formatOutput       = true;
284
        $document->preserveWhiteSpace = false;
285
        $this->initTargetDirectory(dirname($filename));
286
 
287
        file_put_contents($filename, $this->documentAsString($document));
288
    }
289
 
290
    /**
291
     * @throws XmlException
292
     *
293
     * @see https://bugs.php.net/bug.php?id=79191
294
     */
295
    private function documentAsString(DOMDocument $document): string
296
    {
297
        $xmlErrorHandling = libxml_use_internal_errors(true);
298
        $xml              = $document->saveXML();
299
 
300
        if ($xml === false) {
301
            $message = 'Unable to generate the XML';
302
 
303
            foreach (libxml_get_errors() as $error) {
304
                $message .= PHP_EOL . $error->message;
305
            }
306
 
307
            throw new XmlException($message);
308
        }
309
 
310
        libxml_clear_errors();
311
        libxml_use_internal_errors($xmlErrorHandling);
312
 
313
        return $xml;
314
    }
315
}