Completed
Pull Request — master (#126)
by
unknown
14:25
created

Format::setIgnoreUnknownFormats()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace League\JsonGuard\Constraint\DraftFour;
4
5
use League\JsonGuard\Assert;
6
use League\JsonGuard\Constraint\DraftFour\Format\FormatExtensionInterface;
7
use League\JsonGuard\ConstraintInterface;
8
use League\JsonGuard\Validator;
9
use function League\JsonGuard\error;
10
use League\JsonGuard\Exception\InvalidSchemaException;
11
12
final class Format implements ConstraintInterface
13
{
14
    const KEYWORD = 'format';
15
16
    /**
17
     * @see https://tools.ietf.org/html/rfc3339#section-5.6
18
     */
19
    const DATE_TIME_PATTERN =
20
        '/^(?<fullyear>\d{4})-(?<month>0[1-9]|1[0-2])-(?<mday>0[1-9]|[12][0-9]|3[01])' . 'T' .
21
        '(?<hour>[01][0-9]|2[0-3]):(?<minute>[0-5][0-9]):(?<second>[0-5][0-9]|60)(?<secfrac>\.[0-9]+)?' .
22
        '(Z|(\+|-)(?<offset_hour>[01][0-9]|2[0-3]):(?<offset_minute>[0-5][0-9]))$/i';
23
24
    /**
25
     * @internal
26
     */
27
    const HOST_NAME_PATTERN = '/^[_a-z]+\.([_a-z]+\.?)+$/i';
28
29
    /**
30
     * @var string[]
31
     */
32
    private $knownformats = ['date-time', 'uri', 'email', 'ipv4', 'ipv6','hostname'];
33
34
    /**
35
     * @var \League\JsonGuard\Constraint\DraftFour\Format\FormatExtensionInterface[]
36
     */
37
    private $extensions = [];
38 42
39
    /**
40 42
     * @var boolean
41
     */
42 21
    private $ignoreUnknownFormats = true;
43 42
44
    /**
45
     * Any custom format extensions to use, indexed by the format name.
46
     *
47
     * @param array \League\JsonGuard\Constraint\DraftFour\Format\FormatExtensionInterface[]
48
     */
49
    public function __construct(array $extensions = [], $ignoreUnknownFormats = true)
50
    {
51 4
        foreach ($extensions as $format => $extension) {
52
            $this->addExtension($format, $extension);
53 4
        }
54 4
55
        $this->ignoreUnknownFormats = $ignoreUnknownFormats;
56
    }
57
58
    /**
59 42
     * Add a custom format extension.
60
     *
61 42
     * @param string                                                                 $format
62
     * @param \League\JsonGuard\Constraint\DraftFour\Format\FormatExtensionInterface $extension
63 40
     */
64 4
    public function addExtension($format, FormatExtensionInterface $extension)
65
    {
66
        $this->extensions[$format] = $extension;
67
    }
68 36
69 32
    /**
70 32
     * Define if unknown formats shall be ignored
71 32
     *
72 16
     * @param bool
73 16
     */
74 6
    public function setIgnoreUnknownFormats(bool $ignoreUnknownFormats)
75 4
    {
76 4
        $this->ignoreUnknownFormats = $ignoreUnknownFormats;
77 4
    }
78 4
79 2
    /**
80 2
     * {@inheritdoc}
81 4
     */
82 4
    public function validate($value, $parameter, Validator $validator)
83 4
    {
84 4
        Assert::type($parameter, 'string', self::KEYWORD, $validator->getSchemaPath());
85 4
86 2
        if (isset($this->extensions[$parameter])) {
87 2
            return $this->extensions[$parameter]->validate($value, $validator);
88 2
        }
89 2
90 2
        switch ($parameter) {
91 2
            case 'date-time':
92 2
                return self::validateRegex(
93 1
                    $value,
94 1
                    self::DATE_TIME_PATTERN,
95 2
                    $validator
96 2
                );
97 2
            case 'uri':
98 2
                return self::validateFilter(
99 2
                    $value,
100 1
                    FILTER_VALIDATE_URL,
101 1
                    null,
102 2
                    $validator
103 2
                );
104 2
            case 'email':
105 2
                return self::validateFilter(
106 1
                    $value,
107 1
                    FILTER_VALIDATE_EMAIL,
108
                    null,
109
                    $validator
110
                );
111
            case 'ipv4':
112
                return self::validateFilter(
113
                    $value,
114
                    FILTER_VALIDATE_IP,
115
                    FILTER_FLAG_IPV4,
116
                    $validator
117
                );
118
            case 'ipv6':
119 32
                return self::validateFilter(
120
                    $value,
121 32
                    FILTER_VALIDATE_IP,
122 20
                    FILTER_FLAG_IPV6,
123
                    $validator
124
                );
125 14
            case 'hostname':
126
                return self::validateRegex(
127
                    $value,
128
                    self::HOST_NAME_PATTERN,
129
                    $validator
130
                );
131
            default:
132
                if (!$this->ignoreUnknownFormats) {
133
                    throw InvalidSchemaException::invalidParameter(
134
                        $parameter,
135
                        array_merge($this->knownformats, array_keys($this->extensions)),
136
                        self::KEYWORD,
137 6
                        $validator->getSchemaPath()
138
                    );
139 6
                }
140 6
        }
141
    }
142
143
    /**
144
     * @param mixed                       $value
145 2
     * @param string                      $pattern
146 2
     * @param \League\JsonGuard\Validator $validator
147 2
     *
148
     * @return \League\JsonGuard\ValidationError|null
149
     */
150
    private static function validateRegex($value, $pattern, Validator $validator)
151 2
    {
152
        if (!is_string($value) || preg_match($pattern, $value) === 1) {
153
            return null;
154
        }
155
156
        return error('The value {data} must match the format {parameter}.', $validator);
157
    }
158
159
    /**
160
     * @param mixed                       $value
161
     * @param int                         $filter
162
     * @param mixed                       $options
163
     * @param \League\JsonGuard\Validator $validator
164
     *
165
     * @return \League\JsonGuard\ValidationError|null
166
     */
167
    private static function validateFilter($value, $filter, $options, Validator $validator)
168
    {
169
        if (!is_string($value) || filter_var($value, $filter, $options) !== false) {
170
            return null;
171
        }
172
173
        // This workaround allows otherwise valid protocol relative urls to pass.
174
        // @see https://bugs.php.net/bug.php?id=72301
175
        if ($filter === FILTER_VALIDATE_URL && is_string($value) && strpos($value, '//') === 0) {
176
            if (filter_var('http:' . $value, $filter, $options) !== false) {
177
                return null;
178
            }
179
        }
180
181
        return error('The value must match the format {parameter}.', $validator);
182
    }
183
}
184