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::validate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 2
rs 10
c 0
b 0
f 0
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 function bccomp;
17
use function explode;
18
use function filter_var;
19
use function is_int;
20
use function ip2long;
21
use function long2ip;
22
use function mb_strpos;
23
use function mb_substr_count;
24
use function strtr;
25
use function str_replace;
26
use function sprintf;
27
28
29
use Respect\Validation\Exceptions\ComponentException;
30
31
/**
32
 * Validates IP Addresses. This validator uses the native filter_var() PHP function.
33
 * 
34
 * @author Alexandre Gomes Gaigalas <[email protected]>
35
 * @author Danilo Benevides <[email protected]>
36
 * @author Henrique Moody <[email protected]>
37
 * @author Luís Otávio Cobucci Oblonczyk <[email protected]>
38
 */
39
final class Ip extends AbstractRule
40
{
41
    /**
42
     * @var int|string
43
     */
44
    private $ipOptions;
45
46
    /**
47
     * @var array
48
     */
49
    private $range;
50
    
51
    /**
52
     * @var string
53
     */
54
    private $networkRange;
55
56
    /**
57
     * @param int|string $ipOptions
58
     */
59 1
    public function __construct($ipOptions = null)
60
    {
61 1
        if (is_int($ipOptions)) {
62
            $this->ipOptions = $ipOptions;
63
64
            return;
65
        }
66
67 1
        $this->networkRange = $this->parseRange($ipOptions);
68 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...
69 1
    }
70
71 1
    private function createRange(): ?string
72
    {
73 1
        if (!$this->networkRange) {
74
            return null;
75
        }
76
77 1
        $range = $this->networkRange;
78 1
        $message = $range['min'];
79
80 1
        if (isset($range['max'])) {
81 1
            $message .= '-'.$range['max'];
82
        } else {
83
            $message .= '/'.long2ip((int) $range['mask']);
84
        }
85
86 1
        return $message;
87
    }
88
89 1
    private function parseRange($input)
90
    {
91 1
        if (null === $input || '*' == $input || '*.*.*.*' == $input
92 1
            || '0.0.0.0-255.255.255.255' == $input) {
93
            return;
94
        }
95
96 1
        $range = ['min' => null, 'max' => null, 'mask' => null];
97
98 1
        if (false !== mb_strpos($input, '-')) {
99
            list($range['min'], $range['max']) = explode('-', $input);
100 1
        } elseif (false !== mb_strpos($input, '*')) {
101 1
            $this->parseRangeUsingWildcards($input, $range);
102 1
        } elseif (false !== mb_strpos($input, '/')) {
103 1
            $this->parseRangeUsingCidr($input, $range);
104
        } else {
105 1
            throw new ComponentException('Invalid network range');
106
        }
107
108 1
        if (!$this->verifyAddress($range['min'])) {
109
            throw new ComponentException('Invalid network range');
110
        }
111
112 1
        if (isset($range['max']) && !$this->verifyAddress($range['max'])) {
113
            throw new ComponentException('Invalid network range');
114
        }
115
116 1
        return $range;
117
    }
118
119 1
    private function fillAddress(&$input, $char = '*'): void
120
    {
121 1
        while (mb_substr_count($input, '.') < 3) {
122
            $input .= '.'.$char;
123
        }
124 1
    }
125
126 1
    private function parseRangeUsingWildcards($input, &$range): void
127
    {
128 1
        $this->fillAddress($input);
129
130 1
        $range['min'] = strtr($input, '*', '0');
131 1
        $range['max'] = str_replace('*', '255', $input);
132 1
    }
133
134 1
    private function parseRangeUsingCidr($input, &$range): void
135
    {
136 1
        $input = explode('/', $input);
137 1
        $this->fillAddress($input[0], '0');
138
139 1
        $range['min'] = $input[0];
140 1
        $isAddressMask = false !== mb_strpos($input[1], '.');
141
142 1
        if ($isAddressMask && $this->verifyAddress($input[1])) {
143
            $range['mask'] = sprintf('%032b', ip2long($input[1]));
144
145
            return;
146
        }
147
148 1
        if ($isAddressMask || $input[1] < 8 || $input[1] > 30) {
149 1
            throw new ComponentException('Invalid network mask');
150
        }
151
152
        $range['mask'] = sprintf('%032b', ip2long(long2ip(~(2 ** (32 - $input[1]) - 1))));
153
    }
154
155
    /**
156
     * {@inheritdoc}
157
     */
158 34
    public function validate($input): bool
159
    {
160 34
        return $this->verifyAddress($input) && $this->verifyNetwork($input);
161
    }
162
163 34
    private function verifyAddress($address)
164
    {
165 34
        return (bool) filter_var(
166 34
            $address,
167 34
            FILTER_VALIDATE_IP,
168
            [
169 34
                'flags' => $this->ipOptions,
170
            ]
171
        );
172
    }
173
174 28
    private function verifyNetwork($input)
175
    {
176 28
        if (null === $this->networkRange) {
177 3
            return true;
178
        }
179
180 25
        if (isset($this->networkRange['mask'])) {
181 6
            return $this->belongsToSubnet($input);
182
        }
183
184 19
        $input = sprintf('%u', ip2long($input));
185
186 19
        return bccomp($input, sprintf('%u', ip2long($this->networkRange['min']))) >= 0
187 19
               && bccomp($input, sprintf('%u', ip2long($this->networkRange['max']))) <= 0;
188
    }
189
190 6
    private function belongsToSubnet($input)
191
    {
192 6
        $range = $this->networkRange;
193 6
        $min = sprintf('%032b', ip2long($range['min']));
194 6
        $input = sprintf('%032b', ip2long($input));
195
196 6
        return ($input & $range['mask']) === ($min & $range['mask']);
197
    }
198
}
199