Passed
Push — master ( 5eecc9...7a0709 )
by Michael
14:04
created

OnlineHandler   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 367
Duplicated Lines 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
eloc 207
c 6
b 0
f 0
dl 0
loc 367
rs 5.04
wmc 57

10 Methods

Rating   Name   Duplication   Size   Complexity  
B render() 0 55 10
A update() 0 27 6
B showOnline() 0 57 10
A __construct() 0 6 2
A getCount() 0 14 4
A getAll() 0 23 6
A garbageCollection() 0 9 1
B write() 0 52 7
A init() 0 18 4
B checkStatus() 0 28 7

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
declare(strict_types=1);
4
5
namespace XoopsModules\Newbb;
6
7
/**
8
 * NewBB,  the forum module for XOOPS project
9
 *
10
 * @copyright      XOOPS Project (https://xoops.org)
11
 * @license        GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
12
 * @author         Taiwen Jiang (phppp or D.J.) <[email protected]>
13
 * @since          4.00
14
 */
15
16
17
use Xmf\IPAddress;
18
use XoopsDatabase;
19
20
/** @var \XoopsOnlineHandler $xoopsOnlineHandler */
21
require_once \dirname(__DIR__) . '/include/functions.config.php';
22
23
/**
24
 * Class OnlineHandler
25
 */
26
class OnlineHandler
27
{
28
    public ?XoopsDatabase $db;
29
    public int            $forum_id;
30
    public           $forumObject;
31
    public int            $topic_id;
32
    public array          $user_ids = [];
33
34
    /**
35
     * OnlineHandler constructor.
36
     */
37
    public function __construct(XoopsDatabase $db = null)
38
    {
39
        if (null === $db) {
40
            $db = \XoopsDatabaseFactory::getDatabaseConnection();
41
        }
42
        $this->db = $db;
43
    }
44
45
    /**
46
     * @param Forum|string|int|null $forum
47
     * @param Topic|null $forumtopic
48
     * @throws \Exception
49
     */
50
    public function init($forum = null, Topic $forumtopic = null): void
51
    {
52
        if (\is_object($forum)) {
53
            $this->forum_id    = $forum->getVar('forum_id');
54
        } else {
55
            $this->forum_id    = (int)$forum;
56
        }
57
        $this->forumObject = $forum;
58
        if (\is_object($forumtopic)) {
59
            $this->topic_id = $forumtopic->getVar('topic_id');
60
            if (empty($this->forum_id)) {
61
                $this->forum_id = $forumtopic->getVar('forum_id');
62
            }
63
        } else {
64
            $this->topic_id = (int)$forumtopic;
65
        }
66
67
        $this->update();
68
    }
69
70
    /**
71
     * @throws \Exception
72
     */
73
    public function update(): void
74
    {
75
        global $xoopsModule;
76
77
        // set gc probabillity to 10% for now..
78
        if (\random_int(1, 100) < 60) {
79
            $this->garbageCollection(150);
80
        }
81
        if (\is_object($GLOBALS['xoopsUser'])) {
82
            $uid   = $GLOBALS['xoopsUser']->getVar('uid');
83
            $uname = $GLOBALS['xoopsUser']->getVar('uname');
84
            $name  = $GLOBALS['xoopsUser']->getVar('name');
85
        } else {
86
            $uid   = 0;
87
            $uname = '';
88
            $name  = '';
89
        }
90
91
        /** @var \XoopsOnlineHandler $xoopsOnlineHandler */
92
        $xoopsOnlineHandler = \xoops_getHandler('online');
93
        $xoopsupdate        = $xoopsOnlineHandler->write($uid, $uname, \time(), $xoopsModule->getVar('mid'), \Xmf\IPAddress::fromRequest()->asReadable());
94
        if (!$xoopsupdate) {
95
            //xoops_error("newbb online upate error");
96
        }
97
98
        $uname = (empty($GLOBALS['xoopsModuleConfig']['show_realname']) || empty($name)) ? $uname : $name;
99
        $this->write($uid, $uname, \time(), $this->forum_id, IPAddress::fromRequest()->asReadable(), $this->topic_id);
100
    }
101
102
    /**
103
     * @param \Smarty $xoopsTpl
104
     */
105
    public function render(\Smarty $xoopsTpl): void
106
    {
107
        require_once \dirname(__DIR__) . '/include/functions.render.php';
108
        require_once \dirname(__DIR__) . '/include/functions.user.php';
109
        $criteria = null;
110
        if ($this->topic_id) {
111
            $criteria = new \Criteria('online_topic', $this->topic_id);
112
        } elseif ($this->forum_id) {
113
            $criteria = new \Criteria('online_forum', $this->forum_id);
114
        }
115
        $users     = $this->getAll($criteria);
116
        $num_total = \count($users);
117
118
        $num_user     = 0;
119
        $users_id     = [];
120
        $users_online = [];
121
        foreach ($users as $i => $iValue) {
122
            if (empty($iValue['online_uid'])) {
123
                continue;
124
            }
125
            $users_id[]                          = $iValue['online_uid'];
126
            $users_online[$iValue['online_uid']] = [
127
                'link'  => XOOPS_URL . '/userinfo.php?uid=' . $iValue['online_uid'],
128
                'uname' => $iValue['online_uname'],
129
            ];
130
            ++$num_user;
131
        }
132
        $num_anonymous           = $num_total - $num_user;
133
        $online                  = [];
134
        $online['image']         = \newbbDisplayImage('whosonline');
135
        $online['num_total']     = $num_total;
136
        $online['num_user']      = $num_user;
137
        $online['num_anonymous'] = $num_anonymous;
138
        $administrator_list      = \newbbIsModuleAdministrators($users_id);
139
        $moderator_list          = [];
140
        $member_list             = \array_diff(\array_keys($administrator_list), $users_id);
141
        if ($member_list) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $member_list of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
142
            if (\is_object($this->forumObject)) {
143
                $moderator_list = $this->forumObject->getVar('forum_moderator');
144
            } else {
145
                $moderator_list = \newbbIsForumModerators($member_list);
146
            }
147
        }
148
        foreach ($users_online as $uid => $user) {
149
            if (!empty($administrator_list[$uid])) {
150
                $user['level'] = 2;
151
            } elseif (!empty($moderator_list[$uid])) {
152
                $user['level'] = 1;
153
            } else {
154
                $user['level'] = 0;
155
            }
156
            $online['users'][] = $user;
157
        }
158
159
        $xoopsTpl->assign_by_ref('online', $online);
160
    }
