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
![]() |
|||||
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
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
![]() |
|||||
217 | continue; |
||||
218 | } |
||||
219 | |||||
220 | $results[$this->replaceAttributeName($key)] = $this->transformToArray($this->getAttribute($key, $value)); |
||||
221 | } |
||||
222 | |||||
223 | return $results; |
||||
224 | } |
||||
225 | } |
||||
226 |