ReadHandler::__construct()   A
last analyzed

Complexity

Conditions 4
Paths 8

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 8
nop 2
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace XoopsModules\Newbb;
4
5
//
6
//  ------------------------------------------------------------------------ //
7
//                XOOPS - PHP Content Management System                      //
8
//                  Copyright (c) 2000-2020 XOOPS.org                        //
9
//                       <https://xoops.org>                             //
10
//  ------------------------------------------------------------------------ //
11
//  This program is free software; you can redistribute it and/or modify     //
12
//  it under the terms of the GNU General Public License as published by     //
13
//  the Free Software Foundation; either version 2 of the License, or        //
14
//  (at your option) any later version.                                      //
15
//                                                                           //
16
//  You may not change or alter any portion of this comment or credits       //
17
//  of supporting developers from this source code or any supporting         //
18
//  source code which is considered copyrighted (c) material of the          //
19
//  original comment or credit authors.                                      //
20
//                                                                           //
21
//  This program is distributed in the hope that it will be useful,          //
22
//  but WITHOUT ANY WARRANTY; without even the implied warranty of           //
23
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            //
24
//  GNU General Public License for more details.                             //
25
//                                                                           //
26
//  You should have received a copy of the GNU General Public License        //
27
//  along with this program; if not, write to the Free Software              //
28
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA //
29
//  ------------------------------------------------------------------------ //
30
//  Author: phppp (D.J., [email protected])                                  //
31
//  URL: https://xoops.org                                                    //
32
//  Project: Article Project                                                 //
33
//  ------------------------------------------------------------------------ //
34
35
36
37
\defined('NEWBB_FUNCTIONS_INI') || require $GLOBALS['xoops']->path('modules/newbb/include/functions.ini.php');
38
39
/**
40
 * A handler for read/unread handling
41
 *
42
 * @package       newbb
43
 *
44
 * @author        D.J. (phppp, http://xoopsforge.com)
45
 * @copyright     copyright (c) 2005 XOOPS.org
46
 */
47
48
/**
49
 * Class ReadHandler
50
 */
