Passed
Pull Request — master (#65)
by Dave
02:08
created

PhpmdJsonResultsParser::showTypeGuessingWarning()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
/**
4
 * Static Analysis Results Baseliner (sarb).
5
 *
6
 * (c) Dave Liddament
7
 *
8
 * For the full copyright and licence information please view the LICENSE file distributed with this source code.
9
 */
10
11
declare(strict_types=1);
12
13
namespace DaveLiddament\StaticAnalysisResultsBaseliner\Plugins\PhpmdJsonResultsParser;
14
15
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\FileName;
16
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\LineNumber;
17
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\Location;
18
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\ProjectRoot;
19
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\Type;
20
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\File\InvalidFileFormatException;
21
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\ResultsParser\AnalysisResult;
22
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\ResultsParser\AnalysisResults;
23
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\ResultsParser\Identifier;
24
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\ResultsParser\ResultsParser;
25
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Utils\ArrayParseException;
26
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Utils\ArrayUtils;
27
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Utils\JsonParseException;
28
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Utils\JsonUtils;
29
30
/**
31
 * Handles PHPMD JSON output.
32
 */
33
class PhpmdJsonResultsParser implements ResultsParser
34
{
35
    /**
36
     * {@inheritdoc}
37
     */
38
    public function convertFromString(string $resultsAsString, ProjectRoot $projectRoot): AnalysisResults
39
    {
40
        try {
41
            $asArray = JsonUtils::toArray($resultsAsString);
42
        } catch (JsonParseException $e) {
43
            throw new InvalidFileFormatException('Not a valid JSON format');
44
        }
45
46
        return $this->convertFromArray($asArray, $projectRoot);
47
    }
48
49
    /**
50
     * {@inheritdoc}
51
     */
52
    public function convertToString(AnalysisResults $analysisResults): string
53
    {
54
        /** @var array<string, array<mixed>> $files */
55
        $files = [];
56
57
        foreach ($analysisResults->getAnalysisResults() as $analysisResult) {
58
            $fullDetails = JsonUtils::toArray($analysisResult->getFullDetails());
59
            $violation = ArrayUtils::getArrayValue($fullDetails, 'violation');
60
            $absoluteFileName = ArrayUtils::getStringValue($fullDetails, 'fileName');
61
62
            if (!array_key_exists($absoluteFileName, $files)) {
63
                $files[$absoluteFileName] = [];
64
            }
65
            $files[$absoluteFileName][] = $violation;
66
        }
67
68
        $asArray = [
69
            'version' => '@project.version@',
70
            'package' => 'phpmd',
71
            'timestamp' => (new \DateTimeImmutable())->format('c'),
72
            'files' => [],
73
        ];
74
75
        foreach ($files as $fileName => $violations) {
76
            $asArray['files'][] = [
77
                'file' => $fileName,
78
                'violations' => $violations,
79
            ];
80
        }
81
82
        return JsonUtils::toString($asArray);
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88
    public function getIdentifier(): Identifier
89
    {
90
        return new PhpmdJsonIdentifier();
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96
    public function showTypeGuessingWarning(): bool
97
    {
98
        return false;
99
    }
100
101
    /**
102
     * Converts from an array.
103
     *
104
     * @phpstan-param array<mixed> $analysisResultsAsArray
105
     *
106
     * @throws InvalidFileFormatException
107
     */
108
    private function convertFromArray(array $analysisResultsAsArray, ProjectRoot $projectRoot): AnalysisResults
109
    {
110
        $analysisResults = new AnalysisResults();
111
112
        try {
113
            $filesWithProblems = ArrayUtils::getArrayValue($analysisResultsAsArray, 'files');
114
        } catch (ArrayParseException $e) {
115
            throw new InvalidFileFormatException("Missing 'files' key at root level of JSON structure");
116
        }
117
118
        try {
119
            /** @psalm-suppress MixedAssignment */
120
            foreach ($filesWithProblems as $fileWithProblems) {
121
                ArrayUtils::assertArray($fileWithProblems);
122
                $absoluteFileName = ArrayUtils::getStringValue($fileWithProblems, 'file');
123
                $relativeFileName = $projectRoot->getPathRelativeToRootDirectory($absoluteFileName);
124
                $fileName = new FileName($relativeFileName);
125
126
                $violations = ArrayUtils::getArrayValue($fileWithProblems, 'violations');
127
128
                $this->processViolationsInFile($analysisResults, $absoluteFileName, $fileName, $violations);
129
            }
130
        } catch (ArrayParseException $e) {
131
            throw new InvalidFileFormatException("Invalid file format: {$e->getMessage()}");
132
        }
133
134
        return $analysisResults;
135
    }
136
137
    /**
138
     * @phpstan-param array<mixed> $violations
139
     *
140
     * @throws InvalidFileFormatException
141
     */
142
    private function processViolationsInFile(
143
        AnalysisResults $analysisResults,
144
        string $absoulteFileName,
145
        FileName $fileName,
146
        array $violations
147
    ): void {
148
        $violationCount = 1;
149
        /** @psalm-suppress MixedAssignment */
150
        foreach ($violations as $violation) {
151
            try {
152
                ArrayUtils::assertArray($violation);
153
                $analysisResult = $this->processViolation($absoulteFileName, $fileName, $violation);
154
                $analysisResults->addAnalysisResult($analysisResult);
155
                ++$violationCount;
156
            } catch (ArrayParseException | JsonParseException $e) {
157
                throw new InvalidFileFormatException("Can not process violation {$violationCount} for file {$fileName->getFileName()}");
158
            }
159
        }
160
    }
161
162
    /**
163
     * @phpstan-param array<mixed> $violation
164
     *
165
     * @throws ArrayParseException
166
     * @throws JsonParseException
167
     */
168
    private function processViolation(string $aboluteFileName, FileName $fileName, array $violation): AnalysisResult
169
    {
170
        $typeAsString = ArrayUtils::getStringValue($violation, 'rule');
171
        $type = new Type($typeAsString);
172
173
        $message = ArrayUtils::getStringValue($violation, 'description');
174
175
        $lineAsInt = ArrayUtils::getIntValue($violation, 'beginLine');
176
177
        $location = new Location(
178
            $fileName,
179
            new LineNumber($lineAsInt)
180
        );
181
182
        $detials = [
183
            'fileName' => $aboluteFileName,
184
            'violation' => $violation,
185
        ];
186
187
        return new AnalysisResult(
188
            $location,
189
            $type,
190
            $message,
191
            JsonUtils::toString($detials)
192
        );
193
    }
194
}
195