Completed
Pull Request — master (#16)
by
unknown
18:22 queued 03:20
created

Rfc1918::calculateRange()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.584
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
namespace Spatie\ValidationRules\Rules;
4
5
use Illuminate\Contracts\Validation\Rule;
6
7
/**
8
 * Class Rfc1918.
9
 *
10
 * Validates that the value is an IP address and does not overlap with one of the RFC1918 non-routable ranges.
11
 */
12
final class Rfc1918 implements Rule
13
{
14
    const RFC1918_RANGES = [
15
        '10.0.0.0/8',
16
        '172.16.0.0/21',
17
        '192.168.0.0/16',
18
    ];
19
20
    /**
21
     * Determine if the validation rule passes.
22
     *
23
     * @param  string  $attribute
24
     * @param  mixed  $value
25
     * @return bool
26
     */
27
    public function passes($attribute, $value)
28
    {
29
        $parts = explode('/', $value);
30
31
        if (! filter_var($parts[0], FILTER_VALIDATE_IP)) {
32
            return false;
33
        }
34
35
        $ip = inet_pton($parts[0]);
36
37
        $range = $this->calculateRange($ip, count($parts) > 1 ? $parts[1] : 32);
38
39
        foreach (static::RFC1918_RANGES as $rfc1918Range) {
40
            list($privateIp, $privateMask) = explode('/', $rfc1918Range);
41
            $privateRange = $this->calculateRange(inet_pton($privateIp), $privateMask);
42
43
            if ($range[1] >= $privateRange[0] && $range[0] <= $privateRange[1]) {
44
                return false;
45
            }
46
        }
47
48
        return true;
49
    }
50
51
    /**
52
     * @param string $address Packed representation of the address used to define the range.
53
     * @param int $mask
54
     * @return string[] Packed representations of the starting and ending addresses in this range.
55
     */
56
    private function calculateRange(string $address, int $mask): array
57
    {
58
        // Index of returned array starts at 1, because `unpack` is weird.
59
        $words = unpack('n*', $address);
60
61
        $start = [];
62
        $end = [];
63
64
        for ($i = 0; $i < count($words); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
65
            $shift = min(max($mask - 16 * $i, 0), 16);
66
            $wordMask = ~(0xffff >> $shift) & 0xffff;
67
68
            $start[$i] = $words[$i + 1] & $wordMask;
69
            $end[$i] = $words[$i + 1] | ~$wordMask;
70
        }
71
72
        return [
73
            pack('n*', ...$start),
74
            pack('n*', ...$end),
75
        ];
76
    }
77
78
    /**
79
     * Get the validation error message.
80
     *
81
     * @return string
82
     */
83
    public function message()
84
    {
85
        return __('validationRules.rfc1918');
86
    }
87
}
88