Completed
Push — master ( 922b0c...88b497 )
by Boudry
03:58
created

Vote::getTimestamp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
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 19 and the first side effect is on line 80.

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