Complex classes like Election 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 Election, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
25 | class Election |
||
26 | { |
||
27 | |||
28 | /////////// PROPERTIES /////////// |
||
29 | |||
30 | public const MAX_LENGTH_CANDIDATE_ID = 30; // Max length for candidate identifiant string |
||
31 | |||
32 | protected static $_maxParseIteration = null; |
||
33 | protected static $_maxVoteNumber = null; |
||
34 | protected static $_checksumMode = false; |
||
35 | |||
36 | /////////// STATICS METHODS /////////// |
||
37 | |||
38 | // Change max parse iteration |
||
39 | 1 | public static function setMaxParseIteration (?int $value) : ?int |
|
40 | { |
||
41 | 1 | self::$_maxParseIteration = $value; |
|
42 | 1 | return self::$_maxParseIteration; |
|
43 | } |
||
44 | |||
45 | // Change max vote number |
||
46 | 1 | public static function setMaxVoteNumber (?int $value) : ?int |
|
47 | { |
||
48 | 1 | self::$_maxVoteNumber = $value; |
|
49 | 1 | return self::$_maxVoteNumber; |
|
50 | } |
||
51 | |||
52 | |||
53 | // Check JSON format |
||
54 | public static function isJson (string $string) : bool |
||
66 | |||
67 | |||
68 | // Generic action before parsing data from string input |
||
69 | 70 | public static function prepareParse (string $input, bool $allowFile) : array |
|
94 | |||
95 | |||
96 | public static function prepareJson (string $input) |
||
104 | |||
105 | |||
106 | /////////// CONSTRUCTOR /////////// |
||
107 | |||
108 | use CondorcetVersion; |
||
109 | |||
110 | // Data and global options |
||
111 | protected $_Candidates = []; // Candidate list |
||
112 | protected $_Votes; // Votes list |
||
113 | |||
114 | // Mechanics |
||
115 | protected $_i_CandidateId = 'A'; |
||
116 | protected $_State = 1; // 1 = Add Candidates / 2 = Voting / 3 = Some result have been computing |
||
117 | protected $_timer; |
||
118 | protected $_nextVoteTag = 0; |
||
119 | protected $_ignoreStaticMaxVote = false; |
||
120 | |||
121 | // Params |
||
122 | protected $_ImplicitRanking = true; |
||
123 | protected $_VoteWeightRule = false; |
||
124 | |||
125 | // Result |
||
126 | protected $_Pairwise; |
||
127 | protected $_Calculator; |
||
128 | |||
129 | ////// |
||
130 | |||
131 | 89 | public function __construct () |
|
137 | |||
138 | 1 | public function __destruct () |
|
139 | { |
||
140 | 1 | $this->destroyAllLink(); |
|
141 | 1 | } |
|
142 | |||
143 | 1 | public function __sleep () : array |
|
144 | { |
||
145 | // Don't include others data |
||
146 | $include = [ |
||
147 | 1 | '_Candidates', |
|
148 | '_Votes', |
||
149 | |||
150 | '_i_CandidateId', |
||
151 | '_State', |
||
152 | '_nextVoteTag', |
||
153 | '_objectVersion', |
||
154 | '_ignoreStaticMaxVote', |
||
155 | |||
156 | '_ImplicitRanking', |
||
157 | '_VoteWeightRule', |
||
158 | |||
159 | '_Pairwise', |
||
160 | '_Calculator', |
||
161 | ]; |
||
162 | |||
163 | 1 | !self::$_checksumMode && array_push($include, '_timer'); |
|
164 | |||
165 | 1 | return $include; |
|
166 | } |
||
167 | |||
168 | public function __wakeup () |
||
174 | |||
175 | public function __clone () |
||
179 | |||
180 | |||
181 | /////////// INTERNAL GENERIC REGULATION /////////// |
||
182 | |||
183 | |||
184 | protected function registerAllLinks () : void |
||
196 | |||
197 | 1 | protected function destroyAllLink () : void |
|
198 | { |
||
199 | 1 | foreach ($this->_Candidates as $value) : |
|
200 | $value->destroyLink($this); |
||
201 | endforeach; |
||
202 | |||
203 | 1 | if ($this->_State > 1) : |
|
204 | foreach ($this->_Votes as $value) : |
||
205 | $value->destroyLink($this); |
||
206 | endforeach; |
||
207 | endif; |
||
208 | 1 | } |
|
209 | |||
210 | ////// |
||
211 | |||
212 | |||
213 | 2 | public function getGlobalTimer (bool $float = false) { |
|
216 | |||
217 | 2 | public function getLastTimer (bool $float = false) { |
|
220 | |||
221 | 67 | public function getTimerManager () : Timer_Manager { |
|
224 | |||
225 | 1 | public function getChecksum () : string |
|
226 | { |
||
227 | 1 | self::$_checksumMode = true; |
|
228 | |||
229 | 1 | $r = hash_init('sha256'); |
|
230 | |||
231 | 1 | foreach ($this->_Candidates as $value) : |
|
232 | 1 | hash_update($r, (string) $value); |
|
233 | endforeach; |
||
234 | |||
235 | 1 | foreach ($this->_Votes as $value) : |
|
236 | 1 | hash_update($r, (string) $value); |
|
237 | endforeach; |
||
238 | |||
239 | 1 | $this->_Pairwise !== null |
|
240 | 1 | && hash_update($r,serialize($this->_Pairwise->getExplicitPairwise())); |
|
241 | |||
242 | 1 | hash_update($r, $this->getObjectVersion('major')); |
|
243 | |||
244 | 1 | self::$_checksumMode = false; |
|
245 | |||
246 | 1 | return hash_final($r); |
|
247 | } |
||
248 | |||
249 | public function ignoreMaxVote (bool $state = true) : bool |
||
254 | |||
255 | 72 | public function getImplicitRankingRule () : bool |
|
259 | |||
260 | 3 | public function setImplicitRanking (bool $rule = true) : bool |
|
261 | { |
||
262 | 3 | $this->_ImplicitRanking = $rule; |
|
263 | 3 | $this->cleanupResult(); |
|
264 | 3 | return $this->getImplicitRankingRule(); |
|
265 | } |
||
266 | |||
267 | 68 | public function isVoteWeightIsAllowed () : bool |
|
271 | |||
272 | 1 | public function allowVoteWeight (bool $rule = true) : bool |
|
273 | { |
||
274 | 1 | $this->_VoteWeightRule = $rule; |
|
275 | 1 | $this->cleanupResult(); |
|
276 | 1 | return $this->isVoteWeightIsAllowed(); |
|
277 | } |
||
278 | |||
279 | |||
280 | /////////// CANDIDATES /////////// |
||
281 | |||
282 | |||
283 | // Add a vote candidate before voting |
||
284 | 84 | public function addCandidate ($candidate_id = null) : Candidate |
|
323 | |||
324 | 84 | public function canAddCandidate ($candidate_id) : bool |
|
328 | |||
329 | |||
330 | // Destroy a register vote candidate before voting |
||
331 | 3 | public function removeCandidate ($list) : array |
|
364 | |||
365 | |||
366 | public function jsonCandidates (string $input) |
||
385 | |||
386 | |||
387 | 6 | public function parseCandidates (string $input, bool $allowFile = true) |
|
388 | { |
||
389 | 6 | $input = self::prepareParse($input, $allowFile); |
|
390 | 6 | if ($input === false) : |
|
391 | return $input; |
||
392 | endif; |
||
393 | |||
394 | 6 | $adding = []; |
|
395 | 6 | foreach ($input as $line) : |
|
396 | // Empty Line |
||
397 | 6 | if (empty($line)) : |
|
398 | 1 | continue; |
|
399 | endif; |
||
400 | |||
401 | // addCandidate |
||
402 | try { |
||
403 | 6 | if (self::$_maxParseIteration !== null && count($adding) >= self::$_maxParseIteration) : |
|
404 | throw new CondorcetException(12, self::$_maxParseIteration); |
||
405 | endif; |
||
406 | |||
407 | 6 | $adding[] = $this->addCandidate($line); |
|
408 | } catch (CondorcetException $e) { |
||
409 | if ($e->getCode() === 12) |
||
410 | 6 | {throw $e;} |
|
411 | } |
||
412 | endforeach; |
||
413 | |||
414 | 6 | return $adding; |
|
415 | } |
||
416 | |||
417 | |||
418 | //:: CANDIDATES TOOLS ::// |
||
419 | |||
420 | // Count registered candidates |
||
421 | 72 | public function countCandidates () : int |
|
425 | |||
426 | // Get the list of registered CANDIDATES |
||
427 | 73 | public function getCandidatesList (bool $stringMode = false) : array |
|
441 | |||
442 | 77 | public function getCandidateKey ($candidate_id) |
|
450 | |||
451 | 63 | public function getCandidateId (int $candidate_key, bool $onlyName = false) |
|
459 | |||
460 | 84 | public function existCandidateId ($candidate_id, bool $strict = true) : bool |
|
464 | |||
465 | 2 | public function getCandidateObjectByName (string $s) |
|
476 | |||
477 | |||
478 | |||
479 | /////////// VOTING /////////// |
||
480 | |||
481 | |||
482 | // Close the candidate config, be ready for voting (optional) |
||
483 | 79 | public function setStateToVote () : bool |
|
499 | |||
500 | |||
501 | // Add a single vote. Array key is the rank, each candidate in a rank are separate by ',' It is not necessary to register the last rank. |
||
502 | 79 | public function addVote ($vote, $tag = null) : Vote |
|
503 | { |
||
504 | 79 | $this->prepareVoteInput($vote, $tag); |
|
505 | |||
506 | // Check Max Vote Count |
||
507 | 79 | if ( self::$_maxVoteNumber !== null && !$this->_ignoreStaticMaxVote && $this->countVotes() >= self::$_maxVoteNumber ) : |
|
508 | 1 | throw new CondorcetException(16, self::$_maxVoteNumber); |
|
509 | endif; |
||
510 | |||
511 | |||
512 | // Register vote |
||
513 | 79 | return $this->registerVote($vote, $tag); // Return the vote object |
|
514 | } |
||
515 | |||
516 | // return True or throw an Exception |
||
517 | 5 | public function prepareModifyVote (Vote $existVote) |
|
527 | |||
528 | // Return the well formated vote to use. |
||
529 | 79 | protected function prepareVoteInput (&$vote, $tag = null) : void |
|
540 | |||
541 | |||
542 | 79 | public function checkVoteCandidate (Vote $vote) : bool |
|
573 | |||
574 | // Write a new vote |
||
575 | 79 | protected function registerVote (Vote $vote, $tag = null) : Vote |
|
591 | |||
592 | |||
593 | 5 | public function removeVote ($in, bool $with = true) : array |
|
623 | |||
624 | |||
625 | public function jsonVotes (string $input) |
||
657 | |||
658 | 68 | public function parseVotes (string $input, bool $allowFile = true) |
|
659 | { |
||
660 | 68 | $input = self::prepareParse($input, $allowFile); |
|
661 | 68 | if ($input === false) : |
|
662 | return $input; |
||
663 | endif; |
||
664 | |||
665 | // Check each lines |
||
666 | 68 | $adding = []; |
|
667 | 68 | foreach ($input as $line) : |
|
668 | // Empty Line |
||
669 | 68 | if (empty($line)) : |
|
670 | 61 | continue; |
|
671 | endif; |
||
672 | |||
673 | // Multiples |
||
674 | 68 | $is_multiple = mb_strpos($line, '*'); |
|
675 | 68 | if ($is_multiple !== false) : |
|
676 | 61 | $multiple = trim( substr($line, $is_multiple + 1) ); |
|
677 | |||
678 | // Errors |
||
679 | 61 | if ( !is_numeric($multiple) ) : |
|
680 | throw new CondorcetException(13, null); |
||
681 | endif; |
||
682 | |||
683 | 61 | $multiple = intval($multiple); |
|
684 | |||
685 | // Reformat line |
||
686 | 61 | $line = substr($line, 0, $is_multiple); |
|
687 | else : |
||
688 | 9 | $multiple = 1; |
|
689 | endif; |
||
690 | |||
691 | // Vote Weight |
||
692 | 68 | $is_voteWeight = mb_strpos($line, '^'); |
|
693 | 68 | if ($is_voteWeight !== false) : |
|
694 | 2 | $weight = trim( substr($line, $is_voteWeight + 1) ); |
|
695 | |||
696 | // Errors |
||
697 | 2 | if ( !is_numeric($weight) ) : |
|
698 | throw new CondorcetException(13, null); |
||
699 | endif; |
||
700 | |||
701 | 2 | $weight = intval($weight); |
|
702 | |||
703 | // Reformat line |
||
704 | 2 | $line = substr($line, 0, $is_voteWeight); |
|
705 | else : |
||
706 | 67 | $weight = 1; |
|
707 | endif; |
||
708 | |||
709 | // Tags + vote |
||
710 | 68 | if (mb_strpos($line, '||') !== false) : |
|
711 | 4 | $data = explode('||', $line); |
|
712 | |||
713 | 4 | $vote = $data[1]; |
|
714 | 4 | $tags = $data[0]; |
|
715 | // Vote without tags |
||
716 | else : |
||
717 | 67 | $vote = $line; |
|
718 | 67 | $tags = null; |
|
719 | endif; |
||
720 | |||
721 | // addVote |
||
722 | 68 | for ($i = 0; $i < $multiple; $i++) : |
|
723 | 68 | if (self::$_maxParseIteration !== null && count($adding) >= self::$_maxParseIteration) : |
|
724 | 1 | throw new CondorcetException(12, self::$_maxParseIteration); |
|
725 | endif; |
||
726 | |||
727 | try { |
||
728 | 68 | $adding[] = ($newVote = $this->addVote($vote, $tags)); |
|
729 | 68 | $newVote->setWeight($weight); |
|
730 | 1 | } catch (CondorcetException $e) {} |
|
1 ignored issue
–
show
|
|||
731 | endfor; |
||
732 | endforeach; |
||
733 | |||
734 | 68 | return $adding; |
|
735 | } |
||
736 | |||
737 | |||
738 | //:: LARGE ELECTION MODE ::// |
||
739 | |||
740 | 3 | public function setExternalDataHandler (DataHandlerDriverInterface $driver) : bool |
|
741 | { |
||
742 | 3 | if (!$this->_Votes->isUsingHandler()) : |
|
743 | 3 | $this->_Votes->importHandler($driver); |
|
744 | 3 | return true; |
|
745 | else : |
||
746 | throw new CondorcetException(24); |
||
747 | endif; |
||
748 | } |
||
749 | |||
750 | 1 | public function removeExternalDataHandler () : bool |
|
751 | { |
||
752 | 1 | if ($this->_Votes->isUsingHandler()) : |
|
753 | 1 | $this->_Votes->closeHandler(); |
|
754 | 1 | return true; |
|
755 | else : |
||
756 | throw new CondorcetException(23); |
||
757 | endif; |
||
758 | } |
||
759 | |||
760 | |||
761 | //:: VOTING TOOLS ::// |
||
762 | |||
763 | // How many votes are registered ? |
||
764 | 15 | public function countVotes ($tag = null, bool $with = true) : int |
|
768 | |||
769 | // Get the votes registered list |
||
770 | 7 | public function getVotesList ($tag = null, bool $with = true) : array |
|
774 | |||
775 | 3 | public function getVotesListAsString () : string |
|
776 | { |
||
777 | 3 | return $this->_Votes->getVotesListAsString(); |
|
778 | } |
||
779 | |||
780 | 68 | public function getVotesManager () : VotesManager { |
|
783 | |||
784 | 4 | public function getVoteKey (Vote $vote) { |
|
787 | |||
788 | public function getVoteByKey (int $key) { |
||
795 | |||
796 | |||
797 | /////////// RESULTS /////////// |
||
798 | |||
799 | |||
800 | //:: PUBLIC FUNCTIONS ::// |
||
801 | |||
802 | // Generic function for default result with ability to change default object method |
||
803 | 61 | public function getResult ($method = true, array $options = []) : Result |
|
849 | |||
850 | |||
851 | 65 | public function getWinner (?string $substitution = null) |
|
867 | |||
868 | |||
869 | 61 | public function getLoser (?string $substitution = null) |
|
885 | |||
886 | 66 | protected function condorcetBasicSubstitution ($substitution) : string { |
|
903 | |||
904 | |||
905 | public function computeResult ($method = true) : void |
||
909 | |||
910 | |||
911 | //:: TOOLS FOR RESULT PROCESS ::// |
||
912 | |||
913 | |||
914 | // Prepare to compute results & caching system |
||
915 | 67 | protected function prepareResult () : bool |
|
934 | |||
935 | |||
936 | 67 | protected function initResult (string $class) : void |
|
942 | |||
943 | |||
944 | // Cleanup results to compute again with new votes |
||
945 | 67 | protected function cleanupResult () : void |
|
960 | |||
961 | |||
962 | 61 | protected function formatResultOptions (array $arg) : array |
|
977 | |||
978 | |||
979 | //:: GET RAW DATA ::// |
||
980 | |||
981 | 64 | public function getPairwise (bool $explicit = true) |
|
987 | |||
988 | } |
||
989 |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.