Passed
Push — drivers ( 453ead...6c2ab9 )
by Joe
01:44
created

HasArrayableAttributes::reduceValueArray()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 11
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 1
1
<?php
2
3
namespace PhpWinTools\WmiScripting\Concerns;
4
5
use Exception;
6
use Illuminate\Support\Arr;
7
use PhpWinTools\Support\StringModule;
8
use PhpWinTools\WmiScripting\Contracts\Arrayable;
9
use PhpWinTools\WmiScripting\Collections\ArrayCollection;
10
use function PhpWinTools\WmiScripting\Support\class_has_trait;
11
use function PhpWinTools\WmiScripting\Support\class_has_property;
12
use Illuminate\Contracts\Support\Arrayable as IlluminateArrayable;
13
14
trait HasArrayableAttributes
15
{
16
    protected $unmapped_attributes = [];
17
18
    protected $attribute_name_replacements = [];
19
20
    public function getAttribute($attribute, $default = null)
21
    {
22
//        dump($attribute);
23
//        if (strpos($attribute, '.') !== false) {
24
//            dump(explode('.', $attribute));
25
//        }
26
27
        if ($this->hasAttributeMethod($attribute)) {
28
            return $this->getAttributeMethodValue('get' . StringModule::studly($attribute) . 'Attribute', $attribute);
29
        }
30
31
        $value = $default;
32
33
        if (class_has_property(get_called_class(), $attribute)) {
34
            $value = $this->{$attribute};
35
        }
36
37
        if ($key = array_search($attribute, $this->attribute_name_replacements)) {
38
            $value = $this->{$key};
39
        }
40
41
        if (array_key_exists($attribute, $this->unmapped_attributes)) {
42
            $value = $this->unmapped_attributes[$attribute];
43
        }
44
45
        if (class_has_trait(get_called_class(), HasCastableAttributes::class) && $this->hasCast($attribute)) {
46
            $value = $this->cast($attribute, $value);
47
        }
48
49
        return $value;
50
    }
51
52
    public function toArray(): array
53
    {
54
        return array_merge(
55
            $this->getCalculatedAttributes(),
56
            $this->iterateAttributes(get_class_vars(get_called_class())),
57
            $this->iterateAttributes($this->unmapped_attributes)
58
        );
59
    }
60
61
    public function collect(array $array)
62
    {
63
        return ArrayCollection::collect($array);
64
    }
65
66
    public function setUnmappedAttribute($key, $value)
67
    {
68
        $this->unmapped_attributes[$key] = $value;
69
70
        return $this;
71
    }
72
73
    protected function getAttributeMethodValue($method, $attribute)
74
    {
75
        if (class_has_property(get_called_class(), $attribute)) {
76
            return $this->{$method}($this->{$attribute});
77
        }
78
79
        if ($this->hasUnmappedAttribute($attribute)) {
80
            return  $this->{$method}($this->unmapped_attributes[$attribute]);
81
        }
82
83
        return $this->{$method}($attribute);
84
    }
85
86
    protected function getCalculatedAttributes()
87
    {
88
        return $this->collect($this->calculatedAttributes())->mapWithKeys(function ($attribute) {
89
            return [$attribute => $this->{'get' . lcfirst($attribute) . 'Attribute'}()];
90
        })->toArray();
91
    }
92
93
    protected function calculatedAttributes()
94
    {
95
        return $this->collect($this->getAttributeMethods())->map(function ($method) {
96
            return $this->getAttributeNameFromMethod($method);
97
        })->filter(function ($attribute) {
98
            return !class_has_property(get_called_class(), $attribute) && !$this->hasUnmappedAttribute($attribute);
99
        })->values()->toArray();
100
    }
101
102
    protected function hasUnmappedAttribute($attribute)
103
    {
104
        return array_key_exists($attribute, $this->unmapped_attributes);
105
    }
106
107
    protected function hasAttributeMethod($attribute)
108
    {
109
        return $this->collect($this->getAttributeMethods())->filter(function ($method) use ($attribute) {
110
            if (($method_name = $this->getAttributeNameFromMethod($method)) === $attribute) {
111
                return true;
112
            }
113
114
            $method_name = StringModule::snake($method_name);
115
116
            if ($method_name === $attribute) {
117
                return true;
118
            }
119
120
            return false;
121
        })->isNotEmpty();
122
    }
123
124
    protected function getAttributeNameFromMethod($method)
125
    {
126
        return lcfirst(substr($method, 3, -9));
127
    }
128
129
    protected function getAttributeMethods()
130
    {
131
        return $this->collect(get_class_methods(get_called_class()))->filter(function ($method) {
132
            return substr($method, 0, 3) === 'get' && substr($method, -9) === 'Attribute' && $method !== 'getAttribute';
133
        })->values()->toArray();
134
    }
135
136
    protected function replaceAttributeName($key)
137
    {
138
        if (array_key_exists($key, $this->attribute_name_replacements)) {
139
            return $this->attribute_name_replacements[$key];
140
        }
141
142
        return $key;
143
    }
144
145
    protected function objectToArray($value)
146
    {
147
        if (!is_object($value)) {
148
            return $value;
149
        }
150
151
        $array = (array) $value;
152
153
        foreach ($array as $key => $value) {
0 ignored issues
show
introduced by
$value is overwriting one of the parameters of this function.
Loading history...
154
            if (strpos($key, "\x00*\x00") === 0) {
155
                $array[substr($key, 3)] = $value;
156
                unset($array[$key]);
157
            }
158
        }
159
160
        return $array;
161
    }
162
163
    protected function tryToArrayMethod($value)
164
    {
165
        if (!is_object($value)) {
166
            return $value;
167
        }
168
169
        if (is_object($value) && $this->hasToArrayMethod($value)) {
170
            try {
171
                $array = $value->toArray();
172
                if (is_array($array)) {
173
                    return $array;
174
                }
175
            } catch (Exception $exception) {
176
                return $value;
177
            }
178
        }
179
180
        return $value;
181
    }
182
183
    protected function hasToArrayMethod($value)
184
    {
185
        if (!is_object($value)) {
186
            return false;
187
        }
188
189
        if (in_array('toArray', get_class_methods($value))) {
190
            return true;
191
        }
192
193
        return false;
194
    }
195
196
    protected function transformToArray($value, $key = null)
197
    {
198
        if (is_object($value) && ($value instanceof Arrayable || $value instanceof IlluminateArrayable)) {
199
            return $value->toArray();
200
        }
201
202
        if (is_object($value) && is_array(($array = $this->tryToArrayMethod($value)))) {
203
            return $array;
204
        }
205
206
        if (is_object($value)) {
207
            return $this->objectToArray($value);
208
        }
209
210
        if (is_array($value)) {
211
            $value = $this->reduceValueArray($value);
212
        }
213
214
        if (is_array($value)) {
215
            return $this->iterateAttributes($value, $key);
216
        }
217
218
        return $value;
219
    }
220
221
    protected function iterateAttributes(array $attributes, string $current_key = null)
222
    {
223
        $results = [];
224
225
        foreach ($attributes as $key => $value) {
226
            $dot_key = is_null($current_key) ? $key : "{$current_key}.{$key}";
227
228
            if (class_has_trait(get_called_class(), HasHiddenAttributes::class) && $this->isHidden($key)) {
0 ignored issues
show
Bug introduced by
It seems like isHidden() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

228
            if (class_has_trait(get_called_class(), HasHiddenAttributes::class) && $this->/** @scrutinizer ignore-call */ isHidden($key)) {
Loading history...
229
                continue;
230
            }
231
232
            $value = $this->getAttribute($dot_key, $value);
233
234
            $results[$this->replaceAttributeName($key)] = $this->transformToArray($value, $dot_key);
235
        }
236
237
        return $results;
238
    }
239
240
    /**
241
     * @param $value
242
     *
243
     * @return mixed
244
     */
245
    protected function reduceValueArray($value)
246
    {
247
        if (!is_array($value)) {
248
            return $value;
249
        }
250
251
        if (array_key_exists('value', $value)) {
252
            $value = $value['value'];
253
        }
254
255
        return $value;
256
    }
257
}
258