Completed
Push — dev-1.6.x ( 048fe7...a70c53 )
by Boudry
04:20
created

VotesProcess::removeVote()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 30
ccs 14
cts 14
cp 1
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 18
nc 3
nop 2
crap 4
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 9
    public function getVotesList ($tag = null, bool $with = true) : array
44
    {
45 9
        return $this->_Votes->getVotesList(VoteUtil::tagsConvert($tag), $with);
46
    }
47
48 4
    public function getVotesListAsString () : string
49
    {
50 4
        return $this->_Votes->getVotesListAsString();
51
    }
52
53 81
    public function getVotesManager () : VotesManager {
54 81
        return $this->_Votes;
55
    }
56
57 4
    public function getVoteKey (Vote $vote) {
58 4
        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 98
    public function addVote ($vote, $tag = null) : Vote
66
    {
67 98
        $this->prepareVoteInput($vote, $tag);
68
69
        // Check Max Vote Count
70 98
        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 98
        return $this->registerVote($vote, $tag); // Return the vote object
77
    }
78
79
    // return True or throw an Exception
80 5
    public function prepareModifyVote (Vote $existVote)
81
    {
82
        try {
83 5
            $this->prepareVoteInput($existVote);
84 5
            $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
        catch (\Exception $e) {
87
            throw $e;
88
        }
89 5
    }
90
91 98
    public function checkVoteCandidate (Vote $vote) : bool
92
    {
93 98
        $linkCount = $vote->countLinks();
94 98
        $links = $vote->getLinks();
95
96 98
        $mirror = $vote->getRanking();
97 98
        $change = false;
98 98
        foreach ($vote as $rank => $choice) :
99 98
            foreach ($choice as $choiceKey => $candidate) :
100 98
                if ( !$this->existCandidateId($candidate, true) ) :
1 ignored issue
show
Bug introduced by
It seems like existCandidateId() 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...
101 91
                    if ($candidate->getProvisionalState() && $this->existCandidateId($candidate, false)) :
1 ignored issue
show
Bug introduced by
It seems like existCandidateId() 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...
102 90
                        if ( $linkCount === 0 || ($linkCount === 1 && reset($links) === $this) ) :
103 90
                            $mirror[$rank][$choiceKey] = $this->_Candidates[$this->getCandidateKey((string) $candidate)];
2 ignored issues
show
Bug introduced by
The property _Candidates does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
It seems like getCandidateKey() 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...
104 90
                            $change = true;
105
                        else :
106 98
                            return false;
107
                        endif;
108
                    endif;
109
                endif;
110
            endforeach;
111
        endforeach;
112
113 98
        if ($change) :
114 90
            $vote->setRanking(  $mirror,
115 90
                                ( abs($vote->getTimestamp() - microtime(true)) > 0.5 ) ? ($vote->getTimestamp() + 0.001) : null
116
            );
117
        endif;
118
119 98
        return true;
120
    }
121
122
    // Write a new vote
123 98
    protected function registerVote (Vote $vote, $tag = null) : Vote
124
    {
125
        // Vote identifiant
126 98
        $vote->addTags($tag);
127
        
128
        // Register
129
        try {
130 98
            $vote->registerLink($this);
131 98
            $this->_Votes[] = $vote;
132
        } catch (CondorcetException $e) {
133
            // Security : Check if vote object not already register
134
            throw new CondorcetException(6,'Vote object already registred');
135
        }
136
137 98
        return $vote;
138
    }
139
140 5
    public function removeVote ($in, bool $with = true) : array
141
    {    
142 5
        $rem = [];
143
144 5
        if ($in instanceof Vote) :
145 4
            $key = $this->getVoteKey($in);
146 4
            if ($key !== false) :
147 4
                $this->_Votes[$key]->destroyLink($this);
148
149 4
                $rem[] = $this->_Votes[$key];
150
151 4
                unset($this->_Votes[$key]);
152
            endif;
153
        else :
154
            // Prepare Tags
155 3
            $tag = VoteUtil::tagsConvert($in);
156
157
            // Deleting
158 3
            foreach ($this->getVotesList($tag, $with) as $key => $value) :
159 3
                $this->_Votes[$key]->destroyLink($this);
160
161 3
                $rem[] = $this->_Votes[$key];
162
163 3
                unset($this->_Votes[$key]);
164
            endforeach;
165
166
        endif;
167
168 5
        return $rem;
169
    }
170
171
172
/////////// PARSE VOTE ///////////
173
174
    // Return the well formated vote to use.
175 98
    protected function prepareVoteInput (&$vote, $tag = null) : void
176
    {
177 98
        if (!($vote instanceof Vote)) :
178 82
            $vote = new Vote ($vote, $tag);
179
        endif;
180
181
        // Check array format && Make checkVoteCandidate
182 98
        if ( !$this->checkVoteCandidate($vote) ) :
183
            throw new CondorcetException(5);
184
        endif;
185 98
    }
186
187 2
    public function jsonVotes (string $input)
188
    {
189 2
        $input = CondorcetUtil::prepareJson($input);
190 1
        if ($input === false) :
191
            return $input;
192
        endif;
193
194
            //////
195
196 1
        $adding = [];
197
198 1
        foreach ($input as $record) :
199 1
            if (empty($record['vote'])) :
200 1
                continue;
201
            endif;
202
203 1
            $tags = (!isset($record['tag'])) ? null : $record['tag'];
204 1
            $multi = (!isset($record['multi'])) ? 1 : $record['multi'];
205
206 1
            for ($i = 0; $i < $multi; $i++) :
207 1
                if (self::$_maxParseIteration !== null && $this->countVotes() >= self::$_maxParseIteration) :
208
                    throw new CondorcetException(12, self::$_maxParseIteration);
209
                endif;
210
211
                try {
212 1
                    $adding[] = $this->addVote($record['vote'], $tags);
213
                } catch (\Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
214
            endfor;
215
        endforeach;
216
217 1
        return $adding;
218
    }
219
220 70
    public function parseVotes (string $input, bool $allowFile = true)
221
    {
222 70
        $input = CondorcetUtil::prepareParse($input, $allowFile);
223 70
        if ($input === false) :
224
            return $input;
225
        endif;
226
227
        // Check each lines
228 70
        $adding = [];
229 70
        foreach ($input as $line) :
230
            // Empty Line
231 70
            if (empty($line)) :
232 63
                continue;
233
            endif;
234
235
            // Multiples
236 70
            $is_multiple = mb_strpos($line, '*');
237 70 View Code Duplication
            if ($is_multiple !== false) :
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
238 63
                $multiple = trim( substr($line, $is_multiple + 1) );
239
240
                // Errors
241 63
                if ( !is_numeric($multiple) ) :
242
                    throw new CondorcetException(13, null);
243
                endif;
244
245 63
                $multiple = intval($multiple);
246
247
                // Reformat line
248 63
                $line = substr($line, 0, $is_multiple);
249
            else :
250 9
                $multiple = 1;
251
            endif;
252
253
            // Vote Weight
254 70
            $is_voteWeight = mb_strpos($line, '^');
255 70 View Code Duplication
            if ($is_voteWeight !== false) :
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
256 2
                $weight = trim( substr($line, $is_voteWeight + 1) );
257
258
                // Errors
259 2
                if ( !is_numeric($weight) ) :
260
                    throw new CondorcetException(13, null);
261
                endif;
262
263 2
                $weight = intval($weight);
264
265
                // Reformat line
266 2
                $line = substr($line, 0, $is_voteWeight);
267
            else :
268 69
                $weight = 1;
269
            endif;
270
271
            // Tags + vote
272 70
            if (mb_strpos($line, '||') !== false) :
273 4
                $data = explode('||', $line);
274
275 4
                $vote = $data[1];
276 4
                $tags = $data[0];
277
            // Vote without tags
278
            else :
279 69
                $vote = $line;
280 69
                $tags = null;
281
            endif;
282
283
            // addVote
284 70
            for ($i = 0; $i < $multiple; $i++) :
285 70 View Code Duplication
                if (self::$_maxParseIteration !== null && count($adding) >= self::$_maxParseIteration) :
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
286 1
                    throw new CondorcetException(12, self::$_maxParseIteration);
287
                endif;
288
289
                try {
290 70
                    $adding[] = ($newVote = $this->addVote($vote, $tags));
291 70
                    $newVote->setWeight($weight);
292 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...
293
            endfor;
294
        endforeach;
295
296 70
        return $adding;
297
    }
298
299
}
300