Completed
Push — master ( 8f238b...4d49a8 )
by Konstantinos
03:51
created

Match::involvesTeam()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6
Metric Value
dl 0
loc 3
ccs 0
cts 0
cp 0
rs 10
cc 2
eloc 2
nc 2
nop 1
crap 6
1
<?php
2
/**
3
 * This file contains functionality relating to the official matches played in the league
4
 *
5
 * @package    BZiON\Models
6
 * @license    https://github.com/allejo/bzion/blob/master/LICENSE.md GNU General Public License Version 3
7
 */
8
9
/**
10
 * A match played between two teams
11
 * @package    BZiON\Models
12
 */
13
class Match extends UrlModel implements NamedModel
14
{
15
    /**
16
     * The ID of the first team of the match
17
     * @var int
18
     */
19
    protected $team_a;
20
21
    /**
22
     * The ID of the second team of the match
23
     * @var int
24
     */
25
    protected $team_b;
26
27
    /**
28
     * The match points (usually the number of flag captures) Team A scored
29
     * @var int
30
     */
31
    protected $team_a_points;
32
33
    /**
34
     * The match points Team B scored
35
     * @var int
36
     */
37
    protected $team_b_points;
38
39
    /**
40
     * The BZIDs of players part of Team A who participated in the match, separated by commas
41
     * @var string
42
     */
43
    protected $team_a_players;
44
45
    /**
46
     * The BZIDs of players part of Team B who participated in the match, separated by commas
47
     * @var string
48
     */
49
    protected $team_b_players;
50
51
    /**
52
     * The ELO score of Team A after the match
53
     * @var int
54
     */
55
    protected $team_a_elo_new;
56
57
    /**
58
     * The ELO score of Team B after the match
59
     * @var int
60
     */
61
    protected $team_b_elo_new;
62
63
    /**
64
     * The map ID used in the match if the league supports more than one map
65
     * @var int
66
     */
67
    protected $map;
68
69
    /**
70
     * A JSON string of events that happened during a match, such as captures and substitutions
71
     * @var string
72
     */
73
    protected $match_details;
74
75
    /**
76
     * The port of the server where the match took place
77
     * @var int
78
     */
79
    protected $port;
80
81
    /**
82
     * The server location of there the match took place
83
     * @var string
84
     */
85
    protected $server;
86
87
    /**
88
     * The file name of the replay file of the match
89
     * @var string
90
     */
91
    protected $replay_file;
92
93
    /**
94
     * The absolute value of the ELO score difference
95
     * @var int
96
     */
97
    protected $elo_diff;
98
99
    /**
100
     * The timestamp representing when the match was played
101
     * @var TimeDate
102
     */
103
    protected $timestamp;
104
105
    /**
106
     * The timestamp representing when the match information was last updated
107
     * @var TimeDate
108
     */
109
    protected $updated;
110
111
    /**
112
     * The duration of the match in minutes
113
     * @var int
114
     */
115
    protected $duration;
116
117
    /**
118
     * The ID of the person (i.e. referee) who last updated the match information
119
     * @var string
120
     */
121
    protected $entered_by;
122
123
    /**
124
     * The status of the match. Can be 'entered', 'disabled', 'deleted' or 'reported'
125
     * @var string
126
     */
127
    protected $status;
128
129
    /**
130
     * The name of the database table used for queries
131
     */
132
    const TABLE = "matches";
133
134
    const CREATE_PERMISSION = Permission::ENTER_MATCH;
135
    const EDIT_PERMISSION = Permission::EDIT_MATCH;
136
    const SOFT_DELETE_PERMISSION = Permission::SOFT_DELETE_MATCH;
137
    const HARD_DELETE_PERMISSION = Permission::HARD_DELETE_MATCH;
138
139
    /**
140
     * {@inheritdoc}
141
     */
142 9
    protected function assignResult($match)
143
    {
144 9
        $this->team_a = $match['team_a'];
145 9
        $this->team_b = $match['team_b'];
146 9
        $this->team_a_points = $match['team_a_points'];
147 9
        $this->team_b_points = $match['team_b_points'];
148 9
        $this->team_a_players = $match['team_a_players'];
149 9
        $this->team_b_players = $match['team_b_players'];
150 9
        $this->team_a_elo_new = $match['team_a_elo_new'];
151 9
        $this->team_b_elo_new = $match['team_b_elo_new'];
152 9
        $this->map = $match['map'];
153 9
        $this->match_details = $match['match_details'];
154 9
        $this->port = $match['port'];
155 9
        $this->server = $match['server'];
156 9
        $this->replay_file = $match['replay_file'];
157 9
        $this->elo_diff = $match['elo_diff'];
158 9
        $this->timestamp = TimeDate::fromMysql($match['timestamp']);
159 9
        $this->updated = TimeDate::fromMysql($match['updated']);
160 9
        $this->duration = $match['duration'];
161 9
        $this->entered_by = $match['entered_by'];
162 9
        $this->status = $match['status'];
163 9
    }
164
165
    /**
166
     * Get the name of the route that shows the object
167
     * @param  string $action The route's suffix
168
     * @return string
169
     */
170 1
    public static function getRouteName($action = 'show')
171
    {
172 1
        return "match_$action";
173
    }
174
175
    /**
176
     * Get a one word description of a match relative to a team (i.e. win, loss, or draw)
177
     *
178
     * @param int $teamID The team ID we want the noun for
179
     *
180
     * @return string Either "win", "loss", or "draw" relative to the team
181
     */
182 1
    public function getMatchDescription($teamID)
183
    {
184 1
        if ($this->getScore($teamID) > $this->getOpponentScore($teamID)) {
185 1
            return "win";
186 1
        } elseif ($this->getScore($teamID) < $this->getOpponentScore($teamID)) {
187 1
            return "loss";
188
        }
189
190 1
        return "tie";
191
    }
192
193
    /**
194
     * Get a one letter description of a match relative to a team (i.e. W, L, or T)
195
     *
196
     * @param int $teamID The team ID we want the noun for
197
     *
198
     * @return string Either "W", "L", or "T" relative to the team
199
     */
200 1
    public function getMatchLetter($teamID)
201
    {
202 1
        return strtoupper(substr($this->getMatchDescription($teamID), 0, 1));
203
    }
204
205
    /**
206
     * Get the score of a specific team
207
     *
208
     * @param int|Team $teamID The team we want the score for
209
     *
210
     * @return int The score that team received
211
     */
212 2 View Code Duplication
    public function getScore($teamID)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
213
    {
214 2
        if ($teamID instanceof Team) {
215
            // Oh no! The caller gave us a Team model instead of an ID!
216 1
            $teamID = $teamID->getId();
217
        }
218
219 2
        if ($this->getTeamA()->getId() == $teamID) {
220 2
            return $this->getTeamAPoints();
221
        }
222
223 1
        return $this->getTeamBPoints();
224
    }
225
226
    /**
227
     * Get the score of the opponent relative to a team
228
     *
229
     * @param int $teamID The opponent of the team we want the score for
230
     *
231
     * @return int The score of the opponent
232
     */
233 2 View Code Duplication
    public function getOpponentScore($teamID)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
234
    {
235 2
        if ($teamID instanceof Team) {
236
            $teamID = $teamID->getId();
237
        }
238
239 2
        if ($this->getTeamA()->getId() != $teamID) {
240 1
            return $this->getTeamAPoints();
241
        }
242
243 2
        return $this->getTeamBPoints();
244
    }
245
246
    /**
247
     * Get the opponent of a match relative to a team ID
248
     *
249
     * @param int $teamID The team who is known in a match
250
     *
251
     * @return Team The opponent team
252
     */
253 8
    public function getOpponent($teamID)
254
    {
255 8
        if ($this->getTeamA()->getId() == $teamID) {
256 6
            return $this->getTeamB();
257
        }
258
259 4
        return $this->getTeamA();
260
    }
261
262
    /**
263
     * Get the timestamp of the match
264
     *
265
     * @return TimeDate The match's timestamp
266
     */
267 2
    public function getTimestamp()
268
    {
269 2
        return $this->timestamp;
270
    }
271
272
    /**
273
     * Get the timestamp of the last update of the match
274
     *
275
     * @return TimeDate The match's update timestamp
276
     */
277
    public function getUpdated()
278
    {
279
        return $this->updated;
280
    }
281
282
    /**
283
     * Set the timestamp of the match
284
     *
285
     * @param  mixed The match's new timestamp
286
     * @return $this
287
     */
288
    public function setTimestamp($timestamp)
289
    {
290 9
        $this->timestamp = TimeDate::from($timestamp);
291
        $this->update("timestamp", $this->timestamp->toMysql(), "s");
292 9
293
        return $this;
294
    }
295
296
    /**
297
     * Get the first team involved in the match
298
     * @return Team Team A's id
299 9
     */
300
    public function getTeamA()
301 9
    {
302
        return Team::get($this->team_a);
303
    }
304
305
    /**
306
     * Get the second team involved in the match
307
     * @return Team Team B's id
308
     */
309
    public function getTeamB()
310
    {
311
        return Team::get($this->team_b);
312
    }
313
314
    /**
315
     * Get the list of players on Team A who participated in this match
316
     * @return Player[]|null Returns null if there were no players recorded for this match
317
     */
318
    public function getTeamAPlayers()
319
    {
320
        return $this->parsePlayers($this->team_a_players);
321
    }
322
323
    /**
324
     * Get the list of players on Team B who participated in this match
325
     * @return Player[]|null Returns null if there were no players recorded for this match
326
     */
327
    public function getTeamBPlayers()
328
    {
329
        return $this->parsePlayers($this->team_b_players);
330
    }
331
332
    /**
333
     * Get the list of players for a team in a match
334
     * @param  Team|int The team or team ID
335
     * @return Player[]|null Returns null if there were no players recorded for this match
336
     */
337
    public function getPlayers($team)
338
    {
339
        if ($team instanceof Team) {
340
            $team = $team->getId();
341
        }
342
343
        if ($team == $this->team_a) {
344
            return $this->getTeamAPlayers();
345
        } elseif ($team == $this->team_b) {
346
            return $this->getTeamBPlayers();
347
        }
348
349
        return null;
350
    }
351
352
    /**
353
     * Set the players of the match's teams
354
     *
355 4
     * @param int[] $teamAPlayers An array of player IDs
356
     * @param int[] $teamBPlayers An array of player IDs
357 4
     * @return self
358
     */
359
    public function setTeamPlayers($teamAPlayers = array(), $teamBPlayers = array())
360
    {
361
        $this->updateProperty($this->team_a_players, "team_a_players", implode(',', $teamAPlayers), "s");
362
        $this->updateProperty($this->team_b_players, "team_b_players", implode(',', $teamBPlayers), "s");
363
364 4
        return $this;
365
    }
366 4
367
    /**
368
     * Get an array of players based on a string representation
369
     * @param string $playerString
370
     * @return Player[]|null Returns null if there were no players recorded for this match
371
     */
372
    private static function parsePlayers($playerString)
373
    {
374
        if ($playerString == null) {
375
            return null;
376
        }
377
378
        return Player::arrayIdToModel(explode(",", $playerString));
379
    }
380
381
    /**
382
     * Get the first team's points
383
     * @return int Team A's points
384
     */
385
    public function getTeamAPoints()
386
    {
387
        return $this->team_a_points;
388 7
    }
389
390 7
    /**
391
     * Get the second team's points
392
     * @return int Team B's points
393
     */
394
    public function getTeamBPoints()
395
    {
396
        return $this->team_b_points;
397 6
    }
398
399 6
    /**
400
     * Set the match team points
401
     *
402
     * @param  int $teamAPoints Team A's points
403
     * @param  int $teamBPoints Team B's points
404
     * @return self
405
     */
406 6
    public function setTeamPoints($teamAPoints, $teamBPoints)
407
    {
408 6
        $this->updateProperty($this->team_a_points, "team_a_points", $teamAPoints, "i");
409
        $this->updateProperty($this->team_b_points, "team_b_points", $teamBPoints, "i");
410
411
        return $this;
412
    }
413
414
    /**
415 5
     * Get the ELO difference applied to each team's old ELO
416
     * @return int The ELO difference
417 5
     */
418
    public function getEloDiff()
419
    {
420
        return abs($this->elo_diff);
421
    }
422
423
    /**
424 5
     * Get the first team's new ELO
425
     * @return int Team A's new ELO
426 5
     */
427
    public function getTeamAEloNew()
428
    {
429
        return $this->team_a_elo_new;
430
    }
431
432
    /**
433 1
     * Get the second team's new ELO
434
     * @return int Team B's new ELO
435 1
     */
436
    public function getTeamBEloNew()
437
    {
438
        return $this->team_b_elo_new;
439
    }
440
441
    /**
442
     * Get the first team's old ELO
443
     * @return int
444
     */
445
    public function getTeamAEloOld()
446
    {
447
        return $this->team_a_elo_new - $this->elo_diff;
448
    }
449
450
    /**
451
     * Get the second team's old ELO
452
     * @return int
453
     */
454
    public function getTeamBEloOld()
455
    {
456
        return $this->team_b_elo_new + $this->elo_diff;
457
    }
458
459
    /**
460
     * Get the map where the match was played on
461
     * @return Map Returns an invalid map if no map was found
462
     */
463
    public function getMap()
464
    {
465
        return Map::get($this->map);
466
    }
467
468
    /**
469
     * Set the map where the match was played
470
     * @param  int $map The ID of the map
471
     * @return self
472
     */
473
    public function setMap($map)
474
    {
475
        $this->updateProperty($this->map, "map", $map, "s");
476
    }
477
478
    /**
479
     * Get a JSON decoded array of events that occurred during the match
480
     * @return mixed|null Returns null if there were no events recorded for the match
481
     */
482
    public function getMatchDetails()
483
    {
484
        return json_decode($this->match_details);
485
    }
486
487
    /**
488
     * Get the server address of the server where this match took place
489
     * @return string|null Returns null if there was no server address recorded
490
     */
491
    public function getServerAddress()
492
    {
493
        if ($this->port == null || $this->server == null) {
494
            return null;
495
        }
496
497
        return $this->server . ":" . $this->port;
498
    }
499
500
    /**
501
     * Set the server address of the server where this match took place
502
     *
503 2
     * @param  string|null $server The server hostname
504
     * @param  int|null    $port   The server port
505 2
     * @return self
506
     */
507
    public function setServerAddress($server = null, $port = 5154)
508
    {
509
        $this->updateProperty($this->server, "server", $server, "s");
510
        $this->updateProperty($this->port, "port", $port, "i");
511
512
        return $this;
513
    }
514
515
    /**
516
     * Get the name of the replay file for this specific map
517
     * @param  int    $length The length of the replay file name; it will be truncated
518
     * @return string Returns null if there was no replay file name recorded
519
     */
520
    public function getReplayFileName($length = 0)
521
    {
522
        if ($length > 0) {
523 1
            return substr($this->replay_file, 0, $length);
524
        }
525 1
526
        return $this->replay_file;
527
    }
528
529
    /**
530
     * Get the match duration
531
     * @return int The duration of the match in minutes
532
     */
533 8
    public function getDuration()
534
    {
535
        return $this->duration;
536 8
    }
537
538
    /**
539 8
     * Set the match duration
540
     *
541
     * @param  int  $duration The new duration of the match in minutes
542
     * @return self
543
     */
544
    public function setDuration($duration)
545
    {
546
        return $this->updateProperty($this->duration, "duration", $duration, "i");
547 8
    }
548
549
    /**
550 8
     * Get the user who entered the match
551 6
     * @return Player
552 2
     */
553 2
    public function getEnteredBy()
554
    {
555
        return Player::get($this->entered_by);
556
    }
557
558
    /**
559
     * Get the loser of the match
560
     *
561
     * @return Team The team that was the loser or the team with the lower elo if the match was a draw
562
     */
563
    public function getLoser()
564 9
    {
565
        // Get the winner of the match
566 9
        $winner = $this->getWinner();
567
568
        // Get the team that wasn't the winner... Duh
569
        return $this->getOpponent($winner->getId());
570
    }
571
572
    /**
573
     * Get the winner of a match
574
     *
575
     * @return Team The team that was the victor or the team with the lower elo if the match was a draw
576
     */
577
    public function getWinner()
578
    {
579
        // Get the team that had its ELO increased
580
        if ($this->elo_diff > 0) {
581
            return $this->getTeamA();
582
        } elseif ($this->elo_diff < 0) {
583
            return $this->getTeamB();
584
        }
585 1
586
        // If the ELOs are the same, return Team A because well, fuck you that's why
587 1
        return $this->getTeamA();
588 1
    }
589
590 1
    /**
591
     * Determine whether the match was a draw
592 1
     * @return bool True if the match ended without any winning teams
593
     */
594
    public function isDraw()
595
    {
596 1
        return $this->team_a_points == $this->team_b_points;
597
    }
598
599
    /**
600
     * Find out whether the match involves a team
601
     *
602
     * @param  Team $team The team to check
603
     * @return bool
604
     */
605
    public function involvesTeam($team) {
606
        return $team->getId() == $this->team_a || $team->getId() == $this->team_b;
607
    }
608
609
    /**
610
     * Reset the ELOs of the teams participating in the match
611
     *
612
     * @return self
613
     */
614
    public function resetELOs()
615
    {
616 9
        $this->getTeamA()->changeELO(-$this->elo_diff);
617
        $this->getTeamB()->changeELO(+$this->elo_diff);
618
    }
619
620
    /**
621 9
     * Calculate the match's contribution to the team activity
622 9
     *
623 9
     * @return float
624 9
     */
625
    public function getActivity()
626 9
    {
627
        $daysPassed = $this->getTimestamp()->diffInSeconds();
628
        $daysPassed = $daysPassed / TimeDate::SECONDS_PER_MINUTE / TimeDate::MINUTES_PER_HOUR / TimeDate::HOURS_PER_DAY;
629 9
630 9
        $activity = 0.0116687059537612 * (pow(45-$daysPassed, (1/6)) + atan(31.0-$daysPassed)/2.0);
631
632 9
        if (is_nan($activity) || $activity < 0.0) {
633 9
            return 0.0;
634 9
        }
635 9
636 9
        return $activity;
637 9
    }
638 9
639 9
    /**
640 9
     * Enter a new match to the database
641 9
     * @param  int             $a          Team A's ID
642 9
     * @param  int             $b          Team B's ID
643 9
     * @param  int             $a_points   Team A's match points
644 9
     * @param  int             $b_points   Team B's match points
645 9
     * @param  int             $duration   The match duration in minutes
646 9
     * @param  int|null        $entered_by The ID of the player reporting the match
647 9
     * @param  string|DateTime $timestamp  When the match was played
648 9
     * @param  int[]           $a_players  The IDs of the first team's players
649 9
     * @param  int[]           $b_players  The IDs of the second team's players
650 9
     * @param  string|null     $server     The address of the server where the match was played
651
     * @param  int|null        $port       The port of the server where the match was played
652 9
     * @param  string          $replayFile The name of the replay file of the match
653
     * @param  int             $map        The ID of the map where the match was played, only for rotational leagues
654 9
     * @return Match           An object representing the match that was just entered
655
     */
656
    public static function enterMatch(
657
        $a, $b, $a_points, $b_points, $duration, $entered_by, $timestamp = "now",
658
        $a_players = array(), $b_players = array(), $server = null, $port = null,
659
        $replayFile = null, $map = null
660
    ) {
661
        $team_a = Team::get($a);
662
        $team_b = Team::get($b);
663
        $a_elo = $team_a->getElo();
664
        $b_elo = $team_b->getElo();
665
666
        $diff = self::calculateEloDiff($a_elo, $b_elo, $a_points, $b_points, $duration);
667
668
        // Update team ELOs
669
        $team_a->changeElo($diff);
670 9
        $team_b->changeElo(-$diff);
671
672 9
        $match = self::create(array(
673 9
            'team_a'         => $a,
674 5
            'team_b'         => $b,
675 5
            'team_a_points'  => $a_points,
676 4
            'team_b_points'  => $b_points,
677
            'team_a_players' => implode(',', $a_players),
678 1
            'team_b_players' => implode(',', $b_players),
679
            'team_a_elo_new' => $team_a->getElo(),
680
            'team_b_elo_new' => $team_b->getElo(),
681
            'elo_diff'       => $diff,
682 9
            'timestamp'      => TimeDate::from($timestamp)->toMysql(),
683 9
            'duration'       => $duration,
684
            'entered_by'     => $entered_by,
685 9
            'server'         => $server,
686
            'port'           => $port,
687 2
            'replay_file'    => $replayFile,
688
            'map'            => $map,
689
            'status'         => 'entered'
690
        ), 'iiiissiiisiisisis', 'updated');
691 7
692
        $match->updateMatchCount();
693
694
        return $match;
695
    }
696
697
    /**
698
     * Calculate the ELO score difference
699
     *
700
     * Computes the ELO score difference on each team after a match, based on
701
     * GU League's rules.
702
     *
703
     * @param  int $a_elo    Team A's current ELO score
704
     * @param  int $b_elo    Team B's current ELO score
705
     * @param  int $a_points Team A's match points
706
     * @param  int $b_points Team B's match points
707
     * @param  int $duration The match duration in minutes
708
     * @return int The ELO score difference
709
     */
710
    public static function calculateEloDiff($a_elo, $b_elo, $a_points, $b_points, $duration)
711
    {
712
        $prob = 1.0 / (1 + pow(10, (($b_elo - $a_elo) / 400.0)));
713
        if ($a_points > $b_points) {
714
            $diff = 50 * (1 - $prob);
715
        } elseif ($a_points == $b_points) {
716
            $diff = 50 * (0.5 - $prob);
717
        } else {
718
            $diff = 50 * (0 - $prob);
719
        }
720
721
        // Apply ELO modifiers from `config.yml`
722
        $durations = Service::getParameter('bzion.league.duration');
723
        $diff *= (isset($durations[$duration])) ? $durations[$duration] : 1;
724
725
        if (abs($diff) < 1 && $diff != 0) {
726
            // ELOs such as 0.75 should round up to 1...
727
            return ($diff > 0) ? 1 : -1;
728
        }
729
730
        // ...everything else is rounded down (-3.7 becomes -3 and 48.1 becomes 48)
731
        return intval($diff);
732
    }
733
734
    /**
735
     * Find if a match's stored ELO is correct
736 1
     */
737
    public function isEloCorrect()
738 1
    {
739
        return $this->elo_diff === $this->calculateEloDiff(
740
            $this->getTeamAEloOld(),
741
            $this->getTeamBEloOld(),
742
            $this->getTeamAPoints(),
743
            $this->getTeamBPoints(),
744
            $this->getDuration()
745 3
        );
746
    }
747 3
748
    /**
749
     * Recalculate the match's elo and adjust the team ELO values
750
     */
751
    public function recalculateElo()
752
    {
753
        $a = $this->getTeamA();
754
        $b = $this->getTeamB();
755 3
756
        $elo = $this->calculateEloDiff(
757
            $a->getElo(),
758
            $b->getElo(),
759
            $this->getTeamAPoints(),
760
            $this->getTeamBPoints(),
761
            $this->getDuration()
762
        );
763
764
        $this->updateProperty($this->elo_diff, "elo_diff", $elo, "i");
765
766
        $a->changeElo($elo);
767
        $b->changeElo(-$elo);
768
769
        $this->updateProperty($this->team_a_elo_new, "team_a_elo_new", $a->getElo(), "i");
770
        $this->updateProperty($this->team_b_elo_new, "team_b_elo_new", $b->getElo(), "i");
771
    }
772 3
773
    /**
774 3
     * Get all the matches in the database
775
     */
776
    public static function getMatches()
777
    {
778
        return self::getQueryBuilder()->active()->getModels();
779
    }
780 1
781
    /**
782 1
     * Get a query builder for matches
783 1
     * @return MatchQueryBuilder
784 1
     */
785 1
    public static function getQueryBuilder()
786 1
    {
787 1
        return new MatchQueryBuilder('Match', array(
788
            'columns' => array(
789
                'firstTeam'        => 'team_a',
790
                'secondTeam'       => 'team_b',
791
                'firstTeamPoints'  => 'team_a_points',
792
                'secondTeamPoints' => 'team_b_points',
793
                'time'             => 'timestamp',
794
                'status'           => 'status'
795
            ),
796 9
        ));
797
    }
798 9
799
    /**
800 9
     * {@inheritdoc}
801 4
     */
802 4
    public function delete()
803
    {
804 6
        $this->updateMatchCount(true);
805 6
806
        return parent::delete();
807 9
    }
808
809
    /**
810
     * {@inheritdoc}
811
     */
812
    public static function getActiveStatuses()
813
    {
814
        return array('entered');
815
    }
816
817
    /**
818
     * {@inheritdoc}
819
     */
820
    public function getName()
821
    {
822
        return sprintf("(+/- %d) %s [%d] vs [%d] %s",
823
            $this->getEloDiff(),
824
            $this->getWinner()->getName(),
825
            $this->getScore($this->getWinner()),
0 ignored issues
show
Bug introduced by
It seems like $this->getWinner() can be null; however, getScore() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
826
            $this->getScore($this->getLoser()),
0 ignored issues
show
Bug introduced by
It seems like $this->getLoser() can be null; however, getScore() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
827
            $this->getLoser()->getName()
828
        );
829
    }
830
831
    /**
832
     * Update the match count of the teams participating in the match
833
     *
834
     * @param bool $decrement Whether to decrement instead of incrementing the match count
835
     */
836
    private function updateMatchCount($decrement = false)
837
    {
838
        $diff = ($decrement) ? -1 : 1;
839
840
        if ($this->isDraw()) {
841
            $this->getTeamA()->changeMatchCount($diff, 'draw');
842
            $this->getTeamB()->changeMatchCount($diff, 'draw');
843
        } else {
844
            $this->getWinner()->changeMatchCount($diff, 'win');
845
            $this->getLoser()->changeMatchCount($diff, 'loss');
846
        }
847
    }
848
}
849