Completed
Push — 2.1 ( 75349f...bf116e )
by Alexander
29:27
created

UrlValidator::validateAttribute()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 8
cts 8
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 7
nc 3
nop 2
crap 4
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\validators;
9
10
use Yii;
11
use yii\base\InvalidConfigException;
12
13
/**
14
 * UrlValidator validates that the attribute value is a valid http or https URL.
15
 *
16
 * Note that this validator only checks if the URL scheme and host part are correct.
17
 * It does not check the remaining parts of a URL.
18
 *
19
 * @author Qiang Xue <[email protected]>
20
 * @since 2.0
21
 */
22
class UrlValidator extends Validator
23
{
24
    /**
25
     * @var string the regular expression used to validate the attribute value.
26
     * The pattern may contain a `{schemes}` token that will be replaced
27
     * by a regular expression which represents the [[validSchemes]].
28
     */
29
    public $pattern = '/^{schemes}:\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(?::\d{1,5})?(?:$|[?\/#])/i';
30
    /**
31
     * @var array list of URI schemes which should be considered valid. By default, http and https
32
     * are considered to be valid schemes.
33
     */
34
    public $validSchemes = ['http', 'https'];
35
    /**
36
     * @var string the default URI scheme. If the input doesn't contain the scheme part, the default
37
     * scheme will be prepended to it (thus changing the input). Defaults to null, meaning a URL must
38
     * contain the scheme part.
39
     */
40
    public $defaultScheme;
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
    public $enableIDN = false;
48
49
50
    /**
51
     * @inheritdoc
52
     */
53 7
    public function init()
54
    {
55 7
        parent::init();
56 7
        if ($this->enableIDN && !function_exists('idn_to_ascii')) {
57
            throw new InvalidConfigException('In order to use IDN validation intl extension must be installed and enabled.');
58
        }
59 7
        if ($this->message === null) {
60 7
            $this->message = Yii::t('yii', '{attribute} is not a valid URL.');
61
        }
62 7
    }
63
64
    /**
65
     * @inheritdoc
66
     */
67 1
    public function validateAttribute($model, $attribute)
68
    {
69 1
        $value = $model->$attribute;
70 1
        $result = $this->validateValue($value);
71 1
        if (!empty($result)) {
72 1
            $this->addError($model, $attribute, $result[0], $result[1]);
73 1
        } elseif ($this->defaultScheme !== null && strpos($value, '://') === false) {
74 1
            $model->$attribute = $this->defaultScheme . '://' . $value;
75
        }
76 1
    }
77
78
    /**
79
     * @inheritdoc
80
     */
81 7
    protected function validateValue($value)
82
    {
83
        // make sure the length is limited to avoid DOS attacks
84 7
        if (is_string($value) && strlen($value) < 2000) {
85 6
            if ($this->defaultScheme !== null && strpos($value, '://') === false) {
86 3
                $value = $this->defaultScheme . '://' . $value;
87
            }
88
89 6
            if (strpos($this->pattern, '{schemes}') !== false) {
90 5
                $pattern = str_replace('{schemes}', '(' . implode('|', $this->validSchemes) . ')', $this->pattern);
91
            } else {
92 1
                $pattern = $this->pattern;
93
            }
94
95 6
            if ($this->enableIDN) {
96 1
                $value = preg_replace_callback('/:\/\/([^\/]+)/', function ($matches) {
97 1
                    return '://' . $this->idnToAscii($matches[1]);
98 1
                }, $value);
99
            }
100
101 6
            if (preg_match($pattern, $value)) {
102 6
                return null;
103
            }
104
        }
105
106 4
        return [$this->message, []];
107
    }
108
109 1
    private function idnToAscii($idn)
110
    {
111 1
        if (PHP_VERSION_ID < 50600) {
112
            // TODO: drop old PHP versions support
113
            return idn_to_ascii($idn);
114
        }
115
116 1
        return idn_to_ascii($idn, 0, INTL_IDNA_VARIANT_UTS46);
117
    }
118
}
119