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 EnumeratesValues 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 EnumeratesValues, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
38 | trait EnumeratesValues |
||
39 | { |
||
40 | /** |
||
41 | * The methods that can be proxied. |
||
42 | * |
||
43 | * @var array |
||
44 | */ |
||
45 | protected static $proxies = [ |
||
46 | 'average', 'avg', 'contains', 'each', 'every', 'filter', 'first', |
||
47 | 'flatMap', 'groupBy', 'keyBy', 'map', 'max', 'min', 'partition', |
||
48 | 'reject', 'some', 'sortBy', 'sortByDesc', 'sum', 'unique', |
||
49 | ]; |
||
50 | |||
51 | /** |
||
52 | * Create a new collection instance if the value isn't one already. |
||
53 | * |
||
54 | * @param mixed $items |
||
55 | * @return static |
||
56 | */ |
||
57 | 30 | public static function make($items = []) |
|
61 | |||
62 | /** |
||
63 | * Wrap the given value in a collection if applicable. |
||
64 | * |
||
65 | * @param mixed $value |
||
66 | * @return static |
||
67 | */ |
||
68 | 14 | public static function wrap($value) |
|
74 | |||
75 | /** |
||
76 | * Get the underlying items from the given collection if applicable. |
||
77 | * |
||
78 | * @param array|static $value |
||
79 | * @return array |
||
80 | */ |
||
81 | 6 | public static function unwrap($value) |
|
85 | |||
86 | /** |
||
87 | * Alias for the "avg" method. |
||
88 | * |
||
89 | * @param callable|string|null $callback |
||
90 | * @return mixed |
||
91 | */ |
||
92 | 6 | public function average($callback = null) |
|
96 | |||
97 | /** |
||
98 | * Alias for the "contains" method. |
||
99 | * |
||
100 | * @param mixed $key |
||
101 | * @param mixed $operator |
||
102 | * @param mixed $value |
||
103 | * @return bool |
||
104 | */ |
||
105 | 2 | public function some($key, $operator = null, $value = null) |
|
109 | |||
110 | /** |
||
111 | * Determine if an item exists, using strict comparison. |
||
112 | * |
||
113 | * @param mixed $key |
||
114 | * @param mixed $value |
||
115 | * @return bool |
||
116 | */ |
||
117 | 2 | public function containsStrict($key, $value = null) |
|
137 | |||
138 | /** |
||
139 | * Dump the items and end the script. |
||
140 | * |
||
141 | * @param mixed ...$args |
||
142 | * @return void |
||
143 | */ |
||
144 | public function dd(...$args) |
||
150 | |||
151 | /** |
||
152 | * Dump the items. |
||
153 | * |
||
154 | * @return $this |
||
155 | */ |
||
156 | public function dump() |
||
166 | |||
167 | /** |
||
168 | * Execute a callback over each item. |
||
169 | * |
||
170 | * @param callable $callback |
||
171 | * @return $this |
||
172 | */ |
||
173 | 15 | public function each(callable $callback) |
|
183 | |||
184 | /** |
||
185 | * Execute a callback over each nested chunk of items. |
||
186 | * |
||
187 | * @param callable $callback |
||
188 | * @return static |
||
189 | */ |
||
190 | 2 | public function eachSpread(callable $callback) |
|
198 | |||
199 | /** |
||
200 | * Determine if all items pass the given test. |
||
201 | * |
||
202 | * @param string|callable $key |
||
203 | * @param mixed $operator |
||
204 | * @param mixed $value |
||
205 | * @return bool |
||
206 | */ |
||
207 | 2 | public function every($key, $operator = null, $value = null) |
|
223 | |||
224 | /** |
||
225 | * Get the first item by the given key value pair. |
||
226 | * |
||
227 | * @param string $key |
||
228 | * @param mixed $operator |
||
229 | * @param mixed $value |
||
230 | * @return mixed |
||
231 | */ |
||
232 | 2 | public function firstWhere($key, $operator = null, $value = null) |
|
236 | |||
237 | /** |
||
238 | * Determine if the collection is not empty. |
||
239 | * |
||
240 | * @return bool |
||
241 | */ |
||
242 | 18 | public function isNotEmpty() |
|
246 | |||
247 | /** |
||
248 | * Run a map over each nested chunk of items. |
||
249 | * |
||
250 | * @param callable $callback |
||
251 | * @return static |
||
252 | */ |
||
253 | 2 | public function mapSpread(callable $callback) |
|
261 | |||
262 | /** |
||
263 | * Run a grouping map over the items. |
||
264 | * |
||
265 | * The callback should return an associative array with a single key/value pair. |
||
266 | * |
||
267 | * @param callable $callback |
||
268 | * @return static |
||
269 | */ |
||
270 | 4 | public function mapToGroups(callable $callback) |
|
276 | |||
277 | /** |
||
278 | * Map a collection and flatten the result by a single level. |
||
279 | * |
||
280 | * @param callable $callback |
||
281 | * @return static |
||
282 | */ |
||
283 | 2 | public function flatMap(callable $callback) |
|
287 | |||
288 | /** |
||
289 | * Map the values into a new class. |
||
290 | * |
||
291 | * @param string $class |
||
292 | * @return static |
||
293 | */ |
||
294 | 2 | public function mapInto($class) |
|
300 | |||
301 | /** |
||
302 | * Get the min value of a given key. |
||
303 | * |
||
304 | * @param callable|string|null $callback |
||
305 | * @return mixed |
||
306 | */ |
||
307 | 2 | View Code Duplication | public function min($callback = null) |
319 | |||
320 | /** |
||
321 | * Get the max value of a given key. |
||
322 | * |
||
323 | * @param callable|string|null $callback |
||
324 | * @return mixed |
||
325 | */ |
||
326 | 2 | View Code Duplication | public function max($callback = null) |
338 | |||
339 | /** |
||
340 | * "Paginate" the collection by slicing it into a smaller collection. |
||
341 | * |
||
342 | * @param int $page |
||
343 | * @param int $perPage |
||
344 | * @return static |
||
345 | */ |
||
346 | 2 | public function forPage($page, $perPage) |
|
352 | |||
353 | /** |
||
354 | * Partition the collection into two arrays using the given callback or key. |
||
355 | * |
||
356 | * @param callable|string $key |
||
357 | * @param mixed $operator |
||
358 | * @param mixed $value |
||
359 | * @return static |
||
360 | */ |
||
361 | 14 | public function partition($key, $operator = null, $value = null) |
|
380 | |||
381 | /** |
||
382 | * Get the sum of the given values. |
||
383 | * |
||
384 | * @param callable|string|null $callback |
||
385 | * @return mixed |
||
386 | */ |
||
387 | 16 | public function sum($callback = null) |
|
401 | |||
402 | /** |
||
403 | * Apply the callback if the value is truthy. |
||
404 | * |
||
405 | * @param bool $value |
||
406 | * @param callable $callback |
||
407 | * @param callable $default |
||
408 | * @return static|mixed |
||
409 | */ |
||
410 | 24 | public function when($value, callable $callback, callable $default = null) |
|
420 | |||
421 | /** |
||
422 | * Apply the callback if the collection is empty. |
||
423 | * |
||
424 | * @param callable $callback |
||
425 | * @param callable $default |
||
426 | * @return static|mixed |
||
427 | */ |
||
428 | 8 | public function whenEmpty(callable $callback, callable $default = null) |
|
432 | |||
433 | /** |
||
434 | * Apply the callback if the collection is not empty. |
||
435 | * |
||
436 | * @param callable $callback |
||
437 | * @param callable $default |
||
438 | * @return static|mixed |
||
439 | */ |
||
440 | 8 | public function whenNotEmpty(callable $callback, callable $default = null) |
|
444 | |||
445 | /** |
||
446 | * Apply the callback if the value is falsy. |
||
447 | * |
||
448 | * @param bool $value |
||
449 | * @param callable $callback |
||
450 | * @param callable $default |
||
451 | * @return static|mixed |
||
452 | */ |
||
453 | 4 | public function unless($value, callable $callback, callable $default = null) |
|
457 | |||
458 | /** |
||
459 | * Apply the callback unless the collection is empty. |
||
460 | * |
||
461 | * @param callable $callback |
||
462 | * @param callable $default |
||
463 | * @return static|mixed |
||
464 | */ |
||
465 | 4 | public function unlessEmpty(callable $callback, callable $default = null) |
|
469 | |||
470 | /** |
||
471 | * Apply the callback unless the collection is not empty. |
||
472 | * |
||
473 | * @param callable $callback |
||
474 | * @param callable $default |
||
475 | * @return static|mixed |
||
476 | */ |
||
477 | 4 | public function unlessNotEmpty(callable $callback, callable $default = null) |
|
481 | |||
482 | /** |
||
483 | * Filter items by the given key value pair. |
||
484 | * |
||
485 | * @param string $key |
||
486 | * @param mixed $operator |
||
487 | * @param mixed $value |
||
488 | * @return static |
||
489 | */ |
||
490 | 6 | public function where($key, $operator = null, $value = null) |
|
494 | |||
495 | /** |
||
496 | * Filter items by the given key value pair using strict comparison. |
||
497 | * |
||
498 | * @param string $key |
||
499 | * @param mixed $value |
||
500 | * @return static |
||
501 | */ |
||
502 | 2 | public function whereStrict($key, $value) |
|
506 | |||
507 | /** |
||
508 | * Filter items by the given key value pair. |
||
509 | * |
||
510 | * @param string $key |
||
511 | * @param mixed $values |
||
512 | * @param bool $strict |
||
513 | * @return static |
||
514 | */ |
||
515 | 4 | View Code Duplication | public function whereIn($key, $values, $strict = false) |
523 | |||
524 | /** |
||
525 | * Filter items by the given key value pair using strict comparison. |
||
526 | * |
||
527 | * @param string $key |
||
528 | * @param mixed $values |
||
529 | * @return static |
||
530 | */ |
||
531 | 2 | public function whereInStrict($key, $values) |
|
535 | |||
536 | /** |
||
537 | * Filter items such that the value of the given key is between the given values. |
||
538 | * |
||
539 | * @param string $key |
||
540 | * @param array $values |
||
541 | * @return static |
||
542 | */ |
||
543 | 2 | public function whereBetween($key, $values) |
|
547 | |||
548 | /** |
||
549 | * Filter items such that the value of the given key is not between the given values. |
||
550 | * |
||
551 | * @param string $key |
||
552 | * @param array $values |
||
553 | * @return static |
||
554 | */ |
||
555 | 2 | public function whereNotBetween($key, $values) |
|
561 | |||
562 | /** |
||
563 | * Filter items by the given key value pair. |
||
564 | * |
||
565 | * @param string $key |
||
566 | * @param mixed $values |
||
567 | * @param bool $strict |
||
568 | * @return static |
||
569 | */ |
||
570 | 4 | View Code Duplication | public function whereNotIn($key, $values, $strict = false) |
578 | |||
579 | /** |
||
580 | * Filter items by the given key value pair using strict comparison. |
||
581 | * |
||
582 | * @param string $key |
||
583 | * @param mixed $values |
||
584 | * @return static |
||
585 | */ |
||
586 | 2 | public function whereNotInStrict($key, $values) |
|
590 | |||
591 | /** |
||
592 | * Filter the items, removing any items that don't match the given type. |
||
593 | * |
||
594 | * @param string $type |
||
595 | * @return static |
||
596 | */ |
||
597 | 2 | public function whereInstanceOf($type) |
|
603 | |||
604 | /** |
||
605 | * Pass the collection to the given callback and return the result. |
||
606 | * |
||
607 | * @param callable $callback |
||
608 | * @return mixed |
||
609 | */ |
||
610 | 2 | public function pipe(callable $callback) |
|
614 | |||
615 | /** |
||
616 | * Pass the collection to the given callback and then return it. |
||
617 | * |
||
618 | * @param callable $callback |
||
619 | * @return $this |
||
620 | */ |
||
621 | 2 | public function tap(callable $callback) |
|
627 | |||
628 | /** |
||
629 | * Create a collection of all elements that do not pass a given truth test. |
||
630 | * |
||
631 | * @param callable|mixed $callback |
||
632 | * @return static |
||
633 | */ |
||
634 | 26 | public function reject($callback = true) |
|
644 | |||
645 | /** |
||
646 | * Return only unique items from the collection array. |
||
647 | * |
||
648 | * @param string|callable|null $key |
||
649 | * @param bool $strict |
||
650 | * @return static |
||
651 | */ |
||
652 | 18 | public function unique($key = null, $strict = false) |
|
666 | |||
667 | /** |
||
668 | * Return only unique items from the collection array using strict comparison. |
||
669 | * |
||
670 | * @param string|callable|null $key |
||
671 | * @return static |
||
672 | */ |
||
673 | 2 | public function uniqueStrict($key = null) |
|
677 | |||
678 | /** |
||
679 | * Collect the values into a collection. |
||
680 | * |
||
681 | * @return \IlluminateAgnostic\Collection\Support\Collection |
||
682 | */ |
||
683 | 85 | public function collect() |
|
687 | |||
688 | /** |
||
689 | * Get the collection of items as a plain array. |
||
690 | * |
||
691 | * @return array |
||
692 | */ |
||
693 | 119 | public function toArray() |
|
699 | |||
700 | /** |
||
701 | * Convert the object into something JSON serializable. |
||
702 | * |
||
703 | * @return array |
||
704 | */ |
||
705 | 8 | public function jsonSerialize() |
|
719 | |||
720 | /** |
||
721 | * Get the collection of items as JSON. |
||
722 | * |
||
723 | * @param int $options |
||
724 | * @return string |
||
725 | */ |
||
726 | 8 | public function toJson($options = 0) |
|
730 | |||
731 | /** |
||
732 | * Get a CachingIterator instance. |
||
733 | * |
||
734 | * @param int $flags |
||
735 | * @return \CachingIterator |
||
736 | */ |
||
737 | 2 | public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING) |
|
741 | |||
742 | /** |
||
743 | * Count the number of items in the collection using a given truth test. |
||
744 | * |
||
745 | * @param callable|null $callback |
||
746 | * @return static |
||
747 | */ |
||
748 | 4 | public function countBy($callback = null) |
|
760 | |||
761 | /** |
||
762 | * Convert the collection to its string representation. |
||
763 | * |
||
764 | * @return string |
||
765 | */ |
||
766 | 6 | public function __toString() |
|
770 | |||
771 | /** |
||
772 | * Add a method to the list of proxied methods. |
||
773 | * |
||
774 | * @param string $method |
||
775 | * @return void |
||
776 | */ |
||
777 | 2 | public static function proxy($method) |
|
781 | |||
782 | /** |
||
783 | * Dynamically access collection proxies. |
||
784 | * |
||
785 | * @param string $key |
||
786 | * @return mixed |
||
787 | * |
||
788 | * @throws \Exception |
||
789 | */ |
||
790 | 33 | public function __get($key) |
|
798 | |||
799 | /** |
||
800 | * Results array of items from Collection or Arrayable. |
||
801 | * |
||
802 | * @param mixed $items |
||
803 | * @return array |
||
804 | */ |
||
805 | 485 | protected function getArrayableItems($items) |
|
823 | |||
824 | /** |
||
825 | * Get an operator checker callback. |
||
826 | * |
||
827 | * @param string $key |
||
828 | * @param string $operator |
||
829 | * @param mixed $value |
||
830 | * @return \Closure |
||
831 | */ |
||
832 | 18 | protected function operatorForWhere($key, $operator = null, $value = null) |
|
872 | |||
873 | /** |
||
874 | * Determine if the given value is callable, but not a string. |
||
875 | * |
||
876 | * @param mixed $value |
||
877 | * @return bool |
||
878 | */ |
||
879 | 108 | protected function useAsCallable($value) |
|
883 | |||
884 | /** |
||
885 | * Get a value retrieving callback. |
||
886 | * |
||
887 | * @param callable|string|null $value |
||
888 | * @return callable |
||
889 | */ |
||
890 | 85 | protected function valueRetriever($value) |
|
900 | } |
||
901 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.