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