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 Queue 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 Queue, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
16 | final class Queue implements QueueInterface |
||
17 | { |
||
18 | /** |
||
19 | * Maximum millisecond value to use for UTCDateTime creation. |
||
20 | * |
||
21 | * @var integer |
||
22 | */ |
||
23 | const MONGO_INT32_MAX = PHP_INT_MAX; |
||
24 | |||
25 | /** |
||
26 | * mongo collection to use for queue. |
||
27 | * |
||
28 | * @var \MongoDB\Collection |
||
29 | */ |
||
30 | private $collection; |
||
31 | |||
32 | /** |
||
33 | * Construct queue. |
||
34 | * |
||
35 | * @param \MongoDB\Collection|string $collectionOrUrl A MongoCollection instance or the mongo connection url. |
||
36 | * @param string $db the mongo db name |
||
37 | * @param string $collection the collection name to use for the queue |
||
38 | * |
||
39 | * @throws \InvalidArgumentException $collectionOrUrl, $db or $collection was not a string |
||
40 | */ |
||
41 | public function __construct($collectionOrUrl, $db = null, $collection = null) |
||
64 | |||
65 | /** |
||
66 | * Ensure an index for the get() method. |
||
67 | * |
||
68 | * @param array $beforeSort Fields in get() call to index before the sort field in same format |
||
69 | * as \MongoDB\Collection::ensureIndex() |
||
70 | * @param array $afterSort Fields in get() call to index after the sort field in same format as |
||
71 | * \MongoDB\Collection::ensureIndex() |
||
72 | * |
||
73 | * @return void |
||
74 | * |
||
75 | * @throws \InvalidArgumentException value of $beforeSort or $afterSort is not 1 or -1 for ascending and descending |
||
76 | * @throws \InvalidArgumentException key in $beforeSort or $afterSort was not a string |
||
77 | */ |
||
78 | public function ensureGetIndex(array $beforeSort = [], array $afterSort = []) |
||
93 | |||
94 | /** |
||
95 | * Ensure an index for the count() method. |
||
96 | * Is a no-op if the generated index is a prefix of an existing one. If you have a similar ensureGetIndex call, |
||
97 | * call it first. |
||
98 | * |
||
99 | * @param array $fields fields in count() call to index in same format as \MongoDB\Collection::createIndex() |
||
100 | * @param bool $includeRunning whether to include the running field in the index |
||
101 | * |
||
102 | * @return void |
||
103 | * |
||
104 | * @throws \InvalidArgumentException $includeRunning was not a boolean |
||
105 | * @throws \InvalidArgumentException key in $fields was not a string |
||
106 | * @throws \InvalidArgumentException value of $fields is not 1 or -1 for ascending and descending |
||
107 | */ |
||
108 | public function ensureCountIndex(array $fields, $includeRunning) |
||
124 | |||
125 | /** |
||
126 | * Get a non running message from the queue. |
||
127 | * |
||
128 | * @param array $query in same format as \MongoDB\Collection::find() where top level fields do not contain operators. |
||
129 | * Lower level fields can however. eg: valid {a: {$gt: 1}, "b.c": 3}, |
||
130 | * invalid {$and: [{...}, {...}]} |
||
131 | * @param int $runningResetDuration second duration the message can stay unacked before it resets and can be |
||
132 | * retreived again. |
||
133 | * @param int $waitDurationInMillis millisecond duration to wait for a message. |
||
134 | * @param int $pollDurationInMillis millisecond duration to wait between polls. |
||
135 | * |
||
136 | * @return array|null the message or null if one is not found |
||
137 | * |
||
138 | * @throws \InvalidArgumentException $runningResetDuration, $waitDurationInMillis or $pollDurationInMillis was not |
||
139 | * an int |
||
140 | * @throws \InvalidArgumentException key in $query was not a string |
||
141 | */ |
||
142 | public function get(array $query, $runningResetDuration, $waitDurationInMillis = 3000, $pollDurationInMillis = 200) |
||
213 | //@codeCoverageIgnoreEnd |
||
214 | |||
215 | private function getIdFromMessage($message) |
||
227 | |||
228 | /** |
||
229 | * Count queue messages. |
||
230 | * |
||
231 | * @param array $query in same format as \MongoDB\Collection::find() where top level fields do not contain operators. |
||
232 | * Lower level fields can however. eg: valid {a: {$gt: 1}, "b.c": 3}, invalid {$and: [{...}, {...}]} |
||
233 | * @param bool|null $running query a running message or not or all |
||
234 | * |
||
235 | * @return int the count |
||
236 | * |
||
237 | * @throws \InvalidArgumentException $running was not null and not a bool |
||
238 | * @throws \InvalidArgumentException key in $query was not a string |
||
239 | */ |
||
240 | public function count(array $query, $running = null) |
||
263 | |||
264 | /** |
||
265 | * Acknowledge a message was processed and remove from queue. |
||
266 | * |
||
267 | * @param array $message message received from get() |
||
268 | * |
||
269 | * @return void |
||
270 | * |
||
271 | * @throws \InvalidArgumentException $message does not have a field "id" that is a MongoDB\BSON\ObjectID |
||
272 | */ |
||
273 | public function ack(array $message) |
||
286 | |||
287 | /** |
||
288 | * Atomically acknowledge and send a message to the queue. |
||
289 | * |
||
290 | * @param array $message the message to ack received from get() |
||
291 | * @param array $payload the data to store in the message to send. Data is handled same way |
||
292 | * as \MongoDB\Collection::insertOne() |
||
293 | * @param int $earliestGet earliest unix timestamp the message can be retreived. |
||
294 | * @param float $priority priority for order out of get(). 0 is higher priority than 1 |
||
295 | * @param bool $newTimestamp true to give the payload a new timestamp or false to use given message timestamp |
||
296 | * |
||
297 | * @return void |
||
298 | * |
||
299 | * @throws \InvalidArgumentException $message does not have a field "id" that is a ObjectID |
||
300 | * @throws \InvalidArgumentException $earliestGet was not an int |
||
301 | * @throws \InvalidArgumentException $priority was not a float |
||
302 | * @throws \InvalidArgumentException $priority is NaN |
||
303 | * @throws \InvalidArgumentException $newTimestamp was not a bool |
||
304 | */ |
||
305 | public function ackSend(array $message, array $payload, $earliestGet = 0, $priority = 0.0, $newTimestamp = true) |
||
348 | |||
349 | /** |
||
350 | * Requeue message to the queue. Same as ackSend() with the same message. |
||
351 | * |
||
352 | * @param array $message message received from get(). |
||
353 | * @param int $earliestGet earliest unix timestamp the message can be retreived. |
||
354 | * @param float $priority priority for order out of get(). 0 is higher priority than 1 |
||
355 | * @param bool $newTimestamp true to give the payload a new timestamp or false to use given message timestamp |
||
356 | * |
||
357 | * @return void |
||
358 | * |
||
359 | * @throws \InvalidArgumentException $message does not have a field "id" that is a ObjectID |
||
360 | * @throws \InvalidArgumentException $earliestGet was not an int |
||
361 | * @throws \InvalidArgumentException $priority was not a float |
||
362 | * @throws \InvalidArgumentException priority is NaN |
||
363 | * @throws \InvalidArgumentException $newTimestamp was not a bool |
||
364 | */ |
||
365 | public function requeue(array $message, $earliestGet = 0, $priority = 0.0, $newTimestamp = true) |
||
371 | |||
372 | /** |
||
373 | * Send a message to the queue. |
||
374 | * |
||
375 | * @param array $payload the data to store in the message. Data is handled same way as \MongoDB\Collection::insertOne() |
||
376 | * @param int $earliestGet earliest unix timestamp the message can be retreived. |
||
377 | * @param float $priority priority for order out of get(). 0 is higher priority than 1 |
||
378 | * |
||
379 | * @return void |
||
380 | * |
||
381 | * @throws \InvalidArgumentException $earliestGet was not an int |
||
382 | * @throws \InvalidArgumentException $priority was not a float |
||
383 | * @throws \InvalidArgumentException $priority is NaN |
||
384 | */ |
||
385 | public function send(array $payload, $earliestGet = 0, $priority = 0.0) |
||
411 | |||
412 | /** |
||
413 | * Ensure index of correct specification and a unique name whether the specification or name already exist or not. |
||
414 | * Will not create index if $index is a prefix of an existing index |
||
415 | * |
||
416 | * @param array $index index to create in same format as \MongoDB\Collection::createIndex() |
||
417 | * |
||
418 | * @return void |
||
419 | * |
||
420 | * @throws \Exception couldnt create index after 5 attempts |
||
421 | */ |
||
422 | private function ensureIndex(array $index) |
||
455 | |||
456 | /** |
||
457 | * Helper method to validate keys and values for the given sort array |
||
458 | * |
||
459 | * @param array $sort The proposed sort for a mongo index. |
||
460 | * @param string $label The name of the variable given to the public ensureXIndex method. |
||
461 | * @param array &$completedFields The final index array with payload. prefix added to fields. |
||
462 | * |
||
463 | * @return void |
||
464 | */ |
||
465 | private static function verifySort(array $sort, $label, &$completeFields) |
||
481 | } |
||
482 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.