Completed
Push — master ( c672a3...ac777e )
by Stefan
01:27
created

Port   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 248
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 34
lcom 1
cbo 2
dl 0
loc 248
rs 9.68
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
A for() 0 4 1
A __construct() 0 4 1
A sanitizeDomain() 0 8 2
A getDomain() 0 4 1
A getDefaultPorts() 0 4 1
A getProtocols() 0 4 1
A addResult() 0 8 1
A check() 0 18 2
A getUri() 0 4 1
A checkPort() 0 16 3
A checkNonUdpPort() 0 13 2
A checkUdpPort() 0 37 4
A useTcp() 0 6 1
A useTls() 0 6 1
A useUdp() 0 6 1
A useSsl() 0 6 1
A setTimeout() 0 6 1
A setProtocol() 0 4 1
A addPortWithProtocol() 0 4 1
B resolvePorts() 0 25 7
1
<?php
2
3
namespace Gemz\Port;
4
5
use React\Promise\Deferred;
6
use React\Promise\PromiseInterface;
7
use React\Socket\Connector;
8
use React\EventLoop\Factory;
9
use React\EventLoop\LoopInterface;
10
use React\Socket\ConnectionInterface;
11
use Gemz\Port\Exceptions\InvalidArgument;
12
13
class Port
14
{
15
    const PROTOCOL_TCP = 'tcp';
16
    const PROTOCOL_UDP = 'udp';
17
    const PROTOCOL_SSL = 'ssl';
18
    const PROTOCOL_TLS = 'tls';
19
20
    /** @var string */
21
    protected $domain;
22
23
    /** @var string */
24
    protected $protocol = self::PROTOCOL_TCP;
25
26
    /** @var float */
27
    protected $timeout = 0.5;
28
29
    /** @var array */
30
    protected $protocols = ['tcp', 'tls', 'udp', 'ssl'];
31
32
    /**
33
     * @var array
34
     */
35
    protected $defaultPorts = [
36
        22 => 'tcp',
37
        80 => 'tcp',
38
        443 => 'tls',
39
        8080 => 'tcp',
40
        3306 => 'tcp',
41
    ];
42
43
    /** @var array */
44
    protected $result = [];
45
46
    /** @var array */
47
    protected $portsWithProtocol = [];
48
49
    public static function for(string $domain): self
50
    {
51
        return new self($domain);
52
    }
53
54
    public function __construct(string $domain)
55
    {
56
        $this->domain = $this->sanitizeDomain($domain);
57
    }
58
59
    protected function sanitizeDomain(string $domain): string
60
    {
61
        if (empty($domain)) {
62
            throw InvalidArgument::domainIsNotValid($domain);
63
        }
64
65
        return str_replace(['http://', 'https://'], '', $domain);
66
    }
67
68
    public function getDomain(): string
69
    {
70
        return $this->domain;
71
    }
72
73
    public function getDefaultPorts(): array
74
    {
75
        return $this->defaultPorts;
76
    }
77
78
    public function getProtocols(): array
79
    {
80
        return $this->protocols;
81
    }
82
83
    protected function addResult(int $port, string $protocol, bool $isOpen): void
84
    {
85
        array_push($this->result, [
86
           'port' => $port,
87
           'protocol' => $protocol,
88
           'open' => $isOpen
89
        ]);
90
    }
91
92
    /**
93
     * @param array|int ...$ports
94
     *
95
     * @return array
96
     */
97
    public function check(...$ports): array
98
    {
99
        $this->resolvePorts($ports);
100
101
        foreach ($this->portsWithProtocol as $port => $protocol) {
102
            $this->checkPort($port, $protocol)
103
                ->then(
104
                    function () use ($port, $protocol) {
105
                        $this->addResult($port, $protocol, true);
106
                    },
107
                    function () use ($port, $protocol) {
108
                        $this->addResult($port, $protocol, false);
109
                    }
110
                );
111
        }
112
113
        return $this->result;
114
    }
115
116
    protected function getUri(string $protocol): string
117
    {
118
        return "{$protocol}://{$this->domain}";
119
    }
120
121
    protected function checkPort(int $port, string $protocol): PromiseInterface
122
    {
123
        $deferred = new Deferred();
124
125
        $portIsOpen = ($protocol == self::PROTOCOL_UDP)
126
            ? $this->checkUdpPort($port)
127
            : $portIsOpen = $this->checkNonUdpPort($port, $protocol);
128
129
        if ($portIsOpen) {
130
            $deferred->resolve();
131
        } else {
132
            $deferred->reject();
133
        }
134
135
        return $deferred->promise();
136
    }
137
138
    protected function checkNonUdpPort(int $port, string $protocol): bool
139
    {
140
        $handler = @fsockopen(
141
            $this->getUri($protocol), $port, $errno, $errstr, $this->timeout
142
        );
143
144
        if (is_resource($handler)) {
145
            fclose($handler);
146
            return true;
147
        }
148
149
        return false;
150
    }
151
152
    protected function checkUdpPort($port): bool
153
    {
154
        $handler = @fsockopen(
155
            $this->getUri(self::PROTOCOL_UDP), $port, $errno, $errstr, $this->timeout
156
        );
157
158
        if (! $handler) {
159
            return false;
160
        }
161
162
        socket_set_timeout($handler, $this->timeout);
163
164
        $write = fwrite($handler, "x00");
165
166
        if (! $write) {
167
            return false;
168
        }
169
170
        $startTime = time();
171
172
        $header = fread($handler, 1);
0 ignored issues
show
Unused Code introduced by
$header is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
173
174
        $endTime = time();
175
176
        $timeDiff = $endTime - $startTime;
177
178
179
        if ($timeDiff >= $this->timeout) {
180
            fclose($handler);
181
182
            return true;
183
        }
184
185
        fclose($handler);
186
187
        return false;
188
    }
189
190
    public function useTcp(): self
191
    {
192
        $this->setProtocol(self::PROTOCOL_TCP);
193
194
        return $this;
195
    }
196
197
    public function useTls(): self
198
    {
199
        $this->setProtocol(self::PROTOCOL_TLS);
200
201
        return $this;
202
    }
203
204
    public function useUdp(): self
205
    {
206
        $this->setProtocol(self::PROTOCOL_UDP);
207
208
        return $this;
209
    }
210
211
    public function useSsl(): self
212
    {
213
        $this->setProtocol(self::PROTOCOL_SSL);
214
215
        return $this;
216
    }
217
218
    public function setTimeout(float $seconds): self
219
    {
220
        $this->timeout = $seconds;
221
222
        return $this;
223
    }
224
225
    protected function setProtocol(string $protocol): void
226
    {
227
        $this->protocol = $protocol;
228
    }
229
230
    protected function addPortWithProtocol(int $port, string $protocol): void
231
    {
232
        $this->portsWithProtocol[$port] = $protocol;
233
    }
234
235
    protected function resolvePorts(array $ports = []): void
236
    {
237
        if (empty($ports)) {
238
            $ports = $this->defaultPorts;
239
        }
240
241
        $ports = is_array($ports[0] ?? null)
242
            ? $ports[0]
243
            : $ports;
244
245
        foreach ($ports as $port => $protocol) {
246
247
            if (in_array($protocol, $this->protocols) && is_int($port)) {
248
                $this->addPortWithProtocol($port, $protocol);
249
                continue;
250
            }
251
252
            if (is_int($protocol)) {
253
                $this->addPortWithProtocol($protocol, $this->protocol);
254
                continue;
255
            }
256
257
            throw InvalidArgument::portOrProtocolIsNotValid($port, $protocol);
258
        }
259
    }
260
}
261