Completed
Pull Request — master (#175)
by Alexander
02:53 queued 02:53
created

Url::__construct()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 32
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 3
nc 2
nop 8
dl 0
loc 32
ccs 4
cts 4
cp 1
crap 3
rs 10
c 1
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Rule;
6
7
use Attribute;
8
use RuntimeException;
9
use Yiisoft\Validator\FormatterInterface;
10
use Yiisoft\Validator\Result;
11
use Yiisoft\Validator\Rule;
12
use Yiisoft\Validator\ValidationContext;
13
14
use function is_string;
15
use function strlen;
16
17
/**
18
 * Validates that the value is a valid HTTP or HTTPS URL.
19
 *
20
 * Note that this rule only checks if the URL scheme and host part are correct.
21
 * It does not check the remaining parts of a URL.
22
 */
23
#[Attribute(Attribute::TARGET_PROPERTY)]
24
final class Url extends Rule
25
{
26 15
    public function __construct(
27
        /**
28
         * @var string the regular expression used to validate the value.
29
         * The pattern may contain a `{schemes}` token that will be replaced
30
         * by a regular expression which represents the {@see $schemes}.
31
         *
32
         * Note that if you want to reuse the pattern in HTML5 input it should have ^ and $, should not have any
33
         * modifiers and should not be case-insensitive.
34
         */
35
        private string $pattern = '/^{schemes}:\/\/(([a-zA-Z0-9][a-zA-Z0-9_-]*)(\.[a-zA-Z0-9][a-zA-Z0-9_-]*)+)(?::\d{1,5})?([?\/#].*$|$)/',
36
        /**
37
         * @var array list of URI schemes which should be considered valid. By default, http and https
38
         * are considered to be valid schemes.
39
         */
40
        private array $validSchemes = ['http', 'https'],
41
        /**
42
         * @var bool whether validation process should take into account IDN (internationalized
43
         * domain names). Defaults to false meaning that validation of URLs containing IDN will always
44
         * fail. Note that in order to use IDN validation you have to install and enable `intl` PHP
45
         * extension, otherwise an exception would be thrown.
46
         */
47
        private bool $enableIDN = false,
48
        private string $message = 'This value is not a valid URL.',
49
        ?FormatterInterface $formatter = null,
50
        bool $skipOnEmpty = false,
51
        bool $skipOnError = false,
52
        $when = null
53
    ) {
54 15
        parent::__construct(formatter: $formatter, skipOnEmpty: $skipOnEmpty, skipOnError: $skipOnError, when: $when);
55
56 15
        if ($enableIDN && !function_exists('idn_to_ascii')) {
57 1
            throw new RuntimeException('In order to use IDN validation intl extension must be installed and enabled.');
58
        }
59
    }
60
61 11
    protected function validateValue($value, ?ValidationContext $context = null): Result
62
    {
63 11
        $result = new Result();
64
65
        // make sure the length is limited to avoid DOS attacks
66 11
        if (is_string($value) && strlen($value) < 2000) {
67 10
            if ($this->enableIDN) {
68 6
                $value = $this->convertIdn($value);
69
            }
70
71 10
            if (preg_match($this->getPattern(), $value)) {
72 7
                return $result;
73
            }
74
        }
75
76 7
        $result->addError($this->formatMessage($this->message));
77
78 7
        return $result;
79
    }
80
81 6
    private function idnToAscii(string $idn): string
82
    {
83 6
        $result = idn_to_ascii($idn, 0, INTL_IDNA_VARIANT_UTS46);
84
85 6
        return $result === false ? '' : $result;
86
    }
87
88 6
    private function convertIdn(string $value): string
89
    {
90 6
        if (strpos($value, '://') === false) {
91 4
            return $this->idnToAscii($value);
92
        }
93
94 2
        return preg_replace_callback(
95
            '/:\/\/([^\/]+)/',
96 2
            fn ($matches) => '://' . $this->idnToAscii($matches[1]),
97
            $value
98
        );
99
    }
100
101 10
    private function getPattern(): string
102
    {
103 10
        if (strpos($this->pattern, '{schemes}') !== false) {
104 8
            return str_replace('{schemes}', '((?i)' . implode('|', $this->validSchemes) . ')', $this->pattern);
105
        }
106
107 2
        return $this->pattern;
108
    }
109
110 6
    public function getOptions(): array
111
    {
112 6
        return array_merge(parent::getOptions(), [
113 6
            'pattern' => $this->pattern,
114 6
            'validSchemes' => $this->validSchemes,
115 6
            'enableIDN' => $this->enableIDN,
116 6
            'message' => $this->formatMessage($this->message),
117
        ]);
118
    }
119
}
120