Passed
Pull Request — master (#222)
by Alexander
04:47 queued 02:23
created

IpValidator::validate()   D

Complexity

Conditions 20
Paths 15

Size

Total Lines 64
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 40
CRAP Score 20.3004

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 20
eloc 45
c 1
b 0
f 0
nc 15
nop 4
dl 0
loc 64
ccs 40
cts 44
cp 0.9091
crap 20.3004
rs 4.1666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Rule\Ip;
6
7
use InvalidArgumentException;
8
use RuntimeException;
9
use Yiisoft\NetworkUtilities\IpHelper;
10
use Yiisoft\Validator\Result;
11
use Yiisoft\Validator\Rule\RuleValidatorInterface;
12
use Yiisoft\Validator\ValidationContext;
13
use Yiisoft\Validator\ValidatorInterface;
14
use function is_string;
15
16
/**
17
 * Checks if the value is a valid IPv4/IPv6 address or subnet.
18
 *
19
 * It also may change the value if normalization of IPv6 expansion is enabled.
20
 */
21
final class IpValidator implements RuleValidatorInterface
22
{
23
    /**
24
     * Negation char.
25
     *
26
     * Used to negate {@see $ranges} or {@see $network} or to negate validating value when {@see $allowNegation}
27
     * is used.
28
     */
29
    private const NEGATION_CHAR = '!';
30
31 117
    public function validate(mixed $value, object $rule, ValidatorInterface $validator, ?ValidationContext $context = null): Result
32
    {
33 117
        if (!$rule->allowIpv4 && !$rule->allowIpv6) {
34 1
            throw new RuntimeException('Both IPv4 and IPv6 checks can not be disabled at the same time.');
35
        }
36 116
        $result = new Result();
37 116
        if (!is_string($value)) {
38 4
            $result->addError($rule->message);
39 4
            return $result;
40
        }
41
42 112
        if (preg_match($rule->getIpParsePattern(), $value, $matches) === 0) {
43 26
            $result->addError($rule->message);
44 26
            return $result;
45
        }
46 86
        $negation = !empty($matches['not'] ?? null);
47 86
        $ip = $matches['ip'];
48 86
        $cidr = $matches['cidr'] ?? null;
49 86
        $ipCidr = $matches['ipCidr'];
50
51
        try {
52 86
            $ipVersion = IpHelper::getIpVersion($ip, false);
53
        } catch (InvalidArgumentException $e) {
54
            $result->addError($rule->message);
55
            return $result;
56
        }
57
58 86
        if ($rule->requireSubnet === true && $cidr === null) {
59 4
            $result->addError($rule->noSubnetMessage);
60 4
            return $result;
61
        }
62 82
        if ($rule->allowSubnet === false && $cidr !== null) {
63 4
            $result->addError($rule->hasSubnetMessage);
64 4
            return $result;
65
        }
66 78
        if ($rule->allowNegation === false && $negation) {
67 4
            $result->addError($rule->message);
68 4
            return $result;
69
        }
70 74
        if ($ipVersion === IpHelper::IPV6 && !$rule->allowIpv6) {
71 1
            $result->addError($rule->ipv6NotAllowedMessage);
72 1
            return $result;
73
        }
74 73
        if ($ipVersion === IpHelper::IPV4 && !$rule->allowIpv4) {
75 2
            $result->addError($rule->ipv4NotAllowedMessage);
76 2
            return $result;
77
        }
78 71
        if (!$result->isValid()) {
79
            return $result;
80
        }
81 71
        if ($cidr !== null) {
82
            try {
83 28
                IpHelper::getCidrBits($ipCidr);
84 2
            } catch (InvalidArgumentException $e) {
85 2
                $result->addError($rule->wrongCidrMessage);
86 2
                return $result;
87
            }
88
        }
89 69
        if (!$rule->isAllowed($ipCidr)) {
90 16
            $result->addError($rule->notInRangeMessage);
91 16
            return $result;
92
        }
93
94 53
        return $result;
95
    }
96
97
    /**
98
     * Used to get the Regexp pattern for initial IP address parsing.
99
     */
100
    public function getIpParsePattern(): string
101
    {
102
        return '/^(?<not>' . preg_quote(
103
            self::NEGATION_CHAR,
104
            '/'
105
        ) . ')?(?<ipCidr>(?<ip>(?:' . IpHelper::IPV4_PATTERN . ')|(?:' . IpHelper::IPV6_PATTERN . '))(?:\/(?<cidr>-?\d+))?)$/';
106
    }
107
}
108