HasArrayableAttributes::collect()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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

216
            if (class_has_trait(get_called_class(), HasHiddenAttributes::class) && $this->/** @scrutinizer ignore-call */ isHidden($key)) {
Loading history...
217
                continue;
218
            }
219
220
            $results[$this->replaceAttributeName($key)] = $this->transformToArray($this->getAttribute($key, $value));
221
        }
222
223
        return $results;
224
    }
225
}
226