161
162
    /**
163
     * Deprecated
164
     */
165
    public function showOnline(): array
166
    {
167
        require_once \dirname(__DIR__) . '/include/functions.render.php';
168
        require_once \dirname(__DIR__) . '/include/functions.user.php';
169
        $criteria = null;
170
        if ($this->topic_id) {
171
            $criteria = new \Criteria('online_topic', $this->topic_id);
172
        } elseif ($this->forum_id) {
173
            $criteria = new \Criteria('online_forum', $this->forum_id);
174
        }
175
        $users     = $this->getAll($criteria);
176
        $num_total = \count($users);
177
178
        $num_user     = 0;
179
        $users_id     = [];
180
        $users_online = [];
181
        foreach ($users as $i => $iValue) {
182
            if (empty($iValue['online_uid'])) {
183
                continue;
184
            }
185
            $users_id[]                          = $iValue['online_uid'];
186
            $users_online[$iValue['online_uid']] = [
187
                'link'  => XOOPS_URL . '/userinfo.php?uid=' . $iValue['online_uid'],
188
                'uname' => $iValue['online_uname'],
189
            ];
190
            ++$num_user;
191
        }
192
        $num_anonymous           = $num_total - $num_user;
193
        $online                  = [];
194
        $online['image']         = \newbbDisplayImage('whosonline');
195
        $online['statistik']     = \newbbDisplayImage('statistik');
196
        $online['num_total']     = $num_total;
197
        $online['num_user']      = $num_user;
198
        $online['num_anonymous'] = $num_anonymous;
199
        $administrator_list      = \newbbIsModuleAdministrators($users_id);
200
        $moderator_list          = [];
201
        $member_list             = \array_diff($users_id, \array_keys($administrator_list));
202
        if ($member_list) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $member_list of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
203
            if (\is_object($this->forumObject)) {
204
                $moderator_list = $this->forumObject->getVar('forum_moderator');
205
            } else {
206
                $moderator_list = \newbbIsForumModerators($member_list);
207
            }
208
        }
