Completed
Push — master ( 9ee175...c01904 )
by Boudry
02:34
created

Election   F

Complexity

Total Complexity 164

Size/Duplication

Total Lines 902
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 87.84%

Importance

Changes 0
Metric Value
wmc 164
lcom 1
cbo 11
dl 0
loc 902
ccs 325
cts 370
cp 0.8784
rs 3.9999
c 0
b 0
f 0

55 Methods

Rating   Name   Duplication   Size   Complexity  
A setMaxParseIteration() 0 5 1
A setMaxVoteNumber() 0 5 1
A __construct() 0 6 1
A __destruct() 0 4 1
B __sleep() 0 24 2
A __clone() 0 4 1
A registerAllLinks() 0 12 4
A getGlobalTimer() 0 3 1
A getLastTimer() 0 3 1
A getTimerManager() 0 3 1
B getChecksum() 0 23 4
A ignoreMaxVote() 0 5 1
A getImplicitRankingRule() 0 4 1
A setImplicitRanking() 0 6 1
A isVoteWeightIsAllowed() 0 4 1
A allowVoteWeight() 0 6 1
D addCandidate() 0 39 10
A canAddCandidate() 0 4 1
B removeCandidate() 0 33 6
C parseCandidates() 0 29 8
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 setStateToVote() 0 16 4
A addVote() 0 13 4
A prepareModifyVote() 0 10 2
A prepareVoteInput() 0 11 3
C checkVoteCandidate() 0 31 11
A registerVote() 0 16 2
B removeVote() 0 30 4
C parseVotes() 0 78 13
A setExternalDataHandler() 0 9 2
A removeExternalDataHandler() 0 9 2
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
D jsonVotes() 0 32 10
A __wakeup() 0 6 2
A destroyAllLink() 0 12 4
A jsonCandidates() 0 19 4
A getCandidateObjectByName() 0 11 3
B getResult() 0 46 6
A getWinner() 0 16 3
A getLoser() 0 16 3
A condorcetBasicSubstitution() 0 17 4
A computeResult() 0 4 1
A prepareResult() 0 19 3
A initResult() 0 6 2
A cleanupResult() 0 15 2
A formatResultOptions() 0 15 4
A getPairwise() 0 6 2

How to fix   Complexity   

Complex Class

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

