Passed
Pull Request — master (#222)
by Rustam
02:32
created

UrlHandler   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 54
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 11
eloc 23
dl 0
loc 54
ccs 24
cts 24
cp 1
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A convertIdn() 0 10 2
A validate() 0 26 6
A idnToAscii() 0 5 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Rule;
6
7
use Yiisoft\Validator\Exception\UnexpectedRuleException;
8
use Yiisoft\Validator\Formatter;
9
use Yiisoft\Validator\FormatterInterface;
10
use Yiisoft\Validator\Result;
11
use Yiisoft\Validator\ValidationContext;
12
13
use function is_string;
14
use function strlen;
15
16
/**
17
 * Validates that the value is a valid HTTP or HTTPS URL.
18
 *
19
 * Note that this rule only checks if the URL scheme and host part are correct.
20
 * It does not check the remaining parts of a URL.
21
 */
22
final class UrlHandler implements RuleHandlerInterface
23
{
24
    private FormatterInterface $formatter;
25
26 44
    public function __construct(?FormatterInterface $formatter = null)
27
    {
28 44
        $this->formatter = $formatter ?? new Formatter();
29
    }
30
31 44
    public function validate(mixed $value, object $rule, ?ValidationContext $context = null): Result
32
    {
33 44
        if (!$rule instanceof Url) {
34 1
            throw new UnexpectedRuleException(Url::class, $rule);
35
        }
36
37 43
        $result = new Result();
38
39
        // make sure the length is limited to avoid DOS attacks
40 43
        if (is_string($value) && strlen($value) < 2000) {
41 41
            if ($rule->isEnableIDN()) {
42 9
                $value = $this->convertIdn($value);
43
            }
44
45 41
            if (preg_match($rule->getPattern(), $value)) {
46 24
                return $result;
47
            }
48
        }
49
50 19
        $formattedMessage = $this->formatter->format(
51 19
            $rule->getMessage(),
52 19
            ['attribute' => $context?->getAttribute(), 'value' => $value]
53
        );
54 19
        $result->addError($formattedMessage);
55
56 19
        return $result;
57
    }
58
59 9
    private function idnToAscii(string $idn): string
60
    {
61 9
        $result = idn_to_ascii($idn, 0, INTL_IDNA_VARIANT_UTS46);
62
63 9
        return $result === false ? '' : $result;
64
    }
65
66 9
    private function convertIdn(string $value): string
67
    {
68 9
        if (!str_contains($value, '://')) {
69 5
            return $this->idnToAscii($value);
70
        }
71
72 4
        return preg_replace_callback(
73
            '/:\/\/([^\/]+)/',
74 4
            fn ($matches) => '://' . $this->idnToAscii($matches[1]),
75
            $value
76
        );
77
    }
78
}
79