209
210
        foreach ($users_online as $uid => $user) {
211
            if (\in_array($uid, $administrator_list, true)) {
212
                $user['level'] = 2;
213
            } elseif (\in_array($uid, $moderator_list, true)) {
214
                $user['level'] = 1;
215
            } else {
216
                $user['level'] = 0;
217
            }
218
            $online['users'][] = $user;
219
        }
220
221
        return $online;
222
    }
223
224
    /**
225
     * Write online information to the database
226
     *
227
     * @param int    $uid      UID of the active user
228
     * @param string $uname    Username
229
     * @param int    $time
230
     * @param int    $forum_id Current forum_id
231
     * @param string $ip       User's IP adress
232
     * @param int    $topic_id
233
     * @return bool   TRUE on success
234
     * @internal param string $timestamp
235
     */
236
    public function write(int $uid, string $uname, int $time, int $forum_id, string $ip, int $topic_id): bool
237
    {
238
        global $xoopsModule, $xoopsDB;
239
240
        $uid = (int)$uid;
241
        if ($uid > 0) {
242
            $sql = 'SELECT COUNT(*) FROM ' . $this->db->prefix('newbb_online') . ' WHERE online_uid=' . $uid;
0 ignored issues
show
Bug introduced by
The method prefix() does not exist on null. ( Ignorable by Annotation )

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

242
            $sql = 'SELECT COUNT(*) FROM ' . $this->db->/** @scrutinizer ignore-call */ prefix('newbb_online') . ' WHERE online_uid=' . $uid;

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
243
        } else {
244
            $sql = 'SELECT COUNT(*) FROM ' . $this->db->prefix('newbb_online') . ' WHERE online_uid=' . $uid . " AND online_ip='" . $ip . "'";
245
        }
246
        $result = $this->db->queryF($sql);
247
        if (!$this->db->isResultSet($result)) {
248
            \trigger_error("Query Failed! SQL: $sql- Error: " . $this->db->error(), E_USER_ERROR);
249
        }
250
        [$count] = $this->db->fetchRow($result);
251
        if ($count > 0) {
252
            $sql = 'UPDATE ' . $this->db->prefix('newbb_online') . " SET online_updated= '" . $time . "', online_forum = '" . $forum_id . "', online_topic = '" . $topic_id . "' WHERE online_uid = " . $uid;
253
            if (0 == $uid) {
254
                $sql .= " AND online_ip='" . $ip . "'";
255
            }
256
        } else {
257
            $sql = \sprintf('INSERT INTO `%s` (online_uid, online_uname, online_updated, online_ip, online_forum, online_topic) VALUES (%u, %s, %u, %s, %u, %u)', $this->db->prefix('newbb_online'), $uid, $this->db->quote($uname), $time, $this->db->quote($ip), $forum_id, $topic_id);
258
        }
259
        if (!$this->db->queryF($sql)) {
260
            //xoops_error($this->db->error());
261
            return false;
262
        }
263
264
        /** @var \XoopsOnlineHandler $xoopsOnlineHandler */
265
        $xoopsOnlineHandler = \xoops_getHandler('online');
266
        $xoopsOnlineTable   = $xoopsOnlineHandler->table;
267
268
        $sql = 'DELETE FROM '
269
               . $this->db->prefix('newbb_online')
270
               . ' WHERE'
271
               . ' ( online_uid > 0 AND online_uid NOT IN ( SELECT online_uid FROM '
272
               . $xoopsOnlineTable
273
               . ' WHERE online_module ='
274
               . $xoopsModule->getVar('mid')
275
               . ' ) )'
276
               . ' OR ( online_uid = 0 AND online_ip NOT IN ( SELECT online_ip FROM '
277
               . $xoopsOnlineTable
278
               . ' WHERE online_module ='
279
               . $xoopsModule->getVar('mid')
280
               . ' AND online_uid = 0 ) )';
281
282
        $result = $this->db->queryF($sql);
283
        if ($result) {
284
            return true;
285
        }
286
        //xoops_error($this->db->error());
287
        return false;
288
    }