1
<?php
0 ignored issues
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 26 and the first side effect is on line 120.

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\Condorcet;
13
use Condorcet\CondorcetException;
14
use Condorcet\CondorcetVersion;
15
use Condorcet\Result;
16
use Condorcet\Util;
17
use Condorcet\Vote;
18
use Condorcet\Algo\Pairwise;
19
use Condorcet\DataManager\VotesManager;
20
use Condorcet\DataManager\DataHandlerDrivers\DataHandlerDriverInterface;
21
use Condorcet\Timer\Chrono as Timer_Chrono;
22
use Condorcet\Timer\Manager as Timer_Manager;
23
24
25
// Base Condorcet class
26
class Election
27
{
28
29
/////////// PROPERTIES ///////////
30
31
    public const MAX_LENGTH_CANDIDATE_ID = 30; // Max length for candidate identifiant string
32
33
    protected static $_maxParseIteration = null;
34
    protected static $_maxVoteNumber = null;
35
    protected static $_checksumMode = false;
36
37
/////////// STATICS METHODS ///////////
38
39
    // Change max parse iteration
40 1
    public static function setMaxParseIteration (?int $value) : ?int
41
    {
42 1
        self::$_maxParseIteration = $value;
43 1
        return self::$_maxParseIteration;
44
    }
45
46
    // Change max vote number
47 1
    public static function setMaxVoteNumber (?int $value) : ?int
48
    {
49 1
        self::$_maxVoteNumber = $value;
50 1
        return self::$_maxVoteNumber;
51
    }
52
53
54
/////////// CONSTRUCTOR ///////////
55
56
    use CondorcetVersion;
57
58
    // Data and global options
59
    protected $_Candidates = []; // Candidate list
60
    protected $_Votes; // Votes list
61
62
    // Mechanics
63
    protected $_i_CandidateId = 'A';
64
    protected $_State = 1; // 1 = Add Candidates / 2 = Voting / 3 = Some result have been computing
65
    protected $_timer;
66
    protected $_nextVoteTag = 0;
67
    protected $_ignoreStaticMaxVote = false;
68
69
    // Params
70
    protected $_ImplicitRanking = true;
71
    protected $_VoteWeightRule = false;
72
73
    // Result
74
    protected $_Pairwise;
75
    protected $_Calculator;
76
77
        //////
78
79 108
    public function __construct ()
80
    {
81 108
        $this->_Candidates = [];
82 108
        $this->_Votes = new VotesManager ($this);
83 108
        $this->_timer = new Timer_Manager;
84 108
    }
85
86 1
    public function __destruct ()
87
    {
88 1
        $this->destroyAllLink();
89 1
    }
90
91 2
    public function __sleep () : array
92
    {
93
        // Don't include others data
94
        $include = [
95 2
            '_Candidates',
96
            '_Votes',
97
98
            '_i_CandidateId',
99
            '_State',
100
            '_nextVoteTag',
101
            '_objectVersion',
102
            '_ignoreStaticMaxVote',
103
104
            '_ImplicitRanking',
105
            '_VoteWeightRule',
106
107
            '_Pairwise',
108
            '_Calculator',
109
        ];
110
111 2
        !self::$_checksumMode && array_push($include, '_timer');
112
113 2
        return $include;
114
    }
115
116 1
    public function __wakeup ()
117
    {
118 1
        if ( version_compare($this->getObjectVersion('MAJOR'),Condorcet::getVersion('MAJOR'),'!=') ) :
119
            throw new CondorcetException(11, 'Your object version is '.$this->getObjectVersion().' but the class engine version is '.Condorcet::getVersion('ENV'));
120
        endif;
121 1
    }
122
123
    public function __clone ()
124
    {
125
        $this->registerAllLinks();
126
    }
127
128
129
/////////// INTERNAL GENERIC REGULATION ///////////
130
131
132
    protected function registerAllLinks () : void
133
    {
134
        foreach ($this->_Candidates as $value) :
135
            $value->registerLink($this);
136
        endforeach;
137
138
        if ($this->_State > 1) :
139
            foreach ($this->_Votes as $value) :
140
                $value->registerLink($this);
141
            endforeach;
142
        endif;
143
    }
144
145 1
    protected function destroyAllLink () : void
146
    {
147 1
        foreach ($this->_Candidates as $value) :
148 1
            $value->destroyLink($this);
149
        endforeach;
150
151 1
        if ($this->_State > 1) :
152 1
            foreach ($this->_Votes as $value) :
153 1
                $value->destroyLink($this);
154
            endforeach;
155
        endif;
156 1
    }
157
158
        //////
159
160
161 2
    public function getGlobalTimer (bool $float = false) {
162 2
        return $this->_timer->getGlobalTimer($float);
163
    }
164
165 2
    public function getLastTimer (bool $float = false) {
166 2
        return $this->_timer->getLastTimer($float);
167
    }
168
169 79
    public function getTimerManager () : Timer_Manager {
170 79
        return $this->_timer;
171
    }
172
173 1
    public function getChecksum () : string
174
    {
175 1
        self::$_checksumMode = true;
176
177 1
        $r = hash_init('sha256');
178
179 1
        foreach ($this->_Candidates as $value) :
180 1
            hash_update($r, (string) $value);
181
        endforeach;
182
183 1
        foreach ($this->_Votes as $value) :
184 1
            hash_update($r, (string) $value);
185
        endforeach;
186
187 1
        $this->_Pairwise !== null
188 1
            && hash_update($r,serialize($this->_Pairwise->getExplicitPairwise()));
189
190 1
        hash_update($r, $this->getObjectVersion('major'));
191
192 1
        self::$_checksumMode = false;
193
194 1
        return hash_final($r);
195
    }
196
197
    public function ignoreMaxVote (bool $state = true) : bool
198
    {
199
        $this->_ignoreStaticMaxVote = $state;
200
        return $this->_ignoreStaticMaxVote;
201
    }
202
203 85
    public function getImplicitRankingRule () : bool
204
    {
205 85
        return $this->_ImplicitRanking;
206
    }
207
208 3
    public function setImplicitRanking (bool $rule = true) : bool
209
    {
210 3
        $this->_ImplicitRanking = $rule;
211 3
        $this->cleanupResult();
212 3
        return $this->getImplicitRankingRule();
213
    }
214
215 81
    public function isVoteWeightIsAllowed () : bool
216
    {
217 81
        return $this->_VoteWeightRule;
218
    }
219
220 1
    public function allowVoteWeight (bool $rule = true) : bool
221
    {
222 1
        $this->_VoteWeightRule = $rule;
223 1
        $this->cleanupResult();
224 1
        return $this->isVoteWeightIsAllowed();
225
    }
226
227
228
/////////// CANDIDATES ///////////
229
230
231
    // Add a vote candidate before voting
232 103
    public function addCandidate ($candidate_id = null) : Candidate
233
    {
234
        // only if the vote has not started
235 103
        if ( $this->_State > 1 ) :
236
            throw new CondorcetException(2);
237
        endif;
238
239
        // Filter
240 103
        if ( is_bool($candidate_id) || is_array($candidate_id) || (is_object($candidate_id) && !($candidate_id instanceof Candidate)) ) :
241
            throw new CondorcetException(1, $candidate_id);
242
        endif;
243
244
245
        // Process
246 103
        if ( empty($candidate_id) ) :
247 5
            while ( !$this->canAddCandidate($this->_i_CandidateId) ) :
248 5
                $this->_i_CandidateId++;
249
            endwhile;
250
251 5
            $newCandidate = new Candidate($this->_i_CandidateId);
252
        else : // Try to add the candidate_id
253 100
            $newCandidate = ($candidate_id instanceof Candidate) ? $candidate_id : new Candidate ((string) $candidate_id);
254
255 100
            if ( !$this->canAddCandidate($newCandidate) ) :
256
                throw new CondorcetException(3,$candidate_id);
257
            endif;
258
        endif;
259
260
        // Register it
261 103
        $this->_Candidates[] = $newCandidate;
262
263
        // Linking
264 103
        $newCandidate->registerLink($this);
265
266
        // Disallow other candidate object name matching 
267 103
        $newCandidate->setProvisionalState(false);
268
269 103
        return $newCandidate;
270
    }
271
272 103
        public function canAddCandidate ($candidate_id) : bool
273
        {
274 103
            return !$this->existCandidateId($candidate_id, false);
275
        }
276
277
278
    // Destroy a register vote candidate before voting
279 3
    public function removeCandidate ($list) : array
280
    {
281
        // only if the vote has not started
282 3
        if ( $this->_State > 1 ) :
283
            throw new CondorcetException(2);
284
        endif;
285
286
        
287 3
        if ( !is_array($list) ) :
288 3
            $list = [$list];
289
        endif;
290
291 3
        foreach ($list as &$candidate_id) :
292 3
            $candidate_key = $this->getCandidateKey($candidate_id);
293
294 3
            if ( $candidate_key === false ) :
295
                throw new CondorcetException(4,$candidate_id);
296
            endif;
297
298 3
            $candidate_id = $candidate_key;
299
        endforeach;
300
301 3
        $rem = [];
302 3
        foreach ($list as $candidate_key) :
303 3
            $this->_Candidates[$candidate_key]->destroyLink($this);
304
305 3
            $rem[] = $this->_Candidates[$candidate_key];
306
307 3
            unset($this->_Candidates[$candidate_key]);
308
        endforeach;
309
310 3
        return $rem;
311
    }
312
313
314 1
    public function jsonCandidates (string $input)
315
    {
316 1
        $input = Util::prepareJson($input);
317 1
        if ($input === false) :
318
            return $input;
319
        endif;
320
321
            //////
322
323 1
        $adding = [];
324 1
        foreach ($input as $candidate) :
325
            try {
326 1
                $adding[] = $this->addCandidate($candidate);
327
            }
328 1
            catch (CondorcetException $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
329
        endforeach;
330
331 1
        return $adding;
332
    }
333
334 7
    public function parseCandidates (string $input, bool $allowFile = true)
335
    {
336 7
        $input = Util::prepareParse($input, $allowFile);
337 7
        if ($input === false) :
338
            return $input;
339
        endif;
340
341 7
        $adding = [];
342 7
        foreach ($input as $line) :
343
            // Empty Line
344 7
            if (empty($line)) :
345 1
                continue;
346
            endif;
347
348
            // addCandidate
349
            try {
350 7
                if (self::$_maxParseIteration !== null && count($adding) >= self::$_maxParseIteration) :
351
                    throw new CondorcetException(12, self::$_maxParseIteration);
352
                endif;
353
354 7
                $adding[] = $this->addCandidate($line);
355
            } catch (CondorcetException $e) {
356
                if ($e->getCode() === 12)
357 7
                    {throw $e;}
358
            }
359
        endforeach;
360
361 7
        return $adding;
362
    }
363
364
365
        //:: CANDIDATES TOOLS :://
366
367
        // Count registered candidates
368 86
        public function countCandidates () : int
369
        {
370 86
            return count($this->_Candidates);
371
        }
372
373
        // Get the list of registered CANDIDATES
374 87
        public function getCandidatesList (bool $stringMode = false) : array
375
        {
376 87
            if (!$stringMode) :
377 85
                return $this->_Candidates;
378
            else :
379 4
                $result = [];
380
381 4
                foreach ($this->_Candidates as $candidateKey => &$oneCandidate) :
382 4
                    $result[$candidateKey] = $oneCandidate->getName();
383
                endforeach;
384
385 4
                return $result;
386
            endif;
387
        }
388
389 92
        public function getCandidateKey ($candidate_id)
390
        {
391 92
            if ($candidate_id instanceof Candidate) :
392 79
                return array_search($candidate_id, $this->_Candidates, true);
393
            else:
394 91
                return array_search(trim((string) $candidate_id), $this->_Candidates, false);
395
            endif;
396
        }
397
398 75
        public function getCandidateId (int $candidate_key, bool $onlyName = false)
399
        {
400 75
            if (!array_key_exists($candidate_key, $this->_Candidates)) :
401
                return false;
402
            else :
403 75
                return ($onlyName) ? $this->_Candidates[$candidate_key]->getName() : $this->_Candidates[$candidate_key];
404
            endif;
405
        }
406
407 103
        public function existCandidateId ($candidate_id, bool $strict = true) : bool
408
        {
409 103
            return ($strict) ? in_array($candidate_id, $this->_Candidates, true) : in_array((string) $candidate_id, $this->_Candidates);
410
        }
411
412 3
        public function getCandidateObjectByName (string $s)
413
        {
414 3
            foreach ($this->_Candidates as $oneCandidate) :
415
416 3
                if ($oneCandidate->getName() === $s) :
417 3
                    return $oneCandidate;
418
                endif;
419
            endforeach;
420
421 1
            return false;
422
        }
423
424
425
426
/////////// VOTING ///////////
427
428
429
    // Close the candidate config, be ready for voting (optional)
430 97
    public function setStateToVote () : bool
431
    {
432 97
        if ( $this->_State === 1 ) :
433 97
                if (empty($this->_Candidates)) :
434
                    throw new CondorcetException(20);
435
                endif;
436
437 97
                $this->_State = 2;
438
439
        // If voting continues after a first set of results
440 85
        elseif ( $this->_State > 2 ) :
441 6
                $this->cleanupResult();
442
        endif;
443
444 97
        return true;
445
    }
446
447
448
    // 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.
449 97
    public function addVote ($vote, $tag = null) : Vote
450
    {
451 97
        $this->prepareVoteInput($vote, $tag);
452
453
        // Check Max Vote Count
454 97
        if ( self::$_maxVoteNumber !== null && !$this->_ignoreStaticMaxVote && $this->countVotes() >= self::$_maxVoteNumber ) :
455 1
            throw new CondorcetException(16, self::$_maxVoteNumber);
456
        endif;
457
458
459
        // Register vote
460 97
        return $this->registerVote($vote, $tag); // Return the vote object
461
    }
462
463
        // return True or throw an Exception
464 5
        public function prepareModifyVote (Vote $existVote)
465
        {
466
            try {
467 5
                $this->prepareVoteInput($existVote);
468 5
                $this->setStateToVote();
469
            }
470
            catch (\Exception $e) {
471
                throw $e;
472
            }
473 5
        }
474
475
        // Return the well formated vote to use.
476 97
        protected function prepareVoteInput (&$vote, $tag = null) : void
477
        {
478 97
            if (!($vote instanceof Vote)) :
479 82
                $vote = new Vote ($vote, $tag);
480
            endif;
481
482
            // Check array format && Make checkVoteCandidate
483 97
            if ( !$this->checkVoteCandidate($vote) ) :
484
                throw new CondorcetException(5);
485
            endif;
486 97
        }
487
488
489 97
        public function checkVoteCandidate (Vote $vote) : bool
490
        {
491 97
            $linkCount = $vote->countLinks();
492 97
            $links = $vote->getLinks();
493
494 97
            $mirror = $vote->getRanking();
495 97
            $change = false;
496 97
            foreach ($vote as $rank => $choice) :
497 97
                foreach ($choice as $choiceKey => $candidate) :
498 97
                    if ( !$this->existCandidateId($candidate, true) ) :
499 91
                        if ($candidate->getProvisionalState() && $this->existCandidateId($candidate, false)) :
500 90
                            if ( $linkCount === 0 || ($linkCount === 1 && reset($links) === $this) ) :
501 90
                                $mirror[$rank][$choiceKey] = $this->_Candidates[$this->getCandidateKey((string) $candidate)];
502 90
                                $change = true;
503
                            else :
504 97
                                return false;
505
                            endif;
506
                        endif;
507
                    endif;
508
                endforeach;
509
            endforeach;
510
511 97
            if ($change) :
512 90
                $vote->setRanking(
513 90
                                    $mirror,
514 90
                                    ( abs($vote->getTimestamp() - microtime(true)) > 0.5 ) ? ($vote->getTimestamp() + 0.001) : false
0 ignored issues
show
Bug introduced by
It seems like abs($vote->getTimestamp(...stamp() + 0.001 : false can also be of type double; however, Condorcet\Vote::setRanking() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
515
                );
516
            endif;
517
518 97
            return true;
519
        }
520
521
        // Write a new vote
522 97
        protected function registerVote (Vote $vote, $tag = null) : Vote
523
        {
524
            // Vote identifiant
525 97
            $vote->addTags($tag);
526
            
527
            // Register
528
            try {
529 97
                $vote->registerLink($this);
530 97
                $this->_Votes[] = $vote;
531
            } catch (CondorcetException $e) {
532
                // Security : Check if vote object not already register
533
                throw new CondorcetException(6,'Vote object already registred');
534
            }
535
536 97
            return $vote;
537
        }
538
539
540 5
    public function removeVote ($in, bool $with = true) : array
541
    {    
542 5
        $rem = [];
543
544 5
        if ($in instanceof Vote) :
545 4
            $key = $this->getVoteKey($in);
546 4
            if ($key !== false) :
547 4
                $this->_Votes[$key]->destroyLink($this);
548
549 4
                $rem[] = $this->_Votes[$key];
550
551 4
                unset($this->_Votes[$key]);
552
            endif;
553
        else :
554
            // Prepare Tags
555 3
            $tag = Vote::tagsConvert($in);
556
557
            // Deleting
558 3
            foreach ($this->getVotesList($tag, $with) as $key => $value) :
559 3
                $this->_Votes[$key]->destroyLink($this);
560
561 3
                $rem[] = $this->_Votes[$key];
562
563 3
                unset($this->_Votes[$key]);
564
            endforeach;
565
566
        endif;
567
568 5
        return $rem;
569
    }
570
571
572 2
    public function jsonVotes (string $input)
573
    {
574 2
        $input = Util::prepareJson($input);
575 1
        if ($input === false) :
576
            return $input;
577
        endif;
578
579
            //////
580
581 1
        $adding = [];
582
583 1
        foreach ($input as $record) :
584 1
            if (empty($record['vote'])) :
585 1
                continue;
586
            endif;
587
588 1
            $tags = (!isset($record['tag'])) ? null : $record['tag'];
589 1
            $multi = (!isset($record['multi'])) ? 1 : $record['multi'];
590
591 1
            for ($i = 0; $i < $multi; $i++) :
592 1
                if (self::$_maxParseIteration !== null && $this->countVotes() >= self::$_maxParseIteration) :
593
                    throw new CondorcetException(12, self::$_maxParseIteration);
594
                endif;
595
596
                try {
597 1
                    $adding[] = $this->addVote($record['vote'], $tags);
598
                } catch (\Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
599
            endfor;
600
        endforeach;
601
602 1
        return $adding;
603
    }
604
605 70
    public function parseVotes (string $input, bool $allowFile = true)
606
    {
607 70
        $input = Util::prepareParse($input, $allowFile);
608 70
        if ($input === false) :
609
            return $input;
610
        endif;
611
612
        // Check each lines
613 70
        $adding = [];
614 70
        foreach ($input as $line) :
615
            // Empty Line
616 70
            if (empty($line)) :
617 63
                continue;
618
            endif;
619
620
            // Multiples
621 70
            $is_multiple = mb_strpos($line, '*');
622 70
            if ($is_multiple !== false) :
623 63
                $multiple = trim( substr($line, $is_multiple + 1) );
624
625
                // Errors
626 63
                if ( !is_numeric($multiple) ) :
627
                    throw new CondorcetException(13, null);
628
                endif;
629
630 63
                $multiple = intval($multiple);
631
632
                // Reformat line
633 63
                $line = substr($line, 0, $is_multiple);
634
            else :
635 9
                $multiple = 1;
636
            endif;
637
638
            // Vote Weight
639 70
            $is_voteWeight = mb_strpos($line, '^');
640 70
            if ($is_voteWeight !== false) :
641 2
                $weight = trim( substr($line, $is_voteWeight + 1) );
642
643
                // Errors
644 2
                if ( !is_numeric($weight) ) :
645
                    throw new CondorcetException(13, null);
646
                endif;
647
648 2
                $weight = intval($weight);
649
650
                // Reformat line
651 2
                $line = substr($line, 0, $is_voteWeight);
652
            else :
653 69
                $weight = 1;
654
            endif;
655
656
            // Tags + vote
657 70
            if (mb_strpos($line, '||') !== false) :
658 4
                $data = explode('||', $line);
659
660 4
                $vote = $data[1];
661 4
                $tags = $data[0];
662
            // Vote without tags
663
            else :
664 69
                $vote = $line;
665 69
                $tags = null;
666
            endif;
667
668
            // addVote
669 70
            for ($i = 0; $i < $multiple; $i++) :
670 70
                if (self::$_maxParseIteration !== null && count($adding) >= self::$_maxParseIteration) :
671 1
                    throw new CondorcetException(12, self::$_maxParseIteration);
672
                endif;
673
674
                try {
675 70
                    $adding[] = ($newVote = $this->addVote($vote, $tags));
676 70
                    $newVote->setWeight($weight);
677 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...
678
            endfor;
679
        endforeach;
680
681 70
        return $adding;
682
    }
683
684
685
    //:: LARGE ELECTION MODE :://
686
687 3
    public function setExternalDataHandler (DataHandlerDriverInterface $driver) : bool
688
    {
689 3
        if (!$this->_Votes->isUsingHandler()) :
690 3
            $this->_Votes->importHandler($driver);
691 3
            return true;
692
        else :
693
            throw new CondorcetException(24);
694
        endif;
695
    }
696
697 1
    public function removeExternalDataHandler () : bool
698
    {
699 1
        if ($this->_Votes->isUsingHandler()) :
700 1
            $this->_Votes->closeHandler();
701 1
            return true;
702
        else :
703
            throw new CondorcetException(23);
704
        endif;
705
    }
706
707
708
    //:: VOTING TOOLS :://
709
710
    // How many votes are registered ?
711 10
    public function countVotes ($tag = null, bool $with = true) : int
712
    {
713 10
        return $this->_Votes->countVotes(Vote::tagsConvert($tag),$with);
714
    }
715
716
    // Get the votes registered list
717 8
    public function getVotesList ($tag = null, bool $with = true) : array
718
    {
719 8
        return $this->_Votes->getVotesList(Vote::tagsConvert($tag), $with);
720
    }
721
722 4
    public function getVotesListAsString () : string
723
    {
724 4
        return $this->_Votes->getVotesListAsString();
725
    }
726
727 80
    public function getVotesManager () : VotesManager {
728 80
        return $this->_Votes;
729
    }
730
731 4
    public function getVoteKey (Vote $vote) {
732 4
        return $this->_Votes->getVoteKey($vote);
733
    }
734
735
736
/////////// RESULTS ///////////
737
738
739
    //:: PUBLIC FUNCTIONS :://
740
741
    // Generic function for default result with ability to change default object method
742 73
    public function getResult ($method = true, array $options = []) : Result
743
    {
744 73
        $options = $this->formatResultOptions($options);
745
746
        // Filter if tag is provided & return
747 73
        if ($options['%tagFilter']) :
748 2
            $chrono = new Timer_Chrono ($this->_timer, 'GetResult with filter');
749
750 2
            $filter = new self;
751
752 2
            foreach ($this->getCandidatesList() as $candidate) :
753 2
                $filter->addCandidate($candidate);
754
            endforeach;
755
756 2
            foreach ($this->getVotesList($options['tags'], $options['withTag']) as $vote) :
757 2
                $filter->addVote($vote);
758
            endforeach;
759
760 2
            unset($chrono);
761
762 2
            return $filter->getResult($method);
763
        endif;
764
765
            ////// Start //////
766
767
        // Prepare
768 73
        $this->prepareResult();
769
770
            //////
771
772 73
        $chrono = new Timer_Chrono ($this->_timer);
773
774 73
        if ($method === true) :
775 6
            $this->initResult(Condorcet::getDefaultMethod());
776 6
            $result = $this->_Calculator[Condorcet::getDefaultMethod()]->getResult();
777 70
        elseif ($method = Condorcet::isAuthMethod((string) $method)) :
778 70
            $this->initResult($method);
779 68
            $result = $this->_Calculator[$method]->getResult();
780
        else :
781
            throw new CondorcetException(8,$method);
782
        endif;
783
784 70
        $chrono->setRole('GetResult for '.$method);
785
786 70
        return $result;
787
    }
788
789
790 77
    public function getWinner (?string $substitution = null)
791
    {
792 77
        $algo = $this->condorcetBasicSubstitution($substitution);
793
794
            //////
795
796 77
        if ($algo === Condorcet::CONDORCET_BASIC_CLASS) :
797 75
            new Timer_Chrono ($this->_timer, 'GetWinner for CondorcetBasic');
798 75
            $this->initResult($algo);
799 75
            $result = $this->_Calculator[$algo]->getWinner();
800
801 75
            return ($result === null) ? null : $this->getCandidateId($result);
802
        else :
803 49
            return $this->getResult($algo)->getWinner();
804
        endif;
805
    }
806
807
808 73
    public function getLoser (?string $substitution = null)
809
    {
810 73
        $algo = $this->condorcetBasicSubstitution($substitution);
811
812
            //////
813
814 73
        if ($algo === Condorcet::CONDORCET_BASIC_CLASS) :
815 73
            new Timer_Chrono ($this->_timer, 'GetLoser for CondorcetBasic');
816 73
            $this->initResult($algo);
817 73
            $result = $this->_Calculator[$algo]->getLoser();
818
819 73
            return ($result === null) ? null : $this->getCandidateId($result);
820
        else :
821 2
            return $this->getResult($algo)->getLoser();
822
        endif;
823
    }
824
825 78
        protected function condorcetBasicSubstitution ($substitution) : string {
826 78
            if ( $substitution ) :
827 49
                if ($substitution === true) :
828
                    $substitution = Condorcet::getDefaultMethod();
829
                endif;
830
                
831 49
                if ( Condorcet::isAuthMethod($substitution) ) :
832 49
                    $algo = $substitution;
833
                else :
834 49
                    throw new CondorcetException(9,$substitution);
835
                endif;
836
            else :
837 76
                $algo = Condorcet::CONDORCET_BASIC_CLASS;
838
            endif;
839
840 78
            return $algo;
841
        }
842
843
844
    public function computeResult ($method = true) : void
845
    {
846
        $this->getResult($method);
847
    }
848
849
850
    //:: TOOLS FOR RESULT PROCESS :://
851
852
853
    // Prepare to compute results & caching system
854 79
    protected function prepareResult () : bool
855
    {
856 79
        if ($this->_State > 2) :
857 72
            return false;
858 79
        elseif ($this->_State === 2) :
859 79
            $this->cleanupResult();
860
861
            // Do Pairewise
862 79
            $this->_Pairwise = new Pairwise ($this);
863
864
            // Change state to result
865 79
            $this->_State = 3;
866
867
            // Return
868 79
            return true;
869
        else :
870
            throw new CondorcetException(6);
871
        endif;
872
    }
873
874
875 79
    protected function initResult (string $class) : void
876
    {
877 79
        if ( !isset($this->_Calculator[$class]) ) :
878 79
            $this->_Calculator[$class] = new $class($this);
879
        endif;
880 77
    }
881
882
883
    // Cleanup results to compute again with new votes
884 79
    protected function cleanupResult () : void
885
    {
886
        // Reset state
887 79
        if ($this->_State > 2) : 
888 8
            $this->_State = 2;
889
        endif;
890
891
            //////
892
893
        // Clean pairwise
894 79
        $this->_Pairwise = null;
895
896
        // Algos
897 79
        $this->_Calculator = null;
898 79
    }
899
900
901 73
    protected function formatResultOptions (array $arg) : array
902
    {
903
        // About tag filter
904 73
        if (isset($arg['tags'])):
905 2
            $arg['%tagFilter'] = true;
906
907 2
            if ( !isset($arg['withTag']) || !is_bool($arg['withTag']) ) :
908 2
                $arg['withTag'] = true;
909
            endif;
910
        else:
911 73
            $arg['%tagFilter'] = false;
912
        endif;
913
914 73
        return $arg;
915
    }
916
917
918
    //:: GET RAW DATA :://
919
920 76
    public function getPairwise (bool $explicit = true)
921
    {
922 76
        $this->prepareResult();
923
924 76
        return (!$explicit) ? $this->_Pairwise : $this->_Pairwise->getExplicitPairwise();
925
    }
926
927
}
928