Utility   F
last analyzed

Complexity

Total Complexity 150

Size/Duplication

Total Lines 785
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 475
c 1
b 0
f 0
dl 0
loc 785
rs 2
wmc 150

16 Methods

Rating   Name   Duplication   Size   Complexity  
C toXml() 0 41 13
F getFilterElement() 0 181 42
B getFilterURLComponents() 0 33 9
A recurseCopy() 0 14 5
B shortenUrl() 0 36 6
A getToken() 0 7 1
A checkVerPhp() 0 24 5
A ucword() 0 8 2
D xml2array() 0 127 31
A copyFile() 0 3 1
A checkVerXoops() 0 22 4
B getIPData() 0 61 7
B checkVerModule() 0 50 11
A isIpv6() 0 11 3
A createFolder() 0 21 6
A obj2array() 0 14 4

How to fix   Complexity   

Complex Class

Complex classes like Utility often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Utility, and based on these observations, apply Extract Interface, too.

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
Bug introduced by
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
Bug introduced by
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
Bug introduced by
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
Bug introduced by
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
Bug introduced by
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
Bug introduced by
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...
Bug introduced by
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
Bug introduced by
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
Bug introduced by
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
Bug introduced by
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
Unused Code introduced by
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
Bug introduced by
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
Unused Code introduced by
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
Bug introduced by
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...
Bug introduced by
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
Unused Code introduced by
The assignment to $parents is dead and can be removed.
Loading history...
651
        $opened_tags = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $opened_tags is dead and can be removed.
Loading history...
652
        $arr         = [];
0 ignored issues
show
Unused Code introduced by
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