Completed
Push — master ( 17ecb7...725fdf )
by Alexander
15:14
created

Url::validateValue()   A

Complexity

Conditions 6
Paths 9

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 14
c 1
b 0
f 0
nc 9
nop 1
dl 0
loc 25
cc 6
rs 9.2222
1
<?php
2
3
namespace Yiisoft\Validator\Rule;
4
5
use Yiisoft\Validator\Result;
6
use Yiisoft\Validator\Rule;
7
8
/**
9
 * UrlValidator validates that the attribute value is a valid http or https URL.
10
 *
11
 * Note that this validator only checks if the URL scheme and host part are correct.
12
 * It does not check the remaining parts of a URL.
13
 */
14
class Url extends Rule
15
{
16
    /**
17
     * @var string the regular expression used to validateValue the attribute value.
18
     * The pattern may contain a `{schemes}` token that will be replaced
19
     * by a regular expression which represents the [[validSchemes]].
20
     */
21
    private $pattern = '/^{schemes}:\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(?::\d{1,5})?(?:$|[?\/#])/i';
22
    /**
23
     * @var array list of URI schemes which should be considered valid. By default, http and https
24
     * are considered to be valid schemes.
25
     */
26
    private $validSchemes = ['http', 'https'];
27
    /**
28
     * @var bool whether validation process should take into account IDN (internationalized
29
     * domain names). Defaults to false meaning that validation of URLs containing IDN will always
30
     * fail. Note that in order to use IDN validation you have to install and enable `intl` PHP
31
     * extension, otherwise an exception would be thrown.
32
     */
33
    private $enableIDN = false;
34
35
    private $message;
36
37
    public function __construct()
38
    {
39
        if ($this->enableIDN && !function_exists('idn_to_ascii')) {
40
            throw new \RuntimeException('In order to use IDN validation intl extension must be installed and enabled.');
41
        }
42
43
        $this->message = '{attribute} is not a valid URL.';
44
    }
45
46
    public function validateValue($value): Result
47
    {
48
        $result = new Result();
49
50
        // make sure the length is limited to avoid DOS attacks
51
        if (is_string($value) && strlen($value) < 2000) {
52
            if (strpos($this->pattern, '{schemes}') !== false) {
53
                $pattern = str_replace('{schemes}', '(' . implode('|', $this->validSchemes) . ')', $this->pattern);
54
            } else {
55
                $pattern = $this->pattern;
56
            }
57
58
            if ($this->enableIDN) {
59
                $value = preg_replace_callback('/:\/\/([^\/]+)/', function ($matches) {
60
                    return '://' . $this->idnToAscii($matches[1]);
61
                }, $value);
62
            }
63
64
            if (preg_match($pattern, $value)) {
65
                return $result;
66
            }
67
        }
68
69
        $result->addError($this->formatMessage($this->message));
70
        return $result;
71
    }
72
73
    private function idnToAscii($idn)
74
    {
75
        return idn_to_ascii($idn, 0, INTL_IDNA_VARIANT_UTS46);
76
    }
77
78
    public function pattern(string $pattern): self
79
    {
80
        $this->pattern = $pattern;
81
        return $this;
82
    }
83
84
    public function enableIDN(): self
85
    {
86
        $this->enableIDN = true;
87
        return $this;
88
    }
89
90
    public function schemes(array $schemes): self
91
    {
92
        $this->validSchemes = $schemes;
93
        return $this;
94
    }
95
96
    public function message(string $message): self
97
    {
98
        $this->message = $message;
99
        return $this;
100
    }
101
102
}
103