289
290
    /**
291
     * Garbage Collection
292
     *
293
     * Delete all online information that has not been updated for a certain time
294
     *
295
     * @param int $expire Expiration time in seconds
296
     */
297
    public function garbageCollection(int $expire): void
298
    {
299
        global $xoopsModule;
300
        $sql = 'DELETE FROM ' . $this->db->prefix('newbb_online') . ' WHERE online_updated < ' . (\time() - (int)$expire);
301
        $this->db->queryF($sql);
302
303
        /** @var \XoopsOnlineHandler $xoopsOnlineHandler */
304
        $xoopsOnlineHandler = \xoops_getHandler('online');
305
        $xoopsOnlineHandler->gc($expire);
306
    }
307
308
    /**
309
     * Get an array of online information
310
     *
311
     * @param \CriteriaElement|null $criteria {@link \CriteriaElement}
312
     * @return array           Array of associative arrays of online information
313
     */
314
    public function getAll(\CriteriaElement $criteria = null): array
315
    {
316
        $ret   = [];
317
        $limit = $start = 0;
318
        $sql   = 'SELECT * FROM ' . $this->db->prefix('newbb_online');
319
        if (($criteria instanceof \CriteriaCompo || $criteria instanceof \Criteria)) {
320
            $sql   .= ' ' . $criteria->renderWhere();
321
            $limit = $criteria->getLimit();
322
            $start = $criteria->getStart();
323
        }
324
        $result = $this->db->query($sql, $limit, $start);
325
        if ($this->db->isResultSet($result)) {
326
            while (false !== ($myrow = $this->db->fetchArray($result))) {
327
                $ret[] = $myrow;
328
                if ($myrow['online_uid'] > 0) {
329
                    $this->user_ids[] = $myrow['online_uid'];
330
                }
331
                unset($myrow);
332
            }
333
            $this->user_ids = \array_unique($this->user_ids);
334
        }
335
336
        return $ret;
337
    }
338
339
    /**
340
     * @param array $uids
341
     * @return array
342
     */
343
    public function checkStatus(array $uids): array
344
    {
345
        $online_users = [];
346
        $ret          = [];
347
        if (!empty($this->user_ids)) {
348
            $online_users = $this->user_ids;
349
        } else {
350
            $sql = 'SELECT online_uid FROM ' . $this->db->prefix('newbb_online');
351
            if (!empty($uids)) {
352
                $sql .= ' WHERE online_uid IN (' . \implode(', ', \array_map('\intval', $uids)) . ')';
353
            }
354
355
            $result = $this->db->query($sql);
356
            if (!$this->db->isResultSet($result)) {
357
//                \trigger_error("Query Failed! SQL: $sql- Error: " . $this->db->error(), E_USER_ERROR);
358
                return $ret;
359
            }
360
            while ([$uid] = $this->db->fetchRow($result)) {
361
                $online_users[] = $uid;
362
            }
363
        }
364
        foreach ($uids as $uid) {
365
            if (\in_array($uid, $online_users, true)) {
366
                $ret[$uid] = 1;
367
            }
368
        }
369
370
        return $ret;
371
    }
372
373
    /**
374
     * Count the number of online users
375
     *
376
     * @param \CriteriaElement|\CriteriaCompo|null $criteria {@link \CriteriaElement}
377
     * @return bool
378
     */
379
    public function getCount(\CriteriaElement $criteria = null): bool
380
    {
381
        $sql = 'SELECT COUNT(*) FROM ' . $this->db->prefix('newbb_online');
382
        if (($criteria instanceof \CriteriaCompo || $criteria instanceof \Criteria)) {
383
            $sql .= ' ' . $criteria->renderWhere();
384
        }
385
        $result = $this->db->query($sql);
386
        if (!$this->db->isResultSet($result)) {
387
            //                \trigger_error("Query Failed! SQL: $sql- Error: " . $this->db->error(), E_USER_ERROR);
388
            return false;
389
        }
390
        [$ret] = $this->db->fetchRow($result);
391
392
        return $ret;
393
    }
394
}
395