Ban   B
last analyzed

Complexity

Total Complexity 39

Size/Duplication

Total Lines 450
Duplicated Lines 3.33 %

Coupling/Cohesion

Components 2
Dependencies 5

Test Coverage

Coverage 31.36%

Importance

Changes 0
Metric Value
wmc 39
lcom 2
cbo 5
dl 15
loc 450
ccs 37
cts 118
cp 0.3136
rs 8.2857
c 0
b 0
f 0

30 Methods

Rating   Name   Duplication   Size   Complexity  
A assignLazyResult() 0 4 1
A setExpiration() 0 8 2
A getAuthor() 0 4 1
A getCreated() 0 4 1
A getExpiration() 0 4 1
A getReason() 0 4 1
A setServerMessage() 0 4 1
A setReason() 0 4 1
A updateEditTimestamp() 0 4 1
A assignResult() 0 12 2
A getIpAddresses() 0 6 1
A addIP() 7 7 1
A removeIP() 8 8 1
A setIPs() 0 20 3
A getServerMessage() 0 4 1
A getUpdated() 0 4 1
A getVictimID() 0 4 1
A getVictim() 0 4 1
A isSoftBan() 0 4 1
A isExpired() 0 8 2
A isPermanent() 0 4 1
A expire() 0 6 1
A setSoftBan() 0 4 1
B addBan() 0 25 4
A getQueryBuilder() 0 10 1
A getName() 0 4 1
A delete() 0 4 1
A getLazyColumns() 0 4 1
A getBans() 0 4 1
A getBan() 0 10 2

How to fix   Duplicated Code   

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:

1
<?php
2
/**
3
 * This file contains functionality relating to the banned league players
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 ban imposed by an admin on a player
11
 * @package BZiON\Models
12
 */
