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 | declare(strict_types=1); |
||
3 | /** |
||
4 | * Created by PhpStorm. |
||
5 | * User: benedikt |
||
6 | * Date: 1/2/18 |
||
7 | * Time: 2:32 PM |
||
8 | */ |
||
9 | |||
10 | namespace Tfboe\FmLib\Service\RankingSystem; |
||
11 | |||
12 | |||
13 | use Doctrine\Common\Collections\Collection; |
||
14 | use Doctrine\ORM\EntityManagerInterface; |
||
15 | use Doctrine\ORM\QueryBuilder; |
||
16 | use Tfboe\FmLib\Entity\Helpers\AutomaticInstanceGeneration; |
||
17 | use Tfboe\FmLib\Entity\Helpers\TournamentHierarchyEntity; |
||
18 | use Tfboe\FmLib\Entity\Helpers\TournamentHierarchyInterface; |
||
19 | use Tfboe\FmLib\Entity\PlayerInterface; |
||
20 | use Tfboe\FmLib\Entity\RankingSystemChangeInterface; |
||
21 | use Tfboe\FmLib\Entity\RankingSystemInterface; |
||
0 ignored issues
–
show
|
|||
22 | use Tfboe\FmLib\Entity\RankingSystemListEntryInterface; |
||
23 | use Tfboe\FmLib\Entity\RankingSystemListInterface; |
||
24 | use Tfboe\FmLib\Entity\TournamentInterface; |
||
25 | use Tfboe\FmLib\Exceptions\PreconditionFailedException; |
||
26 | use Tfboe\FmLib\Service\ObjectCreatorServiceInterface; |
||
27 | |||
28 | |||
29 | /** |
||
30 | * Class RankingSystemService |
||
31 | * @package Tfboe\FmLib\Service\RankingSystemService |
||
32 | * @SuppressWarnings(PHPMD) TODO: refactor this class and remove suppress warnings |
||
33 | */ |
||
34 | abstract class RankingSystemService implements \Tfboe\FmLib\Service\RankingSystem\RankingSystemInterface |
||
35 | { |
||
36 | //<editor-fold desc="Fields"> |
||
37 | /** @var EntityManagerInterface */ |
||
38 | private $entityManager; |
||
39 | /** @var TimeServiceInterface */ |
||
40 | private $timeService; |
||
41 | /** @var EntityComparerInterface */ |
||
42 | private $entityComparer; |
||
43 | /** |
||
44 | * @var RankingSystemChangeInterface[][] |
||
45 | * first key: tournament hierarchy entity id |
||
46 | * second key: player id |
||
47 | */ |
||
48 | private $changes; |
||
49 | /** |
||
50 | * @var RankingSystemChangeInterface[][] |
||
51 | * first key: tournament hierarchy entity id |
||
52 | * second key: player id |
||
53 | */ |
||
54 | private $oldChanges; |
||
55 | /** |
||
56 | * List of ranking systems for which update ranking got already called, indexed by id |
||
57 | * @var RankingSystemService[] |
||
58 | */ |
||
59 | private $updateRankingCalls; |
||
60 | |||
61 | /** @var ObjectCreatorServiceInterface */ |
||
62 | private $objectCreatorService; |
||
63 | //</editor-fold desc="Fields"> |
||
64 | |||
65 | //<editor-fold desc="Constructor"> |
||
66 | /** |
||
67 | * RankingSystemService constructor. |
||
68 | * @param EntityManagerInterface $entityManager |
||
69 | * @param TimeServiceInterface $timeService |
||
70 | * @param EntityComparerInterface $entityComparer |
||
71 | * @param ObjectCreatorServiceInterface $objectCreatorService |
||
72 | */ |
||
73 | public function __construct(EntityManagerInterface $entityManager, TimeServiceInterface $timeService, |
||
74 | EntityComparerInterface $entityComparer, |
||
75 | ObjectCreatorServiceInterface $objectCreatorService) |
||
76 | { |
||
77 | $this->entityManager = $entityManager; |
||
78 | $this->timeService = $timeService; |
||
79 | $this->entityComparer = $entityComparer; |
||
80 | $this->changes = []; |
||
81 | $this->oldChanges = []; |
||
82 | $this->updateRankingCalls = []; |
||
83 | $this->objectCreatorService = $objectCreatorService; |
||
84 | } |
||
85 | //</editor-fold desc="Constructor"> |
||
86 | |||
87 | //<editor-fold desc="Public Methods"> |
||
88 | /** |
||
89 | * @inheritDoc |
||
90 | */ |
||
91 | public function getEarliestInfluence(RankingSystemInterface $ranking, TournamentInterface $tournament): ?\DateTime |
||
92 | { |
||
93 | return $this->getEarliestEntityInfluence($ranking, $tournament, false); |
||
94 | } |
||
95 | |||
96 | /** |
||
97 | * @inheritdoc |
||
98 | */ |
||
99 | public function updateRankingForTournament(RankingSystemInterface $ranking, TournamentInterface $tournament, |
||
100 | ?\DateTime $oldInfluence) |
||
101 | { |
||
102 | $earliestInfluence = $this->getEarliestInfluence($ranking, $tournament); |
||
103 | if ($oldInfluence !== null && |
||
104 | ($earliestInfluence === null || $oldInfluence < $earliestInfluence)) { |
||
105 | $earliestInfluence = $oldInfluence; |
||
106 | } |
||
107 | if ($earliestInfluence !== null) { |
||
108 | $this->updateRankingFrom($ranking, $earliestInfluence); |
||
109 | } |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * @inheritDoc |
||
114 | */ |
||
115 | public function updateRankingFrom(RankingSystemInterface $ranking, \DateTime $from) |
||
116 | { |
||
117 | // can only be called once per ranking system!!! |
||
118 | if (array_key_exists($ranking->getId(), $this->updateRankingCalls)) { |
||
119 | throw new PreconditionFailedException(); |
||
120 | } |
||
121 | $this->updateRankingCalls[$ranking->getId()] = $ranking; |
||
122 | //find first reusable |
||
123 | /** @var RankingSystemListInterface[] $lists */ |
||
124 | $lists = array_values($ranking->getLists()->toArray()); |
||
125 | |||
126 | $current = null; |
||
127 | /** @var RankingSystemListInterface $lastReusable */ |
||
128 | $lastReusable = null; |
||
129 | $toUpdate = []; |
||
130 | |||
131 | foreach ($lists as $list) { |
||
132 | if ($list->isCurrent()) { |
||
133 | $current = $list; |
||
134 | } else if ($list->getLastEntryTime() >= $from) { |
||
135 | $toUpdate[] = $list; |
||
136 | } else if ($lastReusable === null || $list->getLastEntryTime() > $lastReusable->getLastEntryTime()) { |
||
137 | $lastReusable = $list; |
||
138 | } |
||
139 | } |
||
140 | |||
141 | if ($current !== null && $current->getLastEntryTime() < $from) { |
||
142 | $lastReusable = $current; |
||
143 | } |
||
144 | |||
145 | if ($lastReusable === null) { |
||
146 | $lastReusable = $this->objectCreatorService->createObjectFromInterface(RankingSystemListInterface::class); |
||
147 | } |
||
148 | |||
149 | usort($toUpdate, function (RankingSystemListInterface $list1, RankingSystemListInterface $list2) { |
||
150 | return $list1->getLastEntryTime() <=> $list2->getLastEntryTime(); |
||
151 | }); |
||
152 | |||
153 | |||
154 | $lastListTime = null; |
||
155 | foreach ($toUpdate as $list) { |
||
156 | $entities = $this->getNextEntities($ranking, $lastReusable, $list); |
||
157 | if ($lastListTime == null) { |
||
158 | if (count($entities) > 0) { |
||
159 | $lastListTime = max($lastReusable->getLastEntryTime(), $this->timeService->getTime($entities[0])); |
||
160 | } else { |
||
161 | $lastListTime = $lastReusable->getLastEntryTime(); |
||
162 | } |
||
163 | } |
||
164 | $this->recomputeBasedOn($list, $lastReusable, $entities, $lastListTime); |
||
165 | $lastReusable = $list; |
||
166 | $lastListTime = $lastReusable->getLastEntryTime(); |
||
167 | } |
||
168 | |||
169 | if ($current === null) { |
||
170 | /** @var RankingSystemListInterface $current */ |
||
171 | $current = $this->objectCreatorService->createObjectFromInterface(RankingSystemListInterface::class); |
||
172 | $current->setCurrent(true); |
||
173 | $this->entityManager->persist($current); |
||
174 | $current->setRankingSystem($ranking); |
||
175 | } |
||
176 | |||
177 | $entities = $this->getNextEntities($ranking, $lastReusable, $current); |
||
178 | if ($lastListTime == null) { |
||
179 | if (count($entities) > 0) { |
||
180 | $lastListTime = max($lastReusable->getLastEntryTime(), $this->timeService->getTime($entities[0])); |
||
181 | } else { |
||
182 | $lastListTime = $lastReusable->getLastEntryTime(); |
||
183 | } |
||
184 | } |
||
185 | $this->recomputeBasedOn($current, $lastReusable, $entities, $lastListTime); |
||
186 | $this->deleteOldChanges(); |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * @param RankingSystemInterface $ranking |
||
191 | * @param RankingSystemListInterface $lastReusable |
||
192 | * @param RankingSystemListInterface $list |
||
193 | * @return array|TournamentHierarchyEntity[] |
||
194 | */ |
||
195 | private function getNextEntities(RankingSystemInterface $ranking, RankingSystemListInterface $lastReusable, |
||
196 | RankingSystemListInterface $list) |
||
197 | { |
||
198 | $this->deleteOldChanges(); |
||
199 | $entities = $this->getEntities($ranking, $lastReusable->getLastEntryTime(), |
||
200 | $list->isCurrent() ? $this->getMaxDate() : $list->getLastEntryTime()); |
||
201 | |||
202 | //sort entities |
||
203 | $this->timeService->clearTimes(); |
||
204 | usort($entities, function ($entity1, $entity2) { |
||
205 | return $this->entityComparer->compareEntities($entity1, $entity2); |
||
206 | }); |
||
207 | |||
208 | $this->markOldChangesAsDeleted($ranking, $entities); |
||
209 | |||
210 | return $entities; |
||
211 | } |
||
212 | //</editor-fold desc="Public Methods"> |
||
213 | //<editor-fold desc="Protected Final Methods"> |
||
214 | /** |
||
215 | * Computes the average rating of the given entries |
||
216 | * @param RankingSystemListEntryInterface[] $entries |
||
217 | * @return float |
||
218 | */ |
||
219 | protected final function getAverage(array $entries): float |
||
220 | { |
||
221 | $sum = 0.0; |
||
222 | foreach ($entries as $entry) { |
||
223 | $sum += $entry->getPoints(); |
||
224 | } |
||
225 | if (count($entries) === 0) { |
||
226 | return 0.0; |
||
227 | } else { |
||
228 | return $sum / count($entries); |
||
229 | } |
||
230 | } |
||
231 | |||
232 | /** |
||
233 | * Gets the relevant entities for updating |
||
234 | * @param RankingSystemInterface $ranking the ranking for which to get the entities |
||
235 | * @param \DateTime $from search for entities with a time value LARGER than $from, i.e. don't search for entities with |
||
236 | * time value exactly $from |
||
237 | * @param \DateTime to search for entities with a time value SMALLER OR EQUAL than $to |
||
238 | * @return TournamentHierarchyEntity[] |
||
239 | */ |
||
240 | protected final function getEntities(RankingSystemInterface $ranking, \DateTime $from, \DateTime $to): array |
||
241 | { |
||
242 | $query = $this->getEntitiesQueryBuilder($ranking, $from, $to); |
||
243 | return $query->getQuery()->getResult(); |
||
244 | } |
||
245 | |||
246 | /** |
||
247 | * @return EntityManagerInterface |
||
248 | */ |
||
249 | protected final function getEntityManager(): EntityManagerInterface |
||
250 | { |
||
251 | return $this->entityManager; |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * @param Collection|PlayerInterface[] $players |
||
256 | * @param RankingSystemListInterface $list |
||
257 | * @return RankingSystemListEntryInterface[] $entries |
||
258 | */ |
||
259 | protected final function getEntriesOfPlayers(Collection $players, RankingSystemListInterface $list): array |
||
260 | { |
||
261 | $result = []; |
||
262 | foreach ($players as $player) { |
||
263 | $result[] = $this->getOrCreateRankingSystemListEntry($list, $player); |
||
264 | } |
||
265 | return $result; |
||
266 | } |
||
267 | |||
268 | /** @noinspection PhpDocMissingThrowsInspection */ //PropertyNotExistingException |
||
269 | /** |
||
270 | * Gets or creates a tournament system change entry for the given entity, ranking and player. |
||
271 | * @param TournamentHierarchyInterface $entity the tournament hierarchy entity to search for |
||
272 | * @param RankingSystemInterface $ranking the ranking system to search for |
||
273 | * @param PlayerInterface $player the player to search for |
||
274 | * @return RankingSystemChangeInterface the found or newly created ranking system change |
||
275 | */ |
||
276 | protected final function getOrCreateChange(TournamentHierarchyInterface $entity, RankingSystemInterface $ranking, |
||
277 | PlayerInterface $player) |
||
278 | { |
||
279 | $key1 = $entity->getId(); |
||
280 | $key2 = $player->getId(); |
||
281 | if (!array_key_exists($key1, $this->changes)) { |
||
282 | $this->changes[$key1] = []; |
||
283 | } |
||
284 | if (array_key_exists($key1, $this->oldChanges) && array_key_exists($key2, $this->oldChanges[$key1])) { |
||
285 | $this->changes[$key1][$key2] = $this->oldChanges[$key1][$key2]; |
||
286 | unset($this->oldChanges[$key1][$key2]); |
||
287 | } |
||
288 | if (!array_key_exists($key2, $this->changes[$key1])) { |
||
289 | //create new change |
||
290 | /** @var RankingSystemChangeInterface $change */ |
||
291 | $change = $this->objectCreatorService->createObjectFromInterface(RankingSystemChangeInterface::class, |
||
292 | [array_merge(array_keys($this->getAdditionalFields()), $this->getAdditionalChangeFields())]); |
||
293 | foreach ($this->getAdditionalFields() as $field => $value) { |
||
294 | // PropertyNotExistingException => we know for sure that the property exists (see 2 lines above) |
||
295 | /** @noinspection PhpUnhandledExceptionInspection */ |
||
296 | $change->setProperty($field, 0); |
||
297 | } |
||
298 | $change->setHierarchyEntity($entity); |
||
299 | $change->setRankingSystem($ranking); |
||
300 | $change->setPlayer($player); |
||
301 | $this->entityManager->persist($change); |
||
302 | $this->changes[$key1][$key2] = $change; |
||
303 | } |
||
304 | return $this->changes[$key1][$key2]; |
||
305 | } |
||
306 | |||
307 | /** @noinspection PhpDocMissingThrowsInspection */ //PropertyNotExistingException |
||
308 | /** |
||
309 | * @param RankingSystemListInterface $list the list in which to search for the entry or in which to add it |
||
310 | * @param PlayerInterface $player the player to search for |
||
311 | * @return RankingSystemListEntryInterface the found or the new entry |
||
312 | */ |
||
313 | protected final function getOrCreateRankingSystemListEntry(RankingSystemListInterface $list, |
||
314 | PlayerInterface $player): RankingSystemListEntryInterface |
||
315 | { |
||
316 | $playerId = $player->getId(); |
||
317 | if (!$list->getEntries()->containsKey($playerId)) { |
||
318 | /** @var RankingSystemListEntryInterface $entry */ |
||
319 | $entry = $this->objectCreatorService->createObjectFromInterface(RankingSystemListEntryInterface::class, |
||
320 | [array_keys($this->getAdditionalFields())]); |
||
321 | $entry->setPlayer($player); |
||
322 | $entry->setRankingSystemList($list); |
||
323 | $this->resetListEntry($entry); |
||
324 | $this->entityManager->persist($entry); |
||
325 | } |
||
326 | return $list->getEntries()->get($playerId); |
||
327 | } |
||
328 | |||
329 | /** |
||
330 | * @param RankingSystemListEntryInterface $entry |
||
331 | * @throws \Tfboe\FmLib\Exceptions\PropertyNotExistingException |
||
332 | */ |
||
333 | private function resetListEntry(RankingSystemListEntryInterface $entry) |
||
334 | { |
||
335 | $entry->setPoints($this->startPoints()); |
||
336 | $entry->setNumberRankedEntities(0); |
||
337 | foreach ($this->getAdditionalFields() as $field => $value) { |
||
338 | // PropertyNotExistingException => we know for sure that the property exists (see 2 lines above) |
||
339 | /** @noinspection PhpUnhandledExceptionInspection */ |
||
340 | $entry->setProperty($field, $value); |
||
341 | } |
||
342 | } |
||
343 | //</editor-fold desc="Protected Final Methods"> |
||
344 | |||
345 | //<editor-fold desc="Protected Methods"> |
||
346 | /** |
||
347 | * Gets additional fields for this ranking type mapped to its start value |
||
348 | * @return string[] list of additional fields |
||
349 | */ |
||
350 | protected abstract function getAdditionalFields(): array; |
||
351 | |||
352 | /** |
||
353 | * Gets additional fields for this ranking type mapped to its start value |
||
354 | * @return string[] list of additional fields |
||
355 | */ |
||
356 | protected function getAdditionalChangeFields(): array |
||
357 | { |
||
358 | return []; |
||
359 | } |
||
360 | |||
361 | /** |
||
362 | * Gets all ranking changes for the given entity for the given list. Must return a change for each involved player. |
||
363 | * The field pointsAfterwards get calculated afterwards and can be left empty. |
||
364 | * @param TournamentHierarchyEntity $entity the entity for which to compute the ranking changes |
||
365 | * @param RankingSystemListInterface $list the list for which to compute the ranking changes |
||
366 | * @return RankingSystemChangeInterface[] the changes |
||
367 | */ |
||
368 | protected abstract function getChanges(TournamentHierarchyEntity $entity, RankingSystemListInterface $list): array; |
||
369 | |||
370 | /** |
||
371 | * Gets a query for getting the relevant entities for updating |
||
372 | * @param RankingSystemInterface $ranking the ranking for which to get the entities |
||
373 | * @param \DateTime $from search for entities with a time value LARGER than $from, i.e. don't search for entities with |
||
374 | * time value exactly $from |
||
375 | * @param \DateTime to search for entities with a time value SMALLER OR EQUAL than $to |
||
376 | * @return QueryBuilder |
||
377 | */ |
||
378 | protected abstract function getEntitiesQueryBuilder(RankingSystemInterface $ranking, |
||
379 | \DateTime $from, \DateTime $to): QueryBuilder; |
||
380 | |||
381 | /** |
||
382 | * Gets the level of the ranking system service (see Level Enum) |
||
383 | * @return int |
||
384 | */ |
||
385 | protected abstract function getLevel(): int; |
||
386 | |||
387 | /** |
||
388 | * Gets the start points for a new player in the ranking |
||
389 | * @return float |
||
390 | */ |
||
391 | protected function startPoints(): float |
||
392 | { |
||
393 | return 0.0; |
||
394 | } |
||
395 | //</editor-fold desc="Protected Methods"> |
||
396 | |||
397 | //<editor-fold desc="Private Methods"> |
||
398 | /** |
||
399 | * Clones all ranking values from base and inserts them into list, furthermore removes all remaining ranking values of |
||
400 | * list. After this method was called list and base contain exactly the same rankings. |
||
401 | * @param RankingSystemListInterface $list the ranking list to change |
||
402 | * @param RankingSystemListInterface $base the ranking list to use as base list, this doesn't get changed |
||
403 | */ |
||
404 | private function cloneInto(RankingSystemListInterface $list, RankingSystemListInterface $base) |
||
405 | { |
||
406 | /*//first remove all entries from list |
||
407 | foreach($list->getEntries()->toArray() as $entry) |
||
408 | { |
||
409 | $list->getEntries()->removeElement($entry); |
||
410 | $this->entityManager->remove($entry); |
||
411 | }*/ |
||
412 | |||
413 | $clonedPlayers = []; |
||
414 | |||
415 | foreach ($base->getEntries() as $entry) { |
||
416 | $playerId = $entry->getPlayer()->getId(); |
||
417 | $clonedPlayers[$playerId] = true; |
||
418 | if (!$list->getEntries()->containsKey($playerId)) { |
||
419 | //create new entry |
||
420 | /** @var RankingSystemListEntryInterface $entry */ |
||
421 | $clone = $this->objectCreatorService->createObjectFromInterface(RankingSystemListEntryInterface::class, |
||
422 | [[]]); |
||
423 | $this->entityManager->persist($clone); |
||
424 | $clone->setPlayer($entry->getPlayer()); |
||
425 | $clone->setRankingSystemList($list); |
||
426 | } |
||
427 | $foundEntry = $list->getEntries()[$playerId]; |
||
428 | $foundEntry->setNumberRankedEntities($entry->getNumberRankedEntities()); |
||
429 | $foundEntry->setPoints($entry->getPoints()); |
||
430 | $foundEntry->cloneSubClassDataFrom($entry); |
||
431 | } |
||
432 | |||
433 | //remove all unused entries from list |
||
434 | foreach ($list->getEntries()->toArray() as $playerId => $entry) { |
||
435 | if (!array_key_exists($playerId, $clonedPlayers)) { |
||
436 | $this->resetListEntry($entry); |
||
437 | //$list->getEntries()->removeElement($entry); |
||
438 | //$this->entityManager->remove($entry); |
||
439 | } |
||
440 | } |
||
441 | } |
||
442 | |||
443 | |||
444 | /** |
||
445 | * @param RankingSystemInterface $ranking |
||
446 | * @param TournamentHierarchyEntity[] $entities |
||
447 | */ |
||
448 | private function markOldChangesAsDeleted(RankingSystemInterface $ranking, array $entities) |
||
449 | { |
||
450 | assert(count($this->oldChanges) == 0); |
||
451 | $this->changes = []; |
||
452 | $queryBuilder = $this->entityManager->createQueryBuilder(); |
||
453 | /** @var RankingSystemChangeInterface[] $changes */ |
||
454 | $changes = $queryBuilder |
||
455 | ->from(RankingSystemChangeInterface::class, 'c') |
||
456 | ->select('c') |
||
457 | ->where($queryBuilder->expr()->eq('c.rankingSystem', ':ranking')) |
||
458 | ->setParameter('ranking', $ranking) |
||
459 | ->andWhere($queryBuilder->expr()->in('c.hierarchyEntity', ':entities')) |
||
460 | ->setParameter('entities', $entities) |
||
461 | ->getQuery()->getResult(); |
||
462 | foreach ($changes as $change) { |
||
463 | $eId = $change->getHierarchyEntity()->getId(); |
||
464 | $pId = $change->getPlayer()->getId(); |
||
465 | if (array_key_exists($eId, $this->oldChanges) && array_key_exists($pId, $this->oldChanges[$eId])) { |
||
466 | //duplicate entry |
||
467 | assert($this->oldChanges[$eId][$pId]->getRankingSystem()->getId() === |
||
468 | $change->getRankingSystem()->getId()); |
||
469 | $this->entityManager->remove($change); |
||
470 | } else { |
||
471 | $this->oldChanges[$eId][$pId] = $change; |
||
472 | } |
||
473 | } |
||
474 | } |
||
475 | |||
476 | private function deleteOldChanges() |
||
477 | { |
||
478 | foreach ($this->oldChanges as $eId => $changes) { |
||
479 | foreach ($changes as $pId => $change) { |
||
480 | $this->entityManager->remove($change); |
||
481 | } |
||
482 | } |
||
483 | $this->oldChanges = []; |
||
484 | } |
||
485 | |||
486 | /** |
||
487 | * Gets the earliest influence for the given entity |
||
488 | * @param RankingSystemInterface $ranking the ranking system for which to get the influence |
||
489 | * @param TournamentHierarchyInterface $entity the entity to analyze |
||
490 | * @param bool $parentIsRanked true iff a predecessor contained the given ranking in its ranking systems |
||
491 | * @return \DateTime|null the earliest influence or null if $parentIsRanked is false and the entity and all its |
||
492 | * successors do not have the ranking in its ranking systems |
||
493 | */ |
||
494 | private function getEarliestEntityInfluence(RankingSystemInterface $ranking, TournamentHierarchyInterface $entity, |
||
495 | bool $parentIsRanked): ?\DateTime |
||
496 | { |
||
497 | $this->timeService->clearTimes(); |
||
498 | $entityIsRanked = $parentIsRanked || $entity->getRankingSystems()->containsKey($ranking->getId()); |
||
499 | if ($entity->getLevel() === $this->getLevel()) { |
||
500 | if ($entityIsRanked) { |
||
501 | return $this->timeService->getTime($entity); |
||
502 | } else { |
||
503 | return null; |
||
504 | } |
||
505 | } |
||
506 | $result = null; |
||
507 | |||
508 | foreach ($entity->getChildren() as $child) { |
||
509 | $earliest = $this->getEarliestEntityInfluence($ranking, $child, $entityIsRanked); |
||
510 | if ($result === null || ($earliest !== null && $earliest < $result)) { |
||
511 | $result = $earliest; |
||
512 | } |
||
513 | } |
||
514 | return $result; |
||
515 | } |
||
516 | |||
517 | /** |
||
518 | * @param \DateTime $time the time of the last list |
||
519 | * @param int $generationLevel the list generation level |
||
520 | * @return \DateTime the time of the next list generation |
||
521 | */ |
||
522 | private function getNextGenerationTime(\DateTime $time, int $generationLevel): \DateTime |
||
523 | { |
||
524 | $year = (int)$time->format('Y'); |
||
525 | $month = (int)$time->format('m'); |
||
526 | if ($generationLevel === AutomaticInstanceGeneration::MONTHLY) { |
||
527 | $month += 1; |
||
528 | if ($month == 13) { |
||
529 | $month = 1; |
||
530 | $year += 1; |
||
531 | } |
||
532 | } else if ($generationLevel === AutomaticInstanceGeneration::OFF) { |
||
533 | return $this->getMaxDate(); |
||
534 | } else { |
||
535 | $year += 1; |
||
536 | } |
||
537 | return (new \DateTime())->setDate($year, $month, 1)->setTime(0, 0, 0); |
||
538 | } |
||
539 | |||
540 | /** |
||
541 | * @return \DateTime |
||
542 | * @throws \Exception |
||
543 | */ |
||
544 | private function getMaxDate(): \DateTime |
||
545 | { |
||
546 | return (new \DateTime())->add(new \DateInterval('P100Y')); |
||
547 | } |
||
548 | |||
549 | /** @noinspection PhpDocMissingThrowsInspection */ //PropertyNotExistingException |
||
550 | /** |
||
551 | * Recomputes the given ranking list by using base as base list and applying the changes for the given entities |
||
552 | * starting from the given index. If list is not the current list only the entities up to $list->getLastEntryTime() |
||
553 | * are applied and the index gets changed accordingly. |
||
554 | * @param RankingSystemListInterface $list the list to recompute |
||
555 | * @param RankingSystemListInterface $base the list to use as base |
||
556 | * @param TournamentHierarchyEntity[] $entities the list of entities to use for the computation |
||
557 | * @param \DateTime $lastListTime the time of the last list or the first entry |
||
558 | */ |
||
559 | private function recomputeBasedOn(RankingSystemListInterface $list, RankingSystemListInterface $base, |
||
560 | array &$entities, \DateTime $lastListTime) |
||
561 | { |
||
562 | $nextGeneration = $this->getNextGenerationTime($lastListTime, $list->getRankingSystem()->getGenerationInterval()); |
||
563 | $this->cloneInto($list, $base); |
||
564 | for ($i = 0; $i < count($entities); $i++) { |
||
0 ignored issues
–
show
It seems like you are calling the size function
count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||
565 | $time = $this->timeService->getTime($entities[$i]); |
||
566 | if (!$list->isCurrent() && $time > $list->getLastEntryTime()) { |
||
567 | $this->flushAndForgetEntities($entities, $i); |
||
568 | return; |
||
569 | } |
||
570 | if ($nextGeneration < $time) { |
||
571 | /** @var RankingSystemListInterface $newList */ |
||
572 | $newList = $this->objectCreatorService->createObjectFromInterface(RankingSystemListInterface::class); |
||
573 | $newList->setCurrent(false); |
||
574 | $newList->setLastEntryTime($nextGeneration); |
||
575 | $this->entityManager->persist($newList); |
||
576 | $newList->setRankingSystem($list->getRankingSystem()); |
||
577 | $this->cloneInto($newList, $list); |
||
578 | $nextGeneration = $this->getNextGenerationTime($nextGeneration, |
||
579 | $list->getRankingSystem()->getGenerationInterval()); |
||
580 | //clear entityManager to save memory |
||
581 | $this->flushAndForgetEntities($entities, $i); |
||
582 | } |
||
583 | $changes = $this->getChanges($entities[$i], $list); |
||
0 ignored issues
–
show
$entities[$i] of type object<Tfboe\FmLib\Entit...mentHierarchyInterface> is not a sub-type of object<Tfboe\FmLib\Entit...rnamentHierarchyEntity> . It seems like you assume a concrete implementation of the interface Tfboe\FmLib\Entity\Helpe...amentHierarchyInterface to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type. ![]() |
|||
584 | foreach ($changes as $change) { |
||
585 | $entry = $this->getOrCreateRankingSystemListEntry($list, $change->getPlayer()); |
||
586 | $entry->setNumberRankedEntities($entry->getNumberRankedEntities() + 1); |
||
587 | $pointsAfterwards = $entry->getPoints() + $change->getPointsChange(); |
||
588 | $entry->setPoints($pointsAfterwards); |
||
589 | $change->setPointsAfterwards($pointsAfterwards); |
||
590 | //apply further changes |
||
591 | foreach ($this->getAdditionalFields() as $field => $value) { |
||
592 | // PropertyNotExistingException => entry and field have exactly the static properties from getAdditionalFields |
||
593 | /** @noinspection PhpUnhandledExceptionInspection */ |
||
594 | $entry->setProperty($field, $entry->getProperty($field) + $change->getProperty($field)); |
||
595 | } |
||
596 | if ($time > $list->getLastEntryTime()) { |
||
597 | $list->setLastEntryTime($time); |
||
598 | } |
||
599 | $this->entityManager->persist($change); |
||
600 | } |
||
601 | } |
||
602 | $current = count($entities); |
||
603 | $this->flushAndForgetEntities($entities, $current); |
||
604 | } |
||
605 | |||
606 | /** |
||
607 | * @param TournamentHierarchyInterface[] $entities |
||
608 | * @param int &$current |
||
609 | */ |
||
610 | private function flushAndForgetEntities(&$entities, &$current) |
||
611 | { |
||
612 | for ($i = 0; $i < $current; $i++) { |
||
613 | $eId = $entities[$i]->getId(); |
||
614 | if (array_key_exists($eId, $this->oldChanges)) { |
||
615 | foreach ($this->oldChanges[$eId] as $pId => $change) { |
||
616 | $this->entityManager->remove($change); |
||
617 | } |
||
618 | } |
||
619 | unset($this->oldChanges[$eId]); |
||
620 | } |
||
621 | $this->entityManager->flush(); |
||
622 | for ($i = 0; $i < $current; $i++) { |
||
623 | $eId = $entities[$i]->getId(); |
||
624 | $this->entityManager->detach($entities[$i]); |
||
0 ignored issues
–
show
The method
Doctrine\Common\Persiste...ObjectManager::detach() has been deprecated with message: Detach operation is deprecated and will be removed in Persistence 2.0. Please use {@see ObjectManager::clear()} instead.
This method has been deprecated. The supplier of the class has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead. ![]() |
|||
625 | if (array_key_exists($eId, $this->changes)) { |
||
626 | foreach ($this->changes[$eId] as $pId => $change) { |
||
627 | $this->entityManager->detach($change); |
||
0 ignored issues
–
show
The method
Doctrine\Common\Persiste...ObjectManager::detach() has been deprecated with message: Detach operation is deprecated and will be removed in Persistence 2.0. Please use {@see ObjectManager::clear()} instead.
This method has been deprecated. The supplier of the class has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead. ![]() |
|||
628 | } |
||
629 | unset($this->changes[$eId]); |
||
630 | } |
||
631 | } |
||
632 | if ($current >= count($entities)) { |
||
633 | $entities = []; |
||
634 | } else { |
||
635 | array_splice($entities, 0, $current); |
||
636 | } |
||
637 | $current = 0; |
||
638 | } |
||
639 | //</editor-fold desc="Private Methods"> |
||
640 | } |
Let’s assume that you have a directory layout like this:
and let’s assume the following content of
Bar.php
:If both files
OtherDir/Foo.php
andSomeDir/Foo.php
are loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php
However, as
OtherDir/Foo.php
does not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php
, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: