Passed
Push — 2.x ( 31eec5...1a8680 )
by Aleksei
17:13
created

src/Parser/Typecast.php (1 issue)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cycle\ORM\Parser;
6
7
use Cycle\ORM\Exception\TypecastException;
8
use DateTimeImmutable;
9
use Cycle\Database\DatabaseInterface;
10
use Throwable;
11
12
/**
13
 * @internal
14
 */
15
final class Typecast implements CastableInterface
16
{
17
    private const RULES = ['int', 'bool', 'float', 'datetime'];
18
19
    /** @var array<non-empty-string, bool> */
20
    private array $callableRules = [];
21
22
    /** @var array<non-empty-string, mixed> */
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<non-empty-string, mixed> at position 2 could not be parsed: Unknown type name 'non-empty-string' at position 2 in array<non-empty-string, mixed>.
Loading history...
23
    private array $rules = [];
24
25 3630
    public function __construct(
26
        private DatabaseInterface $database
27
    ) {
28
    }
29
30 3630
    public function setRules(array $rules): array
31
    {
32 3630
        foreach ($rules as $key => $rule) {
33 3630
            if (in_array($rule, self::RULES, true)) {
34 3536
                $this->rules[$key] = $rule;
35 3536
                unset($rules[$key]);
36 164
            } elseif (\is_callable($rule)) {
37 162
                $this->callableRules[$key] = true;
38 162
                $this->rules[$key] = $rule;
39 162
                unset($rules[$key]);
40
            }
41
        }
42
43 3630
        return $rules;
44
    }
45
46 3476
    public function cast(array $data): array
47
    {
48
        try {
49 3476
            foreach ($this->rules as $key => $rule) {
50 3474
                if (!isset($data[$key])) {
51 1198
                    continue;
52
                }
53
54 3448
                if (isset($this->callableRules[$key])) {
55 138
                    $data[$key] = $rule($data[$key], $this->database);
56 138
                    continue;
57
                }
58
59 3378
                $data[$key] = $this->castPrimitive($rule, $data[$key]);
60
            }
61
        } catch (Throwable $e) {
62
            throw new TypecastException(
63
                sprintf('Unable to typecast the `%s` field. %s', $key, $e->getMessage()),
64
                $e->getCode(),
65
                $e
66
            );
67
        }
68
69 3476
        return $data;
70
    }
71
72
    /**
73
     * @throws \Exception
74
     */
75 3378
    private function castPrimitive(mixed $rule, mixed $value): mixed
76
    {
77 3378
        return match ($rule) {
78 3108
            'int' => (int)$value,
79 45
            'bool' => (bool)$value,
80 541
            'float' => (float)$value,
81 51
            'datetime' => new DateTimeImmutable(
82
                $value,
83 51
                $this->database->getDriver()->getTimezone()
84
            ),
85 3378
            default => $value,
86
        };
87
    }
88
}
89