GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Downloader   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 199
Duplicated Lines 6.53 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 13
loc 199
rs 10
c 0
b 0
f 0
wmc 20
lcom 1
cbo 4

13 Methods

Rating   Name   Duplication   Size   Complexity  
A forHost() 0 8 1
A downloadCertificateFromUrl() 0 6 1
A usingPort() 0 6 1
A usingSni() 0 6 1
A withFullChain() 0 6 1
A withVerifyPeer() 0 6 1
A withVerifyPeerName() 0 6 1
A setTimeout() 0 6 1
A fromIpAddress() 0 11 2
A getCertificates() 13 27 1
B fetchCertificates() 0 51 5
A buildFailureException() 0 12 3
A withSocketContextOptions() 0 6 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace Spatie\SslCertificate;
4
5
use Spatie\SslCertificate\Exceptions\CouldNotDownloadCertificate;
6
use Spatie\SslCertificate\Exceptions\InvalidIpAddress;
7
8
class Downloader
9
{
10
    /** @var int */
11
    protected $port = 443;
12
13
    /** @var string */
14
    protected $ipAddress = null;
15
16
    /** @var bool */
17
    protected $usingIpAddress = false;
18
19
    /** @var int */
20
    protected $timeout = 30;
21
22
    /** @var bool */
23
    protected $enableSni = true;
24
25
    /** @var bool */
26
    protected $capturePeerChain = false;
27
28
    /** @var array */
29
    protected $socketContextOptions = [];
30
31
    /** @var bool */
32
    protected $verifyPeer = true;
33
34
    /** @var bool */
35
    protected $verifyPeerName = true;
36
37
    public function usingPort(int $port)
38
    {
39
        $this->port = $port;
40
41
        return $this;
42
    }
43
44
    public function usingSni(bool $sni)
45
    {
46
        $this->enableSni = $sni;
47
48
        return $this;
49
    }
50
51
    public function withSocketContextOptions(array $socketContextOptions)
52
    {
53
        $this->socketContextOptions = $socketContextOptions;
54
55
        return $this;
56
    }
57
58
    public function withFullChain(bool $fullChain)
59
    {
60
        $this->capturePeerChain = $fullChain;
61
62
        return $this;
63
    }
64
65
    public function withVerifyPeer(bool $verifyPeer)
66
    {
67
        $this->verifyPeer = $verifyPeer;
68
69
        return $this;
70
    }
71
72
    public function withVerifyPeerName(bool $verifyPeerName)
73
    {
74
        $this->verifyPeerName = $verifyPeerName;
75
76
        return $this;
77
    }
78
79
    public function setTimeout(int $timeOutInSeconds)
80
    {
81
        $this->timeout = $timeOutInSeconds;
82
83
        return $this;
84
    }
85
86
    public function fromIpAddress(string $ipAddress)
87
    {
88
        if (! filter_var($ipAddress, FILTER_VALIDATE_IP)) {
89
            throw InvalidIpAddress::couldNotValidate($ipAddress);
90
        }
91
92
        $this->ipAddress = $ipAddress;
93
        $this->usingIpAddress = true;
94
95
        return $this;
96
    }
97
98
    public function getCertificates(string $hostName): array
99
    {
100
        $response = $this->fetchCertificates($hostName);
101
        $remoteAddress = $response['remoteAddress'];
102
103
        $peerCertificate = $response['options']['ssl']['peer_certificate'];
104
105
        $peerCertificateChain = $response['options']['ssl']['peer_certificate_chain'] ?? [];
106
107
        $fullCertificateChain = array_merge([$peerCertificate], $peerCertificateChain);
108
109 View Code Duplication
        $certificates = array_map(function ($certificate) use ($remoteAddress) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
110
            $certificateFields = openssl_x509_parse($certificate);
111
112
            $fingerprint = openssl_x509_fingerprint($certificate);
113
            $fingerprintSha256 = openssl_x509_fingerprint($certificate, 'sha256');
114
115
            return new SslCertificate(
116
                $certificateFields,
117
                $fingerprint,
118
                $fingerprintSha256,
119
                $remoteAddress
120
            );
121
        }, $fullCertificateChain);
122
123
        return array_unique($certificates);
124
    }
125
126
    public function forHost(string $hostName): SslCertificate
127
    {
128
        $hostName = (new Url($hostName))->getHostName();
129
130
        $certificates = $this->getCertificates($hostName);
131
132
        return $certificates[0] ?? false;
133
    }
134
135
    public static function downloadCertificateFromUrl(string $url, int $timeout = 30): SslCertificate
136
    {
137
        return (new static())
138
            ->setTimeout($timeout)
139
            ->forHost($url);
140
    }
141
142
    protected function fetchCertificates(string $hostName): array
143
    {
144
        $hostName = (new Url($hostName))->getHostName();
145
146
        $sslOptions = [
147
            'capture_peer_cert' => true,
148
            'capture_peer_cert_chain' => $this->capturePeerChain,
149
            'SNI_enabled' => $this->enableSni,
150
            'peer_name' => $hostName,
151
            'verify_peer' => $this->verifyPeer,
152
            'verify_peer_name' => $this->verifyPeerName,
153
        ];
154
155
        $streamContext = stream_context_create([
156
            'socket' => $this->socketContextOptions,
157
            'ssl' => $sslOptions,
158
        ]);
159
160
        $connectTo = ($this->usingIpAddress)
161
            ? $this->ipAddress
162
            : $hostName;
163
164
        $client = @stream_socket_client(
165
            "ssl://{$connectTo}:{$this->port}",
166
            $errorNumber,
167
            $errorDescription,
168
            $this->timeout,
169
            STREAM_CLIENT_CONNECT,
170
            $streamContext
171
        );
172
173
        if (! empty($errorDescription)) {
174
            throw $this->buildFailureException($connectTo, $errorDescription);
175
        }
176
177
        if (! $client) {
178
            $clientErrorMessage = ($this->usingIpAddress)
179
                ? "Could not connect to `{$connectTo}` or it does not have a certificate matching `${hostName}`."
180
                : "Could not connect to `{$connectTo}`.";
181
182
            throw CouldNotDownloadCertificate::unknownError($hostName, $clientErrorMessage);
183
        }
184
185
        $response = stream_context_get_params($client);
186
187
        $response['remoteAddress'] = stream_socket_get_name($client, true);
188
189
        fclose($client);
190
191
        return $response;
192
    }
193
194
    protected function buildFailureException(string $hostName, string $errorDescription)
195
    {
196
        if (str_contains($errorDescription, 'getaddrinfo failed')) {
197
            return CouldNotDownloadCertificate::hostDoesNotExist($hostName);
198
        }
199
200
        if (str_contains($errorDescription, 'error:14090086')) {
201
            return CouldNotDownloadCertificate::noCertificateInstalled($hostName);
202
        }
203
204
        return CouldNotDownloadCertificate::unknownError($hostName, $errorDescription);
205
    }
206
}
207