GitSignatureCheck::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 12
cts 12
cp 1
rs 9.0856
c 0
b 0
f 0
cc 1
eloc 21
nc 1
nop 10
crap 1

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Roave\ComposerGpgVerify\Package\Git;
6
7
use Composer\Package\PackageInterface;
8
9
/**
10
 * @internal do not use: I will cut you.
11
 *
12
 * This class abstracts the parsing of the output of `git verify-commit` and `git tag -v`, which are broken/useless.
13
 * Ideally, we'd massively simplify this class once GIT 2.12 is mainstream
14
 *
15
 * @link https://stackoverflow.com/questions/17371955/verifying-signed-git-commits/32038784#32038784
16
 */
17
class GitSignatureCheck
18
{
19
    /**
20
     * @var string
21
     */
22
    private $packageName;
23
24
    /**
25
     * @var string
26
     */
27
    private $command;
28
29
    /**
30
     * @var string|null
31
     */
32
    private $commitHash;
33
34
    /**
35
     * @var string|null
36
     */
37
    private $tagName;
38
39
    /**
40
     * @var int
41
     */
42
    private $exitCode;
43
44
    /**
45
     * @var string
46
     */
47
    private $output;
48
49
    /**
50
     * @var bool
51
     */
52
    private $isSigned;
53
54
    /**
55
     * @var bool
56
     */
57
    private $isVerified;
58
59
    /**
60
     * @var string|null
61
     */
62
    private $signatureAuthor;
63
64
    /**
65
     * @var string|null
66
     */
67
    private $signatureKey;
68
69 10
    private function __construct(
70
        string $packageName,
71
        ?string $commitHash,
72
        ?string $tagName,
73
        string $command,
74
        int $exitCode,
75
        string $output,
76
        bool $isSigned,
77
        bool $isVerified,
78
        ?string $signatureAuthor,
79
        ?string $signatureKey
80
    ) {
81 10
        $this->packageName     = $packageName; // @TODO get rid of this, or add it to the error messages
82 10
        $this->commitHash      = $commitHash;
83 10
        $this->tagName         = $tagName;
84 10
        $this->command         = $command;
85 10
        $this->exitCode        = $exitCode;
86 10
        $this->output          = $output;
87 10
        $this->isSigned        = $isSigned;
88 10
        $this->isVerified      = $isVerified;
89 10
        $this->signatureAuthor = $signatureAuthor;
90 10
        $this->signatureKey    = $signatureKey;
91 10
    }
92
93 5
    public static function fromGitCommitCheck(
94
        PackageInterface $package,
95
        string $command,
96
        int $exitCode,
97
        string $output
98
    ) : self {
99 5
        $signatureKey = self::extractKeyIdentifier($output);
100 5
        $signed       = $signatureKey && ! $exitCode;
101
102 5
        return new self(
103 5
            $package->getName(),
104 5
            self::extractCommitHash($output),
105 5
            null,
106 5
            $command,
107 5
            $exitCode,
108 5
            $output,
109 5
            $signed,
110 5
            $signed && self::signatureValidationHasNoWarnings($output),
111 5
            self::extractSignatureAuthor($output),
112 5
            $signatureKey
113
        );
114
    }
115
116 5
    public static function fromGitTagCheck(
117
        PackageInterface $package,
118
        string $command,
119
        int $exitCode,
120
        string $output
121
    ) : self {
122 5
        $signatureKey = self::extractKeyIdentifier($output);
123 5
        $signed       = $signatureKey && ! $exitCode;
124
125 5
        return new self(
126 5
            $package->getName(),
127 5
            self::extractCommitHash($output),
128 5
            self::extractTagName($output),
129 5
            $command,
130 5
            $exitCode,
131 5
            $output,
132 5
            $signed,
133 5
            $signed && self::signatureValidationHasNoWarnings($output),
134 5
            self::extractSignatureAuthor($output),
135 5
            self::extractKeyIdentifier($output)
136
        );
137
    }
138
139 10
    public function asHumanReadableString() : string
140
    {
141 10
        return implode(
142 10
            "\n",
143
            [
144 10
                (($this->isSigned || $this->signatureKey) ? '[SIGNED]' : '[NOT SIGNED]')
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->signatureKey of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
145 10
                . ' ' . ($this->isVerified ? '[VERIFIED]' : '[NOT VERIFIED]')
146 10
                . ' ' . ($this->commitHash ? 'Commit #' . $this->commitHash : '')
147 10
                . ' ' . ($this->tagName ? 'Tag ' . $this->tagName : '')
148 10
                . ' ' . ($this->signatureAuthor ? 'By "' . $this->signatureAuthor . '"' : '')
149 10
                . ' ' . ($this->signatureKey ? '(Key ' . $this->signatureKey . ')' : ''),
150 10
                'Command: ' . $this->command,
151 10
                'Exit code: ' . $this->exitCode,
152 10
                'Output: ' . $this->output,
153
            ]
154
        );
155
    }
156
157 10
    public function canBeTrusted() : bool
158
    {
159 10
        return $this->isVerified;
160
    }
161
162 10
    private static function extractCommitHash(string $output) : ?string
163
    {
164 10
        $keys = array_filter(array_map(
165
            function (string $outputRow) {
166 10
                preg_match('/^(tree|object) ([a-fA-F0-9]{40})$/i', $outputRow, $matches);
167
168 10
                return $matches[2] ?? false;
169 10
            },
170 10
            explode("\n", $output)
171
        ));
172
173 10
        return reset($keys) ?: null;
174
    }
175
176 5
    private static function extractTagName(string $output) : ?string
177
    {
178 5
        $keys = array_filter(array_map(
179
            function (string $outputRow) {
180 5
                preg_match('/^tag (.+)$/i', $outputRow, $matches);
181
182 5
                return $matches[1] ?? false;
183 5
            },
184 5
            explode("\n", $output)
185
        ));
186
187 5
        return reset($keys) ?: null;
188
    }
189
190 10
    private static function extractKeyIdentifier(string $output) : ?string
191
    {
192 10
        $keys = array_filter(array_map(
193
            function (string $outputRow) {
194 10
                preg_match('/gpg:.*using .* key ([a-fA-F0-9]+)/i', $outputRow, $matches);
195
196 10
                return $matches[1] ?? false;
197 10
            },
198 10
            explode("\n", $output)
199
        ));
200
201 10
        return reset($keys) ?: null;
202
    }
203
204 10
    private static function extractSignatureAuthor(string $output) : ?string
205
    {
206 10
        $keys = array_filter(array_map(
207
            function (string $outputRow) {
208 10
                preg_match('/gpg: Good signature from "(.+)" \\[.*\\]/i', $outputRow, $matches);
209
210 10
                return $matches[1] ?? false;
211 10
            },
212 10
            explode("\n", $output)
213
        ));
214
215 10
        return reset($keys) ?: null;
216
    }
217
218 4
    private static function signatureValidationHasNoWarnings(string $output) : bool
219
    {
220 4
        return ! array_filter(
221 4
            explode("\n", $output),
222 4
            function (string $outputRow) {
223 4
                return false !== strpos($outputRow, 'gpg: WARNING: ');
224 4
            }
225
        );
226
    }
227
}
228