Format   A
last analyzed

Coupling/Cohesion

Components 1
Dependencies 3

Complexity

Total Complexity 21

Size/Duplication

Total Lines 143
Duplicated Lines 0 %

Test Coverage

Coverage 95.38%

Importance

Changes 0
Metric Value
wmc 21
lcom 1
cbo 3
dl 0
loc 143
ccs 62
cts 65
cp 0.9538
rs 10
c 0
b 0
f 0

5 Methods

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