Complex classes like EditCounter 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 EditCounter, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 18 | class EditCounter extends Model |
||
| 19 | { |
||
| 20 | |||
| 21 | /** @var Project The project. */ |
||
| 22 | protected $project; |
||
| 23 | |||
| 24 | /** @var User The user. */ |
||
| 25 | protected $user; |
||
| 26 | |||
| 27 | /** @var int[] Revision and page counts etc. */ |
||
| 28 | protected $pairData; |
||
| 29 | |||
| 30 | /** @var string[] The start and end dates of revisions. */ |
||
| 31 | protected $revisionDates; |
||
| 32 | |||
| 33 | /** @var int[] The total page counts. */ |
||
| 34 | protected $pageCounts; |
||
| 35 | |||
| 36 | /** @var int[] The lot totals. */ |
||
| 37 | protected $logCounts; |
||
| 38 | |||
| 39 | /** @var mixed[] Total numbers of edits per month */ |
||
| 40 | protected $monthCounts; |
||
| 41 | |||
| 42 | /** @var mixed[] Total numbers of edits per year */ |
||
| 43 | protected $yearCounts; |
||
| 44 | |||
| 45 | /** @var int[] Keys are project DB names. */ |
||
| 46 | protected $globalEditCounts; |
||
| 47 | |||
| 48 | /** @var array Block data, with keys 'set' and 'received'. */ |
||
| 49 | protected $blocks; |
||
| 50 | |||
| 51 | /** @var integer[] Array keys are namespace IDs, values are the edit counts */ |
||
| 52 | protected $namespaceTotals; |
||
| 53 | |||
| 54 | /** @var int Number of semi-automated edits */ |
||
| 55 | protected $autoEditCount; |
||
| 56 | |||
| 57 | /** @var string[] Data needed for time card chart */ |
||
| 58 | protected $timeCardData; |
||
| 59 | |||
| 60 | /** |
||
| 61 | * Revision size data, with keys 'average_size', 'large_edits' and 'small_edits'. |
||
| 62 | * @var string[] As returned by the DB, unconverted to int or float |
||
| 63 | */ |
||
| 64 | protected $editSizeData; |
||
| 65 | |||
| 66 | /** |
||
| 67 | * Duration of the longest block in seconds; -1 if indefinite, |
||
| 68 | * or false if could not be parsed from log params |
||
| 69 | * @var int|bool |
||
| 70 | */ |
||
| 71 | protected $longestBlockSeconds; |
||
| 72 | |||
| 73 | /** |
||
| 74 | * EditCounter constructor. |
||
| 75 | * @param Project $project The base project to count edits |
||
| 76 | * @param User $user |
||
| 77 | */ |
||
| 78 | public function __construct(Project $project, User $user) |
||
| 83 | |||
| 84 | /** |
||
| 85 | * This method asynchronously fetches all the expensive data, waits |
||
| 86 | * for each request to finish, and copies the values to the class instance. |
||
| 87 | * @return null |
||
| 88 | */ |
||
| 89 | public function prepareData() |
||
| 90 | { |
||
| 91 | $project = $this->project->getDomain(); |
||
| 92 | $username = $this->user->getUsername(); |
||
| 93 | |||
| 94 | /** |
||
| 95 | * The URL of each endpoint, keyed by the name of the corresponding class-level |
||
| 96 | * instance variable. |
||
| 97 | * @var array[] |
||
| 98 | */ |
||
| 99 | $endpoints = [ |
||
| 100 | "pairData" => "ec/pairdata/$project/$username", |
||
| 101 | "logCounts" => "ec/logcounts/$project/$username", |
||
| 102 | "namespaceTotals" => "ec/namespacetotals/$project/$username", |
||
| 103 | "editSizeData" => "ec/editsizes/$project/$username", |
||
| 104 | "monthCounts" => "ec/monthcounts/$project/$username", |
||
| 105 | // "globalEditCounts" => "ec-globaleditcounts/$project/$username", |
||
|
1 ignored issue
–
show
|
|||
| 106 | "autoEditCount" => "user/automated_editcount/$project/$username", |
||
| 107 | ]; |
||
| 108 | |||
| 109 | /** |
||
| 110 | * Keep track of all promises so we can wait for all of them to complete. |
||
| 111 | * @var GuzzleHttp\Promise\Promise[] |
||
| 112 | */ |
||
| 113 | $promises = []; |
||
| 114 | |||
| 115 | foreach ($endpoints as $key => $endpoint) { |
||
| 116 | $promise = $this->getRepository()->queryXToolsApi($endpoint, true); |
||
| 117 | $promises[] = $promise; |
||
| 118 | |||
| 119 | // Handle response of $promise asynchronously. |
||
| 120 | $promise->then(function ($response) use ($key, $endpoint) { |
||
| 121 | $result = (array) json_decode($response->getBody()->getContents()); |
||
| 122 | |||
| 123 | if (isset($result)) { |
||
| 124 | // Copy result to the class class instance. From here any subsequent |
||
| 125 | // calls to the getters (e.g. getPairData()) will return these cached values. |
||
| 126 | $this->{$key} = $result; |
||
| 127 | } else { |
||
| 128 | // The API should *always* return something, so if $result is not set, |
||
| 129 | // something went wrong, so we simply won't set it and the getters will in |
||
| 130 | // turn re-attempt to get the data synchronously. |
||
| 131 | // We'll log this to see how often it happens. |
||
| 132 | $this->getRepository() |
||
| 133 | ->getLog() |
||
| 134 | ->error("Failed to fetch data for $endpoint via async, " . |
||
| 135 | "re-attempting synchoronously."); |
||
| 136 | } |
||
| 137 | }); |
||
| 138 | } |
||
| 139 | |||
| 140 | // Wait for all promises to complete, even if some of them fail. |
||
| 141 | GuzzleHttp\Promise\settle($promises)->wait(); |
||
| 142 | |||
| 143 | // Everything we need now lives on the class instance, so we're done. |
||
| 144 | return; |
||
| 145 | } |
||
| 146 | |||
| 147 | /** |
||
| 148 | * Get revision and page counts etc. |
||
| 149 | * @return int[] |
||
| 150 | */ |
||
| 151 | public function getPairData() |
||
| 159 | |||
| 160 | /** |
||
| 161 | * Get revision dates. |
||
| 162 | * @return int[] |
||
| 163 | */ |
||
| 164 | public function getLogCounts() |
||
| 172 | |||
| 173 | /** |
||
| 174 | * Get block data. |
||
| 175 | * @param string $type Either 'set', 'received' |
||
| 176 | * @param bool $blocksOnly Whether to include only blocks, and not reblocks and unblocks. |
||
| 177 | * @return array |
||
| 178 | */ |
||
| 179 | protected function getBlocks($type, $blocksOnly = true) |
||
| 197 | |||
| 198 | /** |
||
| 199 | * Get the total number of currently-live revisions. |
||
| 200 | * @return int |
||
| 201 | */ |
||
| 202 | public function countLiveRevisions() |
||
| 207 | |||
| 208 | /** |
||
| 209 | * Get the total number of the user's revisions that have been deleted. |
||
| 210 | * @return int |
||
| 211 | */ |
||
| 212 | public function countDeletedRevisions() |
||
| 217 | |||
| 218 | /** |
||
| 219 | * Get the total edit count (live + deleted). |
||
| 220 | * @return int |
||
| 221 | */ |
||
| 222 | public function countAllRevisions() |
||
| 226 | |||
| 227 | /** |
||
| 228 | * Get the total number of live revisions with comments. |
||
| 229 | * @return int |
||
| 230 | */ |
||
| 231 | public function countRevisionsWithComments() |
||
| 236 | |||
| 237 | /** |
||
| 238 | * Get the total number of live revisions without comments. |
||
| 239 | * @return int |
||
| 240 | */ |
||
| 241 | public function countRevisionsWithoutComments() |
||
| 245 | |||
| 246 | /** |
||
| 247 | * Get the total number of revisions marked as 'minor' by the user. |
||
| 248 | * @return int |
||
| 249 | */ |
||
| 250 | public function countMinorRevisions() |
||
| 255 | |||
| 256 | /** |
||
| 257 | * Get the total number of non-deleted pages edited by the user. |
||
| 258 | * @return int |
||
| 259 | */ |
||
| 260 | public function countLivePagesEdited() |
||
| 265 | |||
| 266 | /** |
||
| 267 | * Get the total number of deleted pages ever edited by the user. |
||
| 268 | * @return int |
||
| 269 | */ |
||
| 270 | public function countDeletedPagesEdited() |
||
| 275 | |||
| 276 | /** |
||
| 277 | * Get the total number of pages ever edited by this user (both live and deleted). |
||
| 278 | * @return int |
||
| 279 | */ |
||
| 280 | public function countAllPagesEdited() |
||
| 284 | |||
| 285 | /** |
||
| 286 | * Get the total number of pages (both still live and those that have been deleted) created |
||
| 287 | * by the user. |
||
| 288 | * @return int |
||
| 289 | */ |
||
| 290 | public function countPagesCreated() |
||
| 294 | |||
| 295 | /** |
||
| 296 | * Get the total number of pages created by the user, that have not been deleted. |
||
| 297 | * @return int |
||
| 298 | */ |
||
| 299 | public function countCreatedPagesLive() |
||
| 304 | |||
| 305 | /** |
||
| 306 | * Get the total number of pages created by the user, that have since been deleted. |
||
| 307 | * @return int |
||
| 308 | */ |
||
| 309 | public function countPagesCreatedDeleted() |
||
| 314 | |||
| 315 | /** |
||
| 316 | * Get the total number of pages that have been deleted by the user. |
||
| 317 | * @return int |
||
| 318 | */ |
||
| 319 | public function countPagesDeleted() |
||
| 324 | |||
| 325 | /** |
||
| 326 | * Get the total number of pages moved by the user. |
||
| 327 | * @return int |
||
| 328 | */ |
||
| 329 | public function countPagesMoved() |
||
| 334 | |||
| 335 | /** |
||
| 336 | * Get the total number of times the user has blocked a user. |
||
| 337 | * @return int |
||
| 338 | */ |
||
| 339 | public function countBlocksSet() |
||
| 345 | |||
| 346 | /** |
||
| 347 | * Get the total number of times the user has re-blocked a user. |
||
| 348 | * @return int |
||
| 349 | */ |
||
| 350 | public function countReblocksSet() |
||
| 356 | |||
| 357 | /** |
||
| 358 | * Get the total number of times the user has unblocked a user. |
||
| 359 | * @return int |
||
| 360 | */ |
||
| 361 | public function countUnblocksSet() |
||
| 366 | |||
| 367 | /** |
||
| 368 | * Get the total number of blocks that have been lifted (i.e. unblocks) by this user. |
||
| 369 | * @return int |
||
| 370 | */ |
||
| 371 | public function countBlocksLifted() |
||
| 376 | |||
| 377 | /** |
||
| 378 | * Get the total number of times the user has been blocked. |
||
| 379 | * @return int |
||
| 380 | */ |
||
| 381 | public function countBlocksReceived() |
||
| 386 | |||
| 387 | /** |
||
| 388 | * Get the length of the longest block the user received, in seconds. |
||
| 389 | * @return int Number of seconds or false if it could not be determined. |
||
| 390 | * If the user is blocked, the time since the block is returned. If the block is |
||
| 391 | * indefinite, -1 is returned. 0 if there was never a block. |
||
| 392 | */ |
||
| 393 | public function getLongestBlockSeconds() |
||
| 464 | |||
| 465 | /** |
||
| 466 | * Given a block log entry from the database, get the timestamp and duration in seconds. |
||
| 467 | * @param mixed[] $block Block log entry as fetched via self::getBlocks() |
||
| 468 | * @return int[] [ |
||
| 469 | * Unix timestamp, |
||
| 470 | * Duration in seconds (-1 if indefinite, null if unparsable or unblock) |
||
| 471 | * ] |
||
| 472 | */ |
||
| 473 | public function parseBlockLogEntry($block) |
||
| 500 | |||
| 501 | /** |
||
| 502 | * Get the total number of pages protected by the user. |
||
| 503 | * @return int |
||
| 504 | */ |
||
| 505 | public function countPagesProtected() |
||
| 510 | |||
| 511 | /** |
||
| 512 | * Get the total number of pages reprotected by the user. |
||
| 513 | * @return int |
||
| 514 | */ |
||
| 515 | public function countPagesReprotected() |
||
| 520 | |||
| 521 | /** |
||
| 522 | * Get the total number of pages unprotected by the user. |
||
| 523 | * @return int |
||
| 524 | */ |
||
| 525 | public function countPagesUnprotected() |
||
| 530 | |||
| 531 | /** |
||
| 532 | * Get the total number of edits deleted by the user. |
||
| 533 | * @return int |
||
| 534 | */ |
||
| 535 | public function countEditsDeleted() |
||
| 540 | |||
| 541 | /** |
||
| 542 | * Get the total number of pages restored by the user. |
||
| 543 | * @return int |
||
| 544 | */ |
||
| 545 | public function countPagesRestored() |
||
| 550 | |||
| 551 | /** |
||
| 552 | * Get the total number of times the user has modified the rights of a user. |
||
| 553 | * @return int |
||
| 554 | */ |
||
| 555 | public function countRightsModified() |
||
| 560 | |||
| 561 | /** |
||
| 562 | * Get the total number of pages imported by the user (through any import mechanism: |
||
| 563 | * interwiki, or XML upload). |
||
| 564 | * @return int |
||
| 565 | */ |
||
| 566 | public function countPagesImported() |
||
| 574 | |||
| 575 | /** |
||
| 576 | * Get the average number of edits per page (including deleted revisions and pages). |
||
| 577 | * @return float |
||
| 578 | */ |
||
| 579 | public function averageRevisionsPerPage() |
||
| 586 | |||
| 587 | /** |
||
| 588 | * Average number of edits made per day. |
||
| 589 | * @return float |
||
| 590 | */ |
||
| 591 | public function averageRevisionsPerDay() |
||
| 598 | |||
| 599 | /** |
||
| 600 | * Get the total number of edits made by the user with semi-automating tools. |
||
| 601 | */ |
||
| 602 | public function countAutomatedEdits() |
||
| 610 | |||
| 611 | /** |
||
| 612 | * Get the count of (non-deleted) edits made in the given timeframe to now. |
||
| 613 | * @param string $time One of 'day', 'week', 'month', or 'year'. |
||
| 614 | * @return int The total number of live edits. |
||
| 615 | */ |
||
| 616 | public function countRevisionsInLast($time) |
||
| 621 | |||
| 622 | /** |
||
| 623 | * Get the date and time of the user's first edit. |
||
| 624 | * @return DateTime|bool The time of the first revision, or false. |
||
| 625 | */ |
||
| 626 | public function datetimeFirstRevision() |
||
| 631 | |||
| 632 | /** |
||
| 633 | * Get the date and time of the user's first edit. |
||
| 634 | * @return DateTime|bool The time of the last revision, or false. |
||
| 635 | */ |
||
| 636 | public function datetimeLastRevision() |
||
| 641 | |||
| 642 | /** |
||
| 643 | * Get the number of days between the first and last edits. |
||
| 644 | * If there's only one edit, this is counted as one day. |
||
| 645 | * @return int |
||
| 646 | */ |
||
| 647 | public function getDays() |
||
| 657 | |||
| 658 | /** |
||
| 659 | * Get the total number of files uploaded (including those now deleted). |
||
| 660 | * @return int |
||
| 661 | */ |
||
| 662 | public function countFilesUploaded() |
||
| 667 | |||
| 668 | /** |
||
| 669 | * Get the total number of files uploaded to Commons (including those now deleted). |
||
| 670 | * This is only applicable for WMF labs installations. |
||
| 671 | * @return int |
||
| 672 | */ |
||
| 673 | public function countFilesUploadedCommons() |
||
| 678 | |||
| 679 | /** |
||
| 680 | * Get the total number of revisions the user has sent thanks for. |
||
| 681 | * @return int |
||
| 682 | */ |
||
| 683 | public function thanks() |
||
| 688 | |||
| 689 | /** |
||
| 690 | * Get the total number of approvals |
||
| 691 | * @return int |
||
| 692 | */ |
||
| 693 | public function approvals() |
||
| 702 | |||
| 703 | /** |
||
| 704 | * Get the total number of patrols performed by the user. |
||
| 705 | * @return int |
||
| 706 | */ |
||
| 707 | public function patrols() |
||
| 712 | |||
| 713 | /** |
||
| 714 | * Get the total number of accounts created by the user. |
||
| 715 | * @return int |
||
| 716 | */ |
||
| 717 | public function accountsCreated() |
||
| 724 | |||
| 725 | /** |
||
| 726 | * Get the given user's total edit counts per namespace. |
||
| 727 | * @return integer[] Array keys are namespace IDs, values are the edit counts. |
||
| 728 | */ |
||
| 729 | public function namespaceTotals() |
||
| 739 | |||
| 740 | /** |
||
| 741 | * Get a summary of the times of day and the days of the week that the user has edited. |
||
| 742 | * @return string[] |
||
| 743 | */ |
||
| 744 | public function timeCard() |
||
| 753 | |||
| 754 | /** |
||
| 755 | * Get the total numbers of edits per month. |
||
| 756 | * @param null|DateTime $currentTime - *USED ONLY FOR UNIT TESTING* |
||
| 757 | * so we can mock the current DateTime. |
||
| 758 | * @return mixed[] With keys 'yearLabels', 'monthLabels' and 'totals', |
||
| 759 | * the latter keyed by namespace, year and then month. |
||
| 760 | */ |
||
| 761 | public function monthCounts($currentTime = null) |
||
| 847 | |||
| 848 | /** |
||
| 849 | * Get the total numbers of edits per year. |
||
| 850 | * @param null|DateTime [$currentTime] - *USED ONLY FOR UNIT TESTING* |
||
| 851 | * so we can mock the current DateTime. |
||
| 852 | * @return mixed[] With keys 'yearLabels' and 'totals', the latter |
||
| 853 | * keyed by namespace then year. |
||
| 854 | */ |
||
| 855 | public function yearCounts($currentTime = null) |
||
| 872 | |||
| 873 | /** |
||
| 874 | * Get the total edit counts for the top n projects of this user. |
||
| 875 | * @param int $numProjects |
||
| 876 | * @return mixed[] Each element has 'total' and 'project' keys. |
||
| 877 | */ |
||
| 878 | public function globalEditCountsTopN($numProjects = 10) |
||
| 885 | |||
| 886 | /** |
||
| 887 | * Get the total number of edits excluding the top n. |
||
| 888 | * @param int $numProjects |
||
| 889 | * @return int |
||
| 890 | */ |
||
| 891 | public function globalEditCountWithoutTopN($numProjects = 10) |
||
| 901 | |||
| 902 | /** |
||
| 903 | * Get the grand total of all edits on all projects. |
||
| 904 | * @return int |
||
| 905 | */ |
||
| 906 | public function globalEditCount() |
||
| 914 | |||
| 915 | /** |
||
| 916 | * Get the total revision counts for all projects for this user. |
||
| 917 | * @param bool $sorted Whether to sort the list by total, or not. |
||
| 918 | * @return mixed[] Each element has 'total' and 'project' keys. |
||
| 919 | */ |
||
| 920 | public function globalEditCounts($sorted = false) |
||
| 936 | |||
| 937 | /** |
||
| 938 | * Get the most recent n revisions across all projects. |
||
| 939 | * @param int $max The maximum number of revisions to return. |
||
| 940 | * @return Edit[] |
||
| 941 | */ |
||
| 942 | public function globalEdits($max) |
||
| 976 | |||
| 977 | /** |
||
| 978 | * Get average edit size, and number of large and small edits. |
||
| 979 | * @return int[] |
||
| 980 | */ |
||
| 981 | public function getEditSizeData() |
||
| 989 | |||
| 990 | /** |
||
| 991 | * Get the total edit count of this user or 5,000 if they've made more than 5,000 edits. |
||
| 992 | * This is used to ensure percentages of small and large edits are computed properly. |
||
| 993 | * @return int |
||
| 994 | */ |
||
| 995 | public function countLast5000() |
||
| 999 | |||
| 1000 | /** |
||
| 1001 | * Get the number of edits under 20 bytes of the user's past 5000 edits. |
||
| 1002 | * @return int |
||
| 1003 | */ |
||
| 1004 | public function countSmallEdits() |
||
| 1009 | |||
| 1010 | /** |
||
| 1011 | * Get the total number of edits over 1000 bytes of the user's past 5000 edits. |
||
| 1012 | * @return int |
||
| 1013 | */ |
||
| 1014 | public function countLargeEdits() |
||
| 1019 | |||
| 1020 | /** |
||
| 1021 | * Get the average size of the user's past 5000 edits. |
||
| 1022 | * @return float Size in bytes. |
||
| 1023 | */ |
||
| 1024 | public function averageEditSize() |
||
| 1033 | } |
||
| 1034 |
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.