Completed
Pull Request — master (#126)
by
unknown
12:21 queued 11:00
created

Format::setIgnoreUnknownFormats()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
ccs 3
cts 3
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
39
    /**
40
     * @var boolean
41
     */
42
    private $ignoreUnknownFormats = true;
43
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 82
    public function __construct(array $extensions = [], $ignoreUnknownFormats = true)
50
    {
51 82
        foreach ($extensions as $format => $extension) {
52 8
            $this->addExtension($format, $extension);
53 41
        }
54
55 82
        $this->ignoreUnknownFormats = $ignoreUnknownFormats;
56 82
    }
57
58
    /**
59
     * Add a custom format extension.
60
     *
61
     * @param string                                                                 $format
62
     * @param \League\JsonGuard\Constraint\DraftFour\Format\FormatExtensionInterface $extension
63
     */
64 12
    public function addExtension($format, FormatExtensionInterface $extension)
65
    {
66 12
        $this->extensions[$format] = $extension;
67 12
    }
68
69
    /**
70
     * Define if unknown formats shall be ignored
71
     *
72
     * @param boolean
73
     */
74 16
    public function setIgnoreUnknownFormats($ignoreUnknownFormats)
75
    {
76 16
        $this->ignoreUnknownFormats = $ignoreUnknownFormats;
77 16
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82 82
    public function validate($value, $parameter, Validator $validator)
83
    {
84 82
        Assert::type($parameter, 'string', self::KEYWORD, $validator->getSchemaPath());
85
86 80
        if (isset($this->extensions[$parameter])) {
87 12
            return $this->extensions[$parameter]->validate($value, $validator);
88
        }
89
90
        switch ($parameter) {
91 68
            case 'date-time':
92 32
                return self::validateRegex(
93 32
                    $value,
94 32
                    self::DATE_TIME_PATTERN,
95 16
                    $validator
96 16
                );
97 38
            case 'uri':
98 4
                return self::validateFilter(
99 4
                    $value,
100 4
                    FILTER_VALIDATE_URL,
101 4
                    null,
102 2
                    $validator
103 2
                );
104 36
            case 'email':
105 4
                return self::validateFilter(
106 4
                    $value,
107 4
                    FILTER_VALIDATE_EMAIL,
108 4
                    null,
109 2
                    $validator
110 2
                );
111 34
            case 'ipv4':
112 2
                return self::validateFilter(
113 2
                    $value,
114 2
                    FILTER_VALIDATE_IP,
115 2
                    FILTER_FLAG_IPV4,
116 1
                    $validator
117 1
                );
118 34
            case 'ipv6':
119 2
                return self::validateFilter(
120 2
                    $value,
121 2
                    FILTER_VALIDATE_IP,
122 2
                    FILTER_FLAG_IPV6,
123 1
                    $validator
124 1
                );
125 34
            case 'hostname':
126 2
                return self::validateRegex(
127 2
                    $value,
128 2
                    self::HOST_NAME_PATTERN,
129 1
                    $validator
130 1
                );
131 16
            default:
132 32
                if (!$this->ignoreUnknownFormats) {
133 16
                    throw InvalidSchemaException::invalidParameter(
134 16
                        $parameter,
135 16
                        array_merge($this->knownformats, array_keys($this->extensions)),
136 16
                        self::KEYWORD,
137 16
                        $validator->getSchemaPath()
138 8
                    );
139
                }
140 16
        }
141 16
    }
142
143
    /**
144
     * @param mixed                       $value
145
     * @param string                      $pattern
146
     * @param \League\JsonGuard\Validator $validator
147
     *
148
     * @return \League\JsonGuard\ValidationError|null
149
     */
150 32
    private static function validateRegex($value, $pattern, Validator $validator)
151
    {
152 32
        if (!is_string($value) || preg_match($pattern, $value) === 1) {
153 20
            return null;
154
        }
155
156 14
        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 6
    private static function validateFilter($value, $filter, $options, Validator $validator)
168
    {
169 6
        if (!is_string($value) || filter_var($value, $filter, $options) !== false) {
170 6
            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 2
        if ($filter === FILTER_VALIDATE_URL && is_string($value) && strpos($value, '//') === 0) {
176 2
            if (filter_var('http:' . $value, $filter, $options) !== false) {
177 2
                return null;
178
            }
179
        }
180
181 2
        return error('The value must match the format {parameter}.', $validator);
182
    }
183
}
184