Completed
Push — master ( 2bfc67...5f7bba )
by Mehmet
02:21
created

Validator::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
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
final 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
    public function __construct()
52
    {
53
    }
54
55
    /**
56
     * @param string $itemKey
57
     * @param mixed $item
58
     * @param array $properties
59
     * @return bool|string
60
     * @throws InvalidArgumentException
61
     */
62
    public function assertDocItem(string $itemKey, $item, array $properties)
63
    {
64
        $this->checkType($itemKey, $properties);
65
        $dataType = self::$constraints[upperCamelCase($properties['type'])];
66
        unset($properties['type']);
67
        $constraint = new $dataType($itemKey, $item, $properties);
68
        return $constraint->assert();
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
        $this->doesSchemaHasKey($schema, $key, $vKey);
118
        $tmp = $this->isMulti($schema, $myDoc[$key], $key, $vKey);
119
        if ($tmp !== false && is_array($tmp)) {
120
            return $tmp;
121
        }
122
        $myDocKeyType = getType($myDoc[$key]);
123
        $this->checkValueType($myDocKeyType, $schema[$key], $vKey);
124
        $this->assertDocItem($key, $myDoc[$key], $schema[$key]);
125
        return $myDoc[$key];
126
    }
127
128
    private function isMulti(array $schema, $myDoc, $key, array $vKey)
129
    {
130
        if (array_key_exists('_many', $schema[$key])) {
131
            $newDoc = [];
132
            foreach ($myDoc as $mKey => $item) {
133
                $tmpvKey = $vKey + [$mKey];
134
                $newDoc[] = $this->assertDoc($schema[$key]['_many'], $item, $tmpvKey);
135
            }
136
            return $newDoc;
137
        }
138
        return false;
139
    }
140
141
    private function doesSchemaHasKey(array $schema, $key, $vKey)
142
    {
143
        // Does doc has an array key that does not exist in model definition.
144
        if (!array_key_exists($key, $schema)) {
145
            $message = sprintf('Error for key "%s" that does not exist in the model', implode('.', $vKey));
146
            throw new InvalidArgumentException($message);
147
        }
148
    }
149
150
    // Is the value of the array[key] have same variable type
151
    //that stated in the definition of the model array.
152
    private function checkValueType($myDocKeyType, $schemaKey, $vKey)
153
    {
154
        if (array_key_exists('type', $schemaKey)
155
            && $myDocKeyType  !== self::$validTypes[upperCamelCase($schemaKey['type'])]
156
        ) {
157
            $message = sprintf(
158
                'Error for key "%s": %s value given but it must have been %s',
159
                implode('.', $vKey),
160
                $myDocKeyType,
161
                self::$validTypes[upperCamelCase($schemaKey['type'])]
162
            );
163
            throw new InvalidArgumentException($message);
164
        }
165
    }
166
}
167