Completed
Push — fm-matches ( f867e2...7fc64f )
by Vladimir
14:12
created

Match   D

Complexity

Total Complexity 84

Size/Duplication

Total Lines 874
Duplicated Lines 2.86 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 73.09%

Importance

Changes 12
Bugs 2 Features 5
Metric Value
wmc 84
c 12
b 2
f 5
lcom 1
cbo 8
dl 25
loc 874
ccs 182
cts 249
cp 0.7309
rs 4.4444

51 Methods

Rating   Name   Duplication   Size   Complexity  
A assignResult() 0 22 1
A getRouteName() 0 4 1
A getMatchDescription() 0 10 3
A getMatchLetter() 0 4 1
A getScore() 13 13 3
A getOpponentScore() 12 12 3
A getOpponent() 0 8 2
A getUpdated() 0 4 1
A setTimestamp() 0 7 1
A getTeamA() 0 4 1
A getTeamB() 0 4 1
A getTeamAPlayers() 0 4 1
A getTeamBPlayers() 0 4 1
A getPlayers() 0 14 4
A setTeamPlayers() 0 7 1
A parsePlayers() 0 8 2
A getTeamAPoints() 0 4 1
A getTeamBPoints() 0 4 1
A setTeamPoints() 0 7 1
A getEloDiff() 0 4 1
A getTeamAEloNew() 0 4 1
A getTeamBEloNew() 0 4 1
A getTeamAEloOld() 0 4 1
A getTeamBEloOld() 0 4 1
A getTeamEloNew() 0 10 3
A getTeamEloOld() 0 10 3
A getMap() 0 4 1
A setMap() 0 4 1
A getMatchDetails() 0 4 1
A getServerAddress() 0 8 3
A setServerAddress() 0 7 1
A getReplayFileName() 0 8 2
A getDuration() 0 4 1
A setDuration() 0 4 1
A getEnteredBy() 0 4 1
A getLoser() 0 8 1
A getWinner() 0 12 3
A isDraw() 0 4 1
A involvesTeam() 0 4 2
A resetELOs() 0 5 1
A getActivity() 0 13 3
A enterMatch() 0 52 3
C calculateEloDiff() 0 23 7
A isEloCorrect() 0 10 1
A recalculateElo() 0 21 1
A getMatches() 0 4 1
A getQueryBuilder() 0 13 1
A delete() 0 6 1
A getActiveStatuses() 0 4 1
A getName() 0 10 1
A updateMatchCount() 0 12 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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

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
use BZIon\Model\Column\Timestamp;
9
10
/**
11
 * A match played between two teams
12
 * @package    BZiON\Models
13
 */
