Completed
Push — master ( b1e8a9...8a10a4 )
by Alexpts
02:05
created

TypeCast::castValue()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 3
nop 2
crap 3
1
<?php
2
3
namespace PTS\TypeCast;
4
5
use PTS\Tools\DeepArray;
6
use PTS\TypeCast\Types\DateTimeType;
7
8
class TypeCast
9
{
10
    /** @var string */
11
    protected $paramDelimiter = ':';
12
    /** @var string */
13
    protected $keysDelimiter = '.';
14
15
    /** @var array */
16
    protected $types = [];
17
18
    /** @var DeepArray */
19
    protected $deepArrayService;
20
    /** @var PropException */
21
    protected $notExistValue;
22
23 8
    public function __construct(DeepArray $deepArrayService)
24
    {
25 8
        $this->notExistValue = new PropException('Value is not exists');
26 8
        $this->deepArrayService = $deepArrayService;
27
28 8
        $this->registerType('string', $this->setType('string'))
29 8
            ->registerType('int', $this->setType('int'))
30 8
            ->registerType('float', $this->setType('float'))
31 8
            ->registerType('bool', $this->setType('bool'))
32 8
            ->registerType('array', $this->setType('array'))
33 8
            ->registerType('object', $this->setType('object'))
34 8
            ->registerType('null', $this->setType('null'))
35 8
            ->registerType('datetime', new DateTimeType)
36 8
            ->registerType('each', [$this, 'eachHandlerType']);
37 8
    }
38
39
    /**
40
     * @param array $coll
41
     * @param array $rules
42
     * @return array
43
     * @throws PropException
44
     */
45 1
    protected function eachHandlerType(array $coll, array $rules): array
46
    {
47 1
        foreach ($coll as &$value) {
48 1
            $value = $this->castValue($value, $rules);
49
        }
50
51 1
        return $coll;
52
    }
53
54
    /**
55
     * @param string $type
56
     * @return callable
57
     */
58
    protected function setType(string $type): callable
59
    {
60 8
        return function($value) use ($type) {
61 4
            settype($value, $type);
62
63 4
            return $value;
64 8
        };
65
    }
66
67
    /**
68
     * @param string $name
69
     * @param callable $handler
70
     * @return $this
71
     */
72 8
    public function registerType(string $name, callable $handler): self
73
    {
74 8
        $this->types[$name] = $handler;
75 8
        return $this;
76
    }
77
78 2
    public function getTypes(): array
79
    {
80 2
        return $this->types;
81
    }
82
83
    /**
84
     * @param array $data
85
     * @param array $rules
86
     * @return array
87
     * @throws PropException
88
     */
89 6
    public function cast(array $data, array $rules): array
90
    {
91 6
        foreach ($rules as $name => $attrRules) {
92 6
            $value = $this->getValue($name, $data, $this->notExistValue);
93
94 6
            if (!($value instanceof $this->notExistValue)) {
95 6
                $value = $this->castValue($value, $attrRules);
96 5
                $this->setValue($name, $data, $value);
97
            }
98
        }
99
100 5
        return $data;
101
    }
102
103
    /**
104
     * @param mixed $value
105
     * @param array $rules
106
     * @return mixed
107
     * @throws PropException
108
     */
109 6
    protected function castValue($value, array $rules)
110
    {
111 6
        foreach ($rules as $rule) {
112 6
            list($handlerAlias, $params) = is_string($rule)
113 6
                ? $this->extractStringRule($rule)
114 6
                : $this->extractArrayRule($rule);
115
116 6
            $value = $this->applyCastType($value, $params, $handlerAlias);
117
        }
118
119 5
        return $value;
120
    }
121
122
    /**
123
     * @param mixed $value
124
     * @param array $params
125
     * @param string $handlerAlias
126
     * @return mixed
127
     * @throws PropException
128
     */
129 6
    protected function applyCastType($value, array $params, string $handlerAlias)
130
    {
131 6
        $handler = $this->types[$handlerAlias] ?? null;
132 6
        if (!$handler) {
133 1
            throw new PropException("Handler not found for alias: {$handlerAlias}");
134
        }
135
136 5
        $params = $handlerAlias === 'each' ? [$params] : $params;
137 5
        return $handler($value, ...$params);
138
    }
139
140
    /**
141
     * @param string $name
142
     * @param array $data
143
     * @param mixed $default
144
     * @return mixed
145
     */
146 6
    protected function getValue(string $name, array $data, $default)
147
    {
148 6
        $names = explode($this->keysDelimiter, $name);
149 6
        return $this->deepArrayService->getAttr($names, $data, $default);
150
    }
151
152 5
    protected function setValue(string $name, array &$data, $value)
153
    {
154 5
        $names = explode($this->keysDelimiter, $name);
155 5
        $this->deepArrayService->setAttr($names, $data, $value);
156 5
    }
157
158 1
    public function extractArrayRule(array $rule): array
159
    {
160 1
        return [key($rule), current($rule)];
161
    }
162
163 6
    public function extractStringRule(string $rule): array
164
    {
165 6
        $params = explode($this->paramDelimiter, $rule);
166 6
        $handlerAlias = array_shift($params);
167
168 6
        return [$handlerAlias, (array) $params];
169
    }
170
}
171