Issues (304)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

class/log.php (86 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 31 and the first side effect is on line 25.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/*
3
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
*/
11
12
/**
13
 *  userlog module
14
 *
15
 * @copyright       XOOPS Project (https://xoops.org)
16
 * @license         GNU GPL 2 (http://www.gnu.org/licenses/old-licenses/gpl-2.0.html)
17
 * @package         userlog class
18
 * @since           1
19
 * @author          irmtfan ([email protected])
20
 * @author          XOOPS Project <www.xoops.org> <www.xoops.ir>
21
 */
22
23
use Xmf\Request;
24
25
defined('XOOPS_ROOT_PATH') || exit('Restricted access.');
26
require_once __DIR__ . '/../include/common.php';
27
28
/**
29
 * Class UserlogLog
30
 */
31
class UserlogLog extends XoopsObject
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
32
{
33
    /**
34
     * @var string
35
     */
36
    public $userlog = null;
37
38
    public $store = 0; // store: 0,1->db 2->file 3->both
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
39
40
    public $sourceJSON = [
41
        'zget',
42
        'post',
43
        'request',
44
        'files',
45
        'env',
46
        'session',
47
        'cookie',
48
        'header',
49
        'logger'
50
    ];// json_encoded fields
51
52
    /**
53
     * constructor
54
     */
55
    public function __construct()
56
    {
57
        $this->userlog = Userlog::getInstance();
0 ignored issues
show
Documentation Bug introduced by
It seems like \Userlog::getInstance() of type object<Userlog> is incompatible with the declared type string of property $userlog.

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...
58
        $this->initVar('log_id', XOBJ_DTYPE_INT, null, false);
59
        $this->initVar('log_time', XOBJ_DTYPE_INT, null, true);
60
        $this->initVar('uid', XOBJ_DTYPE_INT, null, false);
61
        $this->initVar('uname', XOBJ_DTYPE_TXTBOX, null, false, 50);
62
        $this->initVar('admin', XOBJ_DTYPE_INT, null, false);
63
        $this->initVar('groups', XOBJ_DTYPE_TXTBOX, null, false, 100);
64
        $this->initVar('last_login', XOBJ_DTYPE_INT, null, true);
65
        $this->initVar('user_ip', XOBJ_DTYPE_TXTBOX, null, true, 15);
66
        $this->initVar('user_agent', XOBJ_DTYPE_TXTBOX, null, true, 255);
67
        $this->initVar('url', XOBJ_DTYPE_TXTBOX, null, true, 255);
68
        $this->initVar('script', XOBJ_DTYPE_TXTBOX, null, true, 50);
69
        $this->initVar('referer', XOBJ_DTYPE_TXTBOX, null, true, 255);
70
        $this->initVar('pagetitle', XOBJ_DTYPE_TXTBOX, null, false, 255);
71
        $this->initVar('pageadmin', XOBJ_DTYPE_INT, null, false);
72
        $this->initVar('module', XOBJ_DTYPE_TXTBOX, null, true, 25);
73
        $this->initVar('module_name', XOBJ_DTYPE_TXTBOX, null, true, 50);
74
        $this->initVar('item_name', XOBJ_DTYPE_TXTBOX, null, false, 10);
75
        $this->initVar('item_id', XOBJ_DTYPE_INT, null, false);
76
        $this->initVar('request_method', XOBJ_DTYPE_TXTBOX, null, false, 20);
77
        $this->initVar('zget', XOBJ_DTYPE_SOURCE);
78
        $this->initVar('post', XOBJ_DTYPE_SOURCE);
79
        $this->initVar('request', XOBJ_DTYPE_SOURCE);
80
        $this->initVar('files', XOBJ_DTYPE_SOURCE);
81
        $this->initVar('env', XOBJ_DTYPE_SOURCE);
82
        $this->initVar('session', XOBJ_DTYPE_SOURCE);
83
        $this->initVar('cookie', XOBJ_DTYPE_SOURCE);
84
        $this->initVar('header', XOBJ_DTYPE_SOURCE);
85
        $this->initVar('logger', XOBJ_DTYPE_SOURCE);
86
    }
87
88
    /**
89
     * @param string $method
90
     * @param array  $args
91
     *
92
     * @return mixed
93
     */
94
    public function __call($method, $args)
95
    {
96
        $arg = isset($args[0]) ? $args[0] : null;
97
98
        return $this->getVar($method, $arg);
99
    }
100
101
    /**
102
     * @return UserlogLog
103
     */
104
    public static function getInstance()
105
    {
106
        static $instance;
107
        if (null === $instance) {
108
            $instance = new static();
109
        }
110
111
        return $instance;
112
    }
113
114
    /**
115
     * @return mixed
116
     */
117
    public function getLogTime()
118
    {
119
        return $this->userlog->formatTime($this->getVar('log_time'));
0 ignored issues
show
The method formatTime cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
120
    }
121
122
    /**
123
     * @return bool|string
124
     */
125
    public function last_login()
126
    {
127
        return $this->userlog->formatTime($this->getVar('last_login'));
0 ignored issues
show
The method formatTime cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
128
    }
129
130
    /**
131
     * @return array|mixed
132
     */
133
    public function post()
134
    {
135
        $post = $this->getVar('post');
136
137
        return is_array($post) ? $post : json_decode($post, true);
138
    }
139
140
    /**
141
     * @param int    $limit
142
     * @param int    $start
143
     * @param string $sort
144
     * @param string $order
145
     * @param array  $modules
146
     * @param int    $since
147
     * @param array  $users
148
     * @param array  $groups
149
     *
150
     * @return array
151
     */
152
    public function getViews(
153
        $limit = 10,
154
        $start = 0,
155
        $sort = 'count',
156
        $order = 'DESC',
157
        $modules = [],
158
        $since = 0,
159
        $users = [],
160
        $groups = []
161
    ) {
162
        if (!empty($modules)) {
163
            $criteriaModule = new CriteriaCompo();
164
            foreach ($modules as $module_dir => $items) {
165
                $criteriaItem = new CriteriaCompo();
166
                $criteriaItem->add(new Criteria('module', $module_dir));
167
                $criteriaItemName = new CriteriaCompo();
168
                if (!empty($items['item_name'])) {
169
                    foreach ($items['item_name'] as $item_name) {
170
                        // why we cannot use this $criteriaItemName->add(new Criteria('item_name', $items, "IN"));
0 ignored issues
show
Unused Code Comprehensibility introduced by
49% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
171
                        $criteriaItemName->add(new Criteria('item_name', $item_name), 'OR');
172
                    }
173
                }
174
                $criteriaItem->add($criteriaItemName);
175
                $criteriaScript = new CriteriaCompo();
176
                if (!empty($items['script'])) {
177
                    foreach ($items['script'] as $script_name) {
178
                        $criteriaScript->add(new Criteria('script', $script_name), 'OR');
179
                    }
180
                }
181
                $criteriaItem->add($criteriaScript);
182
                $criteriaModule->add($criteriaItem, 'OR');
183
                unset($criteriaItem, $criteriaItemName, $criteriaScript);
184
            }
185
        }
186
187 View Code Duplication
        if (!empty($since)) {
0 ignored issues
show
This code seems to be duplicated across 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...
188
            $starttime     = time() - $this->userlog->getSinceTime($since);
0 ignored issues
show
The method getSinceTime cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
189
            $criteriaSince = new CriteriaCompo();
190
            $criteriaSince->add(new Criteria('log_time', $starttime, '>'));
191
        }
192
193 View Code Duplication
        if (!empty($users)) {
0 ignored issues
show
This code seems to be duplicated across 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...
194
            $criteriaUser = new CriteriaCompo();
195
            $criteriaUser->add(new Criteria('uid', '(' . implode(',', $users) . ')', 'IN'));
196
        }
197 View Code Duplication
        if (!empty($groups)) {
0 ignored issues
show
This code seems to be duplicated across 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...
198
            $criteriaGroup = new CriteriaCompo();
199
            foreach ($groups as $group) {
200
                $criteriaGroup->add(new Criteria('groups', '%g' . $group . '%', 'LIKE'), 'OR');
201
            }
202
        }
203
204
        // add all criterias
205
        $criteria = new CriteriaCompo();
206
        if (!empty($criteriaModule)) {
207
            $criteria->add($criteriaModule);
208
        }
209
        if (!empty($criteriaSince)) {
210
            $criteria->add($criteriaSince);
211
        }
212
        if (!empty($criteriaUser)) {
213
            $criteria->add($criteriaUser);
214
        }
215
        if (!empty($criteriaGroup)) {
216
            $criteria->add($criteriaGroup);
217
        }
218
        $criteria->setLimit($limit);
219
        $criteria->setStart($start);
220
        $sortItem = ('module_count' === $sort) ? 'module_name' : $sort;
221
        $criteria->setSort($sortItem);
222
        $criteria->setOrder($order);
223
        $fields = [
224
            'uid',
225
            'groups',
226
            'pagetitle',
227
            'pageadmin',
228
            'module',
229
            'module_name',
230
            'script',
231
            'item_name',
232
            'item_id'
233
        ];
234
        $criteria->setGroupBy('pageadmin, module, script, item_name, item_id');
235
236
        list($loglogsObj, $itemViews) = $this->userlog->getHandler('log')->getLogsCounts($criteria, $fields);
0 ignored issues
show
The method getHandler cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
237
        $criteria->setGroupBy('module');
238
        $criteria->setSort(('module_count' === $sort) ? 'count' : 'module');
239
        $moduleViews = $this->userlog->getHandler('log')->getCounts($criteria);
0 ignored issues
show
The method getHandler cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
240
        unset($criteria);
241
        // initializing
242
        $items = []; // very important!!!
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
243
        foreach ($loglogsObj as $key => $loglogObj) {
244
            $module_dirname = $loglogObj->module();
245
            $item_id        = $loglogObj->item_id();
246
            if (!empty($item_id)) {
247
                $link = 'modules/' . $module_dirname . '/' . $loglogObj->script() . '?' . $loglogObj->item_name() . '=' . $item_id;
248
            } elseif ('system-root' !== $module_dirname) {
249
                $link = 'modules/' . $module_dirname . (('system' !== $module_dirname
250
                                                         && $loglogObj->pageadmin()) ? '/admin/' : '/') . $loglogObj->script();
251
            } else {
252
                $link = $loglogObj->script();
253
            }
254
            $items[$link]                 = [];
255
            $items[$link]['count']        = $itemViews[$key];
256
            $items[$link]['pagetitle']    = $loglogObj->pagetitle();
257
            $items[$link]['module']       = $module_dirname;
258
            $items[$link]['module_name']  = $loglogObj->module_name();
259
            $items[$link]['module_count'] = $moduleViews[$module_dirname];
260
        }
261
        foreach ($items as $link => $item) {
262
            $col1[$link] = $item[$sort];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$col1 was never initialized. Although not strictly required by PHP, it is generally a good practice to add $col1 = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
263
            $col2[$link] = $item['count'];//second sort by
0 ignored issues
show
Coding Style Comprehensibility introduced by
$col2 was never initialized. Although not strictly required by PHP, it is generally a good practice to add $col2 = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
264
        }
265
        if (!empty($items)) {
266
            array_multisort($col1, ('ASC' === $order) ? SORT_ASC : SORT_DESC, $col2, SORT_DESC, $items);
0 ignored issues
show
The variable $col2 does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
267
        }
268
269
        return $items;
270
    }
271
272
    /**
273
     * @param      $tolog
274
     * @param bool $force
275
     *
276
     * @return bool
0 ignored issues
show
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
277
     */
278
    public function store($tolog, $force = true)
279
    {
280
        if ($this->store > 1) {
281
            $this->storeFile($tolog);
282
        } // store file
283
        if (2 == $this->store) {
284
            return true;
285
        } // do not store db
286
        $this->storeDb($tolog, $force);
287
288
        return null;
289
    }
290
291
    /**
292
     * @param      $tolog
293
     * @param bool $force
294
     *
295
     * @return mixed
296
     */
297
    public function storeDb($tolog, $force = true)
298
    {
299
        // set vars
300
        foreach ($tolog as $option => $logvalue) {
301
            if (!empty($logvalue)) {
302
                // value array to string. use json_encode
303
                if (is_array($logvalue) && count($logvalue) > 0) {
304
                    $logvalue = json_encode($logvalue, (PHP_VERSION > '5.4.0') ? JSON_UNESCAPED_UNICODE : 0);
305
                }
306
                switch ($option) {
307
                    // update referral in stats table
308
                    case 'referer':
309
                        if (false === strpos($logvalue, XOOPS_URL)) {
310
                            $statsObj = UserlogStats::getInstance();
311
                            $statsObj->update('referral', 0, 1, true, parse_url($logvalue, PHP_URL_HOST)); // auto increment 1
312
                        }
313
                        break;
314
                    // update browser and OS in stats table
315
                    case 'user_agent':
316
                        $statsObj   = UserlogStats::getInstance();
317
                        $browserArr = $this->userlog->getBrowsCap()->getBrowser($logvalue, true);
0 ignored issues
show
The method getBrowsCap cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
318
                        $statsObj->update('browser', 0, 1, true, !empty($browserArr['Parent']) ? (!empty($browserArr['Crawler']) ? 'crawler: ' : '') . $browserArr['Parent'] : 'unknown'); // auto increment 1
319
                        $statsObj->update('OS', 0, 1, true, $browserArr['Platform']); // auto increment 1
320
                        break;
321
                }
322
                $this->setVar($option, $logvalue);
323
            }
324
        }
325
        $ret = $this->userlog->getHandler('log')->insert($this, $force);
0 ignored issues
show
The method getHandler cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
326
        $this->unsetNew();
327
328
        return $ret;
329
    }
330
331
    /**
332
     * @param       $logs
333
     * @param array $skips
334
     *
335
     * @return mixed
336
     */
337
    public function arrayToDisplay($logs, $skips = [])
338
    {
339
        foreach ($logs as $log_id => $log) {
340
            $logs[$log_id]['log_time']   = $this->userlog->formatTime($logs[$log_id]['log_time']);
0 ignored issues
show
The method formatTime cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
341
            $logs[$log_id]['last_login'] = $this->userlog->formatTime($logs[$log_id]['last_login']);
0 ignored issues
show
The method formatTime cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
342
            if (!empty($logs[$log_id]['groups'])) {
343
                // change g1g2 to Webmasters, Registered Users
344
                $groups                  = explode('g', substr($logs[$log_id]['groups'], 1)); // remove the first "g" from string
345
                $userGroupNames          = $this->userlog->getFromKeys($this->userlog->getGroupList(), $groups);
0 ignored issues
show
The method getGroupList cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
The method getFromKeys cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
346
                $logs[$log_id]['groups'] = implode(',', $userGroupNames);
347
            }
348
            foreach ($this->sourceJSON as $option) {
349
                // if value is not string it was decoded in file
350
                if (!is_string($logs[$log_id][$option])) {
351
                    continue;
352
                }
353
                $logArr = json_decode($logs[$log_id][$option], true);
354
                if ($logArr) {
355
                    $logs[$log_id][$option] = var_export($logArr, true);
356
                }
357
            }
358
            // merge all request_method to one column - possibility to log methods when user dont set to log request_method itself
359
            $logs[$log_id]['request_method'] = empty($logs[$log_id]['request_method']) ? '' : $logs[$log_id]['request_method'] . "\n";
360
            foreach ($this->sourceJSON as $option) {
361
                if (!empty($logs[$log_id][$option])) {
362
                    $logs[$log_id]['request_method'] .= '$_' . strtoupper($option) . ' ' . $logs[$log_id][$option] . "\n";
363
                }
364
                if ('env' === $option) {
365
                    break;
366
                } // only $sourceJSON = array("zget","post","request","files","env"
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
367
            }
368
            foreach ($skips as $option) {
369
                unset($logs[$log_id][$option]);
370
            }
371
        }
372
373
        return $logs;
374
    }
375
376
    /**
377
     * @param $tolog
378
     *
379
     * @return bool
380
     */
381
    public function storeFile($tolog)
382
    {
383
        $log_file = $this->userlog->getWorkingFile();
0 ignored issues
show
The method getWorkingFile cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
384
        // file create/open/write
385
        $fileHandler = XoopsFile::getHandler();
386
        $fileHandler->__construct($log_file, false);
387
        if ($fileHandler->size() > $this->userlog->getConfig('maxlogfilesize')) {
0 ignored issues
show
The method getConfig cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
388
            $log_file_name = $this->userlog->getConfig('logfilepath') . '/' . USERLOG_DIRNAME . '/' . $this->userlog->getConfig('logfilename');
0 ignored issues
show
The method getConfig cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
389
            $old_file      = $log_file_name . '_' . date('Y-m-d_H-i-s') . '.' . $this->userlog->logext;
390
            if (!$result = rename($log_file, $old_file)) {
391
                $this->setErrors("ERROR renaming ({$log_file})");
392
393
                return false;
394
            }
395
        }
396
        // force to create file if not exist
397
        if (!$fileHandler->exists()) {
398
            if (!$fileHandler->__construct($log_file, true)) { // create file and folder
399
                // Errors Warning: mkdir() [function.mkdir]: Permission denied in file /class/file/folder.php line 529
400
                $this->setErrors("Cannot create folder/file ({$log_file})");
401
402
                return false;
403
            }
404
            $this->setErrors("File was not exist create file ({$log_file})");
405
            // update the new file in database
406
            $statsObj = UserlogStats::getInstance();
407
            $statsObj->update('file', 0, 0, false, $log_file); // value = 0 to not auto increment
408
            // update old file if exist
409
            if (!empty($old_file)) {
410
                $statsObj->update('file', 0, 0, false, $old_file); // value = 0 to not auto increment
411
            }
412
            $statsObj->updateAll('file', 100); // prob = 100
413
            $data = '';
414
        } else {
415
            $data = "\n";
416
        }
417
        $data .= json_encode($tolog, (PHP_VERSION > '5.4.0') ? JSON_UNESCAPED_UNICODE : 0);
418
        if (false === $fileHandler->open('a')) {
419
            $this->setErrors("Cannot open file ({$log_file})");
420
421
            return false;
422
        }
423
        if (false === $fileHandler->write($data)) {
424
            $this->setErrors("Cannot write to file ({$log_file})");
425
426
            return false;
427
        }
428
        $fileHandler->close();
429
430
        return true;
431
    }
432
433
    /**
434
     * @param array  $log_files
435
     * @param        $headers
436
     * @param string $csvNamePrefix
437
     * @param string $delimiter
438
     *
439
     * @return bool|string
440
     */
441
    public function exportFilesToCsv($log_files = [], $headers, $csvNamePrefix = 'list_', $delimiter = ';')
442
    {
443
        $log_files = $this->parseFiles($log_files);
444
        if (0 == ($totalFiles = count($log_files))) {
445
            $this->setErrors(_AM_USERLOG_FILE_SELECT_ONE);
446
447
            return false;
448
        }
449
        list($logs, $totalLogs) = $this->getLogsFromFiles($log_files);
450
        $logs          = $this->arrayToDisplay($logs);
451
        $csvNamePrefix = basename($csvNamePrefix);
452
        if ($csvFile == $this->exportLogsToCsv($logs, $headers, $csvNamePrefix . 'from_file_total_' . $totalLogs, $delimiter)) {
453
            return $csvFile;
0 ignored issues
show
The variable $csvFile does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
454
        }
455
456
        return false;
457
    }
458
459
    /**
460
     * @param        $logs
461
     * @param        $headers
462
     * @param string $csvNamePrefix
463
     * @param string $delimiter
464
     *
465
     * @return bool|string
466
     */
467
    public function exportLogsToCsv($logs, $headers, $csvNamePrefix = 'list_', $delimiter = ';')
468
    {
469
        $csvFile = $this->userlog->getConfig('logfilepath') . '/' . USERLOG_DIRNAME . '/export/csv/' . $csvNamePrefix . '_' . date('Y-m-d_H-i-s') . '.csv';
0 ignored issues
show
The method getConfig cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
470
        // file create/open/write
471
        /** @var \XoopsFileHandler $fileHandler */
472
        $fileHandler = XoopsFile::getHandler();
473
        $fileHandler->__construct($csvFile, false);
474
        // force to create file if not exist
475
        if (!$fileHandler->exists()) {
476
            $fileHandler->__construct($csvFile, true); // create file and folder
477
            $this->setErrors("File was not exist create file ({$csvFile})");
478
        }
479
        if (false === $fileHandler->open('a')) {
480
            $this->setErrors("Cannot open file ({$csvFile})");
481
482
            return false;
483
        }
484
        if (!fputcsv($fileHandler->handler, $headers, $delimiter)) {
485
            return false;
486
        }
487
        foreach ($logs as $thisRow) {
488
            if (!fputcsv($fileHandler->handler, $thisRow, $delimiter)) {
489
                return false;
490
            }
491
        }
492
        $fileHandler->close();
493
494
        return $csvFile;
495
    }
496
497
    /**
498
     * @param array  $log_files
499
     * @param int    $limit
500
     * @param int    $start
501
     * @param null   $options
502
     * @param string $sort
503
     * @param string $order
504
     *
505
     * @return array
506
     */
507
    public function getLogsFromFiles(
508
        $log_files = [],
509
        $limit = 0,
510
        $start = 0,
511
        $options = null,
512
        $sort = 'log_time',
513
        $order = 'DESC'
514
    ) {
515
        $logs    = [];
516
        $logsStr = $this->readFiles($log_files);
517
        // if no logs return empty array and total = 0
518
        if (empty($logsStr)) {
519
            return [[], 0];
520
        }
521
        foreach ($logsStr as $id => $log) {
522
            $logArr = json_decode($log, true);
523
            // check if data is correct in file before do anything more
524
            if (!is_array($logArr) || !array_key_exists('log_id', $logArr)) {
525
                continue;
526
            }
527
            foreach ($logArr as $option => $logvalue) {
528
                // value array to string
529
                $logs[$id][$option] = is_array($logvalue) ? ((count($logvalue) > 0) ? var_export($logvalue, true) : '') : $logvalue;
530
            }
531
        }
532
        // START Criteria in array
533
        foreach ($options as $key => $val) {
0 ignored issues
show
The expression $options of type null is not traversable.
Loading history...
534
            // if user input an empty variable unset it
535
            if (empty($val)) {
536
                continue;
537
            }
538
            // deal with greater than and lower than
539
            $tt = substr($key, -2);
540
            switch ($tt) {
541
                case 'GT':
542
                    $op = substr($key, 0, -2);
543
                    break;
544
                case 'LT':
545
                    $op = substr($key, 0, -2);
546
                    break;
547
                default:
548
                    $op = $key;
549
                    break;
550
            }
551
            $val_arr = explode(',', $val);
552
            // if type is text
553
            if (!empty($val_arr[0]) && 0 == (int)$val_arr[0]) {
554
                foreach ($logs as $id => $log) {
555
                    if (is_array($log[$op])) {
556
                        $log[$op] = json_encode($log[$op], (PHP_VERSION > '5.4.0') ? JSON_UNESCAPED_UNICODE : 0);
557
                    }
558
                    foreach ($val_arr as $qry) {
559
                        // if !QUERY eg: !logs.php,views.php
560
                        if (0 === strpos($qry, '!')) {
561
                            $flagStr = true;
562 View Code Duplication
                            if (false !== strpos($log[$op], substr($qry, 1))) {
0 ignored issues
show
This code seems to be duplicated across 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...
563
                                $flagStr = false; // have that delete
564
                                break; // means AND
565
                            }
566 View Code Duplication
                        } else {
0 ignored issues
show
This code seems to be duplicated across 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...
567
                            $flagStr = false;
568
                            if (false !== strpos($log[$op], $qry)) {
569
                                $flagStr = true; // have that dont delete
570
                                break; // means OR
571
                            }
572
                        }
573
                    }
574
                    if (!$flagStr) {
0 ignored issues
show
The variable $flagStr does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
575
                        unset($logs[$id]);
576
                    }
577
                }
578
            } else {
579
                // if there is one value - deal with =, > ,<
580
                if (1 == count($val_arr)) {
581
                    $val_int = $val_arr[0];
582
                    if ('log_time' === $op || 'last_login' === $op) {
583
                        $val_int = time() - $this->userlog->getSinceTime($val_int);
0 ignored issues
show
The method getSinceTime cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
584
                    }
585
                    // query is one int $t (=, < , >)
586
                    foreach ($logs as $id => $log) {
587
                        switch ($tt) {
588
                            case 'GT':
589
                                if ($log[$op] <= $val_int) {
590
                                    unset($logs[$id]);
591
                                }
592
                                break;
593
                            case 'LT':
594
                                if ($log[$op] >= $val_int) {
595
                                    unset($logs[$id]);
596
                                }
597
                                break;
598
                            default:
599
                                if ($log[$op] != $val_int) {
600
                                    unset($logs[$id]);
601
                                }
602
                                break;
603
                        }
604
                    }
605
                } else {
606
                    // query is an array of int separate with comma. use OR ???
607
                    foreach ($logs as $id => $log) {
608
                        if (!in_array($log[$op], $val_arr)) {
609
                            unset($logs[$id]);
610
                        }
611
                    }
612
                }
613
            }
614
        }
615
        // END Criteria in array
616
        // if no logs return empty array and total = 0
617
        if (empty($logs)) {
618
            return [[], 0];
619
        }
620
621
        // sort order array. multisort is possible :D
622
        if (!empty($sort)) {
623
            // log_id is just the same as log_time
624
            if ('log_id' === $sort) {
625
                $sort = 'log_time';
626
            }
627
            // $typeFlag = is_numeric($logs[0][$sort]) ? SORT_NUMERIC : SORT_STRING;
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
628
            // Obtain a list of columns
629
            foreach ($logs as $key => $log) {
630
                $col[$key] = $log[$sort];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$col was never initialized. Although not strictly required by PHP, it is generally a good practice to add $col = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
631
                //$col2[$key]  = $log[$sort2];
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
632
            }
633
            // Add $logs as the last parameter, to sort by the common key
634
            array_multisort($col, ('ASC' === $order) ? SORT_ASC : SORT_DESC, $logs);
635
        }
636
        // get count
637
        $total = count($logs);
638
        // now slice the array with desired start and limit
639
        if (!empty($limit)) {
640
            $logs = array_slice($logs, $start, $limit);
641
        }
642
643
        return [$logs, $total];
644
    }
645
646
    /**
647
     * @param array $log_files
648
     *
649
     * @return array
650
     */
651
    public function readFiles($log_files = [])
652
    {
653
        $log_files = $this->parseFiles($log_files);
654
        if (0 == ($totalFiles = count($log_files))) {
655
            return $this->readFile();
656
        }
657
        $logs = [];
658
        foreach ($log_files as $file) {
659
            $logs = array_merge($logs, $this->readFile($file));
660
        }
661
662
        return $logs;
663
    }
664
665
    /**
666
     * @param array $log_files
667
     * @param null  $mergeFileName
668
     *
669
     * @return bool|string
670
     */
671
    public function mergeFiles($log_files = [], $mergeFileName = null)
672
    {
673
        $log_files = $this->parseFiles($log_files);
674
        if (0 == ($totalFiles = count($log_files))) {
675
            $this->setErrors(_AM_USERLOG_FILE_SELECT_ONE);
676
677
            return false;
678
        }
679
        $logs          = [];
0 ignored issues
show
$logs is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
680
        $logsStr       = $this->readFiles($log_files);
681
        $data          = implode("\n", $logsStr);
682
        $mergeFile     = $this->userlog->getConfig('logfilepath') . '/' . USERLOG_DIRNAME . '/';
0 ignored issues
show
The method getConfig cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
683
        $mergeFileName = basename($mergeFileName, '.' . $this->userlog->logext);
684
        if (empty($mergeFileName)) {
685
            $mergeFile .= $this->userlog->getConfig('logfilename') . '_merge_' . count($log_files) . '_files_' . date('Y-m-d_H-i-s');
0 ignored issues
show
The method getConfig cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
686
        } else {
687
            $mergeFile .= $mergeFileName;
688
        }
689
        $mergeFile .= '.' . $this->userlog->logext;
690
691
        // file create/open/write
692
        $fileHandler = XoopsFile::getHandler();
693
        $fileHandler->__construct($mergeFile, false); //to see if file exist
694
        if ($fileHandler->exists()) {
695
            $this->setErrors("file ({$mergeFile}) is exist");
696
697
            return false;
698
        }
699
        $fileHandler->__construct($mergeFile, true); // create file and folder
700
        if (false === $fileHandler->open('a')) {
701
            $this->setErrors("Cannot open file ({$mergeFile})");
702
703
            return false;
704
        }
705
        if (false === $fileHandler->write($data)) {
706
            $this->setErrors("Cannot write to file ({$mergeFile})");
707
708
            return false;
709
        }
710
        $fileHandler->close();
711
712
        return $mergeFile;
713
    }
714
715
    /**
716
     * @param null $log_file
717
     *
718
     * @return array
719
     */
720
    public function readFile($log_file = null)
721
    {
722
        if (!$log_file) {
723
            $log_file = $this->userlog->getWorkingFile();
0 ignored issues
show
The method getWorkingFile cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
724
        }
725
        // file open/read
726
        $fileHandler = XoopsFile::getHandler();
727
        // not create file if not exist
728
        $fileHandler->__construct($log_file, false);
729
        if (!$fileHandler->exists()) {
730
            $this->setErrors("Cannot open file ({$log_file})");
731
732
            return [];
733
        }
734
735
        if (false === ($data = $fileHandler->read())) {
736
            $this->setErrors("Cannot read file ({$log_file})");
737
738
            return [];
739
        }
740
        $fileHandler->close();
741
        $logs = explode("\n", $data);
742
743
        return $logs;
744
    }
745
746
    /**
747
     * @param array $log_files
748
     *
749
     * @return int
0 ignored issues
show
Should the return type not be false|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
750
     */
751
    public function deleteFiles($log_files = [])
752
    {
753
        $log_files = $this->parseFiles($log_files);
754
        if (0 == ($totalFiles = count($log_files))) {
755
            $this->setErrors(_AM_USERLOG_FILE_SELECT_ONE);
756
757
            return false;
758
        }
759
        $deletedFiles = 0;
760
        // file open/read
761
        $fileHandler = XoopsFile::getHandler();
762
        foreach ($log_files as $file) {
763
            $fileHandler->__construct($file, false);
764
            if (!$fileHandler->exists()) {
765
                $this->setErrors("({$file}) is a folder or is not exist");
766
                continue;
767
            }
768
            if (false === ($ret = $fileHandler->delete())) {
769
                $this->setErrors("Cannot delete ({$file})");
770
                continue;
771
            }
772
            ++$deletedFiles;
773
        }
774
        $fileHandler->close();
775
776
        return $deletedFiles;
777
    }
778
779
    /**
780
     * @param null $log_file
781
     * @param null $newFileName
782
     *
783
     * @return bool|string
784
     */
785 View Code Duplication
    public function renameFile($log_file = null, $newFileName = null)
0 ignored issues
show
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...
786
    {
787
        if (!is_string($log_file)) {
788
            $this->setErrors(_AM_USERLOG_FILE_SELECT_ONE);
789
790
            return false;
791
        }
792
        // check if file exist
793
        $fileHandler = XoopsFile::getHandler();
794
        $fileHandler->__construct($log_file, false);
795
        if (!$fileHandler->exists()) {
796
            $this->setErrors("({$log_file}) is a folder or is not exist");
797
798
            return false;
799
        }
800
801
        $newFileName = basename($newFileName, '.' . $this->userlog->logext);
802
        if (empty($newFileName)) {
803
            $newFileName = $fileHandler->name() . '_rename_' . date('Y-m-d_H-i-s');
804
        }
805
        $newFile = dirname($log_file) . '/' . $newFileName . '.' . $this->userlog->logext;
806
        // check if new file exist => return false
807
        $fileHandler->__construct($newFile, false);
808
        if ($fileHandler->exists()) {
809
            $this->setErrors("({$newFile}) is exist");
810
811
            return false;
812
        }
813
        if (!@rename($log_file, $newFile)) {
814
            $this->setErrors("Cannot rename ({$log_file})");
815
816
            return false;
817
        }
818
        $fileHandler->close();
819
820
        return $newFile;
821
    }
822
823
    /**
824
     * @param null $log_file
825
     * @param null $newFileName
826
     *
827
     * @return bool|string
828
     */
829 View Code Duplication
    public function copyFile($log_file = null, $newFileName = null)
0 ignored issues
show
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...
830
    {
831
        if (!is_string($log_file)) {
832
            $this->setErrors(_AM_USERLOG_FILE_SELECT_ONE);
833
834
            return false;
835
        }
836
        // check if file exist
837
        $fileHandler = XoopsFile::getHandler();
838
        $fileHandler->__construct($log_file, false);
839
        if (!$fileHandler->exists()) {
840
            $this->setErrors("({$log_file}) is a folder or is not exist");
841
842
            return false;
843
        }
844
845
        $newFileName = basename($newFileName, '.' . $this->userlog->logext);
846
        if (empty($newFileName)) {
847
            $newFileName = $fileHandler->name() . '_copy_' . date('Y-m-d_H-i-s');
848
        }
849
        $newFile = dirname($log_file) . '/' . $newFileName . '.' . $this->userlog->logext;
850
        // check if new file exist => return false
851
        $fileHandler->__construct($newFile, false);
852
        if ($fileHandler->exists()) {
853
            $this->setErrors("({$newFile}) is exist");
854
855
            return false;
856
        }
857
        if (!@copy($log_file, $newFile)) {
858
            $this->setErrors("Cannot copy ({$log_file})");
859
860
            return false;
861
        }
862
        $fileHandler->close();
863
864
        return $newFile;
865
    }
866
867
    /**
868
     * @param array $folders
869
     *
870
     * @return array
871
     */
872
    public function getFilesFromFolders($folders = [])
873
    {
874
        list($allFiles, $totalFiles) = $this->userlog->getAllLogFiles();
0 ignored issues
show
The method getAllLogFiles cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
875
        if (empty($totalFiles)) {
876
            return [];
877
        }
878
        $pathFiles = [];
879
        $getAll    = false;
880
        if (in_array('all', $folders)) {
881
            $getAll = true;
882
        }
883
        foreach ($allFiles as $path => $files) {
884
            if ($getAll || in_array($path, $folders)) {
885
                foreach ($files as $file) {
886
                    $pathFiles[] = $path . '/' . $file;
887
                }
888
            }
889
        }
890
891
        return $pathFiles;
892
    }
893
894
    /**
895
     * @param array $log_files
896
     *
897
     * @return array
898
     */
899
    public function parseFiles($log_files = [])
900
    {
901
        $pathFiles = $this->getFilesFromFolders($log_files);
902
        $log_files = array_unique(array_merge($log_files, $pathFiles));
903
        // file open/read
904
        $fileHandler = XoopsFile::getHandler();
905
        foreach ($log_files as $key => $file) {
906
            $fileHandler->__construct($file, false);
907
            if (!$fileHandler->exists()) {
908
                $this->setErrors("({$file}) is a folder or is not exist");
909
                unset($log_files[$key]);
910
                continue;
911
            }
912
        }
913
        $fileHandler->close();
914
915
        return $log_files;
916
    }
917
918
    /**
919
     * @param array $log_files
920
     * @param null  $zipFileName
921
     *
922
     * @return string
0 ignored issues
show
Should the return type not be false|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
923
     */
924
    public function zipFiles($log_files = [], $zipFileName = null)
925
    {
926
        $log_files = $this->parseFiles($log_files);
927
        if (0 == ($totalFiles = count($log_files))) {
928
            $this->setErrors('No file to zip');
929
930
            return false;
931
        }
932
        //this folder must be writeable by the server
933
        $zipFolder     = $this->userlog->getConfig('logfilepath') . '/' . USERLOG_DIRNAME . '/zip';
0 ignored issues
show
The method getConfig cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
934
        $folderHandler = XoopsFile::getHandler('folder', $zipFolder, true);// create if not exist
0 ignored issues
show
$folderHandler is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
935
        $zipFileName   = basename($zipFileName, '.zip');
936
        if (empty($zipFileName)) {
937
            $zipFileName = $this->userlog->getConfig('logfilename') . '_zip_' . $totalFiles . '_files_' . date('Y-m-d_H-i-s') . '.zip';
0 ignored issues
show
The method getConfig cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
938
        } else {
939
            $zipFileName .= '.zip';
940
        }
941
        $zipFile = $zipFolder . '/' . $zipFileName;
942
943
        $zip = new ZipArchive();
944
945
        if (true !== $zip->open($zipFile, ZipArchive::CREATE)) {
946
            $this->setErrors("Cannot open ({$zipFile})");
947
948
            return false;
949
        }
950
        foreach ($log_files as $file) {
951
            if (!$zip->addFile($file, basename($file))) {
952
                $this->setErrors("Cannot zip ({$file})");
953
            }
954
        }
955
        // if there are some files existed in zip file and/or some files overwritten
956
        if ($totalFiles != $zip->numFiles) {
957
            $this->setErrors("Number of files operated in zipped file: ({$zip->numFiles})");
958
        }
959
        //$this->setErrors("Zip file name: ({$zip->filename})");
0 ignored issues
show
Unused Code Comprehensibility introduced by
86% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
960
        $zip->close();
961
962
        return $zipFile;
963
    }
964
965
    /**
966
     * @param array $currentFile
967
     * @param bool  $multi
968
     * @param int   $size
969
     *
970
     * @return XoopsFormSelect
971
     */
972
    public function buildFileSelectEle($currentFile = [], $multi = false, $size = 3)
973
    {
974
        // $modversion['config'][$i]['options'] = array(_AM_USERLOG_FILE_WORKING=>'0',_AM_USERLOG_STATS_FILEALL=>'all');
0 ignored issues
show
Unused Code Comprehensibility introduced by
76% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
975
        if (0 == count($currentFile) || '0' == $currentFile[0]) {
976
            $currentFile = $this->userlog->getWorkingFile();
0 ignored issues
show
The method getWorkingFile cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
977
        }
978
        $fileEl = new XoopsFormSelect(_AM_USERLOG_FILE, 'file', $currentFile, $size, $multi);
979
        list($allFiles, $totalFiles) = $this->userlog->getAllLogFiles();
0 ignored issues
show
The method getAllLogFiles cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
980
        if (empty($totalFiles)) {
981
            return $fileEl;
982
        }
983
        $log_file_name = $this->userlog->getConfig('logfilename');
0 ignored issues
show
The method getConfig cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
984
        $working_file  = $log_file_name . '.' . $this->userlog->logext;
985
        $fileEl->addOption('all', _AM_USERLOG_STATS_FILEALL);
986
        foreach ($allFiles as $path => $files) {
987
            $fileEl->addOption($path, '>' . $path);
988
            foreach ($files as $file) {
989
                $fileEl->addOption($path . '/' . $file, '-----' . $file . (($file == $working_file) ? '(' . _AM_USERLOG_FILE_WORKING . ')' : ''));
990
            }
991
        }
992
993
        return $fileEl;
994
    }
995
996
    /**
997
     * @return bool
998
     */
999
    public function setItem()
1000
    {
1001
        // In very rare occasions like newbb the item_id is not in the URL $_REQUEST
1002
        require_once __DIR__ . '/plugin/plugin.php';
1003
        require_once __DIR__ . '/plugin/Abstract.php';
1004
        if ($plugin = Userlog_Module_Plugin::getPlugin($this->userlog->getLogModule()->getVar('dirname'), USERLOG_DIRNAME, true)) {
0 ignored issues
show
The method getLogModule cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
1005
            /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1006
            // get all module scripts can accept an item_name to check if this script is exist
1007
            $scripts = $plugin->item();
1008
            $ii = 0;
1009
            $len_script = count($scripts);
1010
            foreach ($scripts as $item_name=>$script_arr) {
1011
                ++$ii;
1012
                $script_arr = is_array($script_arr) ? $script_arr : array($script_arr);
1013
                if(in_array($this->script(), $script_arr)) break;
1014
                if($ii == $len_script) return false;
1015
            }
1016
            */
1017
            $item = $plugin->item($this->script());
1018
            if (empty($item['item_id'])) {
1019
                return false;
1020
            }
1021
            $this->setVar('item_name', $item['item_name']);
1022
            $this->setVar('item_id', $item['item_id']);
1023
1024
            return true;
1025
        }
1026
        // if there is no plugin, use notifications
1027
        $not_config = $this->userlog->getLogModule()->getInfo('notification');
0 ignored issues
show
The method getLogModule cannot be called on $this->userlog (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
1028
        if (!empty($not_config)) {
1029
            foreach ($not_config['category'] as $category) {
1030
                // if $item_id != 0 ---> return true
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1031
                if (!empty($category['item_name'])
1032
                    && in_array($this->script(), is_array($category['subscribe_from']) ? $category['subscribe_from'] : [$category['subscribe_from']])
1033
                    && $item_id = Request::getInt($category['item_name'], 0)) {
1034
                    $this->setVar('item_name', $category['item_name']);
1035
                    $this->setVar('item_id', $item_id);
1036
1037
                    return true;
1038
                }
1039
            }
1040
        }
1041
1042
        return false;
1043
    }
1044
}
1045
1046
/**
1047
 * Class UserlogLogHandler
1048
 */
1049
class UserlogLogHandler extends XoopsPersistableObjectHandler
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
1050
{
1051
    public $userlog = null;
1052
1053
    /**
1054
     * @param null|XoopsDatabase $db
1055
     */
1056
    public function __construct(XoopsDatabase $db)
1057
    {
1058
        $this->userlog = Userlog::getInstance();
1059
        parent::__construct($db, USERLOG_DIRNAME . '_log', 'UserlogLog', 'log_id', 'log_time');
1060
    }
1061
1062
    /**
1063
     * @param int    $limit
1064
     * @param int    $start
1065
     * @param null   $otherCriteria
1066
     * @param string $sort
1067
     * @param string $order
1068
     * @param null   $fields
1069
     * @param bool   $asObject
1070
     * @param bool   $id_as_key
1071
     *
1072
     * @return mixed
1073
     */
1074 View Code Duplication
    public function getLogs(
0 ignored issues
show
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...
1075
        $limit = 0,
1076
        $start = 0,
1077
        $otherCriteria = null,
1078
        $sort = 'log_id',
1079
        $order = 'DESC',
1080
        $fields = null,
1081
        $asObject = true,
1082
        $id_as_key = true
1083
    ) {
1084
        $criteria = new CriteriaCompo();
1085
        if (!empty($otherCriteria)) {
1086
            $criteria->add($otherCriteria);
1087
        }
1088
        $criteria->setLimit($limit);
1089
        $criteria->setStart($start);
1090
        $criteria->setSort($sort);
1091
        $criteria->setOrder($order);
1092
        $ret =& $this->getAll($criteria, $fields, $asObject, $id_as_key);
1093
1094
        return $ret;
1095
    }
1096
1097
    /**
1098
     * @param null $criteria
1099
     * @param null $fields
1100
     * @param bool $asObject
1101
     * @param bool $id_as_key
1102
     *
1103
     * @return array
1104
     */
1105
    public function getLogsCounts($criteria = null, $fields = null, $asObject = true, $id_as_key = true)
1106
    {
1107
        if (is_array($fields) && count($fields) > 0) {
1108
            if (!in_array($this->keyName, $fields)) {
1109
                $fields[] = $this->keyName;
1110
            }
1111
            $select = implode(',', $fields);
1112
        } else {
1113
            $select = '*';
1114
        }
1115
        $limit = null;
1116
        $start = null;
1117
        $sql   = "SELECT {$select}, COUNT(*) AS count FROM {$this->table}";
1118
        if (isset($criteria) && is_subclass_of($criteria, 'criteriaelement')) {
0 ignored issues
show
Due to PHP Bug #53727, is_subclass_of returns inconsistent results on some PHP versions for interfaces; you could instead use ReflectionClass::implementsInterface.
Loading history...
1119
            $sql .= ' ' . $criteria->renderWhere();
1120
            if ($groupby = $criteria->getGroupby()) {
1121
                $sql .= !strpos($groupby, 'GROUP BY') ? " GROUP BY {$groupby}" : $groupby;
1122
            }
1123
            if ($sort = $criteria->getSort()) {
1124
                $sql .= " ORDER BY {$sort} " . $criteria->getOrder();
1125
            }
1126
            $limit = $criteria->getLimit();
1127
            $start = $criteria->getStart();
1128
        }
1129
        $result   = $this->db->query($sql, $limit, $start);
1130
        $ret      = [];
1131
        $retCount = [];
1132
        if ($asObject) {
1133
            while (false !== ($myrow = $this->db->fetchArray($result))) {
1134 View Code Duplication
                if ($id_as_key) {
0 ignored issues
show
This code seems to be duplicated across 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...
1135
                    $retCount[$myrow[$this->keyName]] = array_pop($myrow);
1136
                } else {
1137
                    $retCount[] = array_pop($myrow);
1138
                }
1139
                $object = $this->create(false);
1140
                $object->assignVars($myrow);
1141
                if ($id_as_key) {
1142
                    $ret[$myrow[$this->keyName]] = $object;
1143
                } else {
1144
                    $ret[] = $object;
1145
                }
1146
                unset($object);
1147
            }
1148
        } else {
1149
            $object = $this->create(false);
1150
            while (false !== ($myrow = $this->db->fetchArray($result))) {
1151 View Code Duplication
                if ($id_as_key) {
0 ignored issues
show
This code seems to be duplicated across 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...
1152
                    $retCount[$myrow[$this->keyName]] = array_pop($myrow);
1153
                } else {
1154
                    $retCount[] = array_pop($myrow);
1155
                }
1156
                $object->assignVars($myrow);
1157
                if ($id_as_key) {
1158
                    $ret[$myrow[$this->keyName]] = $object->getValues(array_keys($myrow));
1159
                } else {
1160
                    $ret[] = $object->getValues(array_keys($myrow));
1161
                }
1162
            }
1163
            unset($object);
1164
        }
1165
1166
        return [$ret, $retCount];
1167
    }
1168
1169
    /**
1170
     * @param null   $otherCriteria
1171
     * @param string $notNullFields
1172
     *
1173
     * @return int
1174
     */
1175
    public function getLogsCount($otherCriteria = null, $notNullFields = '')
1176
    {
1177
        $criteria = new CriteriaCompo();
1178
        if (!empty($otherCriteria)) {
1179
            $criteria->add($otherCriteria);
1180
        }
1181
1182
        return $this->getCount($criteria, $notNullFields);
1183
    }
1184
1185
    /**
1186
     * Change Field in a table
1187
     *
1188
     * @access public
1189
     *
1190
     * @param string $field     - name of the field eg: "my_field"
0 ignored issues
show
Should the type for parameter $field not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1191
     * @param string $structure - structure of the field eg: "VARCHAR(50) NOT NULL default ''"
0 ignored issues
show
Should the type for parameter $structure not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1192
     * @param        bool
1193
     *
1194
     * @return bool
1195
     */
1196
    public function changeField($field = null, $structure = null)
1197
    {
1198
        $sql = "ALTER TABLE {$this->table} CHANGE {$field} {$field} {$structure}";
1199
        if (!$result = $this->db->queryF($sql)) {
1200
            xoops_error($this->db->error() . '<br>' . $sql);
1201
1202
            return false;
1203
        }
1204
1205
        return true;
1206
    }
1207
1208
    /**
1209
     * Show Fields in a table - one field or all fields
1210
     *
1211
     * @access   public
1212
     *
1213
     * @param string $field - name of the field eg: "my_field" or null for all fields
0 ignored issues
show
Should the type for parameter $field not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1214
     *
1215
     * @internal param array $ret [my_field] = Field    Type    Null    Key        Default        Extra
1216
     *
1217
     * @return array|bool
1218
     */
1219 View Code Duplication
    public function showFields($field = null)
0 ignored issues
show
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...
1220
    {
1221
        $sql = "SHOW FIELDS FROM {$this->table}";
1222
        if (isset($field)) {
1223
            $sql .= " LIKE '{$field}'";
1224
        }
1225
        if (!$result = $this->db->queryF($sql)) {
1226
            xoops_error($this->db->error() . '<br>' . $sql);
1227
1228
            return false;
1229
        }
1230
        $ret = [];
1231
        while ($myrow = $this->db->fetchArray($result)) {
1232
            $ret[$myrow['Field']] = $myrow;
1233
        }
1234
1235
        return $ret;
1236
    }
1237
1238
    /**
1239
     * Add Field in a table
1240
     *
1241
     * @access public
1242
     *
1243
     * @param string $field     - name of the field eg: "my_field"
0 ignored issues
show
Should the type for parameter $field not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1244
     * @param string $structure - structure of the field eg: "VARCHAR(50) NOT NULL default '' AFTER item_id"
0 ignored issues
show
Should the type for parameter $structure not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1245
     * @param        bool
1246
     *
1247
     * @return bool
1248
     */
1249 View Code Duplication
    public function addField($field = null, $structure = null)
0 ignored issues
show
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...
1250
    {
1251
        if (empty($field) || empty($structure)) {
1252
            return false;
1253
        }
1254
        if ($this->showFields($field)) {
1255
            return false;
1256
        } // field is exist
1257
        $sql = "ALTER TABLE {$this->table} ADD {$field} {$structure}";
1258
        if (!$result = $this->db->queryF($sql)) {
1259
            xoops_error($this->db->error() . '<br>' . $sql);
1260
1261
            return false;
1262
        }
1263
1264
        return true;
1265
    }
1266
1267
    /**
1268
     * Drop Field in a table
1269
     *
1270
     * @access public
1271
     *
1272
     * @param string $field - name of the field
0 ignored issues
show
Should the type for parameter $field not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1273
     * @param        bool
1274
     *
1275
     * @return bool
1276
     */
1277 View Code Duplication
    public function dropField($field = null)
0 ignored issues
show
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...
1278
    {
1279
        if (empty($field)) {
1280
            return false;
1281
        }
1282
        if (!$this->showFields($field)) {
1283
            return false;
1284
        } // field is not exist
1285
        $sql = "ALTER TABLE {$this->table} DROP {$field}";
1286
        if (!$result = $this->db->queryF($sql)) {
1287
            xoops_error($this->db->error() . '<br>' . $sql);
1288
1289
            return false;
1290
        }
1291
1292
        return true;
1293
    }
1294
1295
    /**
1296
     * Show index in a table
1297
     *
1298
     * @access   public
1299
     *
1300
     * @param string $index - name of the index (will be used in KEY_NAME)
0 ignored issues
show
Should the type for parameter $index not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1301
     * @internal param array $ret = Table    Non_unique    Key_name    Seq_in_index    Column_name        Collation    Cardinality        Sub_part    Packed    Null    Index_type    Comment    Index_comment
1302
     *
1303
     * @return array|bool
1304
     */
1305 View Code Duplication
    public function showIndex($index = null)
0 ignored issues
show
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...
1306
    {
1307
        $sql = "SHOW INDEX FROM {$this->table}";
1308
        if (isset($index)) {
1309
            $sql .= " WHERE KEY_NAME = '{$index}'";
1310
        }
1311
        if (!$result = $this->db->queryF($sql)) {
1312
            xoops_error($this->db->error() . '<br>' . $sql);
1313
1314
            return false;
1315
        }
1316
        $ret = [];
1317
        while ($myrow = $this->db->fetchArray($result)) {
1318
            $ret[] = $myrow;
1319
        }
1320
1321
        return $ret;
1322
    }
1323
1324
    /**
1325
     * Add Index to a table
1326
     *
1327
     * @access public
1328
     *
1329
     * @param string $index      - name of the index
0 ignored issues
show
Should the type for parameter $index not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1330
     * @param array  $fields     - array of table fields should be in the index
1331
     * @param string $index_type - type of the index array("INDEX", "UNIQUE", "SPATIAL", "FULLTEXT")
1332
     * @param        bool
1333
     *
1334
     * @return bool
1335
     */
1336 View Code Duplication
    public function addIndex($index = null, $fields = [], $index_type = 'INDEX')
0 ignored issues
show
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...
1337
    {
1338
        if (empty($index) || empty($fields)) {
1339
            return false;
1340
        }
1341
        if ($this->showIndex($index)) {
1342
            return false;
1343
        } // index is exist
1344
        $index_type = strtoupper($index_type);
1345
        if (!in_array($index_type, ['INDEX', 'UNIQUE', 'SPATIAL', 'FULLTEXT'])) {
1346
            return false;
1347
        }
1348
        $fields = is_array($fields) ? implode(',', $fields) : $fields;
1349
        $sql    = "ALTER TABLE {$this->table} ADD {$index_type} {$index} ( {$fields} )";
1350
        if (!$result = $this->db->queryF($sql)) {
1351
            xoops_error($this->db->error() . '<br>' . $sql);
1352
1353
            return false;
1354
        }
1355
1356
        return true;
1357
    }
1358
1359
    /**
1360
     * Drop index in a table
1361
     *
1362
     * @access public
1363
     *
1364
     * @param string $index - name of the index
0 ignored issues
show
Should the type for parameter $index not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1365
     * @param        bool
1366
     *
1367
     * @return bool
1368
     */
1369 View Code Duplication
    public function dropIndex($index = null)
0 ignored issues
show
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...
1370
    {
1371
        if (empty($index)) {
1372
            return false;
1373
        }
1374
        if (!$this->showIndex($index)) {
1375
            return false;
1376
        } // index is not exist
1377
        $sql = "ALTER TABLE {$this->table} DROP INDEX {$index}";
1378
        if (!$result = $this->db->queryF($sql)) {
1379
            xoops_error($this->db->error() . '<br>' . $sql);
1380
1381
            return false;
1382
        }
1383
1384
        return true;
1385
    }
1386
1387
    /**
1388
     * Change Index = Drop index + Add Index
1389
     *
1390
     * @access public
1391
     *
1392
     * @param string $index      - name of the index
0 ignored issues
show
Should the type for parameter $index not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1393
     * @param array  $fields     - array of table fields should be in the index
1394
     * @param string $index_type - type of the index array("INDEX", "UNIQUE", "SPATIAL", "FULLTEXT")
1395
     * @param        bool
1396
     *
1397
     * @return bool
1398
     */
1399 View Code Duplication
    public function changeIndex($index = null, $fields = [], $index_type = 'INDEX')
0 ignored issues
show
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...
1400
    {
1401
        if ($this->showIndex($index) && !$this->dropIndex($index)) {
1402
            return false;
1403
        } // if index is exist but cannot drop it
1404
1405
        return $this->addIndex($index, $fields, $index_type);
1406
    }
1407
1408
    /**
1409
     * Show if the object table or any other table is exist in database
1410
     *
1411
     * @access   public
1412
     *
1413
     * @param string $table or $db->prefix("{$table}") eg: $db->prefix("bb_forums") or "bb_forums" will return same result
0 ignored issues
show
Should the type for parameter $table not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1414
     * @internal param bool $found
1415
     *
1416
     * @return bool
1417
     */
1418
    public function showTable($table = null)
1419
    {
1420
        if (empty($table)) {
1421
            $table = $this->table;
1422
        } // the table for this object
1423
        // check if database prefix is not added yet and then add it!!!
1424 View Code Duplication
        if (0 !== strpos($table, $this->db->prefix() . '_')) {
0 ignored issues
show
This code seems to be duplicated across 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...
1425
            $table = $this->db->prefix("{$table}");
1426
        }
1427
        $result = $this->db->queryF("SHOW TABLES LIKE '{$table}'");
1428
        $found  = $this->db->getRowsNum($result);
1429
1430
        return empty($found) ? false : true;
1431
    }
1432
1433
    /**
1434
     * Rename an old table to the current object table in database
1435
     *
1436
     * @access public
1437
     *
1438
     * @param string $oldTable or $db->prefix("{$oldTable}") eg: $db->prefix("bb_forums") or "bb_forums" will return same result
1439
     * @param        bool
1440
     *
1441
     * @return bool
1442
     */
1443
    public function renameTable($oldTable)
1444
    {
1445
        if ($this->showTable() || !$this->showTable($oldTable)) {
1446
            return false;
1447
        } // table is current || oldTable is not exist
1448
        // check if database prefix is not added yet and then add it!!!
1449 View Code Duplication
        if (0 !== strpos($oldTable, $this->db->prefix() . '_')) {
0 ignored issues
show
This code seems to be duplicated across 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...
1450
            $oldTable = $this->db->prefix("{$oldTable}");
1451
        }
1452
        if (!$result = $this->db->queryF("ALTER TABLE {$oldTable} RENAME {$this->table}")) {
1453
            xoops_error($this->db->error() . '<br>' . $sql);
0 ignored issues
show
The variable $sql does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1454
1455
            return false;
1456
        }
1457
1458
        return true;
1459
    }
1460
}
1461