14
class Match extends UrlModel implements NamedModel
15
{
16
    const OFFICIAL = "official";
17
    const SPECIAL  = "special";
18
    const FUN      = "fm";
19
20
    use Timestamp;
21
22
    /**
23
     * The ID of the first team of the match
24
     * @var int
25
     */
26
    protected $team_a;
27
28
    /**
29
     * The ID of the second team of the match
30
     * @var int
31
     */
32
    protected $team_b;
33
34
    /**
35
     * The match points (usually the number of flag captures) Team A scored
36
     * @var int
37
     */
38
    protected $team_a_points;
39
40
    /**
41
     * The match points Team B scored
42
     * @var int
43
     */
44
    protected $team_b_points;
45
46
    /**
47
     * The BZIDs of players part of Team A who participated in the match, separated by commas
48
     * @var string
49
     */
50
    protected $team_a_players;
51
52
    /**
53
     * The BZIDs of players part of Team B who participated in the match, separated by commas
54
     * @var string
55
     */
56
    protected $team_b_players;
57
58
    /**
59
     * The ELO score of Team A after the match
60
     * @var int
61
     */
62
    protected $team_a_elo_new;
63
64
    /**
65
     * The ELO score of Team B after the match
66
     * @var int
67
     */
68
    protected $team_b_elo_new;
69
70
    /**
71
     * The map ID used in the match if the league supports more than one map
72
     * @var int
73
     */
74
    protected $map;
75
76
    /**
77
     * A JSON string of events that happened during a match, such as captures and substitutions
78
     * @var string
79
     */
80
    protected $match_details;
81
82
    /**
83
     * The port of the server where the match took place
84
     * @var int
85
     */
86
    protected $port;
87
88
    /**
89
     * The server location of there the match took place
90
     * @var string
91
     */
92
    protected $server;
93
94
    /**
95
     * The file name of the replay file of the match
96
     * @var string
97
     */
98
    protected $replay_file;
99
100
    /**
101
     * The absolute value of the ELO score difference
102
     * @var int
103
     */
104
    protected $elo_diff;
105
106
    /**
107
     * The timestamp representing when the match information was last updated
108
     * @var TimeDate
109
     */
110
    protected $updated;
111
112
    /**
113
     * The duration of the match in minutes
114
     * @var int
115
     */
116
    protected $duration;
117
118
    /**
119
     * The ID of the person (i.e. referee) who last updated the match information
120
     * @var string
121
     */
122
    protected $entered_by;
123
124
    /**
125
     * The status of the match. Can be 'entered', 'disabled', 'deleted' or 'reported'
126
     * @var string
127
     */
128
    protected $status;
129
130
    /**
131
     * The name of the database table used for queries
132
     */
133
    const TABLE = "matches";
134
135
    const CREATE_PERMISSION = Permission::ENTER_MATCH;
136
    const EDIT_PERMISSION = Permission::EDIT_MATCH;
137
    const SOFT_DELETE_PERMISSION = Permission::SOFT_DELETE_MATCH;
138
    const HARD_DELETE_PERMISSION = Permission::HARD_DELETE_MATCH;
139
140
    /**
141
     * {@inheritdoc}
142
     */
143 9
    protected function assignResult($match)
144
    {
145 9
        $this->team_a = $match['team_a'];
146 9
        $this->team_b = $match['team_b'];
147 9
        $this->team_a_points = $match['team_a_points'];
148 9
        $this->team_b_points = $match['team_b_points'];
149 9
        $this->team_a_players = $match['team_a_players'];
150 9
        $this->team_b_players = $match['team_b_players'];
151 9
        $this->team_a_elo_new = $match['team_a_elo_new'];
152 9
        $this->team_b_elo_new = $match['team_b_elo_new'];
153 9
        $this->map = $match['map'];
154 9
        $this->match_details = $match['match_details'];
155 9
        $this->port = $match['port'];
156 9
        $this->server = $match['server'];
157 9
        $this->replay_file = $match['replay_file'];
158 9
        $this->elo_diff = $match['elo_diff'];
159 9
        $this->timestamp = TimeDate::fromMysql($match['timestamp']);
160 9
        $this->updated = TimeDate::fromMysql($match['updated']);
161 9
        $this->duration = $match['duration'];
162 9
        $this->entered_by = $match['entered_by'];
163 9
        $this->status = $match['status'];
164 9
    }
165
166
    /**
167
     * Get the name of the route that shows the object
168
     * @param  string $action The route's suffix
169
     * @return string
170
     */
171 1
    public static function getRouteName($action = 'show')
172
    {
173 1
        return "match_$action";
174
    }
175
176
    /**
177
     * Get a one word description of a match relative to a team (i.e. win, loss, or draw)
178
     *
179
     * @param int $teamID The team ID we want the noun for
180
     *
181
     * @return string Either "win", "loss", or "draw" relative to the team
182
     */
183 1
    public function getMatchDescription($teamID)
184
    {
185 1
        if ($this->getScore($teamID) > $this->getOpponentScore($teamID)) {
186 1
            return "win";
187 1
        } elseif ($this->getScore($teamID) < $this->getOpponentScore($teamID)) {
188 1
            return "loss";
189
        }
190
191 1
        return "tie";
192
    }
193
194
    /**
195
     * Get a one letter description of a match relative to a team (i.e. W, L, or T)
196
     *
197
     * @param int $teamID The team ID we want the noun for
198
     *
199
     * @return string Either "W", "L", or "T" relative to the team
200
     */
201 1
    public function getMatchLetter($teamID)
202
    {
203 1
        return strtoupper(substr($this->getMatchDescription($teamID), 0, 1));
204
    }
205
206
    /**
207
     * Get the score of a specific team
208
     *
209
     * @param int|Team $teamID The team we want the score for
210
     *
211
     * @return int The score that team received
212
     */
213 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...
214
    {
215 2
        if ($teamID instanceof Team) {
216
            // Oh no! The caller gave us a Team model instead of an ID!
217 1
            $teamID = $teamID->getId();
218
        }
219
220 2
        if ($this->getTeamA()->getId() == $teamID) {
221 2
            return $this->getTeamAPoints();
222
        }
223
224 1
        return $this->getTeamBPoints();
225
    }
226
227
    /**
228
     * Get the score of the opponent relative to a team
229
     *
230
     * @param int $teamID The opponent of the team we want the score for
231
     *
232
     * @return int The score of the opponent
233
     */
234 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...
235
    {
236 2
        if ($teamID instanceof Team) {
237
            $teamID = $teamID->getId();
238
        }
239
240 2
        if ($this->getTeamA()->getId() != $teamID) {
241 1
            return $this->getTeamAPoints();
242
        }
243
244 2
        return $this->getTeamBPoints();
245
    }
246
247
    /**
248
     * Get the opponent of a match relative to a team ID
249
     *
250
     * @param int $teamID The team who is known in a match
251
     *
252
     * @return Team The opponent team
253
     */
254 8
    public function getOpponent($teamID)
255
    {
256 8
        if ($this->getTeamA()->getId() == $teamID) {
257 6
            return $this->getTeamB();
258
        }
259
260 4
        return $this->getTeamA();
261
    }
262
263
    /**
264
     * Get the timestamp of the last update of the match
265
     *
266
     * @return TimeDate The match's update timestamp
267
     */
268 1
    public function getUpdated()
269
    {
270 1
        return $this->updated->copy();
271
    }
272
273
    /**
274
     * Set the timestamp of the match
275
     *
276
     * @param  mixed The match's new timestamp
277
     * @return $this
278
     */
279
    public function setTimestamp($timestamp)
280
    {
281
        $this->timestamp = TimeDate::from($timestamp);
282
        $this->update("timestamp", $this->timestamp->toMysql(), "s");
283
284
        return $this;
285
    }
286
287
    /**
288
     * Get the first team involved in the match
289
     * @return Team Team A's id
290
     */
291 9
    public function getTeamA()
292
    {
293 9
        return Team::get($this->team_a);
294
    }
295
296
    /**
297
     * Get the second team involved in the match
298
     * @return Team Team B's id
299
     */
300 9
    public function getTeamB()
301
    {
302 9
        return Team::get($this->team_b);
303
    }
304
305
    /**
306
     * Get the list of players on Team A who participated in this match
307
     * @return Player[]|null Returns null if there were no players recorded for this match
308
     */
309 1
    public function getTeamAPlayers()
310
    {
311 1
        return $this->parsePlayers($this->team_a_players);
312
    }
313
314
    /**
315
     * Get the list of players on Team B who participated in this match
316
     * @return Player[]|null Returns null if there were no players recorded for this match
317
     */
318 1
    public function getTeamBPlayers()
319
    {
320 1
        return $this->parsePlayers($this->team_b_players);
321
    }
322
323
    /**
324
     * Get the list of players for a team in a match
325
     * @param  Team|int The team or team ID
326
     * @return Player[]|null Returns null if there were no players recorded for this match
327
     */
328 1
    public function getPlayers($team)
329
    {
330 1
        if ($team instanceof Team) {
331 1
            $team = $team->getId();
332
        }
333
334 1
        if ($team == $this->team_a) {
335 1
            return $this->getTeamAPlayers();
336 1
        } elseif ($team == $this->team_b) {
337 1
            return $this->getTeamBPlayers();
338
        }
339
340
        return null;
341
    }
342
343
    /**
344
     * Set the players of the match's teams
345
     *
346
     * @param int[] $teamAPlayers An array of player IDs
347
     * @param int[] $teamBPlayers An array of player IDs
348
     * @return self
349
     */
350
    public function setTeamPlayers($teamAPlayers = array(), $teamBPlayers = array())
351
    {
352
        $this->updateProperty($this->team_a_players, "team_a_players", implode(',', $teamAPlayers), "s");
353
        $this->updateProperty($this->team_b_players, "team_b_players", implode(',', $teamBPlayers), "s");
354
355
        return $this;
356
    }
357
358
    /**
359
     * Get an array of players based on a string representation
360
     * @param string $playerString
361
     * @return Player[]|null Returns null if there were no players recorded for this match
362
     */
363 1
    private static function parsePlayers($playerString)
364
    {
365 1
        if ($playerString == null) {
366 1
            return null;
367
        }
368
369
        return Player::arrayIdToModel(explode(",", $playerString));
370
    }
371
372
    /**
373
     * Get the first team's points
374
     * @return int Team A's points
375
     */
376 4
    public function getTeamAPoints()
377
    {
378 4
        return $this->team_a_points;
379
    }
380
381
    /**
382
     * Get the second team's points
383
     * @return int Team B's points
384
     */
385 4
    public function getTeamBPoints()
386
    {
387 4
        return $this->team_b_points;
388
    }
389
390
    /**
391
     * Set the match team points
392
     *
393
     * @param  int $teamAPoints Team A's points
394
     * @param  int $teamBPoints Team B's points
395
     * @return self
396
     */
397
    public function setTeamPoints($teamAPoints, $teamBPoints)
398
    {
399
        $this->updateProperty($this->team_a_points, "team_a_points", $teamAPoints, "i");
400
        $this->updateProperty($this->team_b_points, "team_b_points", $teamBPoints, "i");
401
402
        return $this;
403
    }
404
405
    /**
406
     * Get the ELO difference applied to each team's old ELO
407
     * @return int The ELO difference
408
     */
409 7
    public function getEloDiff()
410
    {
411 7
        return abs($this->elo_diff);
412
    }
413
414
    /**
415
     * Get the first team's new ELO
416
     * @return int Team A's new ELO
417
     */
418 7
    public function getTeamAEloNew()
419
    {
420 7
        return $this->team_a_elo_new;
421
    }
422
423
    /**
424
     * Get the second team's new ELO
425
     * @return int Team B's new ELO
426
     */
427 7
    public function getTeamBEloNew()
428
    {
429 7
        return $this->team_b_elo_new;
430
    }
431
432
    /**
433
     * Get the first team's old ELO
434
     * @return int
435
     */
436 6
    public function getTeamAEloOld()
437
    {
438 6
        return $this->team_a_elo_new - $this->elo_diff;
439
    }
440
441
    /**
442
     * Get the second team's old ELO
443
     * @return int
444
     */
445 6
    public function getTeamBEloOld()
446
    {
447 6
        return $this->team_b_elo_new + $this->elo_diff;
448
    }
449
450
    /**
451
     * Get the team's new ELO
452
     * @param  Team $team The team whose new ELO to return
453
     * @return int|null   The new ELO, or null if the team provided has not
454
     *                    participated in the match
455
     */
456 1
    public function getTeamEloNew(Team $team)
457
    {
458 1
        if ($team->getId() === $this->team_a) {
459 1
            return $this->getTeamAEloNew();
460 1
        } elseif ($team->getId() === $this->team_b) {
461 1
            return $this->getTeamBEloNew();
462
        }
463
464
        return null;
465
    }
466
467
    /**
468
     * Get the team's old ELO
469
     * @param  Team $team The team whose old ELO to return
470
     * @return int|null   The old ELO, or null if the team provided has not
471
     *                    participated in the match
472
     */
473 1
    public function getTeamEloOld(Team $team)
474
    {
475 1
        if ($team->getId() === $this->team_a) {
476 1
            return $this->getTeamAEloOld();
477 1
        } elseif ($team->getId() === $this->team_b) {
478 1
            return $this->getTeamBEloOld();
479
        }
480
481
        return null;
482
    }
483
484
    /**
485
     * Get the map where the match was played on
486
     * @return Map Returns an invalid map if no map was found
487
     */
488 1
    public function getMap()
489
    {
490 1
        return Map::get($this->map);
491
    }
492
493
    /**
494
     * Set the map where the match was played
495
     * @param  int $map The ID of the map
496
     * @return self
497
     */
498
    public function setMap($map)
499
    {
500
        $this->updateProperty($this->map, "map", $map, "s");
501
    }
502
503
    /**
504
     * Get a JSON decoded array of events that occurred during the match
505
     * @return mixed|null Returns null if there were no events recorded for the match
506
     */
507
    public function getMatchDetails()
508
    {
509
        return json_decode($this->match_details);
510
    }
511
512
    /**
513
     * Get the server address of the server where this match took place
514
     * @return string|null Returns null if there was no server address recorded
515
     */
516
    public function getServerAddress()
517
    {
518
        if ($this->port == null || $this->server == null) {
519
            return null;
520
        }
521
522
        return $this->server . ":" . $this->port;
523
    }
524
525
    /**
526
     * Set the server address of the server where this match took place
527
     *
528
     * @param  string|null $server The server hostname
529
     * @param  int|null    $port   The server port
530
     * @return self
531
     */
532
    public function setServerAddress($server = null, $port = 5154)
533
    {
534
        $this->updateProperty($this->server, "server", $server, "s");
535
        $this->updateProperty($this->port, "port", $port, "i");
536
537
        return $this;
538
    }
539
540
    /**
541
     * Get the name of the replay file for this specific map
542
     * @param  int    $length The length of the replay file name; it will be truncated
543
     * @return string Returns null if there was no replay file name recorded
544
     */
545
    public function getReplayFileName($length = 0)
546
    {
547
        if ($length > 0) {
548
            return substr($this->replay_file, 0, $length);
549
        }
550
551
        return $this->replay_file;
552
    }
553
554
    /**
555
     * Get the match duration
556
     * @return int The duration of the match in minutes
557
     */
558 3
    public function getDuration()
559
    {
560 3
        return $this->duration;
561
    }
562
563
    /**
564
     * Set the match duration
565
     *
566
     * @param  int  $duration The new duration of the match in minutes
567
     * @return self
568
     */
569
    public function setDuration($duration)
570
    {
571
        return $this->updateProperty($this->duration, "duration", $duration, "i");
572
    }
573
574
    /**
575
     * Get the user who entered the match
576
     * @return Player
577
     */
578 2
    public function getEnteredBy()
579
    {
580 2
        return Player::get($this->entered_by);
581
    }
582
583
    /**
584
     * Get the loser of the match
585
     *
586
     * @return Team The team that was the loser or the team with the lower elo if the match was a draw
587
     */
588 8
    public function getLoser()
589
    {
590
        // Get the winner of the match
591 8
        $winner = $this->getWinner();
592
593
        // Get the team that wasn't the winner... Duh
594 8
        return $this->getOpponent($winner->getId());
595
    }
596
597
    /**
598
     * Get the winner of a match
599
     *
600
     * @return Team The team that was the victor or the team with the lower elo if the match was a draw
601
     */
602 8
    public function getWinner()
603
    {
604
        // Get the team that had its ELO increased
605 8
        if ($this->elo_diff > 0) {
606 6
            return $this->getTeamA();
607 2
        } elseif ($this->elo_diff < 0) {
608 2
            return $this->getTeamB();
609
        }
610
611
        // If the ELOs are the same, return Team A because well, fuck you that's why
612
        return $this->getTeamA();
613
    }
614
615
    /**
616
     * Determine whether the match was a draw
617
     * @return bool True if the match ended without any winning teams
618
     */
619 9
    public function isDraw()
620
    {
621 9
        return $this->team_a_points == $this->team_b_points;
622
    }
623
624
    /**
625
     * Find out whether the match involves a team
626
     *
627
     * @param  Team $team The team to check
628
     * @return bool
629
     */
630 1
    public function involvesTeam($team)
631
    {
632 1
        return $team->getId() == $this->team_a || $team->getId() == $this->team_b;
633
    }
634
635
    /**
636
     * Reset the ELOs of the teams participating in the match
637
     *
638
     * @return self
639
     */
640
    public function resetELOs()
641
    {
642
        $this->getTeamA()->changeELO(-$this->elo_diff);
643
        $this->getTeamB()->changeELO(+$this->elo_diff);
644
    }
645
646
    /**
647
     * Calculate the match's contribution to the team activity
648
     *
649
     * @return float
650
     */
651 1
    public function getActivity()
652
    {
653 1
        $daysPassed = $this->getTimestamp()->diffInSeconds();
654 1
        $daysPassed = $daysPassed / TimeDate::SECONDS_PER_MINUTE / TimeDate::MINUTES_PER_HOUR / TimeDate::HOURS_PER_DAY;
655
656 1
        $activity = 0.0116687059537612 * (pow(45 - $daysPassed, (1 / 6)) + atan(31.0 - $daysPassed) / 2.0);
657
658 1
        if (is_nan($activity) || $activity < 0.0) {
659
            return 0.0;
660
        }
661
662 1
        return $activity;
663
    }
664
665
    /**
666
     * Enter a new match to the database
667
     * @param  int             $a          Team A's ID
668
     * @param  int             $b          Team B's ID
669
     * @param  int             $a_points   Team A's match points
670
     * @param  int             $b_points   Team B's match points
671
     * @param  int             $duration   The match duration in minutes
672
     * @param  int|null        $entered_by The ID of the player reporting the match
673
     * @param  string|DateTime $timestamp  When the match was played
674
     * @param  int[]           $a_players  The IDs of the first team's players
675
     * @param  int[]           $b_players  The IDs of the second team's players
676
     * @param  string|null     $server     The address of the server where the match was played
677
     * @param  int|null        $port       The port of the server where the match was played
678
     * @param  string          $replayFile The name of the replay file of the match
679
     * @param  int             $map        The ID of the map where the match was played, only for rotational leagues
680
     * @param  string          $matchType  The type of match (e.g. official, fm, special)
681
     * @return Match           An object representing the match that was just entered
682
     */
683 9
    public static function enterMatch(
684
        $a, $b, $a_points, $b_points, $duration, $entered_by, $timestamp = "now",
685
        $a_players = array(), $b_players = array(), $server = null, $port = null,
686
        $replayFile = null, $map = null, $matchType = "official"
687
    ) {
688
        $matchData = array(
689 9
            'team_a_points'  => $a_points,
690 9
            'team_b_points'  => $b_points,
691 9
            'team_a_players' => implode(',', $a_players),
692 9
            'team_b_players' => implode(',', $b_players),
693 9
            'timestamp'      => TimeDate::from($timestamp)->toMysql(),
694 9
            'duration'       => $duration,
695 9
            'entered_by'     => $entered_by,
696 9
            'server'         => $server,
697 9
            'port'           => $port,
698 9
            'replay_file'    => $replayFile,
699 9
            'map'            => $map,
700 9
            'status'         => 'entered',
701 9
            'match_type'     => $matchType
702
        );
703 9
        $matchDataTypes = 'iisssiisisiss';
704
705 9
        if ($matchType === Match::OFFICIAL) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
706 9
            $team_a = Team::get($a);
707 9
            $team_b = Team::get($b);
708 9
            $a_elo = $team_a->getElo();
709 9
            $b_elo = $team_b->getElo();
710
711 9
            $diff = self::calculateEloDiff($a_elo, $b_elo, $a_points, $b_points, $duration);
712
713
            // Update team ELOs
714 9
            $team_a->changeElo($diff);
715 9
            $team_b->changeElo(-$diff);
716
717 9
            $matchData = array_merge($matchData, array(
718 9
                'team_a'         => $a,
719 9
                'team_b'         => $b,
720 9
                'team_a_elo_new' => $team_a->getElo(),
721 9
                'team_b_elo_new' => $team_b->getElo(),
722 9
                'elo_diff'       => $diff
723
            ));
724 9
            $matchDataTypes .= 'iiiii';
725
        }
726
727 9
        $match = self::create($matchData, $matchDataTypes, 'updated');
728
729 9
        if ($matchType === Match::OFFICIAL) {
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
730 9
            $match->updateMatchCount();
731
        }
732
733 9
        return $match;
734
    }
