Completed
Push — master ( 1182bc...6f12a1 )
by Mehmet
02:26
created

Validator::assertItem()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 12
nc 6
nop 4
dl 0
loc 18
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
4
namespace Selami\Entity;
5
6
use Selami\Entity\DataType;
7
use InvalidArgumentException;
8
9
class Validator
10
{
11
    static protected $constraints = [
12
        'Boolean'   => DataType\Boolean::class,
13
        'Date'      => DataType\Date::class,
14
        'Double'    => DataType\Double::class,
15
        'Email'     => DataType\Email::class,
16
        'Enum'      => DataType\Enum::class,
17
        'FilePath'  => DataType\FilePath::class,
18
        'Html'      => DataType\Html::class,
19
        'Integer'   => DataType\Integer::class,
20
        'Ipv4'      => DataType\Ipv4::class,
21
        'Ipv6'      => DataType\Ipv6::class,
22
        'MacAddress'    => DataType\MacAddress::class,
23
        'PhoneNumber'   => DataType\PhoneNumber::class,
24
        'Regex'     => DataType\Regex::class,
25
        'Slug'      => DataType\Slug::class,
26
        'Text'      => DataType\Text::class,
27
        'Url'       => DataType\Url::class,
28
        'Uuid'      => DataType\Uuid::class
29
    ];
30
31
    static protected $validTypes = [
32
        'Boolean'   => 'boolean',
33
        'Date'      => 'string',
34
        'Double'    => 'float',
35
        'Email'     => 'string',
36
        'Enum'      => 'string',
37
        'FilePath'  => 'string',
38
        'Html'      => 'string',
39
        'Integer'   => 'integer',
40
        'Ipv4'      => 'string',
41
        'Ipv6'      => 'string',
42
        'MacAddress'    => 'string',
43
        'PhoneNumber'   => 'string',
44
        'Regex'     => 'string',
45
        'Slug'      => 'string',
46
        'Text'      => 'string',
47
        'Url'       => 'string',
48
        'Uuid'      => 'string'
49
    ];
50
51
    /**
52
     * @param string $itemKey
53
     * @param mixed $item
54
     * @param array $properties
55
     * @return bool|string
56
     * @throws InvalidArgumentException
57
     */
58
    public function assertDocItem(string $itemKey, $item, array $properties)
59
    {
60
        $this->checkType($itemKey, $properties);
61
        try {
62
            $dataType = self::$constraints[upperCamelCase($properties['type'])];
63
            unset($properties['type']);
64
            $constraint = new $dataType($itemKey, $item, $properties);
65
            return $constraint->assert();
66
        } catch (InvalidArgumentException $e) {
67
            return $e->getMessage();
68
        }
69
    }
70
71
    /**
72
     * @param string $itemKey
73
     * @param array $properties
74
     * @return bool
75
     * @throws InvalidArgumentException
76
     */
77
    private function checkType(string $itemKey, array $properties)
78
    {
79
        if (!array_key_exists('type', $properties)) {
80
            throw new InvalidArgumentException(sprintf('%s must have "type" property.', $itemKey));
81
        }
82
        if (!array_key_exists(upperCamelCase($properties['type']), self::$constraints)) {
83
            throw new InvalidArgumentException(sprintf('%s is not valid DataType.', $properties['type']));
84
        }
85
        return true;
86
    }
87
88
    /**
89
     * Validate given documents
90
     *
91
     * @param array $schema
92
     * @param array $myDoc
93
     * @param array $myKey
94
     * @return array
95
     * @throws InvalidArgumentException
96
     */
97
    public function assertDoc(array $schema, array $myDoc, array $myKey = null)
98
    {
99
        $myKeys = array_keys($myDoc);
100
        foreach ($myKeys as $key) {
101
            $myDoc[$key] = $this->assertItem($schema, $myDoc, $key, $myKey);
102
        }
103
        return $myDoc;
104
    }
105
106
    /**
107
     * @param array $schema
108
     * @param array $myDoc
109
     * @param string $key
110
     * @param array $myKey
111
     * @return array|bool|string
112
     * @throws InvalidArgumentException;
113
     */
114
    private function assertItem(array $schema, array $myDoc, string $key, array $myKey = null)
115
    {
116
        $vKey = is_array($myKey) ? $myKey + [$key] : [$key];
117
        $tmp = $this->isMulti($schema, $myDoc[$key], $key, $vKey);
118
        if ($tmp !== false && is_array($tmp)) {
119
            return $tmp;
120
        }
121
        $this->doesSchemaHasKey($schema, $key, $vKey);
122
123
        $myDocKeyType = getType($myDoc[$key]);
124
         $this->checkValueType($myDocKeyType, $schema[$key], $vKey);
125
126
        $assertedItem = $this->assertDocItem($key, $myDoc[$key], $schema[$key]);
127
        if ($assertedItem !== true) {
128
            throw new InvalidArgumentException($assertedItem);
129
        }
130
        return $myDoc[$key];
131
    }
132
133
    private function isMulti(array $schema, $myDoc, $key, array $vKey)
134
    {
135
        if (array_key_exists($key, $schema) && is_array($schema[$key]) && array_key_exists('_many', $schema[$key])) {
136
            $newDoc = [];
137
            foreach ($myDoc as $mKey => $item) {
138
                $tmpvKey = $vKey + [$mKey];
139
                $newDoc[] = $this->assertDoc($schema[$key]['_many'], $item, $tmpvKey);
140
            }
141
            return $newDoc;
142
        }
143
        return false;
144
    }
145
146
    private function doesSchemaHasKey(array $schema, $key, $vKey)
147
    {
148
        // Does doc has an array key that does not exist in model definition.
149
        if (!isset($schema[$key])) {
150
            $message = sprintf('Error for key "%s" that does not exist in the model', implode('.', $vKey));
151
            throw new InvalidArgumentException($message);
152
        }
153
    }
154
155
    // Is the value of the array[key] have same variable type
156
    //that stated in the definition of the model array.
157
    private function checkValueType($myDocKeyType, $schemaKey, $vKey)
158
    {
159
        if (
160
            is_array($schemaKey)
161
            && array_key_exists('type', $schemaKey)
162
            && $myDocKeyType  !== self::$validTypes[upperCamelCase($schemaKey['type'])]
163
        ) {
164
            $message = sprintf(
165
                'Error for key "%s": %s value given but it must have been %s',
166
                implode('.', $vKey),
167
                $myDocKeyType,
168
                self::$validTypes[upperCamelCase($schemaKey['type'])]
169
            );
170
            throw new InvalidArgumentException($message);
171
        }
172
    }
173
}
174