13
class Ban extends UrlModel implements NamedModel
14
{
15
    /**
16
     * The id of the banned player
17
     * @var int
18
     */
19
    protected $player;
20
21
    /**
22
     * The ban expiration date
23
     * @var TimeDate
24
     */
25
    protected $expiration;
26
27
    /**
28
     * The message that will appear when a player is denied connecting to a game server
29
     * @var string
30
     */
31
    protected $server_message;
32
33
    /**
34
     * The ban reason
35
     * @var string
36
     */
37
    protected $reason;
38
39
    /**
40
     * The ban creation date
41
     * @var TimeDate
42
     */
43
    protected $created;
44
45
    /**
46
     * The date the ban was last updated
47
     * @var TimeDate
48
     */
49
    protected $updated;
50
51
    /**
52
     * The id of the ban author
53
     * @var int
54
     */
55
    protected $author;
56
57
    /**
58
     * Set to true when a player should NOT be penalized throughout the website while this ban is active
59
     * @var bool
60
     */
61
    protected $is_soft_ban;
62
63
    /**
64
     * The IP of the banned player if the league would like to implement a global ban list
65
     * @var string[]
66
     */
67
    protected $ipAddresses;
68
69
    /**
70
     * The ban's status
71
     * @var string
72
     */
73
    protected $status;
74
75
    const DEFAULT_STATUS = 'public';
76
77
    /**
78
     * The name of the database table used for queries
79
     */
80
    const TABLE = "bans";
81
82
    const CREATE_PERMISSION = Permission::ADD_BAN;
83
    const EDIT_PERMISSION = Permission::EDIT_BAN;
84
    const SOFT_DELETE_PERMISSION = Permission::SOFT_DELETE_BAN;
85
    const HARD_DELETE_PERMISSION = Permission::HARD_DELETE_BAN;
86
87
    /**
88 2
     * {@inheritdoc}
89
     */
90 2
    protected function assignResult($ban)
91 2
    {
92 1
        $this->player = $ban['player'];
93 1
        $this->expiration = ($ban['expiration'] === null) ? null : TimeDate::fromMysql($ban['expiration']);
94 2
        $this->server_message = $ban['server_message'];
95 2
        $this->reason = $ban['reason'];
96 2
        $this->is_soft_ban = $ban['is_soft_ban'];
97 2
        $this->created = TimeDate::fromMysql($ban['created']);
98 2
        $this->updated = TimeDate::fromMysql($ban['updated']);
99 2
        $this->author = $ban['author'];
100 2
        $this->status = $ban['status'];
101 2
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106
    protected function assignLazyResult($result)
107
    {
108
        $this->ipAddresses = self::fetchIds('WHERE ban_id = ?', [$this->getId()], 'banned_ips', 'ip_address');
0 ignored issues
show
Documentation Bug introduced by
It seems like self::fetchIds('WHERE ba...ned_ips', 'ip_address') of type array<integer,integer> is incompatible with the declared type array<integer,string> of property $ipAddresses.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
109
    }
110
111
    /**
112
     * Get the IP address of the banned player
113
     * @return string[]
114
     */
115
    public function getIpAddresses()
116
    {
117
        $this->lazyLoad();
118
119
        return $this->ipAddresses;
120
    }
121
122
    /**
123
     * Add an IP to the ban
124
     *
125
     * @param string $ipAddress The IP to add to a ban
126
     */
127 View Code Duplication
    public function addIP($ipAddress)
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...
128
    {
129
        $this->lazyLoad();
130
131
        $this->ipAddresses[] = $ipAddress;
132
        $this->db->execute('INSERT IGNORE INTO banned_ips (id, ban_id, ip_address) VALUES (NULL, ?, ?)', [$this->getId(), $ipAddress]);
133
    }
134
135
    /**
136
     * Remove an IP from the ban
137
     *
138
     * @param string $ipAddress The IP to remove from the ban
139
     */
140 View Code Duplication
    public function removeIP($ipAddress)
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...
141
    {
142
        $this->lazyLoad();
143
144
        // Remove $ipAddress from $this->ipAddresses
145
        $this->ipAddresses = array_diff($this->ipAddresses, [$ipAddress]);
146
        $this->db->execute('DELETE FROM banned_ips WHERE ban_id = ? AND ip_address = ?', [$this->getId(), $ipAddress]);
147
    }
148
149
    /**
150
     * Set the IP addresses of the ban
151
     *
152
     * @todo   Is it worth making this faster?
153
     * @param  string[] $ipAddresses The new IP addresses of the ban
154
     * @return self
155
     */
156
    public function setIPs($ipAddresses)
157
    {
158
        $this->lazyLoad();
159
160
        $oldIPs = $this->ipAddresses;
161
        $this->ipAddresses = $ipAddresses;
162
163
        $newIPs     = array_diff($ipAddresses, $oldIPs);
164
        $removedIPs = array_diff($oldIPs, $ipAddresses);
165
166
        foreach ($newIPs as $ip) {
167
            $this->addIP($ip);
168
        }
169
170
        foreach ($removedIPs as $ip) {
171
            $this->removeIP($ip);
172
        }
173
174
        return $this;
175
    }
176
177
    /**
178
     * Get the user who imposed the ban
179
     * @return Player The banner
180
     */
181
    public function getAuthor()
182
    {
183
        return Player::get($this->author);
184
    }
185
186
    /**
187
     * Get the creation time of the ban
188
     * @return TimeDate The creation time
189
     */
190
    public function getCreated()
191
    {
192
        return $this->created->copy();
193
    }
194
195
    /**
196
     * Get the expiration time of the ban
197
     * @return TimeDate
198
     */
199
    public function getExpiration()
200
    {
201
        return $this->expiration->copy();
202
    }
203
204
    /**
205
     * Get the ban's description
206
     * @return string
207
     */
208
    public function getReason()
209
    {
210
        return $this->reason;
211
    }
212
213
    /**
214
     * Get the ban summary that will appear when a player is denied access to a league server on join
215
     * @return string The ban summary
216
     */
217
    public function getServerMessage()
218
    {
219
        return $this->server_message;
220
    }
221
222
    /**
223
     * Get the time when the ban was last updated
224
     * @return TimeDate
225
     */
226
    public function getUpdated()
227
    {
228
        return $this->updated->copy();
229
    }
230
231
    /**
232
     * Get the ID of the player who was banned
233
     *
234
     * @return int The ID of the victim of the ban
235
     */
236
    public function getVictimID()
237
    {
238
        return $this->player;
239
    }
240
241
    /**
242
     * Get the player who was banned
243
     *
244
     * @return Player The banned player
245
     */
246
    public function getVictim()
247
    {
248
        return Player::get($this->player);
249
    }
250
251
    /**
252
     * Get whether or not the player should be penalized on the site.
253
     *
254
     * @return bool True if the player should NOT be penalized on the site.
255
     */
256
    public function isSoftBan()
257
    {
258
        return (bool) $this->is_soft_ban;
259
    }
260
261
    /**
262
     * Calculate whether a ban has expired or not.
263
     *
264
     * @return bool True if the ban's expiration time has already passed
265
     */
266
    public function isExpired()
267
    {
268
        if ($this->expiration === null) {
269
            return false;
270
        }
271
272
        return TimeDate::now()->gte($this->expiration);
273
    }
274
275
    /**
276
     * Check whether the ban will expire automatically
277
     *
278
     * @return bool
279
     */
280
    public function isPermanent()
281
    {
282
        return $this->expiration === null;
283
    }
284
285
    /**
286
     * Mark the ban as expired
287
     *
288
     * @return self
289
     */
290
    public function expire()
291
    {
292
        $this->setExpiration(TimeDate::now());
293
294
        return $this;
295
    }
296
297
    /**
298
     * Set the expiration date of the ban
299
     *
300
     * @param  TimeDate $expiration The expiration
301
     *
302
     * @return self
303
     */
304 1
    public function setExpiration($expiration)
305
    {
306 1
        if ($expiration !== null) {
307 1
            $expiration = TimeDate::from($expiration);
308
        }
309
310 1
        return $this->updateProperty($this->expiration, 'expiration', $expiration);
311
    }
312
313
    /**
314
     * Set the server message of the ban
315
     *
316
     * @param  string $message The new server message
317
     *
318
     * @return self
319
     */
320
    public function setServerMessage($message)
321
    {
322
        return $this->updateProperty($this->server_message, 'server_message', $message);
323
    }
324
325
    /**
326
     * Set the reason of the ban
327
     * @param  string $reason The new ban reason
328
     * @return self
329
     */
330
    public function setReason($reason)
331
    {
332
        return $this->updateProperty($this->reason, 'reason', $reason);
333
    }
334
335
    /**
336
     * Update the last edit timestamp
337
     * @return self
338
     */
339
    public function updateEditTimestamp()
340
    {
341
        return $this->updateProperty($this->updated, 'updated', TimeDate::now());
342
    }
343
344
    /**
345
     * Set whether the ban's victim is allowed to enter a match server
346
     *
347
     * @param  bool $is_soft_ban
348
     *
349
     * @return self
350
     */
351
    public function setSoftBan($is_soft_ban)
352
    {
353
        return $this->updateProperty($this->is_soft_ban, 'is_soft_ban', (bool) $is_soft_ban);
354
    }
355
356
    /**
357
     * Add a new ban.
358
     *
359
     * @param int        $playerID    The ID of the victim of the ban
360
     * @param int        $authorID    The ID of the player responsible for the ban
361
     * @param mixed|null $expiration  The expiration of the ban (set to NULL for permanent ban)
362
     * @param string     $reason      The full reason for the ban (supports markdown)
363
     * @param string     $serverMsg   A summary of the ban to be displayed on server banlists (max 150 characters)
364
     * @param string[]   $ipAddresses An array of IPs that have been banned
365 2
     * @param bool       $is_soft_ban Whether or not the player is allowed to join match servers
366
     *
367 2
     * @return Ban An object representing the ban that was just entered or false if the ban was not created
368
     */
369 2
    public static function addBan($playerID, $authorID, $expiration, $reason, $serverMsg = '', $ipAddresses = [], $is_soft_ban = false)
370 1
    {
371
        if ($expiration !== null) {
372 1
            $expiration = TimeDate::from($expiration)->toMysql();
373
        }
374
375
        $ban = self::create(array(
376 2
            'player'         => $playerID,
377 2
            'expiration'     => $expiration,
378
            'server_message' => $serverMsg,
379
            'reason'         => $reason,
380 2
            'is_soft_ban'    => $is_soft_ban,
381 2
            'author'         => $authorID,
382 2
        ), ['created', 'updated']);
383 2
384 2
        if (!is_array($ipAddresses)) {
385 2
            $ipAddresses = [$ipAddresses];
386 2
        }
387 2
388
        foreach ($ipAddresses as $ip) {
389 2
            $ban->addIP($ip);
390 2
        }
391 2
392
        return $ban;
393
    }
394
395
    /**
396
     * Get a query builder for news
397 2
     *
398
     * @return QueryBuilder
399
     */
400
    public static function getQueryBuilder()
401
    {
402
        return new QueryBuilder('Ban', array(
403
            'columns' => array(
404
                'player'  => 'player',
405
                'status'  => 'status',
406
                'updated' => 'updated'
407
            ),
408
        ));
409
    }
410
411
    /**
412
     * {@inheritdoc}
413
     */
414
    public function getName()
415
    {
416
        return 'Ban against ' . $this->getVictim()->getUsername();
417
    }
418
419
    /**
420
     * {@inheritdoc}
421
     */
422
    public function delete()
423
    {
424
        parent::delete();
425
    }
426
427
    /**
428
     * {@inheritdoc}
429
     */
430
    public static function getLazyColumns()
431
    {
432
        return null;
433
    }
434
435
    /**
436
     * Get all the bans in the database that aren't disabled or deleted
437
     *
438
     * @return Ban[] An array of ban objects
439
     */
440
    public static function getBans()
441
    {
442
        return self::arrayIdToModel(self::fetchIds('ORDER BY updated DESC'));
443
    }
444
445
    /**
446
     * Get an active ban for the player
447
     *
448
     * @param  int      $playerId The player's ID
449
     *
450
     * @return Ban|null null if the player isn't currently banned
451
     */
452
    public static function getBan($playerId)
453
    {
454
        $bans = self::fetchIdsFrom('player', [$playerId], false, 'AND (expiration IS NULL OR expiration > UTC_TIMESTAMP())');
455
456
        if (empty($bans)) {
457
            return null;
458
        }
459
460
        return self::get($bans[0]);
461 76
    }
462
}
463