Passed
Push — multiproject/globalbans ( 8e9416 )
by Simon
04:59
created

Ban   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 403
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 43
eloc 138
dl 0
loc 403
ccs 0
cts 134
cp 0
rs 8.96
c 0
b 0
f 0

31 Methods

Rating   Name   Duplication   Size   Complexity  
A isActive() 0 3 1
B save() 0 61 5
A getActiveBans() 0 19 2
A setActive() 0 3 2
A getUser() 0 3 1
A getName() 0 3 1
A getDomain() 0 3 1
A setName() 0 3 1
A getUseragent() 0 3 1
A setUser() 0 3 1
A getByIdList() 0 28 3
A getReason() 0 3 1
A getIp() 0 7 2
A setReason() 0 3 1
A getDate() 0 3 1
A getAction() 0 3 1
A setDomain() 0 3 1
A setEmail() 0 3 1
A getVisibility() 0 3 1
A setIp() 0 10 2
A getIpMask() 0 3 1
A getTargetQueueObject() 0 5 2
A setTargetQueue() 0 3 1
A getEmail() 0 3 1
A setDuration() 0 3 1
A setUseragent() 0 3 1
A setAction() 0 3 1
A getTargetQueue() 0 3 1
A setVisibility() 0 3 1
A getDuration() 0 3 1
A getActiveId() 0 23 2

How to fix   Complexity   

Complex Class

Complex classes like Ban 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.

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 Ban, and based on these observations, apply Extract Interface, too.

1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 *                                                                            *
5
 * All code in this file is released into the public domain by the ACC        *
6
 * Development Team. Please see team.json for a list of contributors.         *
7
 ******************************************************************************/
8
9
namespace Waca\DataObjects;
10
11
use Exception;
12
use PDO;
13
use Waca\DataObject;
14
use Waca\Exceptions\OptimisticLockFailedException;
15
use Waca\PdoDatabase;
16
17
/**
18
 * Ban data object
19
 */
