Completed
Push — master ( 6f12a1...ca99f7 )
by Mehmet
02:42
created

Validator::isMulti()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 4
dl 0
loc 12
rs 9.4285
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
        $dataType = self::$constraints[upperCamelCase($properties['type'])];
62
        unset($properties['type']);
63
        $constraint = new $dataType($itemKey, $item, $properties);
64
        return $constraint->assert();
65
    }
66
67
    /**
68
     * @param string $itemKey
69
     * @param array $properties
70
     * @return bool
71
     * @throws InvalidArgumentException
72
     */
73
    private function checkType(string $itemKey, array $properties)
74
    {
75
        if (!array_key_exists('type', $properties)) {
76
            throw new InvalidArgumentException(sprintf('%s must have "type" property.', $itemKey));
77
        }
78
        if (!array_key_exists(upperCamelCase($properties['type']), self::$constraints)) {
79
            throw new InvalidArgumentException(sprintf('%s is not valid DataType.', $properties['type']));
80
        }
81
        return true;
82
    }
83
84
    /**
85
     * Validate given documents
86
     *
87
     * @param array $schema
88
     * @param array $myDoc
89
     * @param array $myKey
90
     * @return array
91
     * @throws InvalidArgumentException
92
     */
93
    public function assertDoc(array $schema, array $myDoc, array $myKey = null)
94
    {
95
        $myKeys = array_keys($myDoc);
96
        foreach ($myKeys as $key) {
97
            $myDoc[$key] = $this->assertItem($schema, $myDoc, $key, $myKey);
98
        }
99
        return $myDoc;
100
    }
101
102
    /**
103
     * @param array $schema
104
     * @param array $myDoc
105
     * @param string $key
106
     * @param array $myKey
107
     * @return array|bool|string
108
     * @throws InvalidArgumentException;
109
     */
110
    private function assertItem(array $schema, array $myDoc, string $key, array $myKey = null)
111
    {
112
        $vKey = is_array($myKey) ? $myKey + [$key] : [$key];
113
        $this->doesSchemaHasKey($schema, $key, $vKey);
114
        $tmp = $this->isMulti($schema, $myDoc[$key], $key, $vKey);
115
        if ($tmp !== false && is_array($tmp)) {
116
            return $tmp;
117
        }
118
        $myDocKeyType = getType($myDoc[$key]);
119
        $this->checkValueType($myDocKeyType, $schema[$key], $vKey);
120
        $this->assertDocItem($key, $myDoc[$key], $schema[$key]);
121
        return $myDoc[$key];
122
    }
123
124
    private function isMulti(array $schema, $myDoc, $key, array $vKey)
125
    {
126
        if (array_key_exists('_many', $schema[$key])) {
127
            $newDoc = [];
128
            foreach ($myDoc as $mKey => $item) {
129
                $tmpvKey = $vKey + [$mKey];
130
                $newDoc[] = $this->assertDoc($schema[$key]['_many'], $item, $tmpvKey);
131
            }
132
            return $newDoc;
133
        }
134
        return false;
135
    }
136
137
    private function doesSchemaHasKey(array $schema, $key, $vKey)
138
    {
139
        // Does doc has an array key that does not exist in model definition.
140
        if (!array_key_exists($key, $schema)) {
141
            $message = sprintf('Error for key "%s" that does not exist in the model', implode('.', $vKey));
142
            throw new InvalidArgumentException($message);
143
        }
144
    }
145
146
    // Is the value of the array[key] have same variable type
147
    //that stated in the definition of the model array.
148
    private function checkValueType($myDocKeyType, $schemaKey, $vKey)
149
    {
150
        if (array_key_exists('type', $schemaKey)
151
            && $myDocKeyType  !== self::$validTypes[upperCamelCase($schemaKey['type'])]
152
        ) {
153
            $message = sprintf(
154
                'Error for key "%s": %s value given but it must have been %s',
155
                implode('.', $vKey),
156
                $myDocKeyType,
157
                self::$validTypes[upperCamelCase($schemaKey['type'])]
158
            );
159
            throw new InvalidArgumentException($message);
160
        }
161
    }
162
}
163