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 (#1101)
by
unknown
02:37
created

Ip::createRange()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3.0987

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 16
ccs 7
cts 9
cp 0.7778
rs 9.9666
c 0
b 0
f 0
cc 3
nc 3
nop 0
crap 3.0987
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_int;
22
use function long2ip;
23
use function mb_strpos;
24
use function mb_substr_count;
25
use function sprintf;
26
use function str_replace;
27
use function strtr;
28
29
/**
30
 * Validates IP Addresses. This validator uses the native filter_var() PHP function.
31
 *
32
 * @author Alexandre Gomes Gaigalas <[email protected]>
33
 * @author Danilo Benevides <[email protected]>
34
 * @author Henrique Moody <[email protected]>
35
 * @author Luís Otávio Cobucci Oblonczyk <[email protected]>
36
 */
37
final class Ip extends AbstractRule
38
{
39
    /**
40
     * @var int|string
41
     */
42
    private $ipOptions;
43
44
    /**
45
     * @var array
46
     */
47
    private $range;
48
49
    /**
50
     * @var string
51
     */
52
    private $networkRange;
53
54
    /**
55
     * @param int|string $ipOptions
56
     */
57 1
    public function __construct($ipOptions = null)
58
    {
59 1
        if (is_int($ipOptions)) {
60
            $this->ipOptions = $ipOptions;
61
62
            return;
63
        }
64
65 1
        $this->networkRange = $this->parseRange($ipOptions);
66 1
        $this->range = $this->createRange();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->createRange() can also be of type string. However, the property $range is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
67 1
    }
68
69 1
    private function createRange(): ?string
70
    {
71 1
        if (!$this->networkRange) {
72
            return null;
73
        }
74
75 1
        $range = $this->networkRange;
76 1
        $message = $range['min'];
77
78 1
        if (isset($range['max'])) {
79 1
            $message .= '-'.$range['max'];
80
        } else {
81
            $message .= '/'.long2ip((int) $range['mask']);
82
        }
83
84 1
        return $message;
85
    }
86
87 1
    private function parseRange($input)
88
    {
89 1
        if (null === $input || '*' == $input || '*.*.*.*' == $input
90 1
            || '0.0.0.0-255.255.255.255' == $input) {
91
            return;
92
        }
93
94 1
        $range = ['min' => null, 'max' => null, 'mask' => null];
95
96 1
        if (false !== mb_strpos($input, '-')) {
97
            list($range['min'], $range['max']) = explode('-', $input);
98 1
        } elseif (false !== mb_strpos($input, '*')) {
99 1
            $this->parseRangeUsingWildcards($input, $range);
100 1
        } elseif (false !== mb_strpos($input, '/')) {
101 1
            $this->parseRangeUsingCidr($input, $range);
102
        } else {
103 1
            throw new ComponentException('Invalid network range');
104
        }
105
106 1
        if (!$this->verifyAddress($range['min'])) {
107
            throw new ComponentException('Invalid network range');
108
        }
109
110 1
        if (isset($range['max']) && !$this->verifyAddress($range['max'])) {
111
            throw new ComponentException('Invalid network range');
112
        }
113
114 1
        return $range;
115
    }
116
117 1
    private function fillAddress(&$input, $char = '*'): void
118
    {
119 1
        while (mb_substr_count($input, '.') < 3) {
120
            $input .= '.'.$char;
121
        }
122 1
    }
123
124 1
    private function parseRangeUsingWildcards($input, &$range): void
125
    {
126 1
        $this->fillAddress($input);
127
128 1
        $range['min'] = strtr($input, '*', '0');
129 1
        $range['max'] = str_replace('*', '255', $input);
130 1
    }
131
132 1
    private function parseRangeUsingCidr($input, &$range): void
133
    {
134 1
        $input = explode('/', $input);
135 1
        $this->fillAddress($input[0], '0');
136
137 1
        $range['min'] = $input[0];
138 1
        $isAddressMask = false !== mb_strpos($input[1], '.');
139
140 1
        if ($isAddressMask && $this->verifyAddress($input[1])) {
141
            $range['mask'] = sprintf('%032b', ip2long($input[1]));
142
143
            return;
144
        }
145
146 1
        if ($isAddressMask || $input[1] < 8 || $input[1] > 30) {
147 1
            throw new ComponentException('Invalid network mask');
148
        }
149
150
        $range['mask'] = sprintf('%032b', ip2long(long2ip(~(2 ** (32 - $input[1]) - 1))));
151
    }
152
153
    /**
154
     * {@inheritdoc}
155
     */
156 34
    public function validate($input): bool
157
    {
158 34
        return $this->verifyAddress($input) && $this->verifyNetwork($input);
159
    }
160
161 34
    private function verifyAddress($address)
162
    {
163 34
        return (bool) filter_var(
164 34
            $address,
165 34
            FILTER_VALIDATE_IP,
166
            [
167 34
                'flags' => $this->ipOptions,
168
            ]
169
        );
170
    }
171
172 28
    private function verifyNetwork($input)
173
    {
174 28
        if (null === $this->networkRange) {
175 3
            return true;
176
        }
177
178 25
        if (isset($this->networkRange['mask'])) {
179 6
            return $this->belongsToSubnet($input);
180
        }
181
182 19
        $input = sprintf('%u', ip2long($input));
183
184 19
        return bccomp($input, sprintf('%u', ip2long($this->networkRange['min']))) >= 0
185 19
               && bccomp($input, sprintf('%u', ip2long($this->networkRange['max']))) <= 0;
186
    }
187
188 6
    private function belongsToSubnet($input)
189
    {
190 6
        $range = $this->networkRange;
191 6
        $min = sprintf('%032b', ip2long($range['min']));
192 6
        $input = sprintf('%032b', ip2long($input));
193
194 6
        return ($input & $range['mask']) === ($min & $range['mask']);
195
    }
196
}
197