Completed
Push — master ( 41ae82...437c90 )
by Dave
13s queued 11s
created

PhpstanJsonResultsParser::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
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\ResultsParsers\PhpstanJsonResultsParser;
14
15
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\AbsoluteFileName;
16
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\InvalidPathException;
17
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\LineNumber;
18
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\Location;
19
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\ProjectRoot;
20
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Common\Type;
21
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\ResultsParser\AnalysisResult;
22
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\ResultsParser\AnalysisResults;
23
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\ResultsParser\AnalysisResultsBuilder;
24
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\ResultsParser\Identifier;
25
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\ResultsParser\ResultsParser;
26
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Utils\ArrayParseException;
27
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Utils\ArrayUtils;
28
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Utils\FqcnRemover;
29
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Utils\JsonUtils;
30
use DaveLiddament\StaticAnalysisResultsBaseliner\Domain\Utils\ParseAtLocationException;
31
32
/**
33
 * Handles PHPStan's JSON output.
34
 *
35
 * NOTE: SARB only deals with errors that are attached to a particular line in a file.
36
 * PHPStan can report general errors (not specific to file). These are ignored by SARB.
37
 */
38
class PhpstanJsonResultsParser implements ResultsParser
39
{
40
    private const LINE = 'line';
41
    private const TYPE = 'message';
42
    private const FILES = 'files';
43
    private const MESSAGES = 'messages';
44
45
    /**
46
     * @var FqcnRemover
47
     */
48
    private $fqcnRemover;
49
50
    public function __construct(FqcnRemover $fqcnRemover)
51
    {
52
        $this->fqcnRemover = $fqcnRemover;
53
    }
54
55
    public function convertFromString(string $resultsAsString, ProjectRoot $projectRoot): AnalysisResults
56
    {
57
        $analysisResultsAsArray = JsonUtils::toArray($resultsAsString);
58
59
        $analysisResultsBuilder = new AnalysisResultsBuilder();
60
61
        try {
62
            $filesErrors = ArrayUtils::getArrayValue($analysisResultsAsArray, self::FILES);
63
        } catch (ArrayParseException $e) {
64
            throw ParseAtLocationException::issueParsing($e, 'Root node');
65
        }
66
67
        /** @psalm-suppress MixedAssignment */
68
        foreach ($filesErrors as $absoluteFileNameAsString => $fileErrors) {
69
            try {
70
                if (!is_string($absoluteFileNameAsString)) {
71
                    throw new ArrayParseException('Expected filename to be of type string');
72
                }
73
74
                ArrayUtils::assertArray($fileErrors);
75
76
                $absoluteFileName = new AbsoluteFileName($absoluteFileNameAsString);
77
78
                $messages = ArrayUtils::getArrayValue($fileErrors, self::MESSAGES);
79
80
                foreach ($messages as $message) {
81
                    ArrayUtils::assertArray($message);
82
                    $analysisResult = $this->convertAnalysisResultFromArray($message, $absoluteFileName, $projectRoot);
83
                    $analysisResultsBuilder->addAnalysisResult($analysisResult);
84
                }
85
            } catch (ArrayParseException | InvalidPathException $e) {
86
                throw ParseAtLocationException::issueParsing($e, "Result [$absoluteFileNameAsString]");
87
            }
88
        }
89
90
        return $analysisResultsBuilder->build();
91
    }
92
93
    /**
94
     * @psalm-param array<mixed> $analysisResultAsArray
95
     *
96
     * @throws ArrayParseException
97
     * @throws InvalidPathException
98
     */
99
    private function convertAnalysisResultFromArray(
100
        array $analysisResultAsArray,
101
        AbsoluteFileName $absoluteFileName,
102
        ProjectRoot $projectRoot
103
    ): AnalysisResult {
104
        $lineAsInt = ArrayUtils::getIntOrNullValue($analysisResultAsArray, self::LINE);
105
106
        // PHPStan sometimes reports errors not assigned to any line number. In this case give the line number as 0
107
        if (null === $lineAsInt) {
108
            $lineAsInt = 0;
109
        }
110
111
        $rawType = ArrayUtils::getStringValue($analysisResultAsArray, self::TYPE);
112
        $type = $this->fqcnRemover->removeRqcn($rawType);
113
114
        $location = Location::fromAbsoluteFileName(
115
            $absoluteFileName,
116
            $projectRoot,
117
            new LineNumber($lineAsInt)
118
        );
119
120
        return new AnalysisResult(
121
            $location,
122
            new Type($type),
123
            $rawType,
124
            $analysisResultAsArray
125
        );
126
    }
127
128
    public function getIdentifier(): Identifier
129
    {
130
        return new PhpstanJsonIdentifier();
131
    }
132
133
    public function showTypeGuessingWarning(): bool
134
    {
135
        return true;
136
    }
137
}
138