20
class Ban extends DataObject
21
{
22
    const ACTION_BLOCK = 'block';
23
    const ACTION_DROP = 'drop';
24
    const ACTION_DEFER = 'defer';
25
    const ACTION_NONE = 'none';
26
27
    /** @var string|null */
28
    private $name;
29
    /** @var string|null */
30
    private $ip;
31
    /** @var int|null */
32
    private $ipmask;
33
    /** @var string|null */
34
    private $email;
35
    /** @var string|null */
36
    private $useragent;
37
38
    private $user;
39
    private $reason;
40
    private $date;
41
    private $duration;
42
    private $active;
43
    private $action = self::ACTION_BLOCK;
44
    private $targetqueue;
45
    private $visibility = 'user';
46
    private ?int $domain;
47
48
    /**
49
     * Gets all active bans, filtered by the optional target.
50
     *
51
     * @return Ban[]
52
     */
53
    public static function getActiveBans(PdoDatabase $database, int $domain)
54
    {
55
        $query = <<<SQL
56
SELECT * FROM ban 
57
WHERE (duration > UNIX_TIMESTAMP() OR duration is null) 
58
    AND active = 1
59
    AND (domain IS NULL OR domain = :domain);
60
SQL;
61
        $statement = $database->prepare($query);
62
        $statement->execute([':domain' => $domain]);
63
        $result = array();
64
65
        /** @var Ban $v */
66
        foreach ($statement->fetchAll(PDO::FETCH_CLASS, get_called_class()) as $v) {
67
            $v->setDatabase($database);
68
            $result[] = $v;
69
        }
70
71
        return $result;
72
    }
73
74
    /**
75
     * Gets a ban by its ID if it's currently active.
76
     *
77
     * @return Ban|false
78
     */
79
    public static function getActiveId($id, PdoDatabase $database, int $domain)
80
    {
81
        $statement = $database->prepare(<<<SQL
82
SELECT *
83
FROM ban
84
WHERE id = :id  
85
  AND (domain IS NULL OR domain = :domain)
86
  AND (duration > UNIX_TIMESTAMP() OR duration is null) 
87
  AND active = 1;
88
SQL
89
        );
90
        $statement->bindValue(":id", $id);
91
        $statement->bindValue(":domain", $domain);
92
93
        $statement->execute();
94
95
        $resultObject = $statement->fetchObject(get_called_class());
96
97
        if ($resultObject !== false) {
98
            $resultObject->setDatabase($database);
99
        }
100
101
        return $resultObject;
102
    }
103
104
    public static function getByIdList($values, PdoDatabase $database, int $domain): array
105
    {
106
        if (count($values) === 0) {
107
            return [];
108
        }
109
110
        // use the provided array to produce a list of question marks of the same length as the array.
111
        $valueCount = count($values);
112
        $inSection = str_repeat('?,', $valueCount - 1) . '?';
113
114
        // this is still parameterised! It's using positional parameters instead of named ones.
115
        $query = 'SELECT * FROM ban WHERE id IN (' . $inSection . ')';
116
117
        $query .= ' AND (domain IS NULL OR domain = ?)';
118
        $values[] = $domain;
119
120
        $statement = $database->prepare($query);
121
122
        // execute the statement with the provided parameter list.
123
        $statement->execute($values);
124
125
        $result = [];
126
        foreach ($statement->fetchAll(PDO::FETCH_CLASS, get_called_class()) as $v) {
127
            $v->setDatabase($database);
128
            $result[] = $v;
129
        }
130
131
        return $result;
132
    }
133
134
    /**
135
     * @throws Exception
136
     */
137
    public function save()
138
    {
139
        if ($this->isNew()) {
140
            // insert
141
            $statement = $this->dbObject->prepare(<<<SQL
142
INSERT INTO `ban` (name, email, ip, ipmask, useragent, user, reason, date, duration, active, action, targetqueue, visibility, domain)
143
VALUES (:name, :email, :ip, :ipmask, :useragent, :user, :reason, CURRENT_TIMESTAMP(), :duration, :active, :action, :targetqueue, :visibility, :domain);
144
SQL
145
            );
146
147
            $statement->bindValue(":name", $this->name);
148
            $statement->bindValue(":email", $this->email);
149
            $statement->bindValue(":ip", $this->ip);
150
            $statement->bindValue(":ipmask", $this->ipmask);
151
            $statement->bindValue(":useragent", $this->useragent);
152
153
            $statement->bindValue(":user", $this->user);
154
            $statement->bindValue(":reason", $this->reason);
155
            $statement->bindValue(":duration", $this->duration);
156
            $statement->bindValue(":active", $this->active);
157
            $statement->bindValue(":action", $this->action);
158
            $statement->bindValue(":targetqueue", $this->targetqueue);
159
            $statement->bindValue(":visibility", $this->visibility);
160
            $statement->bindValue(":domain", $this->domain);
161
162
            if ($statement->execute()) {
163
                $this->id = (int)$this->dbObject->lastInsertId();
164
            }
165
            else {
166
                throw new Exception($statement->errorInfo());
0 ignored issues
show
Bug introduced by
$statement->errorInfo() of type array is incompatible with the type string expected by parameter $message of Exception::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

166
                throw new Exception(/** @scrutinizer ignore-type */ $statement->errorInfo());
Loading history...
167
            }
168
        }
169
        else {
170
            // update
171
            $statement = $this->dbObject->prepare(<<<SQL
172
UPDATE `ban`
173
SET duration = :duration, active = :active, user = :user, action = :action, visibility = :visibility, 
174
    targetqueue = :targetqueue, domain = :domain, updateversion = updateversion + 1
175
WHERE id = :id AND updateversion = :updateversion;
176
SQL
177
            );
178
            $statement->bindValue(':id', $this->id);
179
            $statement->bindValue(':updateversion', $this->updateversion);
180
181
            $statement->bindValue(':duration', $this->duration);
182
            $statement->bindValue(':active', $this->active);
183
            $statement->bindValue(':user', $this->user);
184
            $statement->bindValue(":action", $this->action);
185
            $statement->bindValue(":targetqueue", $this->targetqueue);
186
            $statement->bindValue(":visibility", $this->visibility);
187
            $statement->bindValue(":domain", $this->domain);
188
189
            if (!$statement->execute()) {
190
                throw new Exception($statement->errorInfo());
191
            }
192
193
            if ($statement->rowCount() !== 1) {
194
                throw new OptimisticLockFailedException();
195
            }
196
197
            $this->updateversion++;
198
        }
199
    }
200
201
    /**
202
     * @return string
203
     */
204
    public function getReason()
205
    {
206
        return $this->reason;
207
    }
208
209
    /**
210
     * @param string $reason
211
     */
212
    public function setReason($reason)
213
    {
214
        $this->reason = $reason;
215
    }
216
217
    /**
218
     * @return mixed
219
     */
220
    public function getDate()
221
    {
222
        return $this->date;
223
    }
224
225
    /**
226
     * @return mixed
227
     */
228
    public function getDuration()
229
    {
230
        return $this->duration;
231
    }
232
233
    /**
234
     * @param mixed $duration
235
     */
236
    public function setDuration($duration)
237
    {
238
        $this->duration = $duration;
239
    }
240
241
    /**
242
     * @return bool
243
     */
244
    public function isActive()
245
    {
246
        return $this->active == 1;
247
    }
248
249
    /**
250
     * @param bool $active
251
     */
252
    public function setActive($active)
253
    {
254
        $this->active = $active ? 1 : 0;
255
    }
256
257
    /**
258
     * @return int
259
     */
260
    public function getUser()
261
    {
262
        return $this->user;
263
    }
264
265
    /**
266
     * @param int $user UserID of user who is setting the ban
267
     */
268
    public function setUser($user)
269
    {
270
        $this->user = $user;
271
    }
272
273
    /**
274
     * @return string
275
     */
276
    public function getAction(): string
277
    {
278
        return $this->action;
279
    }
280
281
    /**
282
     * @param string $action
283
     */
284
    public function setAction(string $action): void
285
    {
286
        $this->action = $action;
287
    }
288
289
    /**
290
     * @return string
291
     */
292
    public function getVisibility() : string
293
    {
294
        return $this->visibility;
295
    }
296
297
    /**
298
     * @param string $visibility
299
     */
300
    public function setVisibility(string $visibility): void
301
    {
302
        $this->visibility = $visibility;
303
    }
304
305
    /**
306
     * @return string|null
307
     */
308
    public function getName(): ?string
309
    {
310
        return $this->name;
311
    }
312
313
    /**
314
     * @param string|null $name
315
     */
316
    public function setName(?string $name): void
317
    {
318
        $this->name = $name;
319
    }
320
321
    /**
322
     * @return string|null
323
     */
324
    public function getIp(): ?string
325
    {
326
        if ($this->ip === null) {
327
            return null;
328
        }
329
330
        return inet_ntop($this->ip);
331
    }
332
333
    /**
334
     * @return int|null
335
     */
336
    public function getIpMask(): ?int
337
    {
338
        return $this->ipmask;
339
    }
340
341
    /**
342
     * @param string|null $ip
343
     * @param int|null    $mask
344
     */
345
    public function setIp(?string $ip, ?int $mask): void
346
    {
347
        if ($ip === null) {
348
            $this->ip = null;
349
        }
350
        else {
351
            $this->ip = inet_pton($ip);
352
        }
353
354
        $this->ipmask = $mask;
355
    }
356
357
    /**
358
     * @return string|null
359
     */
360
    public function getEmail(): ?string
361
    {
362
        return $this->email;
363
    }
364
365
    /**
366
     * @param string|null $email
367
     */
368
    public function setEmail(?string $email): void
369
    {
370
        $this->email = $email;
371
    }
372
373
    /**
374
     * @return string|null
375
     */
376
    public function getUseragent(): ?string
377
    {
378
        return $this->useragent;
379
    }
380
381
    /**
382
     * @param string|null $useragent
383
     */
384
    public function setUseragent(?string $useragent): void
385
    {
386
        $this->useragent = $useragent;
387
    }
388
389
    /**
390
     * @return int|null
391
     */
392
    public function getTargetQueue(): ?int
393
    {
394
        return $this->targetqueue;
395
    }
396
397
    /**
398
     * @return RequestQueue|null
399
     */
400
    public function getTargetQueueObject(): ?RequestQueue
401
    {
402
        /** @var RequestQueue $queue */
403
        $queue = RequestQueue::getById($this->targetqueue, $this->getDatabase());
404
        return $queue === false ? null : $queue;
0 ignored issues
show
introduced by
The condition $queue === false is always false.
Loading history...
405
    }
406
407
    /**
408
     * @param int|null $targetQueue
409
     */
410
    public function setTargetQueue(?int $targetQueue): void
411
    {
412
        $this->targetqueue = $targetQueue;
413
    }
414
415
    public function setDomain(?int $domain): void
416
    {
417
        $this->domain = $domain;
418
    }
419
420
    public function getDomain(): ?int
421
    {
422
        return $this->domain;
423
    }
424
}
425