Completed
Pull Request — master (#108)
by Matt
11:29
created

Format::validateRegex()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 2
nop 3
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace League\JsonGuard\Constraints\DraftFour;
4
5
use League\JsonGuard\Assert;
6
use League\JsonGuard\Constraint;
7
use League\JsonGuard\Constraints\DraftFour\Format\FormatExtension;
8
use League\JsonGuard\Validator;
9
use function League\JsonGuard\error;
10
11
class Format implements Constraint
12
{
13
    const KEYWORD = 'format';
14
15
    // @codingStandardsIgnoreStart
16
    // @see https://www.w3.org/TR/2012/REC-xmlschema11-2-20120405/datatypes.html#dateTime-lexical-mapping
17
    const DATE_TIME_PATTERN = '/^-?([1-9][0-9]{3,}|0[0-9]{3})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T(([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\.[0-9]+)?|(24:00:00(\.0+)?))(Z|(\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?$/';
18
    // @codingStandardsIgnoreEnd
19
20
    const HOST_NAME_PATTERN = '/^[_a-z]+\.([_a-z]+\.?)+$/i';
21
22
    /**
23
     * @var \League\JsonGuard\Constraints\DraftFour\Format\FormatExtension[]
24
     */
25
    private $extensions = [];
26
27
    /**
28
     * Any custom format extensions to use, indexed by the format name.
29
     *
30
     * @param array \League\JsonGuard\Constraints\DraftFour\Format\FormatExtension[]
31
     */
32
    public function __construct(array $extensions = [])
33
    {
34
        foreach ($extensions as $format => $extension) {
35
            $this->addExtension($format, $extension);
36
        }
37
    }
38
39
    /**
40
     * Add a custom format extension.
41
     *
42
     * @param string                                                         $format
43
     * @param \League\JsonGuard\Constraints\DraftFour\Format\FormatExtension $extension
44
     */
45
    public function addExtension($format, FormatExtension $extension)
46
    {
47
        $this->extensions[$format] = $extension;
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53
    public function validate($value, $parameter, Validator $validator)
54
    {
55
        Assert::type($parameter, 'string', self::KEYWORD, $validator->getSchemaPath());
56
57
        if (isset($this->extensions[$parameter])) {
58
            return $this->extensions[$parameter]->validate($value, $validator);
59
        }
60
61
        switch ($parameter) {
62
            case 'date-time':
63
                return self::validateRegex(
64
                    $value,
65
                    self::DATE_TIME_PATTERN,
66
                    $validator
67
                );
68
            case 'uri':
69
                return self::validateFilter(
70
                    $value,
71
                    FILTER_VALIDATE_URL,
72
                    null,
73
                    $validator
74
                );
75
            case 'email':
76
                return self::validateFilter(
77
                    $value,
78
                    FILTER_VALIDATE_EMAIL,
79
                    null,
80
                    $validator
81
                );
82
            case 'ipv4':
83
                return self::validateFilter(
84
                    $value,
85
                    FILTER_VALIDATE_IP,
86
                    FILTER_FLAG_IPV4,
87
                    $validator
88
                );
89
            case 'ipv6':
90
                return self::validateFilter(
91
                    $value,
92
                    FILTER_VALIDATE_IP,
93
                    FILTER_FLAG_IPV6,
94
                    $validator
95
                );
96
            case 'hostname':
97
                return self::validateRegex(
98
                    $value,
99
                    self::HOST_NAME_PATTERN,
100
                    $validator
101
                );
102
        }
103
    }
104
105
    /**
106
     * @param mixed                       $value
107
     * @param string                      $pattern
108
     * @param \League\JsonGuard\Validator $validator
109
     *
110
     * @return \League\JsonGuard\ValidationError|null
111
     *
112
     */
113
    private static function validateRegex($value, $pattern, Validator $validator)
114
    {
115
        if (!is_string($value) || preg_match($pattern, $value) === 1) {
116
            return null;
117
        }
118
119
        return error('The value {data} must match the format {parameter}.', $validator);
120
    }
121
122
    /**
123
     * @param mixed                       $value
124
     * @param int                         $filter
125
     * @param mixed                       $options
126
     * @param \League\JsonGuard\Validator $validator
127
     *
128
     * @return \League\JsonGuard\ValidationError|null
129
     *
130
     */
131
    private static function validateFilter($value, $filter, $options, Validator $validator)
132
    {
133
        if (!is_string($value) || filter_var($value, $filter, $options) !== false) {
134
            return null;
135
        }
136
137
        // This workaround allows otherwise valid protocol relative urls to pass.
138
        // @see https://bugs.php.net/bug.php?id=72301
139
        if ($filter === FILTER_VALIDATE_URL && is_string($value) && strpos($value, '//') === 0) {
140
            if (filter_var('http:' . $value, $filter, $options) !== false) {
141
                return null;
142
            }
143
        }
144
145
        return error('The value must match the format {parameter}.', $validator);
146
    }
147
}
148