Passed
Push — main ( 60d4fa...8f59c9 )
by Giovanni A. L.
01:24
created

Validation::validate()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 4
eloc 8
c 2
b 0
f 0
nc 3
nop 2
dl 0
loc 17
rs 10
1
<?php
2
3
namespace GiovanniALO\DataValidation;
4
5
use GiovanniALO\DataValidation\Repository\DataRepository;
6
use GiovanniALO\DataValidation\Router\RouterInterface;
7
8
class Validation
9
{
10
    /**
11
     * @var Validation
12
     */
13
    private static Validation $instance;
14
15
    /**
16
     * @param  RouterInterface|null  $router
17
     * @param  DataRepository|null  $repository
18
     * @param  array  $data
19
     * @param  array  $errors
20
     */
21
    private function __construct(
22
        private ?RouterInterface $router,
23
        private ?DataRepository $repository,
24
        private array $data = [],
25
        private array $errors = []
26
    ) {
27
    }
28
29
    /**
30
     * @param  RouterInterface|null  $router
31
     * @param  DataRepository|null  $repository
32
     * @return void
33
     */
34
    public static function init(
35
        ?RouterInterface $router = null,
36
        ?DataRepository $repository = null
37
    ): void {
38
        self::$instance = new Validation($router, $repository);
39
    }
40
41
    /**
42
     * @param  array  $rules
43
     * @param  array  $data
44
     * @return void
45
     */
46
    public static function validate(array $rules, array $data = []): void
47
    {
48
        self::$instance->prepareData($data);
49
50
        foreach ($rules as $field => $ruleDefinitions) {
51
            $parsedRules = self::$instance->parseRules($ruleDefinitions);
52
53
            $value = self::$instance->data[$field] ?? '';
54
55
            if (!$value && !self::$instance->isRequired($parsedRules)) {
56
                continue;
57
            }
58
59
            self::$instance->applyFilters($field, $value, $parsedRules);
60
        }
61
62
        self::$instance->render();
63
    }
64
65
    /**
66
     * @param  array  $data
67
     * @return void
68
     */
69
    private function prepareData(array $data): void
70
    {
71
        if ($this->router) {
72
            self::$instance->data = self::$instance?->router?->data() ?? [];
73
        } else {
74
            self::$instance->data = $data;
75
        }
76
    }
77
78
    /**
79
     * @param  array|string  $ruleDefinitions
80
     * @return array
81
     */
82
    private function parseRules(array|string $ruleDefinitions): array
83
    {
84
        return is_array($ruleDefinitions)
0 ignored issues
show
introduced by
The condition is_array($ruleDefinitions) is always true.
Loading history...
85
            ? $ruleDefinitions
86
            : explode('|', $ruleDefinitions);
87
    }
88
89
    /**
90
     * @param  array  $parsedRules
91
     * @return bool
92
     */
93
    private function isRequired(array $parsedRules): bool
94
    {
95
        return in_array('required', $parsedRules);
96
    }
97
98
    /**
99
     * @param  string  $field
100
     * @param  mixed  $value
101
     * @param  array  $parsedRules
102
     * @return void
103
     */
104
    private function applyFilters(
105
        string $field,
106
        mixed $value,
107
        array $parsedRules
108
    ): void {
109
        foreach ($parsedRules as $filter) {
110
            $params = '';
111
112
            if (strpos($filter, ':')) {
113
                [$filter, $params] = explode(':', $filter);
114
            }
115
116
            self::$instance->$filter($field, $value, $params);
117
118
            if (!$value && $filter == 'required') {
119
                break;
120
            }
121
        }
122
    }
123
124
    /**
125
     * @param  string  $field
126
     * @param $value
127
     * @return void
128
     */
129
    private function string(string $field, $value): void
130
    {
131
        if (!is_string($value)) {
132
            $this->errors[$field][] = 'Deve ser uma string';
133
        }
134
    }
135
136
    /**
137
     * @param  string  $field
138
     * @param  string  $value
139
     * @return void
140
     */
141
    private function integer(string $field, string $value): void
142
    {
143
        if (!filter_var($value, FILTER_VALIDATE_INT)) {
144
            $this->errors[$field][] = 'Deve ser um número inteiro';
145
        }
146
    }
147
148
    /**
149
     * @param  string  $field
150
     * @param  string  $value
151
     * @return void
152
     */
153
    private function float(string $field, string $value): void
154
    {
155
        if (!filter_var($value, FILTER_VALIDATE_FLOAT)) {
156
            $this->errors[$field][] = 'Deve ser um número decimal';
157
        }
158
    }
159
160
    /**
161
     * @param  string  $field
162
     * @param  string  $value
163
     * @return void
164
     */
165
    private function boolean(string $field, string $value): void
166
    {
167
        if (!filter_var($value, FILTER_VALIDATE_BOOLEAN)) {
168
            $this->errors[$field][] = 'Deve ser um valor booleano';
169
        }
170
    }
171
172
    /**
173
     * @param  string  $field
174
     * @param  string  $value
175
     * @return void
176
     */
177
    private function date(string $field, string $value): void
178
    {
179
        if (!strtotime($value)) {
180
            $this->errors[$field][] = 'Deve ser uma data válida';
181
        }
182
    }
183
184
    /**
185
     * @param  string  $field
186
     * @param  string  $value
187
     * @return void
188
     */
189
    private function url(string $field, string $value): void
190
    {
191
        if (!filter_var($value, FILTER_VALIDATE_URL)) {
192
            $this->errors[$field][] = 'Deve ser uma URL válida';
193
        }
194
    }
195
196
    /**
197
     * @param  string  $field
198
     * @param  string  $value
199
     * @return void
200
     */
201
    private function required(string $field, string $value): void
202
    {
203
        if (!$value) {
204
            $this->errors[$field][] = 'É obrigatório';
205
        }
206
    }
207
208
    /**
209
     * @param  string  $field
210
     * @param  string  $value
211
     * @return void
212
     */
213
    private function email(string $field, string $value): void
214
    {
215
        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
216
            $this->errors[$field][] = 'Deve ser um endereço de e-mail válido';
217
        }
218
    }
