Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Completed
Pull Request — master (#1172)
by Henrique
04:24
created

Ip   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 166
Duplicated Lines 0 %

Test Coverage

Coverage 87.88%

Importance

Changes 0
Metric Value
wmc 33
eloc 58
dl 0
loc 166
ccs 58
cts 66
cp 0.8788
rs 9.76
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A parseRangeUsingCidr() 0 18 6
A fillAddress() 0 3 1
A validate() 0 19 6
A __construct() 0 5 1
A createRange() 0 11 5
A parseRangeUsingWildcards() 0 6 1
A belongsToSubnet() 0 6 1
A verifyAddress() 0 3 1
A verifyNetwork() 0 6 2
B parseRange() 0 33 9
1
<?php
2
3
/*
4
 * This file is part of Respect/Validation.
5
 *
6
 * (c) Alexandre Gomes Gaigalas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the "LICENSE.md"
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Respect\Validation\Rules;
15
16
use Respect\Validation\Exceptions\ComponentException;
17
use function bccomp;
18
use function explode;
19
use function filter_var;
20
use function ip2long;
21
use function is_string;
22
use function long2ip;
23
use function mb_strpos;
24
use function mb_substr_count;
25
use function sprintf;
26
use function str_repeat;
27
use function str_replace;
28
use function strtr;
29
30
/**
31
 * Validates whether the input is a valid IP address.
32
 *
33
 * This validator uses the native filter_var() PHP function.
34
 *
35
 * @author Alexandre Gomes Gaigalas <[email protected]>
36
 * @author Danilo Benevides <[email protected]>
37
 * @author Henrique Moody <[email protected]>
38
 * @author Luís Otávio Cobucci Oblonczyk <[email protected]>
39
 */
40
final class Ip extends AbstractRule
41
{
42
    /**
43
     * @var string|null
44
     */
45
    private $range;
46
47
    /**
48
     * @var int|null
49
     */
50
    private $options;
51
52
    /**
53
     * @var string|null
54
     */
55
    private $startAddress;
56
57
    /**
58
     * @var string|null
59
     */
60
    private $endAddress;
61
62
    /**
63
     * @var string|null
64
     */
65
    private $mask;
66
67
    /**
68
     * Initializes the rule defining the range and some options for filter_var().
69
     *
70
     * @param string $range
71
     * @param int|null $options
72
     *
73
     * @throws ComponentException In case the range is invalid
74
     */
75 8
    public function __construct(string $range = '*', int $options = null)
76
    {
77 8
        $this->parseRange($range);
78 1
        $this->range = $this->createRange();
79 1
        $this->options = $options;
80 1
    }
81
82
    /**
83
     * {@inheritdoc}
84
     */
85 35
    public function validate($input): bool
86
    {
87 35
        if (!is_string($input)) {
88 1
            return false;
89
        }
90
91 34
        if (!$this->verifyAddress($input)) {
92 6
            return false;
93
        }
94
95 29
        if ($this->mask) {
96 6
            return $this->belongsToSubnet($input);
97
        }
98
99 23
        if ($this->startAddress && $this->endAddress) {
100 19
            return $this->verifyNetwork($input);
101
        }
102
103 5
        return true;
104
    }
105
106 1
    private function createRange(): ?string
107
    {
108 1
        if ($this->endAddress && $this->endAddress) {
109 1
            return $this->startAddress.'-'.$this->endAddress;
110
        }
111
112 1
        if ($this->startAddress && $this->mask) {
113
            return $this->startAddress.'/'.long2ip((int) $this->mask);
114
        }
115
116 1
        return null;
117
    }
118
119 8
    private function parseRange(string $input): void
120
    {
121 8
        if ('*' == $input || '*.*.*.*' == $input || '0.0.0.0-255.255.255.255' == $input) {
122 1
            return;
123
        }
124
125 8
        if (false !== mb_strpos($input, '-')) {
126 2
            list($this->startAddress, $this->endAddress) = explode('-', $input);
127
128 2
            if (!$this->verifyAddress($this->startAddress)) {
129
                throw new ComponentException('Invalid network range');
130
            }
131
132 2
            if (!$this->verifyAddress($this->endAddress)) {
133 2
                throw new ComponentException('Invalid network range');
134
            }
135
136
            return;
137
        }
138
139 6
        if (false !== mb_strpos($input, '*')) {
140 1
            $this->parseRangeUsingWildcards($input);
141
142 1
            return;
143
        }
144
145 5
        if (false !== mb_strpos($input, '/')) {
146 3
            $this->parseRangeUsingCidr($input);
147
148
            return;
149
        }
150
151 2
        throw new ComponentException('Invalid network range');
152
    }
153
154 4
    private function fillAddress(string $address, string $fill = '*'): string
155
    {
156 4
        return $address.str_repeat('.'.$fill, 3 - mb_substr_count($address, '.'));
157
    }
158
159 1
    private function parseRangeUsingWildcards(string $input): void
160
    {
161 1
        $address = $this->fillAddress($input);
162
163 1
        $this->startAddress = strtr($address, '*', '0');
164 1
        $this->endAddress = str_replace('*', '255', $address);
165 1
    }
166
167 3
    private function parseRangeUsingCidr(string $input): void
168
    {
169 3
        $parts = explode('/', $input);
170
171 3
        $this->startAddress = $this->fillAddress($parts[0], '0');
172 3
        $isAddressMask = false !== mb_strpos($parts[1], '.');
173
174 3
        if ($isAddressMask && $this->verifyAddress($parts[1])) {
175
            $this->mask = sprintf('%032b', ip2long($parts[1]));
176
177
            return;
178
        }
179
180 3
        if ($isAddressMask || $parts[1] < 8 || $parts[1] > 30) {
181 3
            throw new ComponentException('Invalid network mask');
182
        }
183
184
        $this->mask = sprintf('%032b', ip2long(long2ip(~(2 ** (32 - $parts[1]) - 1))));
185
    }
186
187 38
    private function verifyAddress(string $address): bool
188
    {
189 38
        return false !== filter_var($address, FILTER_VALIDATE_IP, ['flags' => $this->options]);
190
    }
191
192 19
    private function verifyNetwork(string $input): bool
193
    {
194 19
        $input = sprintf('%u', ip2long($input));
195
196 19
        return bccomp($input, sprintf('%u', ip2long($this->startAddress))) >= 0
197 19
               && bccomp($input, sprintf('%u', ip2long($this->endAddress))) <= 0;
198
    }
199
200 6
    private function belongsToSubnet(string $input): bool
201
    {
202 6
        $min = sprintf('%032b', ip2long($this->startAddress));
203 6
        $input = sprintf('%032b', ip2long($input));
204
205 6
        return ($input & $this->mask) === ($min & $this->mask);
1 ignored issue
show
Bug introduced by
Are you sure you want to use the bitwise & or did you mean &&?
Loading history...
206
    }
207
}
208