Complex classes like Collection 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 Collection, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 54 | class Collection implements |
||
| 55 | CollectionInterface, |
||
| 56 | Arrayable, |
||
| 57 | Invokable, |
||
| 58 | Countable, |
||
| 59 | Iterator |
||
| 60 | { |
||
| 61 | use IsArrayable, |
||
| 62 | IsContainer, |
||
| 63 | IsSerializable; |
||
| 64 | |||
| 65 | /** |
||
| 66 | * @var array The collection of data this object represents |
||
| 67 | */ |
||
| 68 | private $data = []; |
||
| 69 | |||
| 70 | /** |
||
| 71 | * @var bool True unless we have advanced past the end of the data array |
||
| 72 | */ |
||
| 73 | protected $isValid = true; |
||
| 74 | |||
| 75 | /** |
||
| 76 | * AbstractCollection constructor. |
||
| 77 | * |
||
| 78 | * @param mixed $data The data to wrap |
||
| 79 | */ |
||
| 80 | public function __construct($data = null) |
||
| 94 | |||
| 95 | public function __invoke() |
||
| 115 | |||
| 116 | /** |
||
| 117 | * Set underlying data array. |
||
| 118 | * |
||
| 119 | * Sets the collection data. This method should NEVER be called anywhere other than in __construct(). |
||
| 120 | * |
||
| 121 | * @param array|Traversable $data The data to wrap |
||
| 122 | */ |
||
| 123 | private function setData($data) |
||
| 128 | |||
| 129 | /** |
||
| 130 | * Get copy of underlying data array. |
||
| 131 | * |
||
| 132 | * Returns a copy of this collection's underlying data array. It returns a copy because collections are supposed to |
||
| 133 | * be immutable. Nothing outside of the constructor should ever have direct access to the actual underlying array. |
||
| 134 | * |
||
| 135 | * @return array |
||
| 136 | */ |
||
| 137 | protected function getData() |
||
| 141 | |||
| 142 | /** |
||
| 143 | * @inheritDoc |
||
| 144 | */ |
||
| 145 | public function count() |
||
| 149 | |||
| 150 | /** |
||
| 151 | * Return the current element. |
||
| 152 | * |
||
| 153 | * Returns the current element in the collection. The internal array pointer |
||
| 154 | * of the data array wrapped by the collection should not be advanced by this |
||
| 155 | * method. No side effects. Return current element only. |
||
| 156 | * |
||
| 157 | * @return mixed |
||
| 158 | */ |
||
| 159 | public function current() |
||
| 163 | |||
| 164 | /** |
||
| 165 | * Return the current key. |
||
| 166 | * |
||
| 167 | * Returns the current key in the collection. No side effects. |
||
| 168 | * |
||
| 169 | * @return mixed |
||
| 170 | */ |
||
| 171 | public function key() |
||
| 175 | |||
| 176 | /** |
||
| 177 | * Advance the internal pointer forward. |
||
| 178 | * |
||
| 179 | * Although this method will return the current value after advancing the |
||
| 180 | * pointer, you should not expect it to. The interface does not require it |
||
| 181 | * to return any value at all. |
||
| 182 | * |
||
| 183 | * @return mixed |
||
| 184 | */ |
||
| 185 | public function next() |
||
| 194 | |||
| 195 | /** |
||
| 196 | * Rewind the internal pointer. |
||
| 197 | * |
||
| 198 | * Return the internal pointer to the first element in the collection. Again, |
||
| 199 | * this method is not required to return anything by its interface, so you |
||
| 200 | * should not count on a return value. |
||
| 201 | * |
||
| 202 | * @return mixed |
||
| 203 | */ |
||
| 204 | public function rewind() |
||
| 210 | |||
| 211 | /** |
||
| 212 | * Is internal pointer in a valid position? |
||
| 213 | * |
||
| 214 | * If the internal pointer is advanced beyond the end of the collection, this method will return false. |
||
| 215 | * |
||
| 216 | * @return bool True if internal pointer isn't past the end |
||
| 217 | */ |
||
| 218 | public function valid() |
||
| 222 | |||
| 223 | /** |
||
| 224 | * @inheritDoc |
||
| 225 | */ |
||
| 226 | public function sort($alg = null) |
||
| 235 | |||
| 236 | /** |
||
| 237 | * @inheritDoc |
||
| 238 | */ |
||
| 239 | public function sortkeys($alg = null) |
||
| 248 | |||
| 249 | /** |
||
| 250 | * Does this collection have a value at given index? |
||
| 251 | * |
||
| 252 | * @param mixed $index The index to check |
||
| 253 | * |
||
| 254 | * @return bool |
||
| 255 | */ |
||
| 256 | public function has($index) |
||
| 260 | |||
| 261 | /** |
||
| 262 | * Set value at given index. |
||
| 263 | * |
||
| 264 | * This method simulates setting a value in this collection, but because collections are immutable, it actually |
||
| 265 | * returns a copy of this collection with the value in the new collection set to specified value. |
||
| 266 | * |
||
| 267 | * @param mixed $index The index to set a value at |
||
| 268 | * @param mixed $val The value to set $index to |
||
| 269 | * |
||
| 270 | * @return $this |
||
| 271 | */ |
||
| 272 | public function set($index, $val) |
||
| 278 | |||
| 279 | /** |
||
| 280 | * Unset (delete) value at the given index. |
||
| 281 | * |
||
| 282 | * Get copy of collection with given index removed. |
||
| 283 | * |
||
| 284 | * @param mixed $index The index to unset |
||
| 285 | * |
||
| 286 | * @return Collection |
||
| 287 | */ |
||
| 288 | public function delete($index) |
||
| 294 | |||
| 295 | /** |
||
| 296 | * Get index of a value. |
||
| 297 | * |
||
| 298 | * Given a value, this method will return the index of the first occurrence of that value. |
||
| 299 | * |
||
| 300 | * @param mixed $value Value to get the index of |
||
| 301 | * |
||
| 302 | * @return int|null|string |
||
| 303 | */ |
||
| 304 | public function indexOf($value) |
||
| 313 | |||
| 314 | /** |
||
| 315 | * Get this collection's keys as a collection. |
||
| 316 | * |
||
| 317 | * @return Collection Containing this collection's keys |
||
| 318 | */ |
||
| 319 | public function keys() |
||
| 323 | |||
| 324 | /** |
||
| 325 | * Get this collection's values as a collection. |
||
| 326 | * |
||
| 327 | * This method returns this collection's values but completely re-indexed (numerically). |
||
| 328 | * |
||
| 329 | * @return Collection Containing this collection's values |
||
| 330 | */ |
||
| 331 | public function values() |
||
| 335 | |||
| 336 | /** |
||
| 337 | * Pad collection to a certain size. |
||
| 338 | * |
||
| 339 | * Returns a new collection, padded to the given size, with the given value. |
||
| 340 | * |
||
| 341 | * @param int $size The number of items that should be in the collection |
||
| 342 | * @param mixed $with The value to pad the collection with |
||
| 343 | * |
||
| 344 | * @return Collection A new collection padded to specified length |
||
| 345 | */ |
||
| 346 | public function pad($size, $with = null) |
||
| 352 | |||
| 353 | /** |
||
| 354 | * Apply a callback to each item in collection. |
||
| 355 | * |
||
| 356 | * Applies a callback to each item in collection and returns a new collection |
||
| 357 | * containing each iteration's return value. |
||
| 358 | * |
||
| 359 | * @param callable $mapper The callback to apply |
||
| 360 | * |
||
| 361 | * @return Collection A new collection with callback return values |
||
| 362 | */ |
||
| 363 | public function map(callable $mapper) |
||
| 372 | |||
| 373 | /** |
||
| 374 | * Like map, except done in-place. |
||
| 375 | * |
||
| 376 | * @param callable $transformer A transformer callback |
||
| 377 | * |
||
| 378 | * @return $this |
||
| 379 | */ |
||
| 380 | public function transform(callable $transformer) |
||
| 386 | |||
| 387 | /** |
||
| 388 | * Iterate over each item in the collection, calling $callback on it. Return false to stop iterating. |
||
| 389 | * |
||
| 390 | * @param callable $callback A callback to use |
||
| 391 | * |
||
| 392 | * @return $this |
||
| 393 | */ |
||
| 394 | public function each(callable $callback) |
||
| 405 | |||
| 406 | /** |
||
| 407 | * Filter the collection. |
||
| 408 | * |
||
| 409 | * Using a callback function, this method will filter out unwanted values, returning |
||
| 410 | * a new collection containing only the values that weren't filtered. |
||
| 411 | * |
||
| 412 | * @param callable $predicate The callback function used to filter |
||
| 413 | * |
||
| 414 | * @return Collection A new collection with only values that weren't filtered |
||
| 415 | */ |
||
| 416 | public function filter(callable $predicate = null) |
||
| 431 | |||
| 432 | /** |
||
| 433 | * Filter the collection. |
||
| 434 | * |
||
| 435 | * Using a callback function, this method will filter out unwanted values, returning |
||
| 436 | * a new collection containing only the values that weren't filtered. |
||
| 437 | * |
||
| 438 | * @param callable $predicate The callback function used to filter |
||
| 439 | * |
||
| 440 | * @return Collection A new collection with only values that weren't filtered |
||
| 441 | */ |
||
| 442 | public function exclude(callable $predicate = null) |
||
| 458 | |||
| 459 | /** |
||
| 460 | * Return the first item that meets given criteria. |
||
| 461 | * |
||
| 462 | * Using a callback function, this method will return the first item in the collection |
||
| 463 | * that causes the callback function to return true. |
||
| 464 | * |
||
| 465 | * @param callable|null $predicate The callback function |
||
| 466 | * @param mixed|null $default The default return value |
||
| 467 | * |
||
| 468 | * @return mixed |
||
| 469 | */ |
||
| 470 | public function first(callable $predicate = null, $default = null) |
||
| 486 | |||
| 487 | /** |
||
| 488 | * Return the last item that meets given criteria. |
||
| 489 | * |
||
| 490 | * Using a callback func`tion, this method will return the last item in the collection |
||
| 491 | * that causes the callback function to return true. |
||
| 492 | * |
||
| 493 | * @param callable|null $callback The callback function |
||
| 494 | * @param mixed|null $default The default return value |
||
| 495 | * |
||
| 496 | * @return mixed |
||
| 497 | */ |
||
| 498 | public function last(callable $callback = null, $default = null) |
||
| 506 | |||
| 507 | /** |
||
| 508 | * Returns collection in reverse order. |
||
| 509 | * |
||
| 510 | * @return Collection This collection in reverse order. |
||
| 511 | */ |
||
| 512 | public function reverse() |
||
| 516 | |||
| 517 | /** |
||
| 518 | * Get unique items. |
||
| 519 | * |
||
| 520 | * Returns a collection of all the unique items in this collection. |
||
| 521 | * |
||
| 522 | * @return Collection This collection with duplicate items removed |
||
| 523 | */ |
||
| 524 | public function unique() |
||
| 528 | |||
| 529 | /** |
||
| 530 | * Collection factory method. |
||
| 531 | * |
||
| 532 | * This method will analyze input data and determine the most appropriate Collection |
||
| 533 | * class to use. It will then instantiate said Collection class with the given |
||
| 534 | * data and return it. |
||
| 535 | * |
||
| 536 | * @param mixed $data The data to wrap |
||
| 537 | * |
||
| 538 | * @return Collection A collection containing $data |
||
| 539 | */ |
||
| 540 | public static function factory($data = null) |
||
| 544 | |||
| 545 | /** |
||
| 546 | * Determine if structure contains all numeric values. |
||
| 547 | * |
||
| 548 | * @return bool |
||
| 549 | */ |
||
| 550 | public function isNumeric() |
||
| 559 | |||
| 560 | /** |
||
| 561 | * @inheritdoc |
||
| 562 | */ |
||
| 563 | public function hasOffset($offset) |
||
| 572 | |||
| 573 | /** |
||
| 574 | * @inheritdoc |
||
| 575 | */ |
||
| 576 | public function getOffsetKey($offset) |
||
| 589 | |||
| 590 | /** |
||
| 591 | * @inheritdoc |
||
| 592 | */ |
||
| 593 | public function getOffset($offset) |
||
| 597 | |||
| 598 | /** |
||
| 599 | * Get each key/value as an array pair. |
||
| 600 | * |
||
| 601 | * Returns a collection of arrays where each item in the collection is [key,value] |
||
| 602 | * |
||
| 603 | * @return Collection |
||
| 604 | */ |
||
| 605 | public function pairs() |
||
| 615 | |||
| 616 | // END Iterator methods |
||
| 617 | |||
| 618 | /** |
||
| 619 | * Counts how many times each value occurs in a collection. |
||
| 620 | * |
||
| 621 | * Returns a new collection with values as keys and how many times that |
||
| 622 | * value appears in the collection. Works best with scalar values but will |
||
| 623 | * attempt to work on collections of objects as well. |
||
| 624 | * |
||
| 625 | * @return Collection |
||
| 626 | */ |
||
| 627 | public function frequency() |
||
| 639 | |||
| 640 | /** |
||
| 641 | * @inheritDoc |
||
| 642 | */ |
||
| 643 | public function add($index, $value) |
||
| 650 | |||
| 651 | /** |
||
| 652 | * @inheritdoc |
||
| 653 | * @todo Maybe read would be a better name for this? |
||
| 654 | */ |
||
| 655 | public function get($index, $default = null) |
||
| 663 | |||
| 664 | /** |
||
| 665 | * @inheritdoc |
||
| 666 | * @todo Maybe read would be a better name for this? |
||
| 667 | */ |
||
| 668 | public function retrieve($index) |
||
| 675 | |||
| 676 | /** |
||
| 677 | * @inheritDoc |
||
| 678 | */ |
||
| 679 | public function prepend($item) |
||
| 686 | |||
| 687 | /** |
||
| 688 | * @inheritDoc |
||
| 689 | */ |
||
| 690 | public function append($item) |
||
| 697 | |||
| 698 | /** |
||
| 699 | * @inheritDoc |
||
| 700 | */ |
||
| 701 | public function chunk($size) |
||
| 718 | |||
| 719 | public function combine($values) |
||
| 735 | |||
| 736 | /** |
||
| 737 | * @inheritDoc |
||
| 738 | */ |
||
| 739 | public function diff($data, callable $equals = null) |
||
| 759 | |||
| 760 | /** |
||
| 761 | * @inheritDoc |
||
| 762 | */ |
||
| 763 | public function diffKeys($data, callable $equals = null) |
||
| 772 | |||
| 773 | /** |
||
| 774 | * @inheritDoc |
||
| 775 | */ |
||
| 776 | public function nth($nth, $offset = null) |
||
| 782 | |||
| 783 | /** |
||
| 784 | * @inheritDoc |
||
| 785 | */ |
||
| 786 | public function except($indexes) |
||
| 790 | |||
| 791 | /** |
||
| 792 | * @inheritDoc |
||
| 793 | */ |
||
| 794 | public function flip() |
||
| 798 | |||
| 799 | /** |
||
| 800 | * @inheritDoc |
||
| 801 | */ |
||
| 802 | public function intersect($data) |
||
| 811 | |||
| 812 | /** |
||
| 813 | * @inheritDoc |
||
| 814 | */ |
||
| 815 | public function intersectKeys($data) |
||
| 824 | |||
| 825 | /** |
||
| 826 | * @inheritDoc |
||
| 827 | */ |
||
| 828 | public function isEmpty(callable $callback = null) |
||
| 835 | |||
| 836 | /** |
||
| 837 | * @inheritDoc |
||
| 838 | */ |
||
| 839 | public function only($indices) |
||
| 843 | |||
| 844 | /** |
||
| 845 | * @inheritDoc |
||
| 846 | */ |
||
| 847 | public function pipe(callable $callback) |
||
| 851 | |||
| 852 | /** |
||
| 853 | * @inheritDoc |
||
| 854 | */ |
||
| 855 | public function random($num) |
||
| 859 | |||
| 860 | /** |
||
| 861 | * @inheritDoc |
||
| 862 | */ |
||
| 863 | public function indicesOf($value) |
||
| 871 | |||
| 872 | /** |
||
| 873 | * @inheritDoc |
||
| 874 | */ |
||
| 875 | public function shuffle() |
||
| 879 | |||
| 880 | /** |
||
| 881 | * @inheritDoc |
||
| 882 | */ |
||
| 883 | public function slice($offset, $length = null) |
||
| 887 | |||
| 888 | /** |
||
| 889 | * @inheritDoc |
||
| 890 | */ |
||
| 891 | public function split($num) |
||
| 925 | |||
| 926 | /** |
||
| 927 | * @inheritDoc |
||
| 928 | */ |
||
| 929 | public function union($data) |
||
| 938 | |||
| 939 | /** |
||
| 940 | * @inheritDoc |
||
| 941 | */ |
||
| 942 | public function zip(...$data) |
||
| 969 | |||
| 970 | /** |
||
| 971 | * @inheritDoc |
||
| 972 | */ |
||
| 973 | public function fold(callable $callback, $initial = null) |
||
| 982 | |||
| 983 | /** |
||
| 984 | * @inheritDoc |
||
| 985 | */ |
||
| 986 | public function foldl(callable $callback, $initial = null) |
||
| 990 | |||
| 991 | /** |
||
| 992 | * @inheritDoc |
||
| 993 | */ |
||
| 994 | public function all(callable $callback = null) |
||
| 1003 | |||
| 1004 | /** |
||
| 1005 | * @inheritDoc |
||
| 1006 | */ |
||
| 1007 | public function none(callable $callback = null) |
||
| 1016 | |||
| 1017 | // BEGIN Numeric Collection Methods |
||
| 1018 | // These methods only really work on numeric data. |
||
| 1019 | |||
| 1020 | /** |
||
| 1021 | * Increment an item. |
||
| 1022 | * |
||
| 1023 | * Increment the item specified by $key by one value. Intended for integers |
||
| 1024 | * but also works (using this term loosely) for letters. Any other data type |
||
| 1025 | * it may modify is unintended behavior at best. |
||
| 1026 | * |
||
| 1027 | * This method modifies its internal data array rather than returning a new |
||
| 1028 | * collection. |
||
| 1029 | * |
||
| 1030 | * @param mixed $index The key of the item you want to increment. |
||
| 1031 | * @param int $interval The interval that $key should be incremented by |
||
| 1032 | * |
||
| 1033 | * @return Collection |
||
| 1034 | */ |
||
| 1035 | public function increment($index, $interval = 1) |
||
| 1041 | |||
| 1042 | /** |
||
| 1043 | * Decrement an item. |
||
| 1044 | * |
||
| 1045 | * Frcrement the item specified by $key by one value. Intended for integers. |
||
| 1046 | * Does not work for letters and if it does anything to anything else, it's |
||
| 1047 | * unintended at best. |
||
| 1048 | * |
||
| 1049 | * This method modifies its internal data array rather than returning a new |
||
| 1050 | * collection. |
||
| 1051 | * |
||
| 1052 | * @param mixed $index The key of the item you want to decrement. |
||
| 1053 | * @param int $interval The interval that $key should be decremented by |
||
| 1054 | * |
||
| 1055 | * @return Collection |
||
| 1056 | */ |
||
| 1057 | public function decrement($index, $interval = 1) |
||
| 1063 | |||
| 1064 | /** |
||
| 1065 | * Get the sum. |
||
| 1066 | * |
||
| 1067 | * @return int|float The sum of all values in collection |
||
| 1068 | */ |
||
| 1069 | public function sum() |
||
| 1073 | |||
| 1074 | /** |
||
| 1075 | * Get the average. |
||
| 1076 | * |
||
| 1077 | * @return float|int The average value from the collection |
||
| 1078 | */ |
||
| 1079 | public function average() |
||
| 1083 | |||
| 1084 | /** |
||
| 1085 | * Get the mode. |
||
| 1086 | * |
||
| 1087 | * @return float|int The mode |
||
| 1088 | */ |
||
| 1089 | public function mode() |
||
| 1097 | |||
| 1098 | /** |
||
| 1099 | * Get the median value. |
||
| 1100 | * |
||
| 1101 | * @return float|int The median value |
||
| 1102 | */ |
||
| 1103 | public function median() |
||
| 1120 | |||
| 1121 | /** |
||
| 1122 | * Get the maximum value. |
||
| 1123 | * |
||
| 1124 | * @return mixed The maximum |
||
| 1125 | */ |
||
| 1126 | public function max() |
||
| 1130 | |||
| 1131 | /** |
||
| 1132 | * Get the minimum value. |
||
| 1133 | * |
||
| 1134 | * @return mixed The minimum |
||
| 1135 | */ |
||
| 1136 | public function min() |
||
| 1140 | |||
| 1141 | /** |
||
| 1142 | * Get the number of times each item occurs in the collection. |
||
| 1143 | |||
| 1144 | * This method will return a NumericCollection where keys are the |
||
| 1145 | * values and values are the number of times that value occurs in |
||
| 1146 | * the original collection. |
||
| 1147 | |||
| 1148 | * @return Collection |
||
| 1149 | */ |
||
| 1150 | public function counts() |
||
| 1154 | |||
| 1155 | /** |
||
| 1156 | * @param $serialized |
||
| 1157 | */ |
||
| 1158 | public function unserialize($serialized) |
||
| 1162 | |||
| 1163 | } |
||
| 1164 |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.