Passed
Pull Request — master (#335)
by Sergei
02:59
created

UrlHandler::convertIdn()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 6
c 1
b 0
f 0
dl 0
loc 10
ccs 0
cts 5
cp 0
rs 10
cc 2
nc 2
nop 1
crap 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Rule;
6
7
use Yiisoft\Translator\TranslatorInterface;
8
use Yiisoft\Validator\Exception\UnexpectedRuleException;
9
use Yiisoft\Validator\Result;
10
use Yiisoft\Validator\RuleHandlerInterface;
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 1
    public function __construct(private TranslatorInterface $translator)
25
    {
26
    }
27
28 1
    public function validate(mixed $value, object $rule, ValidationContext $context): Result
29
    {
30 1
        if (!$rule instanceof Url) {
31 1
            throw new UnexpectedRuleException(Url::class, $rule);
32
        }
33
34
        $result = new Result();
35
36
        // make sure the length is limited to avoid DOS attacks
37
        if (is_string($value) && strlen($value) < 2000) {
38
            if ($rule->isEnableIDN()) {
39
                $value = $this->convertIdn($value);
40
            }
41
42
            if (preg_match($rule->getPattern(), $value)) {
43
                return $result;
44
            }
45
        }
46
47
        $formattedMessage = $this->translator->translate(
48
            $rule->getMessage(),
49
            ['attribute' => $context->getAttribute(), 'value' => $value]
50
        );
51
        $result->addError($formattedMessage);
52
53
        return $result;
54
    }
55
56
    private function idnToAscii(string $idn): string
57
    {
58
        $result = idn_to_ascii($idn, 0, INTL_IDNA_VARIANT_UTS46);
59
60
        return $result === false ? '' : $result;
61
    }
62
63
    private function convertIdn(string $value): string
64
    {
65
        if (!str_contains($value, '://')) {
66
            return $this->idnToAscii($value);
67
        }
68
69
        return preg_replace_callback(
70
            '/:\/\/([^\/]+)/',
71
            fn ($matches) => '://' . $this->idnToAscii($matches[1]),
72
            $value
73
        );
74
    }
75
}
76