735
736
    /**
737
     * Calculate the ELO score difference
738
     *
739
     * Computes the ELO score difference on each team after a match, based on
740
     * GU League's rules.
741
     *
742
     * @param  int $a_elo    Team A's current ELO score
743
     * @param  int $b_elo    Team B's current ELO score
744
     * @param  int $a_points Team A's match points
745
     * @param  int $b_points Team B's match points
746
     * @param  int $duration The match duration in minutes
747
     * @return int The ELO score difference
748
     */
749 9
    public static function calculateEloDiff($a_elo, $b_elo, $a_points, $b_points, $duration)
750
    {
751 9
        $prob = 1.0 / (1 + pow(10, (($b_elo - $a_elo) / 400.0)));
752 9
        if ($a_points > $b_points) {
753 5
            $diff = 50 * (1 - $prob);
754 5
        } elseif ($a_points == $b_points) {
755 4
            $diff = 50 * (0.5 - $prob);
756
        } else {
757 1
            $diff = 50 * (0 - $prob);
758
        }
759
760
        // Apply ELO modifiers from `config.yml`
761 9
        $durations = Service::getParameter('bzion.league.duration');
762 9
        $diff *= (isset($durations[$duration])) ? $durations[$duration] : 1;
763
764 9
        if (abs($diff) < 1 && $diff != 0) {
765
            // ELOs such as 0.75 should round up to 1...
766 2
            return ($diff > 0) ? 1 : -1;
767
        }
768
769
        // ...everything else is rounded down (-3.7 becomes -3 and 48.1 becomes 48)
770 7
        return intval($diff);
771
    }
