Passed
Push — TEST/ScrutinizerPHPAnalysisEng... ( c9e065...573acf )
by Boudry
09:25
created

Vote::__construct()   B

Complexity

Conditions 6
Paths 11

Size

Total Lines 29
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 6.0061

Importance

Changes 0
Metric Value
dl 0
loc 29
ccs 17
cts 18
cp 0.9444
rs 8.439
c 0
b 0
f 0
cc 6
crap 6.0061
eloc 22
nc 11
nop 3
1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 21 and the first side effect is on line 82.

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.

Loading history...
2
/*
3
    Condorcet PHP - Election manager and results calculator.
4
    Designed for the Condorcet method. Integrating a large number of algorithms extending Condorcet. Expandable for all types of voting systems.
5
6
    By Julien Boudry and contributors - MIT LICENSE (Please read LICENSE.txt)
7
    https://github.com/julien-boudry/Condorcet
8
*/
9
declare(strict_types=1);
10
11
namespace Condorcet;
12
13
use Condorcet\CondorcetException;
14
use Condorcet\CondorcetVersion;
15
use Condorcet\Candidate;
16
use Condorcet\Election;
17
use Condorcet\Linkable;
18
use Condorcet\ElectionProcess\VoteUtil;
19
20
21
class Vote implements \Iterator
22
{
23
    use Linkable, CondorcetVersion;
24
25
    // Implement Iterator
26
27
        private $position = 1;
28
29 111
        public function rewind() : void {
30 111
            $this->position = 1;
31 111
        }
32
33 100
        public function current() {
34 100
            return $this->getRanking()[$this->position];
35
        }
36
37 100
        public function key() : int {
38 100
            return $this->position;
39
        }
40
41 100
        public function next() : void {
42 100
            ++$this->position;
43 100
        }
44
45 100
        public function valid() : bool {
46 100
            return isset($this->getRanking()[$this->position]);
47
        }
48
49
    // Vote
50
51
    private $_ranking = [];
52
53
    private $_weight = 1;
54
55
    private $_tags = [];
56
57
    private $_hashCode;
58
59
        ///
60
61 111
    public function __construct ($ranking, $tags = null, ?float $ownTimestamp = null)
62
    {
63 111
        $tagsFromString = null;
64
        // Vote Weight
65 111
        if (is_string($ranking)) :
66 94
            $is_voteWeight = mb_strpos($ranking, '^');
67 94
            if ($is_voteWeight !== false) :
68 1
                $weight = intval( trim( substr($ranking, $is_voteWeight + 1) ) );
69 1
                $ranking = substr($ranking, 0,$is_voteWeight);
70
71
                // Errors
72 1
                if ( !is_numeric($weight) ) :
73
                    throw new CondorcetException(13, null);
74
                endif;
75
            endif;
76
77 94
            $is_voteTags = mb_strpos($ranking, '||');
78 94
            if ($is_voteTags !== false) :
79 3
                $tagsFromString = explode(',', trim( substr($ranking, 0, $is_voteTags) ));
80 3
                $ranking = substr($ranking, $is_voteTags + 2);
81
            endif;
82
        endif;
83
84 111
        $this->setRanking($ranking, $ownTimestamp);
85 111
        $this->addTags($tags);
86 111
        $this->addTags($tagsFromString);
87
88 111
        if (isset($weight)) :
89 1
            $this->setWeight($weight);
90
        endif;
91 111
    }
92
93 2
    public function __sleep () : array
94
    {
95 2
        $this->position = 1;
96
97 2
        return array_keys(get_object_vars($this));
98
    }
99
100 2
    public function __clone ()
101
    {
102 2
        $this->destroyAllLink();
103 2
        $this->setHashCode();
104 2
    }
105
106 111
    public function __toString () : string {
107
        
108 111
        if (empty($this->getTags())) :
109 111
            return $this->getSimpleRanking();
110
        else :
111 13
            return $this->getTagsAsString().' || '.$this->getSimpleRanking();
112
        endif;
113
    }
114
115 1
    public function getHashCode () : string {
116 1
        return $this->_hashCode;
117
    }
118
119
        ///
120
121
    // GETTERS
122
123 111
    public function getRanking () : ?array
124
    {
125 111
        if (!empty($this->_ranking)) :
126 111
            return end($this->_ranking)['ranking'];
127
        else :
128
            return null;
129
        endif;
130
    }
131
132 3
    public function getHistory () : array
133
    {
134 3
        return $this->_ranking;
135
    }
136
137
138 111
    public function getTags () : array
139
    {
140 111
        return $this->_tags;
141
    }
142
143 13
    public function getTagsAsString () : string
144
    {
145 13
        return implode(',',$this->getTags());
146
    }
147
148 2
    public function getCreateTimestamp () : float
149
    {
150 2
        return $this->_ranking[0]['timestamp'];
151
    }
152
153 93
    public function getTimestamp () : float
154
    {
155 93
        return end($this->_ranking)['timestamp'];
156
    }
157
158 1
    public function countRankingCandidates () : int
159
    {
160 1
        return end($this->_ranking)['counter'];
161
    }
162
163 88
    public function getAllCandidates () : array
164
    {
165 88
        $list = [];
166
167 88
        foreach ($this->getRanking() as $rank) :
168 88
            foreach ($rank as $oneCandidate) :
169 88
                $list[] = $oneCandidate;
170
            endforeach;
171
        endforeach;
172
173 88
        return $list;
174
    }
175
176 87
    public function getContextualRanking (Election $election, bool $string = false) : array
177
    {
178 87
        if (!$this->haveLink($election)) :
179
            throw new CondorcetException(22);
180
        endif;
181
182 87
        $ranking = $this->getRanking();
183 87
        $present = $this->getAllCandidates();
184 87
        $newRanking = [];
185 87
        $candidates_list = $election->getCandidatesList(false);
186
187 87
        $nextRank = 1;
188 87
        $countContextualCandidate = 0;
189
190 87
        foreach ($ranking as $CandidatesInRanks) :
191 87
            foreach ($CandidatesInRanks as $candidate) :
192 87
                if ( $election->existCandidateId($candidate, true) ) :
193 87
                    $newRanking[$nextRank][] = $candidate;
194 87
                    $countContextualCandidate++;
195
                endif;
196
            endforeach;
197
198 87
            if (isset($newRanking[$nextRank])) :
199 87
                $nextRank++;
200
            endif;
201
        endforeach;
202
203 87
        if ($election->getImplicitRankingRule() && $countContextualCandidate < $election->countCandidates()) :
204 43
            $last_rank = [];
205 43
            foreach ($candidates_list as $oneCandidate) :
206 43
                if (!in_array($oneCandidate, $present, true)) :
207 43
                    $last_rank[] = $oneCandidate;
208
                endif;
209
            endforeach;
210
211 43
            $newRanking[] = $last_rank;
212
        endif;
213
214 87
        if ($string) :
215 1
            foreach ($newRanking as &$rank) :
216 1
                foreach ($rank as &$oneCandidate) :
217 1
                    $oneCandidate = (string) $oneCandidate;
218
                endforeach;
219
            endforeach;
220
        endif;
221
222 87
        return $newRanking;
223
    }
224
225 111
    public function getSimpleRanking (?Election $context = null) : string
226
    {
227 111
        $ranking = ($context) ? $this->getContextualRanking($context) : $this->getRanking();
228
229 111
        $simpleRanking = VoteUtil::getRankingAsString($ranking);
230
231 111
        if ($this->_weight > 1 && ( ($context && $context->isVoteWeightIsAllowed()) || $context === null )  ) :
232 3
            $simpleRanking .= " ^".$this->getWeight();
233
        endif;
234
235 111
        return $simpleRanking;
236
    }
237
238
239
    // SETTERS
240
241 111
    public function setRanking ($rankingCandidate, ?float $ownTimestamp = null) : bool
242
    {
243
        // Timestamp
244 111
        if ($ownTimestamp !== null) :
245 1
            if (!empty($this->_ranking) && $this->getTimestamp() >= $ownTimestamp) :
246
                throw new CondorcetException(21);
247
            endif;
248
        endif;
249
250
        // Ranking
251 111
        $candidateCounter = $this->formatRanking($rankingCandidate);
252
253 111
        $this->archiveRanking($rankingCandidate, $candidateCounter, $ownTimestamp);
254
255 111
        if (!empty($this->_link)) :
256
            try {
257 6
                foreach ($this->_link as &$link) :
258 6
                    $link->prepareModifyVote($this);
259
                endforeach;
260
            }
261
            catch (CondorcetException $e)
262
            {                
263
                array_pop($this->_ranking);
264
265
                throw new CondorcetException(18);
266
            }
267
        endif;
268
269 111
        $this->setHashCode();
270 111
        return true;
271
    }
272
273 111
        private function formatRanking (&$ranking) : int
274
        {
275 111
            if (is_string($ranking)) :
276 95
                $ranking = VoteUtil::convertVoteInput($ranking);
277
            endif;
278
279 111
            if (!is_array($ranking)) :
280
                throw new CondorcetException(5);
281
            endif;
282
283 111
            $ranking = array_filter($ranking, function ($key) {
284 108
                return is_numeric($key);
285 111
            }, ARRAY_FILTER_USE_KEY);
286
287 111
            ksort($ranking);
288
            
289 111
            $i = 1; $vote_r = [];
290 111
            foreach ($ranking as &$value) :
291 108
                if ( !is_array($value) ) :
292 26
                    $vote_r[$i] = [$value];
293
                else :
294 98
                    $vote_r[$i] = $value;
295
                endif;
296
297 108
                $i++;
298
            endforeach;
299
300 111
            $ranking = $vote_r;
301
302 111
            $counter = 0;
303 111
            $list_candidate = [];
304 111
            foreach ($ranking as &$line) :
305 108
                foreach ($line as &$Candidate) :
306 108
                    if ( !($Candidate instanceof Candidate) ) :
307 97
                        $Candidate = new Candidate ($Candidate);
308 97
                        $Candidate->setProvisionalState(true);
309
                    endif;
310
311 108
                    $counter++;
312
313
                // Check Duplicate
314
315
                    // Check objet reference AND check candidates name
316 108
                    if (!in_array($Candidate, $list_candidate)) :
317 108
                        $list_candidate[] = $Candidate;
318
                    else : 
319 108
                        throw new CondorcetException(5);
320
                    endif;
321
322
                endforeach;
323
            endforeach;
324
325 111
            return $counter;
326
        }
327
328
329 1
    public function removeCandidates (array $candidatesList) : bool
330
    {
331 1
        $ranking = $this->getRanking();
332
333 1
        if ($ranking === null) :
334
            return false;
335
        endif;
336
337 1
        $rankingCandidate = $this->getAllCandidates();
338
339 1
        $canRemove = false;
340 1
        foreach ($candidatesList as $oneCandidate) :
341 1
            if (in_array($oneCandidate, $rankingCandidate, false)) :
342 1
                $canRemove = true;
343 1
                break;
344
            endif;
345
        endforeach;
346
347 1
        if (!$canRemove) :
348
            return false;
349
        endif;
350
351 1
        foreach ($ranking as $rankingKey => &$rank) :
352 1
            foreach ($rank as $oneRankKey => $oneRankValue) :
353 1
                if (in_array($oneRankValue, $candidatesList, false)) :
354 1
                    unset($rank[$oneRankKey]);
355
                endif;
356
            endforeach;
357
358 1
            if (empty($rank)) :
359 1
                unset($ranking[$rankingKey]);
360
            endif;
361
        endforeach;
362
363 1
        $this->setRanking($ranking);
364
365 1
        return true;
366
    }
367
368
369 111
    public function addTags ($tags) : bool
370
    {
371 111
        if (is_object($tags) || is_bool($tags)) :
372
            throw new CondorcetException(17);
373
        endif;
374
375 111
        $tags = VoteUtil::tagsConvert($tags);
376
377 111
        if (empty($tags)) :
378 111
            return false;
379
        endif;
380
381
382 13
        foreach ($tags as $key => $tag) :
383 13
            if (is_numeric($tag)) :
384
                throw new CondorcetException(17);
385 13
            elseif (in_array($tag, $this->_tags, true)) :
386 13
                unset($tags[$key]);
387
            endif;
388
        endforeach;
389
390 13
        foreach ($tags as $tag) :
391 13
            $this->_tags[] = $tag;
392
        endforeach;
393
394 13
        $this->setHashCode();
395
396 13
        return true;
397
    }
398
399 2
    public function removeTags ($tags) : array
400
    {
401 2
        if (is_object($tags) || is_bool($tags)) :
402
            throw new CondorcetException(17);
403
        endif;
404
405 2
        $tags = VoteUtil::tagsConvert($tags);
406
407 2
        if (empty($tags)) :
408 1
            return [];
409
        endif;
410
411
412 2
        $rm = [];
413 2
        foreach ($tags as $key => $tag) :
414 2
            $tagK = array_search($tag, $this->_tags, true);
415
416 2
            if ($tagK === false) :
417 1
                unset($tags[$key]);
418
            else :
419 2
                $rm[] = $this->_tags[$tagK];
420 2
                unset($this->_tags[$tagK]);
421
            endif;
422
        endforeach;
423
424 2
        $this->setHashCode();
425 2
        return $rm;
426
    }
427
428 2
    public function removeAllTags () : bool
429
    {
430 2
        $this->removeTags($this->getTags());
431 2
        return true;
432
    }
433
434 72
    public function getWeight () : int
435
    {
436 72
        return $this->_weight;
437
    }
438
439 72
    public function setWeight (int $newWeight) : int
440
    {
441 72
        if ($newWeight < 1) :
442
            throw new CondorcetException(26);
443
        endif;
444
445 72
        $this->_weight = $newWeight;
446
447 72
        if (!empty($this->_link)) :
448 71
            foreach ($this->_link as &$link) :
449 71
                $link->setStateToVote();
450
            endforeach;
451
        endif;
452
453 72
        $this->setHashCode();
454
455 72
        return $this->getWeight();
456
    }
457
458
/////////// INTERNAL ///////////
459
460 111
    private function archiveRanking ($ranking, int $counter, ?float $ownTimestamp) : void
461
    {
462 111
        $this->_ranking[] = [   'ranking' => $ranking,
463 111
                                'timestamp' => ($ownTimestamp !== null) ? $ownTimestamp : microtime(true),
464 111
                                'counter' => $counter   ];
465
466 111
        $this->rewind();
467 111
    }
468
469 111
    private function setHashCode () : string
470
    {
471 111
        return $this->_hashCode = hash('sha224', ((string) $this) . microtime(false));
472
    }
473
}
474