Passed
Push — master ( 88b497...e86a88 )
by Boudry
03:07
created

CandidatesProcess   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 208
Duplicated Lines 1.44 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 86.59%

Importance

Changes 0
Metric Value
wmc 43
lcom 1
cbo 3
dl 3
loc 208
ccs 71
cts 82
cp 0.8659
rs 8.3157
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A countCandidates() 0 4 1
A getCandidatesList() 0 14 3
A getCandidateKey() 0 8 2
A getCandidateId() 0 8 3
A existCandidateId() 0 4 2
A getCandidateObjectByName() 0 11 3
D addCandidate() 0 39 10
A canAddCandidate() 0 4 1
B removeCandidate() 0 32 6
A jsonCandidates() 0 21 4
C parseCandidates() 3 29 8

How to fix   Duplicated Code    Complexity   

Duplicated Code

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 Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like CandidatesProcess 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 CandidatesProcess, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
    Condorcet PHP Class, with Schulze Methods and others !
4
5
    By Julien Boudry - MIT LICENSE (Please read LICENSE.txt)
6
    https://github.com/julien-boudry/Condorcet
7
*/
8
declare(strict_types=1);
9
10
namespace Condorcet\ElectionProcess;
11
12
use Condorcet\Candidate;
13
use Condorcet\CondorcetException;
14
use Condorcet\CondorcetUtil;
15
16
17
// Manage Candidates for Election class
18
trait CandidatesProcess
19
{
20
21
/////////// CONSTRUCTOR ///////////
22
23
    // Data and global options
24
    protected $_Candidates = []; // Candidate list
25
    protected $_i_CandidateId = 'A';
26
27
28
/////////// GET CANDIDATES ///////////
29
30
    // Count registered candidates
31 87
    public function countCandidates () : int
32
    {
33 87
        return count($this->_Candidates);
34
    }
35
36
    // Get the list of registered CANDIDATES
37 88
    public function getCandidatesList (bool $stringMode = false) : array
38
    {
39 88
        if (!$stringMode) :
40 86
            return $this->_Candidates;
41
        else :
42 4
            $result = [];
43
44 4
            foreach ($this->_Candidates as $candidateKey => &$oneCandidate) :
45 4
                $result[$candidateKey] = $oneCandidate->getName();
46
            endforeach;
47
48 4
            return $result;
49
        endif;
50
    }
51
52 93
    public function getCandidateKey ($candidate_id)
53
    {
54 93
        if ($candidate_id instanceof Candidate) :
55 80
            return array_search($candidate_id, $this->_Candidates, true);
56
        else:
57 91
            return array_search(trim((string) $candidate_id), $this->_Candidates, false);
58
        endif;
59
    }
60
61 77
    public function getCandidateId (int $candidate_key, bool $onlyName = false)
62
    {
63 77
        if (!array_key_exists($candidate_key, $this->_Candidates)) :
64
            return false;
65
        else :
66 77
            return ($onlyName) ? $this->_Candidates[$candidate_key]->getName() : $this->_Candidates[$candidate_key];
67
        endif;
68
    }
69
70 106
    public function existCandidateId ($candidate_id, bool $strict = true) : bool
71
    {
72 106
        return ($strict) ? in_array($candidate_id, $this->_Candidates, true) : in_array((string) $candidate_id, $this->_Candidates);
73
    }
74
75 3
    public function getCandidateObjectByName (string $s)
76
    {
77 3
        foreach ($this->_Candidates as $oneCandidate) :
78
79 3
            if ($oneCandidate->getName() === $s) :
80 3
                return $oneCandidate;
81
            endif;
82
        endforeach;
83
84 1
        return false;
85
    }
86
87
88
/////////// ADD & REMOVE CANDIDATE ///////////
89
90
    // Add a vote candidate before voting
91 106
    public function addCandidate ($candidate_id = null) : Candidate
92
    {
93
        // only if the vote has not started
94 106
        if ( $this->_State > 1 ) :
95
            throw new CondorcetException(2);
96
        endif;
97
98
        // Filter
99 106
        if ( is_bool($candidate_id) || is_array($candidate_id) || (is_object($candidate_id) && !($candidate_id instanceof Candidate)) ) :
100
            throw new CondorcetException(1, $candidate_id);
101
        endif;
102
103
104
        // Process
105 106
        if ( empty($candidate_id) ) :
106 5
            while ( !$this->canAddCandidate($this->_i_CandidateId) ) :
107 5
                $this->_i_CandidateId++;
108
            endwhile;
109
110 5
            $newCandidate = new Candidate($this->_i_CandidateId);
111
        else : // Try to add the candidate_id
112 103
            $newCandidate = ($candidate_id instanceof Candidate) ? $candidate_id : new Candidate ((string) $candidate_id);
113
114 103
            if ( !$this->canAddCandidate($newCandidate) ) :
115
                throw new CondorcetException(3,$candidate_id);
116
            endif;
117
        endif;
118
119
        // Register it
120 106
        $this->_Candidates[] = $newCandidate;
121
122
        // Linking
123 106
        $newCandidate->registerLink($this);
124
125
        // Disallow other candidate object name matching 
126 106
        $newCandidate->setProvisionalState(false);
127
128 106
        return $newCandidate;
129
    }
130
131 106
    public function canAddCandidate ($candidate_id) : bool
132
    {
133 106
        return !$this->existCandidateId($candidate_id, false);
134
    }
135
136
    // Destroy a register vote candidate before voting
137 3
    public function removeCandidate ($list) : array
138
    {
139
        // only if the vote has not started
140 3
        if ( $this->_State > 1 ) :
141
            throw new CondorcetException(2);
142
        endif;
143
        
144 3
        if ( !is_array($list) ) :
145 3
            $list = [$list];
146
        endif;
147
148 3
        foreach ($list as &$candidate_id) :
149 3
            $candidate_key = $this->getCandidateKey($candidate_id);
150
151 3
            if ( $candidate_key === false ) :
152
                throw new CondorcetException(4,$candidate_id);
153
            endif;
154
155 3
            $candidate_id = $candidate_key;
156
        endforeach;
157
158 3
        $rem = [];
159 3
        foreach ($list as $candidate_key) :
160 3
            $this->_Candidates[$candidate_key]->destroyLink($this);
161
162 3
            $rem[] = $this->_Candidates[$candidate_key];
163
164 3
            unset($this->_Candidates[$candidate_key]);
165
        endforeach;
166
167 3
        return $rem;
168
    }
169
170
171
/////////// PARSE CANDIDATES ///////////
172
173 1
    public function jsonCandidates (string $input)
174
    {
175 1
        $input = CondorcetUtil::prepareJson($input);
176 1
        if ($input === false) :
177
            return $input;
178
        endif;
179
180
            //////
181
182 1
        $adding = [];
183 1
        foreach ($input as $candidate) :
184
            try {
185 1
                $adding[] = $this->addCandidate($candidate);
186
            }
187 1
            catch (CondorcetException $e) {
188
                // Ignore invalid vote
189
            }
190
        endforeach;
191
192 1
        return $adding;
193
    }
194
195 7
    public function parseCandidates (string $input, bool $allowFile = true)
196
    {
197 7
        $input = CondorcetUtil::prepareParse($input, $allowFile);
198 7
        if ($input === false) :
199
            return $input;
200
        endif;
201
202 7
        $adding = [];
203 7
        foreach ($input as $line) :
204
            // Empty Line
205 7
            if (empty($line)) :
206 1
                continue;
207
            endif;
208
209
            // addCandidate
210
            try {
211 7 View Code Duplication
                if (self::$_maxParseIteration !== null && count($adding) >= self::$_maxParseIteration) :
212
                    throw new CondorcetException(12, self::$_maxParseIteration);
213
                endif;
214
215 7
                $adding[] = $this->addCandidate($line);
216
            } catch (CondorcetException $e) {
217
                if ($e->getCode() === 12)
218 7
                    {throw $e;}
219
            }
220
        endforeach;
221
222 7
        return $adding;
223
    }
224
225
}
226