772
773
    /**
774
     * Find if a match's stored ELO is correct
775
     */
776
    public function isEloCorrect()
777
    {
778
        return $this->elo_diff === $this->calculateEloDiff(
779
            $this->getTeamAEloOld(),
780
            $this->getTeamBEloOld(),
781
            $this->getTeamAPoints(),
782
            $this->getTeamBPoints(),
783
            $this->getDuration()
784
        );
785
    }
786
787
    /**
788
     * Recalculate the match's elo and adjust the team ELO values
789
     */
790
    public function recalculateElo()
791
    {
792
        $a = $this->getTeamA();
793
        $b = $this->getTeamB();
794
795
        $elo = $this->calculateEloDiff(
796
            $a->getElo(),
797
            $b->getElo(),
798
            $this->getTeamAPoints(),
799
            $this->getTeamBPoints(),
800
            $this->getDuration()
801
        );
802
803
        $this->updateProperty($this->elo_diff, "elo_diff", $elo, "i");
804
805
        $a->changeElo($elo);
806
        $b->changeElo(-$elo);
807
808
        $this->updateProperty($this->team_a_elo_new, "team_a_elo_new", $a->getElo(), "i");
809
        $this->updateProperty($this->team_b_elo_new, "team_b_elo_new", $b->getElo(), "i");
810
    }
