ArrayConverter::convert()   A
last analyzed

Complexity

Conditions 5
Paths 4

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 9
c 1
b 0
f 0
nc 4
nop 2
dl 0
loc 18
ccs 10
cts 10
cp 1
crap 5
rs 9.6111
1
<?php
2
3
namespace Cerbero\Dto\Manipulators;
4
5
/**
6
 * The array converter.
7
 *
8
 */
9
class ArrayConverter
10
{
11
    /**
12
     * The snake case pattern
13
     *
14
     * - Look behind for lower case letters or digits
15
     * - Look ahead for upper case letters
16
     */
17
    protected const RE_SNAKE_CASE = '/(?<=[a-z\d])(?=[A-Z])/';
18
19
    /**
20
     * The class instance.
21
     *
22
     * @var self
23
     */
24
    protected static $instance;
25
26
    /**
27
     * The registered conversions.
28
     *
29
     * @var array
30
     */
31
    protected $conversions = [];
32
33
    /**
34
     * The cached value converters.
35
     *
36
     * @var ValueConverter[]
37
     */
38
    protected $cachedConverters = [];
39
40
    /**
41
     * Instantiate the class
42
     *
43
     */
44 8
    protected function __construct()
45
    {
46
        //
47 8
    }
48
49
    /**
50
     * Retrieve the class instance
51
     *
52
     * @return self
53
     */
54 111
    public static function instance(): self
55
    {
56 111
        return static::$instance = static::$instance ?: new static();
57
    }
58
59
    /**
60
     * Set the given value conversions
61
     *
62
     * @param array $conversions
63
     * @return self
64
     */
65 7
    public function setConversions(array $conversions): self
66
    {
67 7
        $this->conversions = $conversions;
68
69 7
        return $this;
70
    }
71
72
    /**
73
     * Retrieve the value conversions
74
     *
75
     * @return array
76
     */
77 9
    public function getConversions(): array
78
    {
79 9
        return $this->conversions;
80
    }
81
82
    /**
83
     * Add a conversion
84
     *
85
     * @param string $class
86
     * @param string $converter
87
     * @return self
88
     */
89 1
    public function addConversion(string $class, string $converter): self
90
    {
91 1
        $this->conversions[$class] = $converter;
92
93 1
        return $this;
94
    }
95
96
    /**
97
     * Remove a conversion
98
     *
99
     * @param string $class
100
     * @return self
101
     */
102 1
    public function removeConversion(string $class): self
103
    {
104 1
        unset($this->conversions[$class]);
105
106 1
        return $this;
107
    }
108
109
    /**
110
     * Convert the given item into an array
111
     *
112
     * @param mixed $item
113
     * @param bool $snakeCase
114
     * @return mixed
115
     */
116 26
    public function convert($item, bool $snakeCase)
117
    {
118 26
        if (is_object($item) && $converter = $this->getConverterByInstance($item)) {
119 1
            return $converter->fromDto($item);
120
        }
121
122 26
        if (!is_iterable($item)) {
123 26
            return $item;
124
        }
125
126 10
        $result = [];
127
128 10
        foreach ($item as $key => $value) {
129 10
            $formattedKey = $this->formatKey($key, $snakeCase);
130 10
            $result[$formattedKey] = $this->convert($value, $snakeCase);
131
        }
132
133 10
        return $result;
134
    }
135
136
    /**
137
     * Retrieve the converter for the given object instance
138
     *
139
     * @param object $instance
140
     * @return ValueConverter|null
141
     */
142 7
    public function getConverterByInstance($instance): ?ValueConverter
143
    {
144 7
        $class = get_class($instance);
145
146 7
        if (isset($this->cachedConverters[$class])) {
147 1
            return $this->cachedConverters[$class];
148
        }
149
150 7
        foreach ($this->getConversions() as $type => $class) {
151 2
            if (is_a($instance, $type)) {
152 2
                $converter = $this->resolveConverter($class);
153 2
                return $this->cachedConverters[$class] = $this->cachedConverters[$type] = $converter;
154
            }
155
        }
156
157 6
        return null;
158
    }
159
160
    /**
161
     * Retrieve the instance of the given converter
162
     *
163
     * @param string $converter
164
     * @return ValueConverter
165
     */
166 4
    protected function resolveConverter(string $converter): ValueConverter
167
    {
168 4
        return new $converter();
169
    }
170
171
    /**
172
     * Retrieve the formatted array key
173
     *
174
     * @param string $key
175
     * @param bool $snakeCase
176
     * @return string
177
     */
178 79
    public function formatKey(string $key, bool $snakeCase): string
179
    {
180 79
        return $snakeCase ? strtolower(preg_replace(static::RE_SNAKE_CASE, '_', $key)) : $key;
181
    }
182
183
    /**
184
     * Retrieve the converter for the given class
185
     *
186
     * @param string $class
187
     * @return ValueConverter|null
188
     */
189 103
    public function getConverterByClass(string $class): ?ValueConverter
190
    {
191 103
        if (isset($this->cachedConverters[$class])) {
192 4
            return $this->cachedConverters[$class];
193
        }
194
195 102
        if ($converter = $this->conversions[$class] ?? null) {
196 2
            return $this->cachedConverters[$class] = $this->resolveConverter($converter);
197
        }
198
199 101
        return null;
200
    }
201
}
202