219
220
    /**
221
     * @param  string  $field
222
     * @param  string  $value
223
     * @param  int  $length
224
     * @return void
225
     */
226
    private function min(string $field, string $value, int $length): void
227
    {
228
        if (strlen($value) < $length) {
229
            $this->errors[$field][] = 'Deve conter pelo menos '.$length.' caracteres';
230
        }
231
    }
232
233
    /**
234
     * @param  string  $field
235
     * @param  string  $value
236
     * @param  int  $length
237
     * @return void
238
     */
239
    private function max(string $field, string $value, int $length): void
240
    {
241
        if (strlen($value) > $length) {
242
            $this->errors[$field][] = 'Deve conter no máximo '.$length.' caracteres';
243
        }
244
    }
245
246
    /**
247
     * @param  string  $field
248
     * @param  string  $value
249
     * @return void
250
     */
251
    private function password(string $field, string $value): void
252
    {
253
        if (!preg_match('/^(?=.[a-z])(?=.[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/', $value)) {
254
            $this->errors[$field][] = 'Deve conter pelo menos 8 caracteres, uma'.
255
                ' letra maiúscula, uma letra minúscula e um número';
256
        }
257
    }
258
259
    /**
260
     * @param  string  $field
261
     * @param  string  $value
262
     * @return void
263
     */
264
    private function confirmation(string $field, string $value): void
265
    {
266
        if (!isset($this->data[$field.'_confirmation'])) {
267
            $this->errors[$field][] = 'A confirmação é obrigatória';
268
        } elseif ($value != $this->data[$field.'_confirmation']) {
269
            $this->errors[$field][] = 'A confirmação não confere';
270
        }
271
    }
272
273
    /**
274
     * @param  string  $field
275
     * @param  string  $value
276
     * @param  string  $str
277
     * @return void
278
     */
279
    private function unique(string $field, string $value, string $str): void
280
    {
281
        if ($this->findInDatabase($value, $str)) {
282
            $this->errors[$field][] = 'Já está em uso';
283
        }
284
    }
285
286
    /**
287
     * @param  string  $field
288
     * @param  string  $value
289
     * @param  string  $str
290
     * @return void
291
     */
292
    private function exists(string $field, string $value, string $str): void
293
    {
294
        if (!$this->findInDatabase($value, $str)) {
295
            $this->errors[$field][] = 'Não existe no banco de dados';
296
        }
297
    }
298
299
    /**
300
     * @param  string  $value
301
     * @param  string  $str
302
     * @return object|null
303
     */
304
    private function findInDatabase(string $value, string $str): ?object
305
    {
306
        [$namespace, $column] = explode(',', $str);
307
308
        return $this?->repository?->find(
309
            $namespace,
310
            "{$column} = :{$column}",
311
            [$column => $value]
312
        );
313
    }
314
315
    /**
316
     * @return void
317
     */
318
    private function render(): void
319
    {
320
        if (count($this->errors)) {
321
            http_response_code(400);
322
323
            echo json_encode(['errors' => $this->errors]);
324
325
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
326
        }
327
    }
328
}
329