setName($name); } public function __toString(): string { return $this->getName(); } public function __serialize(): array { $this->link = null; $r = get_object_vars($this); unset($r['link']); return $r; } // ------- // SETTERS #[PublicAPI] #[Description("Change the candidate name.\n*If this will not cause conflicts if the candidate is already participating in elections and would namesake. This situation will throw an exception.*")] #[FunctionReturn('In case of success, return TRUE')] #[Throws(CandidateInvalidNameException::class)] public function setName( #[FunctionParameter('Candidate Name')] string $name ): bool { $name = trim($name); if (mb_strlen($name) > Election::MAX_CANDIDATE_NAME_LENGTH) { throw new CandidateInvalidNameException($name); } if (preg_match('/<|>|\n|\t|\0|\^|\*|\$|:|;|(\|\|)|"|#/', $name) === 1) { throw new CandidateInvalidNameException($name); } if (!$this->checkNameInElectionContext($name)) { throw new CandidateExistsException("the name '{$name}' is taken by another candidate"); } $this->name[] = ['name' => $name, 'timestamp' => microtime(true)]; return true; } public function setProvisionalState(bool $provisional): void { $this->provisional = $provisional; } // GETTERS #[PublicAPI] #[Description('Get the candidate name.')] #[FunctionReturn('Candidate name.')] #[Related('Candidate::getHistory', 'Candidate::setName')] public function getName(): string { return end($this->name)['name']; } #[PublicAPI] #[Description('Return an history of each namming change, with timestamp.')] #[FunctionReturn('An explicit multi-dimenssional array.')] #[Related('Candidate::getCreateTimestamp')] public function getHistory(): array { return $this->name; } #[PublicAPI] #[Description('Get the timestamp corresponding of the creation of this candidate.')] #[FunctionReturn('Timestamp')] #[Related('Candidate::getTimestamp')] public function getCreateTimestamp(): float { return $this->name[0]['timestamp']; } #[PublicAPI] #[Description('Get the timestamp corresponding of the last namming change.')] #[FunctionReturn('Timestamp')] #[Related('Candidate::getCreateTimestamp')] public function getTimestamp(): float { return end($this->name)['timestamp']; } #[PublicAPI] #[Description("When you create yourself the vote object, without use the Election::addVote or other native election method. And if you use string input (or array of string).\nThen, these string input will be converted to into temporary candidate objects, named \"provisional\". because you don't create the candidate yourself. They have a provisonal statut true.\nWhen the vote will be added for the first time to an election, provisional candidate object with a name that matches an election candidate, will be converted into the election candidate. And first ranking will be save into Vote history (Vote::getHistory).\n\nSee VoteTest::testVoteHistory() test for a demonstration. In principle this is transparent from a usage point of view. If you want to avoid any non-strict comparisons, however, you should prefer to create your votes with the Election object, or with Candidate Objects in input. But, you must never getback a candidate marked as provisional in an another election in the same time, it's will not working.")] #[FunctionReturn('True if candidate object is in a provisional state, false else.')] public function getProvisionalState(): bool { return $this->provisional; } // ------- // INTERNAL private function checkNameInElectionContext(string $name): bool { foreach ($this->getLinks() as $link => $value) { if (!$link->canAddCandidate($name)) { return false; } } return true; } }