51
class ReadHandler extends \XoopsPersistableObjectHandler
52
{
53
    /**
54
     * Object type.
55
     * <ul>
56
     *  <li>forum</li>
57
     *  <li>topic</li>
58
     * </ul>
59
     *
60
     * @var string
61
     */
62
    public $type;
63
64
    /**
65
     * seconds records will persist.
66
     * assigned from $GLOBALS['xoopsModuleConfig']["read_expire"]
67
     * <ul>
68
     *  <li>positive days = delete all read records exist in the tables before expire time // irmtfan add comment</li>
69
     *  <li>0 = never expires // irmtfan change comment</li>
70
     *  <li>-1 or any negative days = never records // irmtfan change comment</li>
71
     * </ul>
72
     *
73
     * @var integer
74
     */
75
    public $lifetime;
76
77
    /**
78
     * storage mode for records.
79
     * assigned from $GLOBALS['xoopsModuleConfig']["read_mode"]
80
     * <ul>
81
     *  <li>0 = never records</li>
82
     *  <li>1 = uses cookie</li>
83
     *  <li>2 = stores in database</li>
84
     * </ul>
85
     *
86
     * @var integer
87
     */
88
    public $mode;
89
90
    /**
91
     * @param \XoopsDatabase     $db
92
     * @param                    $type
93
     */
94
    public function __construct(\XoopsDatabase $db, $type)
95
    {
96
        $type = ('forum' === $type) ? 'forum' : 'topic';
97
        parent::__construct($db, 'newbb_reads_' . $type, Read::class . $type, 'read_id', 'post_id');
98
        $this->type  = $type;
99
        $newbbConfig = \newbbLoadConfig();
100
        // irmtfan if read_expire = 0 dont clean
101
        $this->lifetime = isset($newbbConfig['read_expire']) ? (int)$newbbConfig['read_expire'] * 24 * 3600 : 30 * 24 * 3600;
102
        $this->mode     = isset($newbbConfig['read_mode']) ? $newbbConfig['read_mode'] : 2;
103
    }
104
105
    /**
106
     * Clear garbage
107
     *
108
     * Delete all expired and duplicated records
109
     */
110
    // START irmtfan rephrase function to 1- add clearDuplicate and 2- dont clean when read_expire = 0
111
    public function clearGarbage()
112
    {
113
        // irmtfan clear duplicaed rows
114
        if (!$result = $this->clearDuplicate()) {
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
115
            return false;
116
        }
117
118
        $sql = 'DELETE bb FROM ' . $this->table . ' AS bb' . ' LEFT JOIN ' . $this->table . ' AS aa ON bb.read_item = aa.read_item ' . ' WHERE aa.post_id > bb.post_id';
119
        if (!$result = $this->db->queryF($sql)) {
120
            //xoops_error($this->db->error());
121
            return false;
122
        }
123
        // irmtfan if read_expire = 0 dont clean
124
        if (empty($this->lifetime)) {
125
            return true;
126
        }
127
        // irmtfan move here and rephrase
128
        $expire = \time() - (int)$this->lifetime;
129
        $sql    = 'DELETE FROM ' . $this->table . ' WHERE read_time < ' . $expire;
130
        if (!$result = $this->db->queryF($sql)) {
131
            //xoops_error($this->db->error());
132
            return false;
133
        }
134
135
        return true;
136
    }
137
138
    // END irmtfan rephrase function to 1- add clearDuplicate and 2- dont clean when read_expire = 0
139
140
    /**
141
     * @param                  $read_item
142
     * @param null             $uid
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $uid is correct as it would always require null to be passed?
Loading history...
143
     * @return bool|mixed|null
144
     */
145
    public function getRead($read_item, $uid = null)
146
    {
147
        if (empty($this->mode)) {
148
            return null;
149
        }
150
        if (1 == $this->mode) {
151
            return $this->getReadCookie($read_item);
152
        }
153
154
        return $this->getReadDb($read_item, $uid);
155
    }
156
157
    /**
158
     * @param $item_id
159
     * @return mixed
160
     */
161
    public function getReadCookie($item_id)
162
    {
163
        $cookie_name = ('forum' === $this->type) ? 'LF' : 'LT';
164
        $cookie_var  = $item_id;
165
        // irmtfan set true to return array
166
        $lastview = \newbbGetCookie($cookie_name, true);
167
168
        return @$lastview[$cookie_var];
169
    }
170
171
    /**
172
     * @param $read_item
173
     * @param $uid
174
     * @return bool|null
175
     */
176
    public function getReadDb($read_item, $uid)
177
    {
178
        if (empty($uid)) {
179
            if (\is_object($GLOBALS['xoopsUser'])) {
180
                $uid = $GLOBALS['xoopsUser']->getVar('uid');
181
            } else {
182
                return false;
183
            }
184
        }
185
        $sql = 'SELECT post_id ' . ' FROM ' . $this->table . ' WHERE read_item = ' . (int)$read_item . '     AND uid = ' . (int)$uid;
186
        if (!$result = $this->db->queryF($sql, 1)) {
187
            return null;
188
        }
189
        [$post_id] = $this->db->fetchRow($result);
190
191
        return $post_id;
192
    }
193
194
    /**
195
     * @param                  $read_item
196
     * @param                  $post_id
197
     * @param null             $uid
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $uid is correct as it would always require null to be passed?
Loading history...
198
     * @return bool|mixed|void
199
     */
200
    public function setRead($read_item, $post_id, $uid = null)
201
    {
202
        if (empty($this->mode)) {
203
            return true;
204
        }
205
206
        if (1 == $this->mode) {
207
            return $this->setReadCookie($read_item, $post_id);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->setReadCookie($read_item, $post_id) targeting XoopsModules\Newbb\ReadHandler::setReadCookie() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
208
        }
209
210
        return $this->setReadDb($read_item, $post_id, $uid);
211
    }
212
213
    /**
214
     * @param $read_item
215
     * @param $post_id
216
     */
217
    public function setReadCookie($read_item, $post_id)
0 ignored issues
show
Unused Code introduced by
The parameter $post_id is not used and could be removed. ( Ignorable by Annotation )

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

217
    public function setReadCookie($read_item, /** @scrutinizer ignore-unused */ $post_id)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
218
    {
219
        $cookie_name          = ('forum' === $this->type) ? 'LF' : 'LT';
220
        $lastview             = \newbbGetCookie($cookie_name, true);
221
        $lastview[$read_item] = \time();
222
        \newbbSetCookie($cookie_name, $lastview);
223
    }
224
225
    /**
226
     * @param $read_item
227
     * @param $post_id
228
     * @param $uid
229
     * @return bool|mixed
230
     */
231
    public function setReadDb($read_item, $post_id, $uid)
232
    {
233
        if (empty($uid)) {
234
            if (\is_object($GLOBALS['xoopsUser'])) {
235
                $uid = $GLOBALS['xoopsUser']->getVar('uid');
236
            } else {
237
                return false;
238
            }
239
        }
240
241
        $sql = 'UPDATE ' . $this->table . ' SET post_id = ' . (int)$post_id . ',' . '     read_time =' . \time() . ' WHERE read_item = ' . (int)$read_item . '     AND uid = ' . (int)$uid;
242
        if ($this->db->queryF($sql) && $this->db->getAffectedRows()) {
243
            return true;
244
        }
245
        $object = $this->create();
246
        $object->setVar('read_item', $read_item);
247
        $object->setVar('post_id', $post_id);
248
        $object->setVar('uid', $uid);
249
        $object->setVar('read_time', \time());
250
251
        return parent::insert($object);
252
    }
253
254
    /**
255
     * @param             $items
256
     * @param null        $uid
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $uid is correct as it would always require null to be passed?
Loading history...
257
     * @return array|null
258
     */
259
    public function isReadItems($items, $uid = null)
260
    {
261
        $ret = null;
262
        if (empty($this->mode)) {
263
            return $ret;
264
        }
265
266
        if (1 == $this->mode) {
267
            $ret = $this->isReadItemsCookie($items);
268
        } else {
269
            $ret = $this->isReadItemsDb($items, $uid);
270
        }
271
272
        return $ret;
273
    }
274
275
    /**
276
     * @param $items
277
     * @return array
278
     */
279
    public function isReadItemsCookie($items)
280
    {
281
        $cookie_name = ('forum' === $this->type) ? 'LF' : 'LT';
282
        $cookie_vars = \newbbGetCookie($cookie_name, true);
283
284
        $ret = [];
285
        foreach ($items as $key => $last_update) {
286
            $ret[$key] = (\max(@$GLOBALS['last_visit'], @$cookie_vars[$key]) >= $last_update);
287
        }
288
289
        return $ret;
290
    }
291
292
    /**
293
     * @param $items
294
     * @param $uid
295
     * @return array
296
     */
297
    public function isReadItemsDb($items, $uid)
298
    {
299
        $ret = [];
300
        if (empty($items)) {
301
            return $ret;
302
        }
303
304
        if (empty($uid)) {
305
            if (\is_object($GLOBALS['xoopsUser'])) {
306
                $uid = $GLOBALS['xoopsUser']->getVar('uid');
307
            } else {
308
                return $ret;
309
            }
310
        }
311
312
        $criteria = new \CriteriaCompo(new \Criteria('uid', $uid));
313
        $criteria->add(new \Criteria('read_item', '(' . \implode(', ', \array_map('\intval', \array_keys($items))) . ')', 'IN'));
314
        $itemsObject = $this->getAll($criteria, ['read_item', 'post_id']);
315
316
        $items_list = [];
317
        foreach (\array_keys($itemsObject) as $key) {
318
            $items_list[$itemsObject[$key]->getVar('read_item')] = $itemsObject[$key]->getVar('post_id');
319
        }
320
        unset($itemsObject);
321
322
        foreach ($items as $key => $last_update) {
323
            $ret[$key] = (@$items_list[$key] >= $last_update);
324
        }
325
326
        return $ret;
327
    }
328
329
    // START irmtfan add clear duplicated rows function
330
331
    /**
332
     * @return bool
333
     */
334
    public function clearDuplicate()
335
    {
336
        /**
337
         * This is needed for the following query GROUP BY clauses to work in MySQL 5.7.
338
         * This is a TEMPORARY fix. Needing this function is bad in the first place, but
339
         * needing sloppy SQL to make it work is worse.
340
         * @todo The schema itself should preclude the duplicates
341
         */
342
        $sql = "SET sql_mode=(SELECT REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY', ''))";
343
        $this->db->queryF($sql);
344
345
        $sql = 'CREATE TABLE ' . $this->table . '_duplicate LIKE ' . $this->table . '; ';
346
        if (!$result = $this->db->queryF($sql)) {
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
347
            \xoops_error($this->db->error() . '<br>' . $sql);
348
349
            return false;
350
        }
351
        $sql = 'INSERT ' . $this->table . '_duplicate SELECT * FROM ' . $this->table . ' GROUP BY read_item, uid; ';
352
        if (!$result = $this->db->queryF($sql)) {
353
            \xoops_error($this->db->error() . '<br>' . $sql);
354
355
            return false;
356
        }
357
        $sql = 'RENAME TABLE ' . $this->table . ' TO ' . $this->table . '_with_duplicate; ';
358
        if (!$result = $this->db->queryF($sql)) {
359
            \xoops_error($this->db->error() . '<br>' . $sql);
360
361
            return false;
362
        }
363
        $sql = 'RENAME TABLE ' . $this->table . '_duplicate TO ' . $this->table . '; ';
364
        if (!$result = $this->db->queryF($sql)) {
365
            \xoops_error($this->db->error() . '<br>' . $sql);
366
367
            return false;
368
        }
369
        $sql    = 'SHOW INDEX FROM ' . $this->table . " WHERE KEY_NAME = 'read_item_uid'";
370
        $result = $this->db->queryF($sql);
371
        if (empty($result)) {
372
            $sql .= 'ALTER TABLE ' . $this->table . ' ADD INDEX read_item_uid ( read_item, uid ); ';
373
            if (!$result = $this->db->queryF($sql)) {
374
                \xoops_error($this->db->error() . '<br>' . $sql);
375
376
                return false;
377
            }
378
        }
379
        $sql = 'DROP TABLE ' . $this->table . '_with_duplicate; ';
380
        if (!$result = $this->db->queryF($sql)) {
381
            \xoops_error($this->db->error() . '<br>' . $sql);
382
383
            return false;
384
        }
385
386
        return true;
387
    }
388
389
    // END irmtfan add clear duplicated rows function
390
}
391