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.

include/functions.php (10 issues)

1
<?php declare(strict_types=1);
2
3
use Xmf\Request;
4
use XoopsModules\Songlist\Form\SelectAlbumForm;
5
use XoopsModules\Songlist\Form\SelectArtistForm;
6
use XoopsModules\Songlist\Form\SelectCategoryForm;
7
use XoopsModules\Songlist\Form\SelectGenreForm;
8
use XoopsModules\Songlist\Form\SelectSongForm;
9
use XoopsModules\Songlist\Form\SelectVoiceForm;
10
11
if (!function_exists('songlist_getToken')) {
12
    /**
13
     * @return mixed
14
     */
15
    function songlist_getToken()
16
    {
17
        $sql    = 'SELECT md5(rand()/rand()*rand()/rand()*rand()*rand()/rand()*rand()) as `salt`';
18
        $result = $GLOBALS['xoopsDB']->queryF($sql);
19
        [$salt] = $GLOBALS['xoopsDB']->fetchRow($result);
20
21
        return $salt;
22
    }
23
}
24
25
if (!function_exists('ucword')) {
26
    /**
27
     * @param $string
28
     * @return string
29
     */
30
    function ucword($string): string
31
    {
32
        $ret = [];
33
        foreach (explode(' ', \mb_strtolower($string)) as $part) {
34
            $ret[] = ucfirst($part);
35
        }
36
37
        return implode(' ', $ret);
38
    }
39
}
40
41
if (!function_exists('songlist_getIPData')) {
42
    /**
43
     * @param bool|string $ip
44
     * @return array
45
     */
46
    function songlist_getIPData($ip = false): array
47
    {
48
        $ret = [];
49
        if (is_object($GLOBALS['xoopsUser'])) {
50
            $ret['uid']   = $GLOBALS['xoopsUser']->getVar('uid');
51
            $ret['uname'] = $GLOBALS['xoopsUser']->getVar('uname');
52
            $ret['email'] = $GLOBALS['xoopsUser']->getVar('email');
53
        } else {
54
            $ret['uid']   = 0;
55
            $ret['uname'] = ($_REQUEST['uname'] ?? '');
56
            $ret['email'] = ($_REQUEST['email'] ?? '');
57
        }
58
        $ret['agent'] = $_SERVER['HTTP_USER_AGENT'];
59
        if ($ip) {
60
            $ret['is_proxied']   = false;
61
            $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

61
            $ret['network-addy'] = @gethostbyaddr(/** @scrutinizer ignore-type */ $ip);
Loading history...
62
            $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

62
            $ret['long']         = @ip2long(/** @scrutinizer ignore-type */ $ip);
Loading history...
63
            if (is_ipv6($ip)) {
0 ignored issues
show
It seems like $ip can also be of type true; however, parameter $ip of is_ipv6() 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

63
            if (is_ipv6(/** @scrutinizer ignore-type */ $ip)) {
Loading history...
64
                $ret['ip6'] = true;
65
                $ret['ip4'] = false;
66
            } else {
67
                $ret['ip4'] = true;
68
                $ret['ip6'] = false;
69
            }
70
            $ret['ip'] = $ip;
71
        } elseif (Request::hasVar('HTTP_X_FORWARDED_FOR', 'SERVER')) {
72
                $ip                  = $_SERVER['HTTP_X_FORWARDED_FOR'];
73
                $ret['is_proxied']   = true;
74
                $proxy_ip            = $_SERVER['REMOTE_ADDR'];
75
                $ret['network-addy'] = @gethostbyaddr($ip);
76
                $ret['long']         = @ip2long($ip);
77
                if (is_ipv6($ip)) {
78
                    $ret['ip6']       = true;
79
                    $ret['proxy-ip6'] = true;
80
                    $ret['ip4']       = false;
81
                    $ret['proxy-ip4'] = false;
82
                } else {
83
                    $ret['ip4']       = true;
84
                    $ret['proxy-ip4'] = true;
85
                    $ret['ip6']       = false;
86
                    $ret['proxy-ip6'] = false;
87
                }
88
                $ret['ip']       = $ip;
89
                $ret['proxy-ip'] = $proxy_ip;
90
            } else {
91
                $ret['is_proxied']   = false;
92
                $ip                  = $_SERVER['REMOTE_ADDR'];
93
                $ret['network-addy'] = @gethostbyaddr($ip);
94
                $ret['long']         = @ip2long($ip);
95
                if (is_ipv6($ip)) {
96
                    $ret['ip6'] = true;
97
                    $ret['ip4'] = false;
98
                } else {
99
                    $ret['ip4'] = true;
100
                    $ret['ip6'] = false;
101
                }
102
                $ret['ip'] = $ip;
103
        }
104
        $ret['made'] = time();
105
106
        return $ret;
107
    }
108
}
109
110
if (!function_exists('is_ipv6')) {
111
    /**
112
     * @param string $ip
113
     * @return bool
114
     */
115
    function is_ipv6($ip = ''): bool
116
    {
117
        if ('' == $ip) {
118
            return false;
119
        }
120
121
        if (mb_substr_count($ip, ':') > 0) {
122
            return true;
123
        }
124
125
        return false;
126
    }
127
}
128
129
if (!function_exists('songlist_getFilterElement')) {
130
    /**
131
     * @param        $filter
132
     * @param        $field
133
     * @param string $sort
134
     * @param string $op
135
     * @param string $fct
136
     * @return bool|\XoopsModules\Songlist\Form\SelectAlbumForm|\XoopsModules\Songlist\Form\SelectArtistForm|\XoopsModules\Songlist\Form\SelectCategoryForm|\XoopsModules\Songlist\Form\SelectGenreForm|\XoopsModules\Songlist\Form\SelectVoiceForm
137
     */
138
    function songlist_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

138
    function songlist_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...
139
    {
140
        $components = songlist_getFilterURLComponents($filter, $field, $sort);
141
        $ele        = false;
142
        require_once __DIR__ . '/songlist.object.php';
143
        switch ($field) {
144
            case 'gid':
145
                if ('genre' !== $op) {
146
                    $ele = new SelectGenreForm('', 'filter_' . $field . '', $components['value'], 1, false);
147
                    $ele->setExtra(
148
                        'onchange="window.open(\''
149
                        . $_SERVER['SCRIPT_NAME']
150
                        . '?'
151
                        . $components['extra']
152
                        . '&filter='
153
                        . $components['filter']
154
                        . (!empty($components['filter']) ? '|' : '')
155
                        . $field
156
                        . ',\'+this.options[this.selectedIndex].value'
157
                        . (!empty($components['operator']) ? '+\','
158
                                                             . $components['operator']
159
                                                             . '\'' : '')
160
                        . ',\'_self\')"'
161
                    );
162
                }
163
                break;
164
            case 'vcid':
165
                if ('voice' !== $op) {
166
                    $ele = new SelectVoiceForm('', 'filter_' . $field . '', $components['value'], 1, false);
167
                    $ele->setExtra(
168
                        'onchange="window.open(\''
169
                        . $_SERVER['SCRIPT_NAME']
170
                        . '?'
171
                        . $components['extra']
172
                        . '&filter='
173
                        . $components['filter']
174
                        . (!empty($components['filter']) ? '|' : '')
175
                        . $field
176
                        . ',\'+this.options[this.selectedIndex].value'
177
                        . (!empty($components['operator']) ? '+\','
178
                                                             . $components['operator']
179
                                                             . '\'' : '')
180
                        . ',\'_self\')"'
181
                    );
182
                }
183
                break;
184
            case 'cid':
185
                if ('category' !== $op) {
186
                    $ele = new SelectCategoryForm('', 'filter_' . $field . '', $components['value'], 1, false);
187
                    $ele->setExtra(
188
                        'onchange="window.open(\''
189
                        . $_SERVER['SCRIPT_NAME']
190
                        . '?'
191
                        . $components['extra']
192
                        . '&filter='
193
                        . $components['filter']
194
                        . (!empty($components['filter']) ? '|' : '')
195
                        . $field
196
                        . ',\'+this.options[this.selectedIndex].value'
197
                        . (!empty($components['operator']) ? '+\','
198
                                                             . $components['operator']
199
                                                             . '\'' : '')
200
                        . ',\'_self\')"'
201
                    );
202
                }
203
                break;
204
            case 'pid':
205
                $ele = new SelectCategoryForm('', 'filter_' . $field . '', $components['value'], 1, false);
206
                $ele->setExtra(
207
                    'onchange="window.open(\''
208
                    . $_SERVER['SCRIPT_NAME']
209
                    . '?'
210
                    . $components['extra']
211
                    . '&filter='
212
                    . $components['filter']
213
                    . (!empty($components['filter']) ? '|' : '')
214
                    . $field
215
                    . ',\'+this.options[this.selectedIndex].value'
216
                    . (!empty($components['operator']) ? '+\','
217
                                                         . $components['operator']
218
                                                         . '\'' : '')
219
                    . ',\'_self\')"'
220
                );
221
                break;
222
            case 'abid':
223
                if ('albums' !== $op) {
224
                    $ele = new SelectAlbumForm('', 'filter_' . $field . '', $components['value'], 1, false);
225
                    $ele->setExtra(
226
                        'onchange="window.open(\''
227
                        . $_SERVER['SCRIPT_NAME']
228
                        . '?'
229
                        . $components['extra']
230
                        . '&filter='
231
                        . $components['filter']
232
                        . (!empty($components['filter']) ? '|' : '')
233
                        . $field
234
                        . ',\'+this.options[this.selectedIndex].value'
235
                        . (!empty($components['operator']) ? '+\','
236
                                                             . $components['operator']
237
                                                             . '\'' : '')
238
                        . ',\'_self\')"'
239
                    );
240
                }
241
                break;
242
            case 'aid':
243
                if ('artists' !== $op) {
244
                    $ele = new SelectArtistForm('', 'filter_' . $field . '', $components['value'], 1, false);
245
                    $ele->setExtra(
246
                        'onchange="window.open(\''
247
                        . $_SERVER['SCRIPT_NAME']
248
                        . '?'
249
                        . $components['extra']
250
                        . '&filter='
251
                        . $components['filter']
252
                        . (!empty($components['filter']) ? '|' : '')
253
                        . $field
254
                        . ',\'+this.options[this.selectedIndex].value'
255
                        . (!empty($components['operator']) ? '+\','
256
                                                             . $components['operator']
257
                                                             . '\'' : '')
258
                        . ',\'_self\')"'
259
                    );
260
                }
261
                break;
262
            case 'sid':
263
                if ('songs' !== $op) {
264
                    $ele = new SelectSongForm('', 'filter_' . $field . '', $components['value'], 1, false);
265
                    $ele->setExtra(
266
                        'onchange="window.open(\''
267
                        . $_SERVER['SCRIPT_NAME']
268
                        . '?'
269
                        . $components['extra']
270
                        . '&filter='
271
                        . $components['filter']
272
                        . (!empty($components['filter']) ? '|' : '')
273
                        . $field
274
                        . ',\'+this.options[this.selectedIndex].value'
275
                        . (!empty($components['operator']) ? '+\','
276
                                                             . $components['operator']
277
                                                             . '\'' : '')
278
                        . ',\'_self\')"'
279
                    );
280
                }
281
                break;
282
            case 'name':
283
            case 'title':
284
            case 'artists':
285
            case 'albums':
286
            case 'songs':
287
            case 'hits':
288
            case 'rank':
289
            case 'votes':
290
            case 'description':
291
            case 'lyrics':
292
            case 'songid':
293
            case 'tags':
294
                $ele = new \XoopsFormElementTray('');
295
                $ele->addElement(new \XoopsFormText('', 'filter_' . $field . '', 11, 40, $components['value']));
296
                $button = new \XoopsFormButton('', 'button_' . $field . '', '[+]');
297
                $button->setExtra(
298
                    'onclick="window.open(\''
299
                    . $_SERVER['SCRIPT_NAME']
300
                    . '?'
301
                    . $components['extra']
302
                    . '&filter='
303
                    . $components['filter']
304
                    . (!empty($components['filter']) ? '|' : '')
305
                    . $field
306
                    . ',\'+$(\'#filter_'
307
                    . $field
308
                    . '\').val()'
309
                    . (!empty($components['operator']) ? '+\','
310
                                                         . $components['operator']
311
                                                         . '\'' : '')
312
                    . ',\'_self\')"'
313
                );
314
                $ele->addElement($button);
315
                break;
316
        }
317
318
        return $ele;
319
    }
320
}
321
322
if (!function_exists('songlist_getFilterURLComponents')) {
323
    /**
324
     * @param        $filter
325
     * @param        $field
326
     * @param string $sort
327
     * @return array
328
     */
329
    function songlist_getFilterURLComponents($filter, $field, $sort = 'created'): array
330
    {
331
        $parts     = explode('|', $filter);
332
        $ret       = [];
333
        $value     = '';
0 ignored issues
show
The assignment to $value is dead and can be removed.
Loading history...
334
        $ele_value = '';
335
        $operator  = '';
336
        foreach ($parts as $part) {
337
            $var = explode(',', $part);
338
            if (count($var) > 1) {
339
                if ($var[0] == $field) {
340
                    $ele_value = $var[1];
341
                    if (isset($var[2])) {
342
                        $operator = $var[2];
343
                    }
344
                } elseif (1 != $var[0]) {
345
                    $ret[] = implode(',', $var);
346
                }
347
            }
348
        }
349
        $pagenav          = [];
350
        $pagenav['op']    = $_REQUEST['op'] ?? 'videos';
351
        $pagenav['fct']   = $_REQUEST['fct'] ?? 'list';
352
        $pagenav['limit'] = Request::getInt('limit', 30, 'REQUEST');
353
        $pagenav['start'] = 0;
354
        $pagenav['order'] = !empty($_REQUEST['order']) ? $_REQUEST['order'] : 'DESC';
355
        $pagenav['sort']  = !empty($_REQUEST['sort']) ? '' . $_REQUEST['sort'] . '' : $sort;
356
        $retb             = [];
357
        foreach ($pagenav as $key => $value) {
358
            $retb[] = "$key=$value";
359
        }
360
361
        return ['value' => $ele_value, 'field' => $field, 'operator' => $operator, 'filter' => implode('|', $ret), 'extra' => implode('&', $retb)];
362
    }
363
}
364
365
if (!function_exists('songlist_obj2array')) {
366
    /**
367
     * @param $objects
368
     * @return array
369
     */
370
    function songlist_obj2array($objects): array
371
    {
372
        $ret = [];
373
        foreach ((array)$objects as $key => $value) {
374
            if (is_a($value, 'stdClass')) {
375
                $ret[$key] = songlist_obj2array($value);
376
            } elseif (is_array($value)) {
377
                $ret[$key] = songlist_obj2array($value);
378
            } else {
379
                $ret[$key] = $value;
380
            }
381
        }
382
383
        return $ret;
384
    }
385
}
386
387
if (!function_exists('songlist_shortenurl')) {
388
    /**
389
     * @param $url
390
     * @return mixed
391
     */
392
    function songlist_shortenurl($url)
393
    {
394
        /** @var \XoopsModuleHandler $moduleHandler */
395
        $moduleHandler = xoops_getHandler('module');
396
        /** @var \XoopsConfigHandler $configHandler */
397
        $configHandler                   = xoops_getHandler('config');
398
        $GLOBALS['songlistModule']       = $moduleHandler->getByDirname('songlist');
399
        $GLOBALS['songlistModuleConfig'] = $configHandler->getConfigList($GLOBALS['songlistModule']->getVar('mid'));
400
401
        if (!empty($GLOBALS['songlistModuleConfig']['bitly_username']) && !empty($GLOBALS['songlistModuleConfig']['bitly_apikey'])) {
402
            $source_url = $GLOBALS['songlistModuleConfig']['bitly_apiurl'] . '/shorten?login=' . $GLOBALS['songlistModuleConfig']['bitly_username'] . '&apiKey=' . $GLOBALS['songlistModuleConfig']['bitly_apikey'] . '&format=json&longUrl=' . urlencode($url);
403
            $cookies    = XOOPS_ROOT_PATH . '/uploads/songlist_' . md5($GLOBALS['songlistModuleConfig']['bitly_apikey']) . '.cookie';
404
            if (!$ch = curl_init($source_url)) {
405
                return $url;
406
            }
407
            curl_setopt($ch, CURLOPT_COOKIEJAR, $cookies);
408
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
409
            curl_setopt($ch, CURLOPT_USERAGENT, $GLOBALS['songlistModuleConfig']['user_agent']);
410
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $GLOBALS['songlistModuleConfig']['curl_connect_timeout']);
411
            curl_setopt($ch, CURLOPT_TIMEOUT, $GLOBALS['songlistModuleConfig']['curl_timeout']);
412
            $data = curl_exec($ch);
413
            curl_close($ch);
414
            $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

414
            $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

414
            $result                = /** @scrutinizer ignore-call */ songlist_object2array(json_decode($data));
Loading history...
415
            $result['status_code'] = 200;
416
            if ($result['status_code']) {
417
                if (!empty($result['data']['url'])) {
418
                    return $result['data']['url'];
419
                }
420
421
                return $url;
422
            }
423
424
            return $url;
425
        }
426
427
        return $url;
428
    }
429
}
430
431
if (!function_exists('songlist_xml2array')) {
432
    /**
433
     * @param        $contents
434
     * @param int    $get_attributes
435
     * @param string $priority
436
     * @return array|void
437
     */
438
    function songlist_xml2array($contents, $get_attributes = 1, $priority = 'tag')
439
    {
440
        if (!$contents) {
441
            return [];
442
        }
443
444
        if (!function_exists('xml_parser_create')) {
445
            return [];
446
        }
447
448
        //Get the XML parser of PHP - PHP must have this module for the parser to work
449
        $parser = xml_parser_create('');
450
        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
451
        xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
452
        xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
453
        xml_parse_into_struct($parser, trim($contents), $xml_values);
454
        xml_parser_free($parser);
455
456
        if (!$xml_values) {
457
            return;
458
        }//Hmm...
459
460
        //Initializations
461
        $xml_array   = [];
462
        $parents     = [];
0 ignored issues
show
The assignment to $parents is dead and can be removed.
Loading history...
463
        $opened_tags = [];
0 ignored issues
show
The assignment to $opened_tags is dead and can be removed.
Loading history...
464
        $arr         = [];
0 ignored issues
show
The assignment to $arr is dead and can be removed.
Loading history...
465
466
        $current = &$xml_array; //Refference
467
468
        //Go through the tags.
469
        $repeated_tag_index = []; //Multiple tags with same name will be turned into an array
470
        foreach ($xml_values as $data) {
471
            unset($attributes, $value); //Remove existing values, or there will be trouble
472
473
            //This command will extract these variables into the foreach scope
474
            // tag(string), type(string), level(int), attributes(array).
475
            extract($data); //We could use the array by itself, but this cooler.
476
477
            $result          = [];
478
            $attributes_data = [];
479
480
            if (isset($value)) {
481
                if ('tag' === $priority) {
482
                    $result = $value;
483
                } else {
484
                    $result['value'] = $value;
485
                } //Put the value in a assoc array if we are in the 'Attribute' mode
486
            }
487
488
            //Set the attributes too.
489
            if (isset($attributes) and $get_attributes) {
490
                foreach ($attributes as $attr => $val) {
491
                    if ('tag' === $priority) {
492
                        $attributes_data[$attr] = $val;
493
                    } else {
494
                        $result['attr'][$attr] = $val;
495
                    } //Set all the attributes in a array called 'attr'
496
                }
497
            }
498
499
            //See tag status and do the needed.
500
            if ('open' === $type) {//The starting of the tag '<tag>'
501
                $parent[$level - 1] = &$current;
502
                if (!is_array($current) or (!array_key_exists($tag, $current))) { //Insert New tag
503
                    $current[$tag] = $result;
504
                    if ($attributes_data) {
505
                        $current[$tag . '_attr'] = $attributes_data;
506
                    }
507
                    $repeated_tag_index[$tag . '_' . $level] = 1;
508
509
                    $current = &$current[$tag];
510
                } else { //There was another element with the same tag name
511
                    if (isset($current[$tag][0])) {//If there is a 0th element it is already an array
512
                        $current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
513
                        $repeated_tag_index[$tag . '_' . $level]++;
514
                    } else {//This section will make the value an array if multiple tags with the same name appear together
515
                        $current[$tag]                           = [$current[$tag], $result]; //This will combine the existing item and the new item together to make an array
516
                        $repeated_tag_index[$tag . '_' . $level] = 2;
517
518
                        if (isset($current[$tag . '_attr'])) { //The attribute of the last(0th) tag must be moved as well
519
                            $current[$tag]['0_attr'] = $current[$tag . '_attr'];
520
                            unset($current[$tag . '_attr']);
521
                        }
522
                    }
523
                    $last_item_index = $repeated_tag_index[$tag . '_' . $level] - 1;
524
                    $current         = &$current[$tag][$last_item_index];
525
                }
526
            } elseif ('complete' === $type) { //Tags that ends in 1 line '<tag>'
527
                //See if the key is already taken.
528
                if (isset($current[$tag])) { //If taken, put all things inside a list(array)
529
                    if (isset($current[$tag][0]) and is_array($current[$tag])) {//If it is already an array...
530
                        // ...push the new element into that array.
531
                        $current[$tag][$repeated_tag_index[$tag . '_' . $level]] = $result;
532
533
                        if ('tag' === $priority and $get_attributes and $attributes_data) {
534
                            $current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
535
                        }
536
                        $repeated_tag_index[$tag . '_' . $level]++;
537
                    } else { //If it is not an array...
538
                        $current[$tag]                           = [$current[$tag], $result]; //...Make it an array using using the existing value and the new value
539
                        $repeated_tag_index[$tag . '_' . $level] = 1;
540
                        if ('tag' === $priority and $get_attributes) {
541
                            if (isset($current[$tag . '_attr'])) { //The attribute of the last(0th) tag must be moved as well
542
                                $current[$tag]['0_attr'] = $current[$tag . '_attr'];
543
                                unset($current[$tag . '_attr']);
544
                            }
545
546
                            if ($attributes_data) {
547
                                $current[$tag][$repeated_tag_index[$tag . '_' . $level] . '_attr'] = $attributes_data;
548
                            }
549
                        }
550
                        $repeated_tag_index[$tag . '_' . $level]++; //0 and 1 index is already taken
551
                    }
552
                } else { //New Key
553
                    $current[$tag]                           = $result;
554
                    $repeated_tag_index[$tag . '_' . $level] = 1;
555
                    if ('tag' === $priority and $attributes_data) {
556
                        $current[$tag . '_attr'] = $attributes_data;
557
                    }
558
                }
559
            } elseif ('close' === $type) { //End of tag '</tag>'
560
                $current = &$parent[$level - 1];
561
            }
562
        }
563
564
        return $xml_array;
565
    }
566
}
567
568
if (!function_exists('songlist_toXml')) {
569
    /**
570
     * @param $array
571
     * @param $name
572
     * @param $standalone
573
     * @param $beginning
574
     * @param $nested
575
     * @return string
576
     */
577
    function songlist_toXml($array, $name, $standalone, $beginning, $nested): string
578
    {
579
        $output = '';
580
        if ($beginning) {
581
            if ($standalone) {
582
                header('content-type:text/xml;charset=' . _CHARSET);
583
            }
584
            $output .= '<' . '?' . 'xml version="1.0" encoding="' . _CHARSET . '"' . '?' . '>' . "\n";
585
            $output .= '<' . $name . '>' . "\n";
586
            $nested = 0;
587
        }
588
589
        if (is_array($array)) {
590
            foreach ($array as $key => $value) {
591
                ++$nested;
592
                if (is_array($value)) {
593
                    $output .= str_repeat("\t", (int)$nested) . '<' . (is_string($key) ? $key : $name . '_' . $key) . '>' . "\n";
594
                    ++$nested;
595
                    $output .= songlist_toXml($value, $name, false, false, $nested);
596
                    $nested--;
597
                    $output .= str_repeat("\t", (int)$nested) . '</' . (is_string($key) ? $key : $name . '_' . $key) . '>' . "\n";
598
                } elseif ('' != $value) {
599
                        ++$nested;
600
                        $output .= str_repeat("\t", (int)$nested) . '  <' . (is_string($key) ? $key : $name . '_' . $key) . '>' . trim($value) . '</' . (is_string($key) ? $key : $name . '_' . $key) . '>' . "\n";
601
                        $nested--;
602
                }
603
                $nested--;
604
            }
605
        } elseif ('' != $array) {
606
            ++$nested;
607
            $output .= str_repeat("\t", (int)$nested) . trim($array) . "\n";
608
            $nested--;
609
        }
610
611
        if ($beginning) {
612
            $output .= '</' . $name . '>';
613
614
            return $output;
615
        }
616
617
        return $output;
618
    }
619
}
620