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 Serializer 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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 Serializer, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
23 | final class Serializer implements SerializerInterface |
||
24 | { |
||
25 | /** |
||
26 | * Denotes the current object depth of the serializer. |
||
27 | * |
||
28 | * @var int |
||
29 | */ |
||
30 | private $depth = 0; |
||
31 | |||
32 | /** |
||
33 | * {@inheritDoc} |
||
34 | */ |
||
35 | public function serialize(Model $model = null, AdapterInterface $adapter) |
||
43 | |||
44 | /** |
||
45 | * {@inheritDoc} |
||
46 | */ |
||
47 | public function serializeCollection(Collection $collection, AdapterInterface $adapter) |
||
51 | |||
52 | /** |
||
53 | * {@inheritDoc} |
||
54 | */ |
||
55 | public function serializeArray(array $models, AdapterInterface $adapter, $totalCount = null) |
||
67 | |||
68 | /** |
||
69 | * {@inheritDoc} |
||
70 | */ |
||
71 | public function serializeError($title, $message, $httpCode) |
||
79 | |||
80 | /** |
||
81 | * Appends the pagination links to the serialized data. |
||
82 | * |
||
83 | * @param AdapterInterface $adapter |
||
84 | * @param int $totalCount |
||
85 | * @param array $serialized |
||
86 | * @return array |
||
87 | */ |
||
88 | protected function appendPaginationLinks(AdapterInterface $adapter, $totalCount, array $serialized) |
||
125 | |||
126 | /** |
||
127 | * Serializes the "interior" of a model. |
||
128 | * This is the serialization that takes place outside of a "data" container. |
||
129 | * Can be used for root model and relationship model serialization. |
||
130 | * |
||
131 | * @param Model $model |
||
132 | * @param AdapterInterface $adapter |
||
133 | * @return array |
||
134 | */ |
||
135 | protected function serializeModel(Model $model, AdapterInterface $adapter) |
||
178 | |||
179 | /** |
||
180 | * Serializes an attribute value. |
||
181 | * |
||
182 | * @param mixed $value |
||
183 | * @param AttributeMetadata $attrMeta |
||
184 | * @return mixed |
||
185 | */ |
||
186 | protected function serializeAttribute($value, AttributeMetadata $attrMeta) |
||
200 | |||
201 | /** |
||
202 | * Serializes an embed value. |
||
203 | * |
||
204 | * @param Embed|EmbedCollection|null $value |
||
205 | * @param EmbeddedPropMetadata $embeddedPropMeta |
||
206 | * @return array|null |
||
207 | */ |
||
208 | protected function serializeEmbed($value, EmbeddedPropMetadata $embeddedPropMeta) |
||
216 | |||
217 | /** |
||
218 | * Serializes an embed one value. |
||
219 | * |
||
220 | * @param EmbedMetadata $embedMeta |
||
221 | * @param Embed|null $embed |
||
222 | * @return array|null |
||
223 | */ |
||
224 | protected function serializeEmbedOne(EmbedMetadata $embedMeta, Embed $embed = null) |
||
239 | |||
240 | /** |
||
241 | * Serializes an embed many value. |
||
242 | * |
||
243 | * @param EmbedMetadata $embedMeta |
||
244 | * @param EmbedCollection $embed |
||
245 | * @return array |
||
246 | */ |
||
247 | protected function serializeEmbedMany(EmbedMetadata $embedMeta, EmbedCollection $collection) |
||
258 | |||
259 | /** |
||
260 | * Serializes a relationship value |
||
261 | * |
||
262 | * @param Model $owner |
||
263 | * @param Model|Model[]|null $relationship |
||
264 | * @param RelationshipMetadata $relMeta |
||
265 | * @param AdapterInterface $adapter |
||
266 | * @return array |
||
267 | */ |
||
268 | protected function serializeRelationship(Model $owner, $relationship = null, RelationshipMetadata $relMeta, AdapterInterface $adapter) |
||
288 | |||
289 | /** |
||
290 | * Serializes a has-many relationship value |
||
291 | * |
||
292 | * @param Model $owner |
||
293 | * @param Model[]|null $models |
||
294 | * @param AdapterInterface $adapter |
||
295 | * @return array |
||
296 | */ |
||
297 | protected function serializeHasMany(Model $owner, array $models = null, AdapterInterface $adapter) |
||
304 | |||
305 | /** |
||
306 | * Serializes a has-one relationship value |
||
307 | * |
||
308 | * @param Model $owner |
||
309 | * @param Model|null $model |
||
310 | * @param AdapterInterface $adapter |
||
311 | * @return array |
||
312 | */ |
||
313 | protected function serializeHasOne(Model $owner, Model $model = null, AdapterInterface $adapter) |
||
317 | |||
318 | /** |
||
319 | * Encodes the formatted payload array. |
||
320 | * |
||
321 | * @param array $payload |
||
322 | * @return string |
||
323 | */ |
||
324 | private function encode(array $payload) |
||
328 | |||
329 | /** |
||
330 | * Increases the serializer depth. |
||
331 | * |
||
332 | * @return self |
||
333 | */ |
||
334 | protected function increaseDepth() |
||
339 | |||
340 | /** |
||
341 | * Decreases the serializer depth. |
||
342 | * |
||
343 | * @return self |
||
344 | */ |
||
345 | protected function decreaseDepth() |
||
352 | } |
||
353 |
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.
Let’s take a look at an example:
As you can see in this example, the array
$myArray
is initialized the first time when the foreach loop is entered. You can also see that the value of thebar
key is only written conditionally; thus, its value might result from a previous iteration.This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.