Completed
Pull Request — master (#126)
by
unknown
02:24
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 = [], bool $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
                    $allowedParameter = array_merge($this->knownformats, array_keys($this->extensions));
134
                    throw InvalidSchemaException::invalidParameter($parameter, $allowedParameter, self::KEYWORD, $validator->getSchemaPath());
135
                }
136
        }
137 6
    }
138
139 6
    /**
140 6
     * @param mixed                       $value
141
     * @param string                      $pattern
142
     * @param \League\JsonGuard\Validator $validator
143
     *
144
     * @return \League\JsonGuard\ValidationError|null
145 2
     *
146 2
     */
147 2
    private static function validateRegex($value, $pattern, Validator $validator)
148
    {
149
        if (!is_string($value) || preg_match($pattern, $value) === 1) {
150
            return null;
151 2
        }
152
153
        return error('The value {data} must match the format {parameter}.', $validator);
154
    }
155
156
    /**
157
     * @param mixed                       $value
158
     * @param int                         $filter
159
     * @param mixed                       $options
160
     * @param \League\JsonGuard\Validator $validator
161
     *
162
     * @return \League\JsonGuard\ValidationError|null
163
     *
164
     */
165
    private static function validateFilter($value, $filter, $options, Validator $validator)
166
    {
167
        if (!is_string($value) || filter_var($value, $filter, $options) !== false) {
168
            return null;
169
        }
170
171
        // This workaround allows otherwise valid protocol relative urls to pass.
172
        // @see https://bugs.php.net/bug.php?id=72301
173
        if ($filter === FILTER_VALIDATE_URL && is_string($value) && strpos($value, '//') === 0) {
174
            if (filter_var('http:' . $value, $filter, $options) !== false) {
175
                return null;
176
            }
177
        }
178
179
        return error('The value must match the format {parameter}.', $validator);
180
    }
181
}
182