TypeCast::extractStringRule()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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