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

Format::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 6
ccs 4
cts 5
cp 0.8
crap 2.032
rs 9.4285
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
11
final class Format implements ConstraintInterface
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\Constraint\DraftFour\Format\FormatExtensionInterface[]
24
     */
25
    private $extensions = [];
26
27
    /**
28
     * Any custom format extensions to use, indexed by the format name.
29
     *
30
     * @param array \League\JsonGuard\Constraint\DraftFour\Format\FormatExtensionInterface[]
31
     */
32 24
    public function __construct(array $extensions = [])
33
    {
34 24
        foreach ($extensions as $format => $extension) {
35
            $this->addExtension($format, $extension);
36 24
        }
37 24
    }
38
39
    /**
40
     * Add a custom format extension.
41
     *
42
     * @param string                                                                 $format
43
     * @param \League\JsonGuard\Constraint\DraftFour\Format\FormatExtensionInterface $extension
44
     */
45 4
    public function addExtension($format, FormatExtensionInterface $extension)
46
    {
47 4
        $this->extensions[$format] = $extension;
48 4
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53 24
    public function validate($value, $parameter, Validator $validator)
54
    {
55 24
        Assert::type($parameter, 'string', self::KEYWORD, $validator->getSchemaPath());
56
57 22
        if (isset($this->extensions[$parameter])) {
58 4
            return $this->extensions[$parameter]->validate($value, $validator);
59
        }
60
61
        switch ($parameter) {
62 18
            case 'date-time':
63 14
                return self::validateRegex(
64 14
                    $value,
65 14
                    self::DATE_TIME_PATTERN,
66
                    $validator
67 14
                );
68 6
            case 'uri':
69 4
                return self::validateFilter(
70 4
                    $value,
71 4
                    FILTER_VALIDATE_URL,
72 4
                    null,
73
                    $validator
74 4
                );
75 4
            case 'email':
76 4
                return self::validateFilter(
77 4
                    $value,
78 4
                    FILTER_VALIDATE_EMAIL,
79 4
                    null,
80
                    $validator
81 4
                );
82 2
            case 'ipv4':
83 2
                return self::validateFilter(
84 2
                    $value,
85 2
                    FILTER_VALIDATE_IP,
86 2
                    FILTER_FLAG_IPV4,
87
                    $validator
88 2
                );
89 2
            case 'ipv6':
90 2
                return self::validateFilter(
91 2
                    $value,
92 2
                    FILTER_VALIDATE_IP,
93 2
                    FILTER_FLAG_IPV6,
94
                    $validator
95 2
                );
96 2
            case 'hostname':
97 2
                return self::validateRegex(
98 2
                    $value,
99 2
                    self::HOST_NAME_PATTERN,
100
                    $validator
101 2
                );
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 14
    private static function validateRegex($value, $pattern, Validator $validator)
114
    {
115 14
        if (!is_string($value) || preg_match($pattern, $value) === 1) {
116 4
            return null;
117
        }
118
119 12
        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 6
    private static function validateFilter($value, $filter, $options, Validator $validator)
132
    {
133 6
        if (!is_string($value) || filter_var($value, $filter, $options) !== false) {
134 6
            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 2
        if ($filter === FILTER_VALIDATE_URL && is_string($value) && strpos($value, '//') === 0) {
140 2
            if (filter_var('http:' . $value, $filter, $options) !== false) {
141 2
                return null;
142
            }
143
        }
144
145 2
        return error('The value must match the format {parameter}.', $validator);
146
    }
147
}
148