811
812
    /**
813
     * Get all the matches in the database
814
     */
815 1
    public static function getMatches()
816
    {
817 1
        return self::getQueryBuilder()->active()->getModels();
818
    }
819
820
    /**
821
     * Get a query builder for matches
822
     * @return MatchQueryBuilder
823
     */
824 3
    public static function getQueryBuilder()
825
    {
826 3
        return new MatchQueryBuilder('Match', array(
827
            'columns' => array(
828
                'firstTeam'        => 'team_a',
829
                'secondTeam'       => 'team_b',
830
                'firstTeamPoints'  => 'team_a_points',
831
                'secondTeamPoints' => 'team_b_points',
832
                'time'             => 'timestamp',
833
                'status'           => 'status'
834 3
            ),
835
        ));
836
    }
837
838
    /**
839
     * {@inheritdoc}
840
     */
841
    public function delete()
842
    {
843
        $this->updateMatchCount(true);
844
845
        return parent::delete();
846
    }
847
848
    /**
849
     * {@inheritdoc}
850
     */
851 3
    public static function getActiveStatuses()
852
    {
853 3
        return array('entered');
854
    }
855
856
    /**
857
     * {@inheritdoc}
858
     */
859 1
    public function getName()
860
    {
861 1
        return sprintf("(+/- %d) %s [%d] vs [%d] %s",
862 1
            $this->getEloDiff(),
863 1
            $this->getWinner()->getName(),
864 1
            $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...
865 1
            $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...
866 1
            $this->getLoser()->getName()
867
        );
868
    }
869
870
    /**
871
     * Update the match count of the teams participating in the match
872
     *
873
     * @param bool $decrement Whether to decrement instead of incrementing the match count
874
     */
875 9
    private function updateMatchCount($decrement = false)
876
    {
877 9
        $diff = ($decrement) ? -1 : 1;
878
879 9
        if ($this->isDraw()) {
880 4
            $this->getTeamA()->changeMatchCount($diff, 'draw');
881 4
            $this->getTeamB()->changeMatchCount($diff, 'draw');
882
        } else {
883 6
            $this->getWinner()->changeMatchCount($diff, 'win');
884 6
            $this->getLoser()->changeMatchCount($diff, 'loss');
885
        }
886 9
    }
887
}
888