Completed
Push — dev-1.6.x ( e86a88...4ab371 )
by Boudry
03:36
created

VotesProcess   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 288
Duplicated Lines 11.46 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 90.98%

Importance

Changes 0
Metric Value
wmc 56
dl 33
loc 288
ccs 111
cts 122
cp 0.9098
rs 6.5957
c 0
b 0
f 0
lcom 1
cbo 4

14 Methods

Rating   Name   Duplication   Size   Complexity  
A ignoreMaxVote() 0 4 1
A countVotes() 0 4 1
A getVotesList() 0 4 1
A getVotesListAsString() 0 4 1
A getVotesManager() 0 3 1
A getVoteKey() 0 3 1
A addVote() 0 13 4
A prepareModifyVote() 0 14 3
C checkVoteCandidate() 0 30 11
A registerVote() 0 16 2
B removeVote() 0 30 4
A prepareVoteInput() 0 11 3
D jsonVotes() 0 34 10
C parseVotes() 33 78 13

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 VotesProcess 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 VotesProcess, 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\CondorcetException;
13
use Condorcet\CondorcetUtil;
14
use Condorcet\Vote;
15
use Condorcet\DataManager\VotesManager;
16
17
// Manage Results for Election class
18
trait VotesProcess
19
{
20
21
/////////// CONSTRUCTOR ///////////
22
23
    // Data and global options
24
    protected $_Votes; // Votes list
25
    protected $_ignoreStaticMaxVote = false;
26
27
28 1
    public function ignoreMaxVote (bool $state = true) : bool
29
    {
30 1
        return $this->_ignoreStaticMaxVote = $state;
31
    }
32
33
34
/////////// VOTES LIST ///////////
35
36
    // How many votes are registered ?
37 10
    public function countVotes ($tag = null, bool $with = true) : int
38
    {
39 10
        return $this->_Votes->countVotes(VoteUtil::tagsConvert($tag),$with);
40
    }
41
42
    // Get the votes registered list
43 10
    public function getVotesList ($tag = null, bool $with = true) : array
44
    {
45 10
        return $this->_Votes->getVotesList(VoteUtil::tagsConvert($tag), $with);
46
    }
47
48 5
    public function getVotesListAsString () : string
49
    {
50 5
        return $this->_Votes->getVotesListAsString();
51
    }
52
53 86
    public function getVotesManager () : VotesManager {
54 86
        return $this->_Votes;
55
    }
56
57 5
    public function getVoteKey (Vote $vote) {
58 5
        return $this->_Votes->getVoteKey($vote);
59
    }
60
61
62
/////////// ADD & REMOVE VOTE ///////////
63
64
    // 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.
65 99
    public function addVote ($vote, $tag = null) : Vote
66
    {
67 99
        $this->prepareVoteInput($vote, $tag);
68
69
        // Check Max Vote Count
70 99
        if ( self::$_maxVoteNumber !== null && !$this->_ignoreStaticMaxVote && $this->countVotes() >= self::$_maxVoteNumber ) :
71 1
            throw new CondorcetException(16, self::$_maxVoteNumber);
72
        endif;
73
74
75
        // Register vote
76 99
        return $this->registerVote($vote, $tag); // Return the vote object
77
    }
78
79
    // return True or throw an Exception
80 6
    public function prepareModifyVote (Vote $existVote) : void
81
    {
82
        try {
83 6
            $this->prepareVoteInput($existVote);
84 6
            $this->setStateToVote();
1 ignored issue
show
Bug introduced by
It seems like setStateToVote() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
85
86 6
            if ($this->_Votes->isUsingHandler()) : 
87 6
                $this->_Votes[$this->getVoteKey($existVote)] = $existVote;
88
            endif;
89
        }
90
        catch (\Exception $e) {
91
            throw $e;
92
        }
93 6
    }
94
95 99
    public function checkVoteCandidate (Vote $vote) : bool
96
    {
97 99
        $linkCount = $vote->countLinks();
98 99
        $links = $vote->getLinks();
99
100 99
        $mirror = $vote->getRanking();
101 99
        $change = false;
102 99
        foreach ($vote as $rank => $choice) :
103 99
            foreach ($choice as $choiceKey => $candidate) :
104 99
                if ( !$this->existCandidateId($candidate, true) ) :
105 92
                    if ($candidate->getProvisionalState() && $this->existCandidateId($candidate, false)) :
106 91
                        if ( $linkCount === 0 || ($linkCount === 1 && reset($links) === $this) ) :
107 91
                            $mirror[$rank][$choiceKey] = $this->_Candidates[$this->getCandidateKey((string) $candidate)];
108 91
                            $change = true;
109
                        else :
110 99
                            return false;
111
                        endif;
112
                    endif;
113
                endif;
114
            endforeach;
115
        endforeach;
116
117 99
        if ($change) :
118 91
            $vote->setRanking(  $mirror,
119 91
                                ( abs($vote->getTimestamp() - microtime(true)) > 0.5 ) ? ($vote->getTimestamp() + 0.001) : null
120
            );
121
        endif;
122
123 99
        return true;
124
    }
125
126
    // Write a new vote
127 99
    protected function registerVote (Vote $vote, $tag = null) : Vote
128
    {
129
        // Vote identifiant
130 99
        $vote->addTags($tag);
131
        
132
        // Register
133
        try {
134 99
            $vote->registerLink($this);
135 99
            $this->_Votes[] = $vote;
136
        } catch (CondorcetException $e) {
137
            // Security : Check if vote object not already register
138
            throw new CondorcetException(6,'Vote object already registred');
139
        }
140
141 99
        return $vote;
142
    }
143
144 5
    public function removeVote ($in, bool $with = true) : array
145
    {    
146 5
        $rem = [];
147
148 5
        if ($in instanceof Vote) :
149 4
            $key = $this->getVoteKey($in);
150 4
            if ($key !== false) :
151 4
                $this->_Votes[$key]->destroyLink($this);
152
153 4
                $rem[] = $this->_Votes[$key];
154
155 4
                unset($this->_Votes[$key]);
156
            endif;
157
        else :
158
            // Prepare Tags
159 3
            $tag = VoteUtil::tagsConvert($in);
160
161
            // Deleting
162 3
            foreach ($this->getVotesList($tag, $with) as $key => $value) :
163 3
                $this->_Votes[$key]->destroyLink($this);
164
165 3
                $rem[] = $this->_Votes[$key];
166
167 3
                unset($this->_Votes[$key]);
168
            endforeach;
169
170
        endif;
171
172 5
        return $rem;
173
    }
174
175
176
/////////// PARSE VOTE ///////////
177
178
    // Return the well formated vote to use.
179 99
    protected function prepareVoteInput (&$vote, $tag = null) : void
180
    {
181 99
        if (!($vote instanceof Vote)) :
182 83
            $vote = new Vote ($vote, $tag);
183
        endif;
184
185
        // Check array format && Make checkVoteCandidate
186 99
        if ( !$this->checkVoteCandidate($vote) ) :
187
            throw new CondorcetException(5);
188
        endif;
189 99
    }
190
191 2
    public function jsonVotes (string $input)
192
    {
193 2
        $input = CondorcetUtil::prepareJson($input);
194 1
        if ($input === false) :
195
            return $input;
196
        endif;
197
198
            //////
199
200 1
        $adding = [];
201
202 1
        foreach ($input as $record) :
203 1
            if (empty($record['vote'])) :
204 1
                continue;
205
            endif;
206
207 1
            $tags = (!isset($record['tag'])) ? null : $record['tag'];
208 1
            $multi = (!isset($record['multi'])) ? 1 : $record['multi'];
209
210 1
            for ($i = 0; $i < $multi; $i++) :
211 1
                if (self::$_maxParseIteration !== null && $this->countVotes() >= self::$_maxParseIteration) :
212
                    throw new CondorcetException(12, self::$_maxParseIteration);
213
                endif;
214
215
                try {
216 1
                    $adding[] = $this->addVote($record['vote'], $tags);
217
                } catch (\Exception $e) {
218
                    // Ignore invalid vote
219
                }
220
            endfor;
221
        endforeach;
222
223 1
        return $adding;
224
    }
225
226 71
    public function parseVotes (string $input, bool $allowFile = true)
227
    {
228 71
        $input = CondorcetUtil::prepareParse($input, $allowFile);
229 71
        if ($input === false) :
230
            return $input;
231
        endif;
232
233
        // Check each lines
234 71
        $adding = [];
235 71
        foreach ($input as $line) :
236
            // Empty Line
237 71
            if (empty($line)) :
238 63
                continue;
239
            endif;
240
241
            // Multiples
242 71
            $is_multiple = mb_strpos($line, '*');
243 71 View Code Duplication
            if ($is_multiple !== false) :
244 64
                $multiple = trim( substr($line, $is_multiple + 1) );
245
246
                // Errors
247 64
                if ( !is_numeric($multiple) ) :
248
                    throw new CondorcetException(13, null);
249
                endif;
250
251 64
                $multiple = intval($multiple);
252
253
                // Reformat line
254 64
                $line = substr($line, 0, $is_multiple);
255
            else :
256 9
                $multiple = 1;
257
            endif;
258
259
            // Vote Weight
260 71
            $is_voteWeight = mb_strpos($line, '^');
261 71 View Code Duplication
            if ($is_voteWeight !== false) :
262 2
                $weight = trim( substr($line, $is_voteWeight + 1) );
263
264
                // Errors
265 2
                if ( !is_numeric($weight) ) :
266
                    throw new CondorcetException(13, null);
267
                endif;
268
269 2
                $weight = intval($weight);
270
271
                // Reformat line
272 2
                $line = substr($line, 0, $is_voteWeight);
273
            else :
274 70
                $weight = 1;
275
            endif;
276
277
            // Tags + vote
278 71
            if (mb_strpos($line, '||') !== false) :
279 4
                $data = explode('||', $line);
280
281 4
                $vote = $data[1];
282 4
                $tags = $data[0];
283
            // Vote without tags
284
            else :
285 70
                $vote = $line;
286 70
                $tags = null;
287
            endif;
288
289
            // addVote
290 71
            for ($i = 0; $i < $multiple; $i++) :
291 71 View Code Duplication
                if (self::$_maxParseIteration !== null && count($adding) >= self::$_maxParseIteration) :
292 1
                    throw new CondorcetException(12, self::$_maxParseIteration);
293
                endif;
294
295
                try {
296 71
                    $adding[] = ($newVote = $this->addVote($vote, $tags));
297 71
                    $newVote->setWeight($weight);
298 1
                } catch (CondorcetException $e) {}
1 ignored issue
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
299
            endfor;
300
        endforeach;
301
302 71
        return $adding;
303
    }
304
305
}
306