Issues (388)

Security Analysis    not enabled

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

  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.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  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.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  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.
  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.
  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.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  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.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  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.
  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.
  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.
  Header Injection
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/Utility.php (18 issues)

1
<?php declare(strict_types=1);
2
3
namespace XoopsModules\Songlist;
4
5
use Xmf\Request;
6
use XoopsModules\Songlist\Form\{
7
    SelectAlbumForm,
8
    SelectCategoryForm,
9
    SelectGenreForm,
10
    SelectVoiceForm,
11
    SelectSongForm
12
};
13
14
15
16
/**
17
 * Class Utility
18
 */
19
class Utility extends Common\SysUtility
20
{
21
    //--------------- Custom module methods -----------------------------
22
    /**
23
     * Function responsible for checking if a directory exists, we can also write in and create an index.html file
24
     *
25
     * @param string $folder The full path of the directory to check
26
     */
27
    public static function createFolder($folder): void
28
    {
29
        //        try {
30
        //            if (!is_dir($folder) && !mkdir($folder) && !is_dir($folder)) {
31
        //                throw new \RuntimeException(sprintf('Unable to create the %s directory', $folder));
32
        //            } else {
33
        //                file_put_contents($folder . '/index.html', '<script>history.go(-1);</script>');
34
        //            }
35
        //        }
36
        //        catch (\Exception $e) {
37
        //            echo 'Caught exception: ', $e->getMessage(), "\n", '<br>';
38
        //        }
39
        try {
40
            if (!\is_dir($folder)) {
41
                if (!\is_dir($folder) && !\mkdir($folder) && !\is_dir($folder)) {
42
                    throw new \RuntimeException(\sprintf('Unable to create the %s directory', $folder));
43
                }
44
                file_put_contents($folder . '/index.html', '<script>history.go(-1);</script>');
45
            }
46
        } catch (\Throwable $e) {
47
            echo 'Caught exception: ', $e->getMessage(), "\n", '<br>';
48
        }
49
    }
50
51
    /**
52
     * @param string $file
53
     * @param string $folder
54
     * @return bool
55
     */
56
    public static function copyFile(string $file, string $folder): bool
57
    {
58
        return \copy($file, $folder);
59
        //        try {
60
        //            if (!is_dir($folder)) {
61
        //                throw new \RuntimeException(sprintf('Unable to copy file as: %s ', $folder));
62
        //            } else {
63
        //                return copy($file, $folder);
64
        //            }
65
        //        } catch (\Exception $e) {
66
        //            echo 'Caught exception: ', $e->getMessage(), "\n", "<br>";
67
        //        }
68
        //        return false;
69
    }
70
71
    /**
72
     * @param $src
73
     * @param $dst
74
     */
75
    public static function recurseCopy($src, $dst): void
76
    {
77
        $dir = \opendir($src);
78
        //    @mkdir($dst);
79
        while (false !== ($file = \readdir($dir))) {
80
            if (('.' !== $file) && ('..' !== $file)) {
81
                if (\is_dir($src . '/' . $file)) {
82
                    self::recurseCopy($src . '/' . $file, $dst . '/' . $file);
83
                } else {
84
                    \copy($src . '/' . $file, $dst . '/' . $file);
85
                }
86
            }
87
        }
88
        \closedir($dir);
89
    }
90
91
    /**
92
     * Verifies XOOPS version meets minimum requirements for this module
93
     * @static
94
     * @param \XoopsModule|null $module
95
     *
96
     * @param null|string       $requiredVer
97
     * @return bool true if meets requirements, false if not
98
     */
99
    public static function checkVerXoops(\XoopsModule $module = null, $requiredVer = null): bool
100
    {
101
        $moduleDirName = \basename(\dirname(__DIR__));
102
        if (null === $module) {
103
            $module = \XoopsModule::getByDirname($moduleDirName);
104
        }
105
        \xoops_loadLanguage('admin', $moduleDirName);
106
        \xoops_loadLanguage('common', $moduleDirName);
107
108
        //check for minimum XOOPS version
109
        $currentVer = mb_substr(\XOOPS_VERSION, 6); // get the numeric part of string
110
        if (null === $requiredVer) {
111
            $requiredVer = '' . $module->getInfo('min_xoops'); //making sure it's a string
0 ignored issues
show
Are you sure $module->getInfo('min_xoops') of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

111
            $requiredVer = '' . /** @scrutinizer ignore-type */ $module->getInfo('min_xoops'); //making sure it's a string
Loading history...
112
        }
113
        $success = true;
114
115
        if (\version_compare($currentVer, $requiredVer, '<')) {
116
            $success = false;
117
            $module->setErrors(\sprintf(\_AM_SONGLIST_ERROR_BAD_XOOPS, $requiredVer, $currentVer));
118
        }
119
120
        return $success;
121
    }
122
123
    /**
124
     * Verifies PHP version meets minimum requirements for this module
125
     * @static
126
     * @param \XoopsModule|bool|null $module
127
     *
128
     * @return bool true if meets requirements, false if not
129
     */
130
    public static function checkVerPhp(\XoopsModule $module = null): bool
131
    {
132
        $moduleDirName      = \basename(\dirname(__DIR__, 2));
133
        $moduleDirNameUpper = \mb_strtoupper($moduleDirName);
134
        if (null === $module) {
135
            $module = \XoopsModule::getByDirname($moduleDirName);
136
        }
137
        \xoops_loadLanguage('admin', $moduleDirName);
138
        \xoops_loadLanguage('common', $moduleDirName);
139
140
        // check for minimum PHP version
141
        $success = true;
142
143
        $verNum = \PHP_VERSION;
144
        $reqVer = &$module->getInfo('min_php');
145
146
        if (false !== $reqVer && '' !== $reqVer) {
147
            if (\version_compare($verNum, $reqVer, '<')) {
0 ignored issues
show
It seems like $reqVer can also be of type array; however, parameter $version2 of version_compare() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

147
            if (\version_compare($verNum, /** @scrutinizer ignore-type */ $reqVer, '<')) {
Loading history...
148
                $module->setErrors(\sprintf(\constant('CO_' . $moduleDirNameUpper . '_ERROR_BAD_PHP'), $reqVer, $verNum));
0 ignored issues
show
It seems like $reqVer can also be of type array; however, parameter $values of sprintf() does only seem to accept double|integer|string, maybe add an additional type check? ( Ignorable by Annotation )

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

148
                $module->setErrors(\sprintf(\constant('CO_' . $moduleDirNameUpper . '_ERROR_BAD_PHP'), /** @scrutinizer ignore-type */ $reqVer, $verNum));
Loading history...
149
                $success = false;
150
            }
151
        }
152
153
        return $success;
154
    }
155
156
    /**
157
     * compares current module version with the latest GitHub release
158
     * @static
159
     *
160
     * @return string|array info about the latest module version, if newer
161
     */
162
    public static function checkVerModule(\Xmf\Module\Helper $helper, ?string $source = 'github', ?string $default = 'master'): ?array
163
    {
164
        $moduleDirName      = \basename(\dirname(__DIR__, 2));
165
        $moduleDirNameUpper = \mb_strtoupper($moduleDirName);
166
        $update             = '';
167
        $repository         = 'XoopsModules25x/' . $moduleDirName;
168
        //        $repository         = 'XoopsModules25x/publisher'; //for testing only
169
        $ret             = null;
170
        $infoReleasesUrl = "https://api.github.com/repos/$repository/releases";
171
        if ('github' === $source) {
172
            if (\function_exists('curl_init') && false !== ($curlHandle = \curl_init())) {
173
                \curl_setopt($curlHandle, \CURLOPT_URL, $infoReleasesUrl);
174
                \curl_setopt($curlHandle, \CURLOPT_RETURNTRANSFER, true);
175
                \curl_setopt($curlHandle, \CURLOPT_SSL_VERIFYPEER, true); //TODO: how to avoid an error when 'Peer's Certificate issuer is not recognized'
176
                \curl_setopt($curlHandle, \CURLOPT_HTTPHEADER, ["User-Agent:Publisher\r\n"]);
177
                $curlReturn = \curl_exec($curlHandle);
178
                if (false === $curlReturn) {
179
                    \trigger_error(\curl_error($curlHandle));
180
                } elseif (false !== \mb_strpos($curlReturn, 'Not Found')) {
0 ignored issues
show
It seems like $curlReturn can also be of type true; however, parameter $haystack of mb_strpos() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

180
                } elseif (false !== \mb_strpos(/** @scrutinizer ignore-type */ $curlReturn, 'Not Found')) {
Loading history...
181
                    \trigger_error('Repository Not Found: ' . $infoReleasesUrl);
182
                } else {
183
                    $file              = json_decode($curlReturn, false);
0 ignored issues
show
It seems like $curlReturn can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

183
                    $file              = json_decode(/** @scrutinizer ignore-type */ $curlReturn, false);
Loading history...
184
                    $latestVersionLink = \sprintf("https://github.com/$repository/archive/%s.zip", $file ? \reset($file)->tag_name : $default);
185
                    $latestVersion     = $file[0]->tag_name;
186
                    $prerelease        = $file[0]->prerelease;
187
                    if ('master' !== $latestVersionLink) {
188
                        $update = \constant('CO_' . $moduleDirNameUpper . '_' . 'NEW_VERSION') . $latestVersion;
189
                    }
190
                    //"PHP-standardized" version
191
                    $latestVersion = \mb_strtolower($latestVersion);
192
                    if (false !== mb_strpos($latestVersion, 'final')) {
193
                        $latestVersion = \str_replace('_', '', \mb_strtolower($latestVersion));
194
                        $latestVersion = \str_replace('final', '', \mb_strtolower($latestVersion));
195
                    }
196
                    $moduleVersion = ($helper->getModule()->getInfo('version') . '_' . $helper->getModule()->getInfo('module_status'));
0 ignored issues
show
Are you sure $helper->getModule()->getInfo('version') of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

196
                    $moduleVersion = (/** @scrutinizer ignore-type */ $helper->getModule()->getInfo('version') . '_' . $helper->getModule()->getInfo('module_status'));
Loading history...
Are you sure $helper->getModule()->getInfo('module_status') of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

196
                    $moduleVersion = ($helper->getModule()->getInfo('version') . '_' . /** @scrutinizer ignore-type */ $helper->getModule()->getInfo('module_status'));
Loading history...
197
                    //"PHP-standardized" version
198
                    $moduleVersion = \str_replace(' ', '', \mb_strtolower($moduleVersion));
199
                    //                    $moduleVersion = '1.0'; //for testing only
200
                    //                    $moduleDirName = 'publisher'; //for testing only
201
                    if (!$prerelease && \version_compare($moduleVersion, $latestVersion, '<')) {
202
                        $ret   = [];
203
                        $ret[] = $update;
204
                        $ret[] = $latestVersionLink;
205
                    }
206
                }
207
                \curl_close($curlHandle);
208
            }
209
        }
210
211
        return $ret;
212
    }
213
214
    //=========================================================
215
216
    /**
217
     * @return mixed
218
     */
219
    public static function getToken()
220
    {
221
        $sql    = 'SELECT md5(rand()/rand()*rand()/rand()*rand()*rand()/rand()*rand()) as `salt`';
222
        $result = $GLOBALS['xoopsDB']->queryF($sql);
223
        [$salt] = $GLOBALS['xoopsDB']->fetchRow($result);
224
225
        return $salt;
226
    }
227
228
    /**
229
     * @param $string
230
     * @return string
231
     */
232
    public static function ucword($string): string
233
    {
234
        $ret = [];
235
        foreach (explode(' ', \mb_strtolower($string)) as $part) {
236
            $ret[] = ucfirst($part);
237
        }
238
239
        return implode(' ', $ret);
240
    }
241
242
    /**
243
     * @param bool|string $ip
244
     * @return array
245
     */
246
    public static function getIPData($ip = false): array
247
    {
248
        $ret = [];
249
        if (is_object($GLOBALS['xoopsUser'])) {
250
            $ret['uid']   = $GLOBALS['xoopsUser']->getVar('uid');
251
            $ret['uname'] = $GLOBALS['xoopsUser']->getVar('uname');
252
            $ret['email'] = $GLOBALS['xoopsUser']->getVar('email');
253
        } else {
254
            $ret['uid']   = 0;
255
            $ret['uname'] = ($_REQUEST['uname'] ?? '');
256
            $ret['email'] = ($_REQUEST['email'] ?? '');
257
        }
258
        $ret['agent'] = $_SERVER['HTTP_USER_AGENT'];
259
        if ($ip) {
260
            $ret['is_proxied']   = false;
261
            $ret['network-addy'] = @gethostbyaddr($ip);
0 ignored issues
show
It seems like $ip can also be of type true; however, parameter $ip of gethostbyaddr() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

261
            $ret['network-addy'] = @gethostbyaddr(/** @scrutinizer ignore-type */ $ip);
Loading history...
262
            $ret['long']         = @ip2long($ip);
0 ignored issues
show
It seems like $ip can also be of type true; however, parameter $ip of ip2long() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

262
            $ret['long']         = @ip2long(/** @scrutinizer ignore-type */ $ip);
Loading history...
263
            if (isIpv6($ip)) {
0 ignored issues
show
The function isIpv6 was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

263
            if (/** @scrutinizer ignore-call */ isIpv6($ip)) {
Loading history...
264
                $ret['ip6'] = true;
265
                $ret['ip4'] = false;
266
            } else {
267
                $ret['ip4'] = true;
268
                $ret['ip6'] = false;
269
            }
270
            $ret['ip'] = $ip;
271
        } elseif (Request::hasVar('HTTP_X_FORWARDED_FOR', 'SERVER')) {
272
            $ip                  = $_SERVER['HTTP_X_FORWARDED_FOR'];
273
            $ret['is_proxied']   = true;
274
            $proxy_ip            = $_SERVER['REMOTE_ADDR'];
275
            $ret['network-addy'] = @gethostbyaddr($ip);
276
            $ret['long']         = @ip2long($ip);
277
            if (isIpv6($ip)) {
278
                $ret['ip6']       = true;
279
                $ret['proxy-ip6'] = true;
280
                $ret['ip4']       = false;
281
                $ret['proxy-ip4'] = false;
282
            } else {
283
                $ret['ip4']       = true;
284
                $ret['proxy-ip4'] = true;
285
                $ret['ip6']       = false;
286
                $ret['proxy-ip6'] = false;
287
            }
288
            $ret['ip']       = $ip;
289
            $ret['proxy-ip'] = $proxy_ip;
290
        } else {
291
            $ret['is_proxied']   = false;
292
            $ip                  = $_SERVER['REMOTE_ADDR'];
293
            $ret['network-addy'] = @gethostbyaddr($ip);
294
            $ret['long']         = @ip2long($ip);
295
            if (isIpv6($ip)) {
296
                $ret['ip6'] = true;
297
                $ret['ip4'] = false;
298
            } else {
299
                $ret['ip4'] = true;
300
                $ret['ip6'] = false;
301
            }
302
            $ret['ip'] = $ip;
303
        }
304
        $ret['made'] = time();
305
306
        return $ret;
307
    }
308
309
    /**
310
     * @param string $ip
311
     * @return bool
312
     */
313
    public static function isIpv6($ip = ''): bool
314
    {
315
        if ('' == $ip) {
316
            return false;
317
        }
318
319
        if (mb_substr_count($ip, ':') > 0) {
320
            return true;
321
        }
322
323
        return false;
324
    }
325
326
    /**
327
     * @param        $filter
328
     * @param        $field
329
     * @param string $sort
330
     * @param string $op
331
     * @param string $fct
332
     * @return bool|\XoopsModules\Songlist\Form\SelectAlbumForm|\XoopsModules\Songlist\Form\SelectArtistForm|\XoopsModules\Songlist\Form\SelectCategoryForm|\XoopsModules\Songlist\Form\SelectGenreForm|\XoopsModules\Songlist\Form\SelectVoiceForm
333
     */
334
    public static function getFilterElement($filter, $field, $sort = 'created', $op = '', $fct = '')
0 ignored issues
show
The parameter $fct is not used and could be removed. ( Ignorable by Annotation )

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

334
    public static function getFilterElement($filter, $field, $sort = 'created', $op = '', /** @scrutinizer ignore-unused */ $fct = '')

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

Loading history...
335
    {
336
        $components = static::getFilterURLComponents($filter, $field, $sort);
337
        $ele        = false;
338
        require_once dirname(__DIR__) . '/include/songlist.object.php';
339
        switch ($field) {
340
            case 'gid':
341
                if ('genre' !== $op) {
342
                    $ele = new SelectGenreForm('', 'filter_' . $field . '', $components['value'], 1, false);
343
                    $ele->setExtra(
344
                        'onchange="window.open(\''
345
                        . $_SERVER['SCRIPT_NAME']
346
                        . '?'
347
                        . $components['extra']
348
                        . '&filter='
349
                        . $components['filter']
350
                        . (!empty($components['filter']) ? '|' : '')
351
                        . $field
352
                        . ',\'+this.options[this.selectedIndex].value'
353
                        . (!empty($components['operator']) ? '+\','
354
                                                             . $components['operator']
355
                                                             . '\'' : '')
356
                        . ',\'_self\')"'
357
                    );
358
                }
359
                break;
360
            case 'vcid':
361
                if ('voice' !== $op) {
362
                    $ele = new SelectVoiceForm('', 'filter_' . $field . '', $components['value'], 1, false);
363
                    $ele->setExtra(
364
                        'onchange="window.open(\''
365
                        . $_SERVER['SCRIPT_NAME']
366
                        . '?'
367
                        . $components['extra']
368
                        . '&filter='
369
                        . $components['filter']
370
                        . (!empty($components['filter']) ? '|' : '')
371
                        . $field
372
                        . ',\'+this.options[this.selectedIndex].value'
373
                        . (!empty($components['operator']) ? '+\','
374
                                                             . $components['operator']
375
                                                             . '\'' : '')
376
                        . ',\'_self\')"'
377
                    );
378
                }
379
                break;
380
            case 'cid':
381
                if ('category' !== $op) {
382
                    $ele = new SelectCategoryForm('', 'filter_' . $field . '', $components['value'], 1, false);
383
                    $ele->setExtra(
384
                        'onchange="window.open(\''
385
                        . $_SERVER['SCRIPT_NAME']
386
                        . '?'
387
                        . $components['extra']
388
                        . '&filter='
389
                        . $components['filter']
390
                        . (!empty($components['filter']) ? '|' : '')
391
                        . $field
392
                        . ',\'+this.options[this.selectedIndex].value'
393
                        . (!empty($components['operator']) ? '+\','
394
                                                             . $components['operator']
395
                                                             . '\'' : '')
396
                        . ',\'_self\')"'
397
                    );
398
                }
399
                break;
400
            case 'pid':
401
                $ele = new SelectCategoryForm('', 'filter_' . $field . '', $components['value'], 1, false);
402
                $ele->setExtra(
403
                    'onchange="window.open(\''
404
                    . $_SERVER['SCRIPT_NAME']
405
                    . '?'
406
                    . $components['extra']
407
                    . '&filter='
408
                    . $components['filter']
409
                    . (!empty($components['filter']) ? '|' : '')
410
                    . $field
411
                    . ',\'+this.options[this.selectedIndex].value'
412
                    . (!empty($components['operator']) ? '+\','
413
                                                         . $components['operator']
414
                                                         . '\'' : '')
415
                    . ',\'_self\')"'
416
                );
417
                break;
418
            case 'abid':
419
                if ('albums' !== $op) {
420
                    $ele = new SelectAlbumForm('', 'filter_' . $field . '', $components['value'], 1, false);
421
                    $ele->setExtra(
422
                        'onchange="window.open(\''
423
                        . $_SERVER['SCRIPT_NAME']
424
                        . '?'
425
                        . $components['extra']
426
                        . '&filter='
427
                        . $components['filter']
428
                        . (!empty($components['filter']) ? '|' : '')
429
                        . $field
430
                        . ',\'+this.options[this.selectedIndex].value'
431
                        . (!empty($components['operator']) ? '+\','
432
                                                             . $components['operator']
433
                                                             . '\'' : '')
434
                        . ',\'_self\')"'
435
                    );
436
                }
437
                break;
438
            case 'aid':
439
                if ('artists' !== $op) {
440
                    $ele = new SelectArtistForm('', 'filter_' . $field . '', $components['value'], 1, false);
0 ignored issues
show
The type XoopsModules\Songlist\SelectArtistForm was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
441
                    $ele->setExtra(
442
                        'onchange="window.open(\''
443
                        . $_SERVER['SCRIPT_NAME']
444
                        . '?'
445
                        . $components['extra']
446
                        . '&filter='
447
                        . $components['filter']
448
                        . (!empty($components['filter']) ? '|' : '')
449
                        . $field
450
                        . ',\'+this.options[this.selectedIndex].value'
451
                        . (!empty($components['operator']) ? '+\','
452
                                                             . $components['operator']
453
                                                             . '\'' : '')
454
                        . ',\'_self\')"'
455
                    );
456
                }
457
                break;
458
            case 'sid':
459
                if ('songs' !== $op) {
460
                    $ele = new SelectSongForm('', 'filter_' . $field . '', $components['value'], 1, false);
461
                    $ele->setExtra(
462
                        'onchange="window.open(\''
463
                        . $_SERVER['SCRIPT_NAME']
464
                        . '?'
465
                        . $components['extra']
466
                        . '&filter='
467
                        . $components['filter']
468
                        . (!empty($components['filter']) ? '|' : '')
469
                        . $field
470
                        . ',\'+this.options[this.selectedIndex].value'
471
                        . (!empty($components['operator']) ? '+\','
472
                                                             . $components['operator']
473
                                                             . '\'' : '')
474
                        . ',\'_self\')"'
475
                    );
476
                }
477
                break;
478
            case 'name':
479
            case 'title':
480
            case 'artists':
481
            case 'albums':
482
            case 'songs':
483
            case 'hits':
484
            case 'rank':
485
            case 'votes':
486
            case 'description':
487
            case 'lyrics':
488
            case 'songid':
489
            case 'tags':
490
                $ele = new \XoopsFormElementTray('');
491
                $ele->addElement(new \XoopsFormText('', 'filter_' . $field . '', 11, 40, $components['value']));
492
                $button = new \XoopsFormButton('', 'button_' . $field . '', '[+]');
493
                $button->setExtra(
494
                    'onclick="window.open(\''
495
                    . $_SERVER['SCRIPT_NAME']
496
                    . '?'
497
                    . $components['extra']
498
                    . '&filter='
499
                    . $components['filter']
500
                    . (!empty($components['filter']) ? '|' : '')
501
                    . $field
502
                    . ',\'+$(\'#filter_'
503
                    . $field
504
                    . '\').val()'
505
                    . (!empty($components['operator']) ? '+\','
506
                                                         . $components['operator']
507
                                                         . '\'' : '')
508
                    . ',\'_self\')"'
509
                );
510
                $ele->addElement($button);
511
                break;
512
        }
513
514
        return $ele;
515
    }
516
517
    /**
518
     * @param        $filter
519
     * @param        $field
520
     * @param string $sort
521
     * @return array
522
     */
523
    public static function getFilterURLComponents($filter, $field, $sort = 'created'): array
524
    {
525
        $parts     = explode('|', $filter);
526
        $ret       = [];
527
        $value     = '';
0 ignored issues
show
The assignment to $value is dead and can be removed.
Loading history...
528
        $ele_value = '';
529
        $operator  = '';
530
        foreach ($parts as $part) {
531
            $var = explode(',', $part);
532
            if (count($var) > 1) {
533
                if ($var[0] == $field) {
534
                    $ele_value = $var[1];
535
                    if (isset($var[2])) {
536
                        $operator = $var[2];
537
                    }
538
                } elseif (1 != $var[0]) {
539
                    $ret[] = implode(',', $var);
540
                }
541
            }
542
        }
543
        $pagenav          = [];
544
        $pagenav['op']    = $_REQUEST['op'] ?? 'videos';
545
        $pagenav['fct']   = $_REQUEST['fct'] ?? 'list';
546
        $pagenav['limit'] = Request::getInt('limit', 30, 'REQUEST');
547
        $pagenav['start'] = 0;
548
        $pagenav['order'] = !empty($_REQUEST['order']) ? $_REQUEST['order'] : 'DESC';
549
        $pagenav['sort']  = !empty($_REQUEST['sort']) ? '' . $_REQUEST['sort'] . '' : $sort;
550
        $retb             = [];
551
        foreach ($pagenav as $key => $value) {
552
            $retb[] = "$key=$value";
553
        }
554
555
        return ['value' => $ele_value, 'field' => $field, 'operator' => $operator, 'filter' => implode('|', $ret), 'extra' => implode('&', $retb)];
556
    }
557
558
    /**
559
     * @param $objects
560
     * @return array
561
     */
562
    public static function obj2array($objects): array
563
    {
564
        $ret = [];
565
        foreach ((array)$objects as $key => $value) {
566
            if (is_a($value, 'stdClass')) {
567
                $ret[$key] = static::obj2array($value);
568
            } elseif (is_array($value)) {
569
                $ret[$key] = static::obj2array($value);
570
            } else {
571
                $ret[$key] = $value;
572
            }
573
        }
574
575
        return $ret;
576
    }
577
578
    /**
579
     * @param $url
580
     * @return mixed
581
     */
582
    public static function shortenUrl($url)
583
    {
584
        /** @var \XoopsModuleHandler $moduleHandler */
585
        $moduleHandler = xoops_getHandler('module');
586
        /** @var \XoopsConfigHandler $configHandler */
587
        $configHandler                   = xoops_getHandler('config');
588
        $GLOBALS['songlistModule']       = $moduleHandler->getByDirname('songlist');
589
        $GLOBALS['songlistModuleConfig'] = $configHandler->getConfigList($GLOBALS['songlistModule']->getVar('mid'));
590
591
        if (!empty($GLOBALS['songlistModuleConfig']['bitly_username']) && !empty($GLOBALS['songlistModuleConfig']['bitly_apikey'])) {
592
            $source_url = $GLOBALS['songlistModuleConfig']['bitly_apiurl'] . '/shorten?login=' . $GLOBALS['songlistModuleConfig']['bitly_username'] . '&apiKey=' . $GLOBALS['songlistModuleConfig']['bitly_apikey'] . '&format=json&longUrl=' . urlencode($url);
593
            $cookies    = XOOPS_ROOT_PATH . '/uploads/songlist_' . md5($GLOBALS['songlistModuleConfig']['bitly_apikey']) . '.cookie';
594
            if (!$ch = curl_init($source_url)) {
595
                return $url;
596
            }
597
            curl_setopt($ch, CURLOPT_COOKIEJAR, $cookies);
598
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
599
            curl_setopt($ch, CURLOPT_USERAGENT, $GLOBALS['songlistModuleConfig']['user_agent']);
600
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $GLOBALS['songlistModuleConfig']['curl_connect_timeout']);
601
            curl_setopt($ch, CURLOPT_TIMEOUT, $GLOBALS['songlistModuleConfig']['curl_timeout']);
602
            $data = curl_exec($ch);
603
            curl_close($ch);
604
            $result                = songlist_object2array(json_decode($data));
0 ignored issues
show
It seems like $data can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

604
            $result                = songlist_object2array(json_decode(/** @scrutinizer ignore-type */ $data));
Loading history...
The function songlist_object2array was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

604
            $result                = /** @scrutinizer ignore-call */ songlist_object2array(json_decode($data));
Loading history...
605
            $result['status_code'] = 200;
606
            if ($result['status_code']) {
607
                if (!empty($result['data']['url'])) {
608
                    return $result['data']['url'];
609
                }
610
611
                return $url;
612
            }
613
614
            return $url;
615
        }
616
617
        return $url;
618
    }
619
620
    /**
621
     * @param        $contents
622
     * @param int    $get_attributes
623
     * @param string $priority
624
     * @return array|void
625
     */
626
    public static function xml2array($contents, $get_attributes = 1, $priority = 'tag')
627
    {
628
        if (!$contents) {
629
            return [];
630
        }
631
632
        if (!function_exists('xml_parser_create')) {
633
            return [];
634
        }
635
636
        //Get the XML parser of PHP - PHP must have this module for the parser to work
637
        $parser = xml_parser_create('');
638
        xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8'); # https://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss
639
        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
640
        xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
641
        xml_parse_into_struct($parser, trim($contents), $xml_values);
642
        xml_parser_free($parser);
643
644
        if (!$xml_values) {
645
            return;
646
        }//Hmm...
647
648
        //Initializations
649
        $xml_array   = [];
650
        $parents     = [];
0 ignored issues
show
The assignment to $parents is dead and can be removed.
Loading history...
651
        $opened_tags = [];
0 ignored issues
show
The assignment to $opened_tags is dead and can be removed.
Loading history...
652
        $arr         = [];
0 ignored issues
show
The assignment to $arr is dead and can be removed.
Loading history...
653
654
        $current = &$xml_array; //Refference
655
656
        //Go through the tags.
657
        $repeated_tag_index = []; //Multiple tags with same name will be turned into an array
658
        foreach ($xml_values as $data) {
659
            unset($attributes, $value); //Remove existing values, or there will be trouble
660
661
            //This command will extract these variables into the foreach scope
662
            // tag(string), type(string), level(int), attributes(array).
663
            extract($data); //We could use the array by itself, but this cooler.
664
665
            $result          = [];
666
            $attributes_data = [];
667
668
            if (isset($value)) {
669
                if ('tag' === $priority) {
670
                    $result = $value;
671
                } else {
672
                    $result['value'] = $value;
673
                } //Put the value in a assoc array if we are in the 'Attribute' mode
674
            }
675
676
            //Set the attributes too.
677
            if (isset($attributes) and $get_attributes) {
678
                foreach ($attributes as $attr => $val) {
679
                    if ('tag' === $priority) {
680
                        $attributes_data[$attr] = $val;
681
                    } else {
682
                        $result['attr'][$attr] = $val;
683
                    } //Set all the attributes in a array called 'attr'
684
                }
685
            }
686
687
            //See tag status and do the needed.
688
            if ('open' === $type) {//The starting of the tag '<tag>'
689
                $parent[$level - 1] = &$current;
690
                if (!is_array($current) or (!array_key_exists($tag, $current))) { //Insert New tag
691
                    $current[$tag] = $result;
692
                    if ($attributes_data) {
693
                        $current[$tag . '_attr'] = $attributes_data;
694
                    }
695
                    $repeated_tag_index[$tag . '_' . $level] = 1;
696
697
                    $current = &$current[$tag];
698
                } else { //There was another element with the same tag name
699
                    if (isset($current[$tag][0])) {//If there is a 0th element it is already an array
700
                        $current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
701
                        $repeated_tag_index[$tag . '_' . $level]++;
702
                    } else {//This section will make the value an array if multiple tags with the same name appear together
703
                        $current[$tag]                           = [$current[$tag], $result]; //This will combine the existing item and the new item together to make an array
704
                        $repeated_tag_index[$tag . '_' . $level] = 2;
705
706
                        if (isset($current[$tag . '_attr'])) { //The attribute of the last(0th) tag must be moved as well
707
                            $current[$tag]['0_attr'] = $current[$tag . '_attr'];
708
                            unset($current[$tag . '_attr']);
709
                        }
710
                    }
711
                    $last_item_index = $repeated_tag_index[$tag . '_' . $level] - 1;
712
                    $current         = &$current[$tag][$last_item_index];
713
                }
714
            } elseif ('complete' === $type) { //Tags that ends in 1 line '<tag>'
715
                //See if the key is already taken.
716
                if (isset($current[$tag])) { //If taken, put all things inside a list(array)
717
                    if (isset($current[$tag][0]) and is_array($current[$tag])) {//If it is already an array...
718
                        // ...push the new element into that array.
719
                        $current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
720
721
                        if ('tag' === $priority and $get_attributes and $attributes_data) {
722
                            $current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
723
                        }
724
                        $repeated_tag_index[$tag . '_' . $level]++;
725
                    } else { //If it is not an array...
726
                        $current[$tag]                           = [$current[$tag], $result]; //...Make it an array using using the existing value and the new value
727
                        $repeated_tag_index[$tag . '_' . $level] = 1;
728
                        if ('tag' === $priority and $get_attributes) {
729
                            if (isset($current[$tag . '_attr'])) { //The attribute of the last(0th) tag must be moved as well
730
                                $current[$tag]['0_attr'] = $current[$tag . '_attr'];
731
                                unset($current[$tag . '_attr']);
732
                            }
733
734
                            if ($attributes_data) {
735
                                $current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
736
                            }
737
                        }
738
                        $repeated_tag_index[$tag . '_' . $level]++; //0 and 1 index is already taken
739
                    }
740
                } else { //New Key
741
                    $current[$tag]                           = $result;
742
                    $repeated_tag_index[$tag . '_' . $level] = 1;
743
                    if ('tag' === $priority and $attributes_data) {
744
                        $current[$tag . '_attr'] = $attributes_data;
745
                    }
746
                }
747
            } elseif ('close' === $type) { //End of tag '</tag>'
748
                $current = &$parent[$level - 1];
749
            }
750
        }
751
752
        return $xml_array;
753
    }
754
755
    /**
756
     * @param $array
757
     * @param $name
758
     * @param $standalone
759
     * @param $beginning
760
     * @param $nested
761
     * @return string
762
     */
763
    public static function toXml($array, $name, $standalone, $beginning, $nested): string
764
    {
765
        $output = '';
766
        if ($beginning) {
767
            if ($standalone) {
768
                header('content-type:text/xml;charset=' . _CHARSET);
769
            }
770
            $output .= '<' . '?' . 'xml version="1.0" encoding="' . _CHARSET . '"' . '?' . '>' . "\n";
771
            $output .= '<' . $name . '>' . "\n";
772
            $nested = 0;
773
        }
774
775
        if (is_array($array)) {
776
            foreach ($array as $key => $value) {
777
                ++$nested;
778
                if (is_array($value)) {
779
                    $output .= str_repeat("\t", (int)$nested) . '<' . (is_string($key) ? $key : $name . '_' . $key) . '>' . "\n";
780
                    ++$nested;
781
                    $output .= static::toXml($value, $name, false, false, $nested);
782
                    $nested--;
783
                    $output .= str_repeat("\t", (int)$nested) . '</' . (is_string($key) ? $key : $name . '_' . $key) . '>' . "\n";
784
                } elseif ('' != $value) {
785
                    ++$nested;
786
                    $output .= str_repeat("\t", (int)$nested) . '  <' . (is_string($key) ? $key : $name . '_' . $key) . '>' . trim($value) . '</' . (is_string($key) ? $key : $name . '_' . $key) . '>' . "\n";
787
                    $nested--;
788
                }
789
                $nested--;
790
            }
791
        } elseif ('' != $array) {
792
            ++$nested;
793
            $output .= str_repeat("\t", (int)$nested) . trim($array) . "\n";
794
            $nested--;
795
        }
796
797
        if ($beginning) {
798
            $output .= '</' . $name . '>';
799
800
            return $output;
801
        }
802
803
        return $output;
804
    }
805
    
806
}
807