Test Failed
Push — develop ( b59003...d7f8a0 )
by Freddie
05:27 queued 10s
created

SchemaAttribute::fchars()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of FlexPHP.
4
 *
5
 * (c) Freddie Gar <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace FlexPHP\Schema;
11
12
use Exception;
13
use FlexPHP\Schema\Constants\Keyword;
14
use FlexPHP\Schema\Constants\Rule;
15
use FlexPHP\Schema\Exception\InvalidSchemaAttributeException;
16
use FlexPHP\Schema\Validations\SchemaAttributeLogicValidation;
17
use FlexPHP\Schema\Validations\SchemaAttributeValidation;
18
19
final class SchemaAttribute implements SchemaAttributeInterface
20
{
21
    /**
22
     * @var string
23
     */
24
    private $name;
25
26
    /**
27
     * @var string
28
     */
29
    private $dataType;
30
31
    /**
32
     * @var array<string, mixed>
33
     */
34
    private $constraints = [];
35
36
    /**
37
     * @param mixed $constraints
38
     */
39 137
    public function __construct(string $name, string $dataType, $constraints = null)
40
    {
41 137
        $this->setName($name);
42 137
        $this->setDataType($dataType);
43 137
        $this->setConstraints($constraints);
44
45 137
        $this->validate();
46 99
    }
47
48 137
    public function name(): string
49
    {
50 137
        return $this->name;
51
    }
52
53 137
    public function dataType(): string
54
    {
55 137
        return $this->dataType;
56
    }
57
58 137
    public function constraints(): array
59
    {
60 137
        return $this->constraints;
61
    }
62
63 6
    public function type(): ?string
64
    {
65 6
        return $this->constraints[Rule::TYPE] ?? null;
66
    }
67
68 31
    public function isRequired(): bool
69
    {
70 31
        return (bool)($this->constraints[Rule::REQUIRED] ?? false);
71
    }
72
73 78
    public function minLength(): ?int
74
    {
75 78
        return $this->constraints[Rule::MINLENGTH] ?? null;
76
    }
77
78 73
    public function maxLength(): ?int
79
    {
80 73
        return $this->constraints[Rule::MAXLENGTH] ?? null;
81
    }
82
83 32
    public function minCheck(): ?int
84
    {
85 32
        return $this->constraints[Rule::MINCHECK] ?? null;
86
    }
87
88 31
    public function maxCheck(): ?int
89
    {
90 31
        return $this->constraints[Rule::MAXCHECK] ?? null;
91
    }
92
93 62
    public function min(): ?int
94
    {
95 62
        return $this->constraints[Rule::MIN] ?? null;
96
    }
97
98 59
    public function max(): ?int
99
    {
100 59
        return $this->constraints[Rule::MAX] ?? null;
101
    }
102
103 6
    public function equalTo(): ?string
104
    {
105 6
        return $this->constraints[Rule::EQUALTO] ?? null;
106
    }
107
108 116
    public function isPk(): bool
109
    {
110 116
        return (bool)($this->constraints[Rule::PRIMARYKEY] ?? false);
111
    }
112
113 111
    public function isAi(): bool
114
    {
115 111
        return (bool)($this->constraints[Rule::AUTOINCREMENT] ?? false);
116
    }
117
118 24
    public function isFk(): bool
119
    {
120 24
        return (bool)($this->constraints[Rule::FOREIGNKEY] ?? false);
121
    }
122
123 9
    public function fkTable(): ?string
124
    {
125 9
        return $this->constraints[Rule::FOREIGNKEY]['table'] ?? null;
126
    }
127
128 9
    public function fkId(): ?string
129
    {
130 9
        return $this->constraints[Rule::FOREIGNKEY]['id'] ?? null;
131
    }
132
133 9
    public function fkName(): ?string
134
    {
135 9
        return $this->constraints[Rule::FOREIGNKEY]['name'] ?? null;
136
    }
137
138 110
    public function isCa(): bool
139
    {
140 110
        return (bool)($this->constraints[Rule::CREATEDAT] ?? false);
141
    }
142
143 109
    public function isUa(): bool
144
    {
145 109
        return (bool)($this->constraints[Rule::UPDATEDAT] ?? false);
146
    }
147
148 107
    public function isCb(): bool
149
    {
150 107
        return (bool)($this->constraints[Rule::CREATEDBY] ?? false);
151
    }
152
153 105
    public function isUb(): bool
154
    {
155 105
        return (bool)($this->constraints[Rule::UPDATEDBY] ?? false);
156
    }
157
158 110
    public function isBlameAt(): bool
159
    {
160 110
        return $this->isCa() || $this->isUa();
161
    }
162
163 107
    public function isBlameBy(): bool
164
    {
165 107
        return $this->isCb() || $this->isUb();
166
    }
167
168 28
    public function isBlame(): bool
169
    {
170 28
        return $this->isBlameAt() || $this->isBlameBy();
171
    }
172
173 137
    public function filter(): ?string
174
    {
175
        return $this->constraints[Rule::FILTER] ?? null;
176 137
    }
177 137
178 137
    public function format(): ?string
179
    {
180
        return $this->constraints[Rule::FORMAT] ?? null;
181
    }
182 91
183
    public function isFormat(string $format): bool
184
    {
185 91
        return $this->format() === $format;
186
    }
187
188
    public function trim(): bool
189
    {
190
        return (bool)($this->constraints[Rule::TRIM] ?? false);
191
    }
192
193
    public function fchars(): int
194
    {
195
        return $this->constraints[Rule::FCHARS] ?? 0;
196
    }
197
198
    public function properties(): array
199
    {
200
        return [
201
            Keyword::NAME => $this->name(),
202
            Keyword::DATATYPE => $this->dataType(),
203
            Keyword::CONSTRAINTS => $this->constraints(),
204 91
        ];
205 60
    }
206
207
    public function typeHint(): string
208 36
    {
209
        $typeHintByDataType = [
210
            'smallint' => 'int',
211 137
            'integer' => 'int',
212
            'float' => 'float',
213
            'double' => 'float',
214 137
            'bool' => 'bool',
215 134
            'boolean' => 'bool',
216 39
            'date' => '\DateTime',
217 39
            'date_immutable' => '\DateTimeImmutable',
218 39
            'datetime' => '\DateTime',
219
            'datetime_immutable' => '\DateTimeImmutable',
220
            'datetimetz' => '\DateTime',
221 99
            'datetimetz_immutable' => '\DateTimeImmutable',
222
            'time' => '\DateTime',
223 137
            'time_immutable' => '\DateTimeImmutable',
224
            'array' => 'array',
225 137
            'simple_array' => 'array',
226 137
            'json' => 'array',
227
        ];
228 137
229
        if (isset($typeHintByDataType[$this->dataType()])) {
230 137
            return $typeHintByDataType[$this->dataType()];
231 137
        }
232
233
        return 'string';
234
    }
235
236 137
    private function validate(): void
237
    {
238 137
        try {
239 115
            (new SchemaAttributeValidation($this->properties()))->validate();
240 74
            (new SchemaAttributeLogicValidation($this))->validate();
241
        } catch (Exception $e) {
242 41
            throw new InvalidSchemaAttributeException(
243
                \sprintf('Attribute %s > %s', $this->name(), $e->getMessage())
244
            );
245 137
        }
246
    }
247 74
248
    private function setName(string $name): void
249 74
    {
250 74
        $this->name = $name;
251
    }
252 115
253
    private function setDataType(string $dataType): void
254 115
    {
255 115
        $this->dataType = $dataType;
256
    }
257 74
258
    /**
259 74
     * @param mixed $constraints
260
     */
261
    private function setConstraints($constraints): void
262 74
    {
263 74
        if (!empty($constraints)) {
264
            if (\is_string($constraints)) {
265 74
                $this->setConstraintsFromString($constraints);
266 55
            } else {
267
                $this->setConstraintsFromArray($constraints);
268 55
            }
269 2
        }
270 2
    }
271 53
272 12
    private function setConstraintsFromString(string $constraints): void
273 45
    {
274 12
        $this->setConstraintsFromArray($this->getConstraintsFromString($constraints));
275
    }
276
277 55
    private function setConstraintsFromArray(array $constraints): void
278
    {
279 31
        $this->constraints = $this->getConstraintsCast($constraints);
280
    }
281
282 74
    private function getConstraintsFromString(string $constraints): array
283
    {
284
        $_constraints = \explode('|', $constraints);
285 74
286
        /** @var mixed $_constraint */
287
        foreach ($_constraints as $index => $_constraint) {
288 115
            $_rule = \explode(':', $_constraint);
289
290 115
            if (\count($_rule) === 2) {
291 115
                [$_name, $_options] = $_rule;
292 12
293 12
                if (Rule::FOREIGNKEY !== $_name && \strpos($_options, ',') !== false) { // Range
294 108
                    [$min, $max] = \explode(',', $_options);
295 4
                    $_options = \compact('min', 'max');
296 4
                } elseif (\preg_match('/^false$/i', $_options)) { // False as string
297 4
                    $_options = false;
298 104
                } elseif (\preg_match('/^true$/i', $_options)) { // True as string
299 10
                    $_options = true;
300
                }
301 115
302
                $_constraints[$_name] = $_options;
303
            } else {
304
                $_constraints[$_rule[0]] = true;
305 115
            }
306
307
            unset($_constraints[$index]);
308 10
        }
309
310 10
        return $_constraints;
311 10
    }
312 10
313
    private function getConstraintsCast(array $constraints): array
314 10
    {
315 10
        foreach ($constraints as $name => $value) {
316 2
            if (\is_int($name)) {
317
                $constraints[$value] = true;
318 2
                unset($constraints[$name]);
319 8
            } elseif ($name === Rule::CHECK || $name === Rule::LENGTH) {
320 4
                $constraints['min' . $name] = (int)$value['min'];
321
                $constraints['max' . $name] = (int)$value['max'];
322 4
                unset($constraints[$name]);
323
            } elseif ($name === Rule::FOREIGNKEY && \is_string($value)) {
324 4
                $constraints[$name] = $this->getFkOptions($value);
325
            } else {
326 4
                $constraints[$name] = \is_numeric($value) ? (int)$value : $value;
327
            }
328
        }
329
330 10
        return $constraints;
331 10
    }
332 10
333
    private function getFkOptions(string $constraint): array
334
    {
335
        $_vars = \explode(',', $constraint);
336
        $fkName = 'name';
337
        $fkId = 'id';
338
339
        switch (\count($_vars)) {
340
            case 3:
341
                [$fkTable, $fkName, $fkId] = $_vars;
342
343
                break;
344
            case 2:
345
                [$fkTable, $fkName] = $_vars;
346
347
                break;
348
            default:
349
                [$fkTable] = $_vars;
350
351
                break;
352
        }
353
354
        return [
355
            'table' => $fkTable,
356
            'name' => $fkName,
357
            'id' => $fkId,
358
        ];
359
    }
360
}
361