Total Complexity | 56 |
Total Lines | 373 |
Duplicated Lines | 1.34 % |
Coverage | 100% |
Changes | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like AbstractArrayBackedDaftObject often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use AbstractArrayBackedDaftObject, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
14 | abstract class AbstractArrayBackedDaftObject extends AbstractDaftObject implements DaftObjectCreatedByArray |
||
15 | { |
||
16 | /** |
||
17 | * data for this instance. |
||
18 | * |
||
19 | * @var array |
||
20 | */ |
||
21 | private $data = []; |
||
22 | |||
23 | /** |
||
24 | * List of changed properties. |
||
25 | * |
||
26 | * @var bool[] |
||
27 | */ |
||
28 | private $changedProperties = []; |
||
29 | |||
30 | /** |
||
31 | * List of changed properties, for write-once read-many. |
||
32 | * |
||
33 | * @var bool[] |
||
34 | */ |
||
35 | private $wormProperties = []; |
||
36 | |||
37 | /** |
||
38 | * {@inheritdoc} |
||
39 | */ |
||
40 | 189 | public function __construct(array $data = [], bool $writeAll = false) |
|
41 | { |
||
42 | 189 | parent::__construct(); |
|
43 | |||
44 | 187 | if (true === $writeAll) { |
|
45 | 60 | foreach ($data as $k => $v) { |
|
46 | 60 | $this->__set($k, $v); |
|
47 | } |
||
48 | } else { |
||
49 | 129 | foreach ($data as $k => $v) { |
|
50 | 68 | $this->data[$k] = $v; |
|
51 | } |
||
52 | } |
||
53 | 185 | } |
|
54 | |||
55 | /** |
||
56 | * {@inheritdoc} |
||
57 | */ |
||
58 | 33 | public function __isset(string $property) : bool |
|
59 | { |
||
60 | return |
||
61 | 33 | in_array($property, static::PROPERTIES, true) && |
|
62 | 33 | isset($this->data, $this->data[$property]); |
|
63 | } |
||
64 | |||
65 | /** |
||
66 | * {@inheritdoc} |
||
67 | */ |
||
68 | 29 | public function ChangedProperties() : array |
|
69 | { |
||
70 | /** |
||
71 | * @var string[] $out |
||
72 | */ |
||
73 | 29 | $out = array_keys($this->changedProperties); |
|
74 | |||
75 | 29 | return $out; |
|
76 | } |
||
77 | |||
78 | /** |
||
79 | * {@inheritdoc} |
||
80 | */ |
||
81 | 33 | public function MakePropertiesUnchanged(string ...$properties) : void |
|
85 | } |
||
86 | 33 | } |
|
87 | |||
88 | /** |
||
89 | * {@inheritdoc} |
||
90 | */ |
||
91 | 82 | public function HasPropertyChanged(string $property) : bool |
|
96 | } |
||
97 | |||
98 | /** |
||
99 | * {@inheritdoc} |
||
100 | */ |
||
101 | 22 | public function jsonSerialize() : array |
|
126 | } |
||
127 | |||
128 | /** |
||
129 | * {@inheritdoc} |
||
130 | */ |
||
131 | 28 | final public static function DaftObjectFromJsonArray( |
|
132 | array $array, |
||
133 | bool $writeAll = false |
||
134 | ) : DaftJson { |
||
135 | 28 | static::ThrowIfNotDaftJson(); |
|
136 | 12 | $array = static::ThrowIfJsonDefNotValid($array); |
|
137 | 8 | $in = []; |
|
138 | |||
139 | 8 | $jsonDef = static::DaftObjectJsonProperties(); |
|
140 | |||
141 | 8 | foreach (array_keys($array) as $prop) { |
|
142 | 8 | if (isset($jsonDef[$prop])) { |
|
143 | 5 | $jsonType = $jsonDef[$prop]; |
|
144 | |||
145 | 5 | if ('[]' === mb_substr($jsonType, -2)) { |
|
146 | 3 | $in[$prop] = static::DaftObjectFromJsonTypeArray( |
|
147 | 3 | mb_substr($jsonType, 0, -2), |
|
148 | 3 | $prop, |
|
149 | 3 | $array[$prop], |
|
150 | 3 | $writeAll |
|
151 | ); |
||
152 | } else { |
||
153 | 2 | $in[$prop] = static::DaftObjectFromJsonType( |
|
154 | 2 | $jsonType, |
|
155 | 2 | $array[$prop], |
|
156 | 2 | $writeAll |
|
157 | ); |
||
158 | } |
||
159 | } else { |
||
160 | 6 | $in[$prop] = $array[$prop]; |
|
161 | } |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * @var DaftJson $out |
||
166 | */ |
||
167 | 6 | $out = new static($in, $writeAll); |
|
168 | |||
169 | 6 | return $out; |
|
170 | } |
||
171 | |||
172 | 22 | public static function DaftObjectFromJsonString(string $string) : DaftJson |
|
173 | { |
||
174 | 22 | static::ThrowIfNotDaftJson(); |
|
175 | |||
176 | 6 | return static::DaftObjectFromJsonArray(json_decode($string, true)); |
|
177 | } |
||
178 | |||
179 | /** |
||
180 | * @param mixed $propVal |
||
181 | * |
||
182 | * @return DaftJson |
||
183 | */ |
||
184 | 2 | protected static function DaftObjectFromJsonType( |
|
192 | } |
||
193 | |||
194 | /** |
||
195 | * @return DaftJson[] |
||
196 | */ |
||
197 | 3 | protected static function DaftObjectFromJsonTypeArray( |
|
222 | } |
||
223 | |||
224 | /** |
||
225 | * Retrieve a property from data. |
||
226 | * |
||
227 | * @param string $property the property being retrieved |
||
228 | * |
||
229 | * @throws PropertyNotNullableException if value is not set and $property is not listed as nullabe |
||
230 | * |
||
231 | * @return mixed the property value |
||
232 | */ |
||
233 | 44 | protected function RetrievePropertyValueFromData(string $property) |
|
247 | } |
||
248 | |||
249 | /** |
||
250 | * {@inheritdoc} |
||
251 | */ |
||
252 | 134 | protected function NudgePropertyValue(string $property, $value) : void |
|
253 | { |
||
254 | 134 | $this->MaybeThrowOnNudge($property, $value); |
|
255 | |||
256 | $isChanged = ( |
||
257 | 75 | false === array_key_exists($property, $this->data) || |
|
258 | 75 | $this->data[$property] !== $value |
|
259 | ); |
||
260 | |||
261 | 75 | $this->data[$property] = $value; |
|
262 | |||
263 | 75 | if ($isChanged && true !== isset($this->changedProperties[$property])) { |
|
264 | 75 | $this->changedProperties[$property] = true; |
|
265 | 75 | $this->wormProperties[$property] = true; |
|
266 | } |
||
267 | 75 | } |
|
268 | |||
269 | /** |
||
270 | * @param mixed $value |
||
271 | * |
||
272 | * @see AbstractArrayBackedDaftObject::NudgePropertyValue() |
||
273 | */ |
||
274 | 134 | private function MaybeThrowOnNudge(string $property, $value) : void |
|
275 | { |
||
276 | 134 | if (true !== in_array($property, static::PROPERTIES, true)) { |
|
277 | 1 | throw new UndefinedPropertyException(static::class, $property); |
|
278 | } elseif ( |
||
279 | 133 | true === is_null($value) && |
|
280 | 88 | true !== in_array($property, static::NULLABLE_PROPERTIES, true) |
|
281 | ) { |
||
282 | 58 | throw new PropertyNotNullableException(static::class, $property); |
|
283 | } elseif ( |
||
284 | 75 | $this instanceof DaftObjectWorm && |
|
285 | ( |
||
286 | 50 | $this->HasPropertyChanged($property) || |
|
287 | 50 | false === empty($this->wormProperties[$property]) |
|
|
|||
288 | ) |
||
289 | ) { |
||
290 | 50 | throw new PropertyNotRewriteableException( |
|
291 | 50 | static::class, |
|
292 | 50 | $property |
|
293 | ); |
||
294 | } |
||
295 | 75 | } |
|
296 | |||
297 | 12 | private static function ThrowIfJsonDefNotValid(array $array) : array |
|
298 | { |
||
299 | 12 | $jsonProps = []; |
|
300 | |||
301 | 12 | $jsonDef = static::DaftObjectJsonProperties(); |
|
302 | 12 | $nullableProps = static::DaftObjectNullableProperties(); |
|
303 | |||
304 | 12 | foreach ($jsonDef as $k => $v) { |
|
305 | 12 | $prop = $v; |
|
306 | 12 | if (is_string($k)) { |
|
307 | 9 | $prop = $k; |
|
308 | } |
||
309 | if ( |
||
310 | ( |
||
311 | 12 | false === isset($array[$prop]) || |
|
312 | 11 | is_null($array[$prop]) |
|
313 | ) && |
||
314 | 3 | false === in_array($prop, $nullableProps, true) |
|
315 | ) { |
||
316 | 1 | throw new PropertyNotNullableException(static::class, $prop); |
|
317 | } |
||
318 | |||
319 | 11 | $jsonProps[] = $prop; |
|
320 | } |
||
321 | |||
322 | 11 | $out = []; |
|
323 | |||
324 | 11 | foreach ($array as $prop => $propVal) { |
|
325 | if ( |
||
326 | 11 | false === in_array($prop, $jsonProps, true) |
|
327 | ) { |
||
328 | 1 | throw new PropertyNotJsonDecodableException( |
|
329 | 1 | static::class, |
|
330 | 1 | $prop |
|
331 | ); |
||
332 | 11 | } elseif (false === is_null($propVal)) { |
|
333 | 11 | if (isset($jsonDef[$prop])) { |
|
334 | 8 | $jsonType = $jsonDef[$prop]; |
|
335 | |||
336 | 8 | if (false === is_array($propVal)) { |
|
337 | 2 | if ('[]' === mb_substr($jsonType, -2)) { |
|
338 | 1 | throw new PropertyNotJsonDecodableShouldBeArrayException( |
|
339 | 1 | static::class, |
|
340 | 1 | $prop |
|
341 | ); |
||
342 | } |
||
343 | 1 | throw new PropertyNotJsonDecodableShouldBeArrayException( |
|
344 | 1 | $jsonType, |
|
345 | 1 | $prop |
|
346 | ); |
||
347 | } |
||
348 | } |
||
349 | 9 | $out[$prop] = $propVal; |
|
350 | } |
||
351 | } |
||
352 | |||
353 | 8 | return $out; |
|
354 | } |
||
355 | |||
356 | 5 | private static function ThrowIfNotJsonType(string $jsonType) : void |
|
357 | { |
||
358 | 5 | if (false === is_a($jsonType, DaftJson::class, true)) { |
|
359 | 1 | throw new ClassDoesNotImplementClassException( |
|
360 | 1 | $jsonType, |
|
361 | 1 | DaftJson::class |
|
362 | ); |
||
363 | } |
||
364 | 4 | } |
|
365 | |||
366 | 3 | private static function ArrayToJsonType( |
|
367 | string $jsonType, |
||
368 | array $propVal, |
||
369 | bool $writeAll |
||
370 | ) : DaftJson { |
||
371 | /** |
||
372 | * @var DaftJson $jsonType |
||
373 | */ |
||
374 | 3 | $jsonType = $jsonType; |
|
375 | |||
376 | 3 | return $jsonType::DaftObjectFromJsonArray( |
|
377 | 3 | $propVal, |
|
378 | 3 | $writeAll |
|
379 | ); |
||
380 | } |
||
381 | |||
382 | 60 | View Code Duplication | private static function ThrowIfNotDaftJson() : void |
387 | ); |
||
388 | } |
||
389 | 12 | } |
|
390 | } |
||
391 |