Completed
Push — vendor/getid3 ( 1ec141...e63377 )
by Pauli
04:01
created

getid3_lib::truepath()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 6
nop 1
dl 0
loc 24
rs 8.9137
c 0
b 0
f 0
1
<?php
2
3
/////////////////////////////////////////////////////////////////
4
/// getID3() by James Heinrich <[email protected]>               //
5
//  available at https://github.com/JamesHeinrich/getID3       //
6
//            or https://www.getid3.org                        //
7
//            or http://getid3.sourceforge.net                 //
8
//                                                             //
9
// getid3.lib.php - part of getID3()                           //
10
//  see readme.txt for more details                            //
11
//                                                            ///
12
/////////////////////////////////////////////////////////////////
13
14
15
class getid3_lib
16
{
17
	/**
18
	 * @param string $string
19
	 * @param bool   $hex
20
	 * @param bool   $spaces
21
	 * @param string $htmlencoding
22
	 *
23
	 * @return string
24
	 */
25
	public static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8') {
26
		$returnstring = '';
27
		for ($i = 0; $i < strlen($string); $i++) {
28
			if ($hex) {
29
				$returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
30
			} else {
31
				$returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string{$i}) ? $string{$i} : '¤');
32
			}
33
			if ($spaces) {
34
				$returnstring .= ' ';
35
			}
36
		}
37
		if (!empty($htmlencoding)) {
38
			if ($htmlencoding === true) {
39
				$htmlencoding = 'UTF-8'; // prior to getID3 v1.9.0 the function's 4th parameter was boolean
40
			}
41
			$returnstring = htmlentities($returnstring, ENT_QUOTES, $htmlencoding);
42
		}
43
		return $returnstring;
44
	}
45
46
	/**
47
	 * Truncates a floating-point number at the decimal point.
48
	 *
49
	 * @param float $floatnumber
50
	 *
51
	 * @return float|int returns int (if possible, otherwise float)
52
	 */
53 View Code Duplication
	public static function trunc($floatnumber) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
54
		if ($floatnumber >= 1) {
55
			$truncatednumber = floor($floatnumber);
56
		} elseif ($floatnumber <= -1) {
57
			$truncatednumber = ceil($floatnumber);
58
		} else {
59
			$truncatednumber = 0;
60
		}
61
		if (self::intValueSupported($truncatednumber)) {
62
			$truncatednumber = (int) $truncatednumber;
63
		}
64
		return $truncatednumber;
65
	}
66
67
	/**
68
	 * @param int|null $variable
69
	 * @param int      $increment
70
	 *
71
	 * @return bool
72
	 */
73
	public static function safe_inc(&$variable, $increment=1) {
74
		if (isset($variable)) {
75
			$variable += $increment;
76
		} else {
77
			$variable = $increment;
78
		}
79
		return true;
80
	}
81
82
	/**
83
	 * @param int|float $floatnum
84
	 *
85
	 * @return int|float
86
	 */
87
	public static function CastAsInt($floatnum) {
88
		// convert to float if not already
89
		$floatnum = (float) $floatnum;
90
91
		// convert a float to type int, only if possible
92
		if (self::trunc($floatnum) == $floatnum) {
93
			// it's not floating point
94
			if (self::intValueSupported($floatnum)) {
95
				// it's within int range
96
				$floatnum = (int) $floatnum;
97
			}
98
		}
99
		return $floatnum;
100
	}
101
102
	/**
103
	 * @param int $num
104
	 *
105
	 * @return bool
106
	 */
107
	public static function intValueSupported($num) {
108
		// check if integers are 64-bit
109
		static $hasINT64 = null;
110
		if ($hasINT64 === null) { // 10x faster than is_null()
111
			$hasINT64 = is_int(pow(2, 31)); // 32-bit int are limited to (2^31)-1
112
			if (!$hasINT64 && !defined('PHP_INT_MIN')) {
113
				define('PHP_INT_MIN', ~PHP_INT_MAX);
114
			}
115
		}
116
		// if integers are 64-bit - no other check required
117
		if ($hasINT64 || (($num <= PHP_INT_MAX) && ($num >= PHP_INT_MIN))) {
118
			return true;
119
		}
120
		return false;
121
	}
122
123
	/**
124
	 * @param string $fraction
125
	 *
126
	 * @return float
127
	 */
128
	public static function DecimalizeFraction($fraction) {
129
		list($numerator, $denominator) = explode('/', $fraction);
130
		return $numerator / ($denominator ? $denominator : 1);
131
	}
132
133
	/**
134
	 * @param string $binarynumerator
135
	 *
136
	 * @return float
137
	 */
138
	public static function DecimalBinary2Float($binarynumerator) {
139
		$numerator   = self::Bin2Dec($binarynumerator);
140
		$denominator = self::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator)));
141
		return ($numerator / $denominator);
142
	}
143
144
	/**
145
	 * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
146
	 *
147
	 * @param string $binarypointnumber
148
	 * @param int    $maxbits
149
	 *
150
	 * @return array
151
	 */
152 View Code Duplication
	public static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
153
		if (strpos($binarypointnumber, '.') === false) {
154
			$binarypointnumber = '0.'.$binarypointnumber;
155
		} elseif ($binarypointnumber{0} == '.') {
156
			$binarypointnumber = '0'.$binarypointnumber;
157
		}
158
		$exponent = 0;
159
		while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
160
			if (substr($binarypointnumber, 1, 1) == '.') {
161
				$exponent--;
162
				$binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3);
163
			} else {
164
				$pointpos = strpos($binarypointnumber, '.');
165
				$exponent += ($pointpos - 1);
166
				$binarypointnumber = str_replace('.', '', $binarypointnumber);
167
				$binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1);
168
			}
169
		}
170
		$binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT);
171
		return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent);
172
	}
173
174
	/**
175
	 * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
176
	 *
177
	 * @param float $floatvalue
178
	 *
179
	 * @return string
180
	 */
181
	public static function Float2BinaryDecimal($floatvalue) {
182
		$maxbits = 128; // to how many bits of precision should the calculations be taken?
183
		$intpart   = self::trunc($floatvalue);
184
		$floatpart = abs($floatvalue - $intpart);
185
		$pointbitstring = '';
186 View Code Duplication
		while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
187
			$floatpart *= 2;
188
			$pointbitstring .= (string) self::trunc($floatpart);
189
			$floatpart -= self::trunc($floatpart);
190
		}
191
		$binarypointnumber = decbin($intpart).'.'.$pointbitstring;
192
		return $binarypointnumber;
193
	}
194
195
	/**
196
	 * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
197
	 *
198
	 * @param float $floatvalue
199
	 * @param int $bits
200
	 *
201
	 * @return string|false
202
	 */
203
	public static function Float2String($floatvalue, $bits) {
204
		$exponentbits = 0;
0 ignored issues
show
Unused Code introduced by
$exponentbits is not used, you could remove the assignment.

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

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

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

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

Loading history...
205
		$fractionbits = 0;
0 ignored issues
show
Unused Code introduced by
$fractionbits is not used, you could remove the assignment.

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

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

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

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

Loading history...
206 View Code Duplication
		switch ($bits) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
207
			case 32:
208
				$exponentbits = 8;
209
				$fractionbits = 23;
210
				break;
211
212
			case 64:
213
				$exponentbits = 11;
214
				$fractionbits = 52;
215
				break;
216
217
			default:
218
				return false;
219
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
220
		}
221
		if ($floatvalue >= 0) {
222
			$signbit = '0';
223
		} else {
224
			$signbit = '1';
225
		}
226
		$normalizedbinary  = self::NormalizeBinaryPoint(self::Float2BinaryDecimal($floatvalue), $fractionbits);
227
		$biasedexponent    = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent
228
		$exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT);
229
		$fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT);
230
231
		return self::BigEndian2String(self::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false);
232
	}
233
234
	/**
235
	 * @param string $byteword
236
	 *
237
	 * @return float|false
238
	 */
239
	public static function LittleEndian2Float($byteword) {
240
		return self::BigEndian2Float(strrev($byteword));
241
	}
242
243
	/**
244
	 * ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
245
	 *
246
	 * @link http://www.psc.edu/general/software/packages/ieee/ieee.html
247
	 * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
248
	 *
249
	 * @param string $byteword
250
	 *
251
	 * @return float|false
252
	 */
253
	public static function BigEndian2Float($byteword) {
254
		$bitword = self::BigEndian2Bin($byteword);
255
		if (!$bitword) {
256
			return 0;
257
		}
258
		$signbit = $bitword{0};
259
		$floatvalue = 0;
260
		$exponentbits = 0;
0 ignored issues
show
Unused Code introduced by
$exponentbits is not used, you could remove the assignment.

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

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

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

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

Loading history...
261
		$fractionbits = 0;
0 ignored issues
show
Unused Code introduced by
$fractionbits is not used, you could remove the assignment.

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

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

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

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

Loading history...
262
263
		switch (strlen($byteword) * 8) {
264
			case 32:
265
				$exponentbits = 8;
266
				$fractionbits = 23;
267
				break;
268
269
			case 64:
270
				$exponentbits = 11;
271
				$fractionbits = 52;
272
				break;
273
274
			case 80:
275
				// 80-bit Apple SANE format
276
				// http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
277
				$exponentstring = substr($bitword, 1, 15);
278
				$isnormalized = intval($bitword{16});
279
				$fractionstring = substr($bitword, 17, 63);
280
				$exponent = pow(2, self::Bin2Dec($exponentstring) - 16383);
281
				$fraction = $isnormalized + self::DecimalBinary2Float($fractionstring);
282
				$floatvalue = $exponent * $fraction;
283
				if ($signbit == '1') {
284
					$floatvalue *= -1;
285
				}
286
				return $floatvalue;
287
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
288
289
			default:
290
				return false;
291
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
292
		}
293
		$exponentstring = substr($bitword, 1, $exponentbits);
294
		$fractionstring = substr($bitword, $exponentbits + 1, $fractionbits);
295
		$exponent = self::Bin2Dec($exponentstring);
296
		$fraction = self::Bin2Dec($fractionstring);
297
298 View Code Duplication
		if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
299
			// Not a Number
300
			$floatvalue = false;
301
		} elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) {
302
			if ($signbit == '1') {
303
				$floatvalue = '-infinity';
304
			} else {
305
				$floatvalue = '+infinity';
306
			}
307
		} elseif (($exponent == 0) && ($fraction == 0)) {
308
			if ($signbit == '1') {
309
				$floatvalue = -0;
0 ignored issues
show
Unused Code introduced by
$floatvalue is not used, you could remove the assignment.

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

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

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

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

Loading history...
310
			} else {
311
				$floatvalue = 0;
0 ignored issues
show
Unused Code introduced by
$floatvalue is not used, you could remove the assignment.

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

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

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

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

Loading history...
312
			}
313
			$floatvalue = ($signbit ? 0 : -0);
314
		} elseif (($exponent == 0) && ($fraction != 0)) {
315
			// These are 'unnormalized' values
316
			$floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * self::DecimalBinary2Float($fractionstring);
317
			if ($signbit == '1') {
318
				$floatvalue *= -1;
319
			}
320
		} elseif ($exponent != 0) {
321
			$floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + self::DecimalBinary2Float($fractionstring));
322
			if ($signbit == '1') {
323
				$floatvalue *= -1;
324
			}
325
		}
326
		return (float) $floatvalue;
327
	}
328
329
	/**
330
	 * @param string $byteword
331
	 * @param bool   $synchsafe
332
	 * @param bool   $signed
333
	 *
334
	 * @return int|float|false
335
	 * @throws Exception
336
	 */
337
	public static function BigEndian2Int($byteword, $synchsafe=false, $signed=false) {
338
		$intvalue = 0;
339
		$bytewordlen = strlen($byteword);
340
		if ($bytewordlen == 0) {
341
			return false;
342
		}
343 View Code Duplication
		for ($i = 0; $i < $bytewordlen; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
344
			if ($synchsafe) { // disregard MSB, effectively 7-bit bytes
345
				//$intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7); // faster, but runs into problems past 2^31 on 32-bit systems
346
				$intvalue += (ord($byteword{$i}) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7);
347
			} else {
348
				$intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
349
			}
350
		}
351
		if ($signed && !$synchsafe) {
352
			// synchsafe ints are not allowed to be signed
353
			if ($bytewordlen <= PHP_INT_SIZE) {
354
				$signMaskBit = 0x80 << (8 * ($bytewordlen - 1));
355
				if ($intvalue & $signMaskBit) {
356
					$intvalue = 0 - ($intvalue & ($signMaskBit - 1));
357
				}
358
			} else {
359
				throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits ('.strlen($byteword).') in self::BigEndian2Int()');
360
			}
361
		}
362
		return self::CastAsInt($intvalue);
363
	}
364
365
	/**
366
	 * @param string $byteword
367
	 * @param bool   $signed
368
	 *
369
	 * @return int|float|false
370
	 */
371
	public static function LittleEndian2Int($byteword, $signed=false) {
372
		return self::BigEndian2Int(strrev($byteword), false, $signed);
373
	}
374
375
	/**
376
	 * @param string $byteword
377
	 *
378
	 * @return string
379
	 */
380
	public static function LittleEndian2Bin($byteword) {
381
		return self::BigEndian2Bin(strrev($byteword));
382
	}
383
384
	/**
385
	 * @param string $byteword
386
	 *
387
	 * @return string
388
	 */
389 View Code Duplication
	public static function BigEndian2Bin($byteword) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
390
		$binvalue = '';
391
		$bytewordlen = strlen($byteword);
392
		for ($i = 0; $i < $bytewordlen; $i++) {
393
			$binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT);
394
		}
395
		return $binvalue;
396
	}
397
398
	/**
399
	 * @param int  $number
400
	 * @param int  $minbytes
401
	 * @param bool $synchsafe
402
	 * @param bool $signed
403
	 *
404
	 * @return string
405
	 * @throws Exception
406
	 */
407
	public static function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) {
408
		if ($number < 0) {
409
			throw new Exception('ERROR: self::BigEndian2String() does not support negative numbers');
410
		}
411
		$maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF);
412
		$intstring = '';
413
		if ($signed) {
414
			if ($minbytes > PHP_INT_SIZE) {
415
				throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits in self::BigEndian2String()');
416
			}
417
			$number = $number & (0x80 << (8 * ($minbytes - 1)));
418
		}
419 View Code Duplication
		while ($number != 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
420
			$quotient = ($number / ($maskbyte + 1));
421
			$intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring;
422
			$number = floor($quotient);
423
		}
424
		return str_pad($intstring, $minbytes, "\x00", STR_PAD_LEFT);
425
	}
426
427
	/**
428
	 * @param int $number
429
	 *
430
	 * @return string
431
	 */
432 View Code Duplication
	public static function Dec2Bin($number) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
433
		while ($number >= 256) {
434
			$bytes[] = (($number / 256) - (floor($number / 256))) * 256;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$bytes was never initialized. Although not strictly required by PHP, it is generally a good practice to add $bytes = array(); before regardless.

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

Let’s take a look at an example:

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

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

    // do something with $myArray
}

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

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

Loading history...
435
			$number = floor($number / 256);
436
		}
437
		$bytes[] = $number;
0 ignored issues
show
Bug introduced by
The variable $bytes does not seem to be defined for all execution paths leading up to this point.

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

Let’s take a look at an example:

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

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

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

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

Available Fixes

  1. Check for existence of the variable explicitly:

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

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

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
438
		$binstring = '';
439
		for ($i = 0; $i < count($bytes); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
440
			$binstring = (($i == count($bytes) - 1) ? decbin($bytes[$i]) : str_pad(decbin($bytes[$i]), 8, '0', STR_PAD_LEFT)).$binstring;
441
		}
442
		return $binstring;
443
	}
444
445
	/**
446
	 * @param string $binstring
447
	 * @param bool   $signed
448
	 *
449
	 * @return int|float
450
	 */
451
	public static function Bin2Dec($binstring, $signed=false) {
452
		$signmult = 1;
453
		if ($signed) {
454
			if ($binstring{0} == '1') {
455
				$signmult = -1;
456
			}
457
			$binstring = substr($binstring, 1);
458
		}
459
		$decvalue = 0;
460 View Code Duplication
		for ($i = 0; $i < strlen($binstring); $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
461
			$decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i);
462
		}
463
		return self::CastAsInt($decvalue * $signmult);
464
	}
465
466
	/**
467
	 * @param string $binstring
468
	 *
469
	 * @return string
470
	 */
471 View Code Duplication
	public static function Bin2String($binstring) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
472
		// return 'hi' for input of '0110100001101001'
473
		$string = '';
474
		$binstringreversed = strrev($binstring);
475
		for ($i = 0; $i < strlen($binstringreversed); $i += 8) {
476
			$string = chr(self::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string;
477
		}
478
		return $string;
479
	}
480
481
	/**
482
	 * @param int  $number
483
	 * @param int  $minbytes
484
	 * @param bool $synchsafe
485
	 *
486
	 * @return string
487
	 */
488 View Code Duplication
	public static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
489
		$intstring = '';
490
		while ($number > 0) {
491
			if ($synchsafe) {
492
				$intstring = $intstring.chr($number & 127);
493
				$number >>= 7;
494
			} else {
495
				$intstring = $intstring.chr($number & 255);
496
				$number >>= 8;
497
			}
498
		}
499
		return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
500
	}
501
502
	/**
503
	 * @param array $array1
504
	 * @param array $array2
505
	 *
506
	 * @return array|false
507
	 */
508
	public static function array_merge_clobber($array1, $array2) {
509
		// written by kcØhireability*com
510
		// taken from http://www.php.net/manual/en/function.array-merge-recursive.php
511
		if (!is_array($array1) || !is_array($array2)) {
512
			return false;
513
		}
514
		$newarray = $array1;
515
		foreach ($array2 as $key => $val) {
516
			if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
517
				$newarray[$key] = self::array_merge_clobber($newarray[$key], $val);
518
			} else {
519
				$newarray[$key] = $val;
520
			}
521
		}
522
		return $newarray;
523
	}
524
525
	/**
526
	 * @param array $array1
527
	 * @param array $array2
528
	 *
529
	 * @return array|false
530
	 */
531
	public static function array_merge_noclobber($array1, $array2) {
532
		if (!is_array($array1) || !is_array($array2)) {
533
			return false;
534
		}
535
		$newarray = $array1;
536
		foreach ($array2 as $key => $val) {
537
			if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
538
				$newarray[$key] = self::array_merge_noclobber($newarray[$key], $val);
539
			} elseif (!isset($newarray[$key])) {
540
				$newarray[$key] = $val;
541
			}
542
		}
543
		return $newarray;
544
	}
545
546
	/**
547
	 * @param array $array1
548
	 * @param array $array2
549
	 *
550
	 * @return array|false|null
551
	 */
552
	public static function flipped_array_merge_noclobber($array1, $array2) {
553
		if (!is_array($array1) || !is_array($array2)) {
554
			return false;
555
		}
556
		# naturally, this only works non-recursively
557
		$newarray = array_flip($array1);
558
		foreach (array_flip($array2) as $key => $val) {
559
			if (!isset($newarray[$key])) {
560
				$newarray[$key] = count($newarray);
561
			}
562
		}
563
		return array_flip($newarray);
564
	}
565
566
	/**
567
	 * @param array $theArray
568
	 *
569
	 * @return bool
570
	 */
571
	public static function ksort_recursive(&$theArray) {
572
		ksort($theArray);
573
		foreach ($theArray as $key => $value) {
574
			if (is_array($value)) {
575
				self::ksort_recursive($theArray[$key]);
576
			}
577
		}
578
		return true;
579
	}
580
581
	/**
582
	 * @param string $filename
583
	 * @param int    $numextensions
584
	 *
585
	 * @return string
586
	 */
587 View Code Duplication
	public static function fileextension($filename, $numextensions=1) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
588
		if (strstr($filename, '.')) {
589
			$reversedfilename = strrev($filename);
590
			$offset = 0;
591
			for ($i = 0; $i < $numextensions; $i++) {
592
				$offset = strpos($reversedfilename, '.', $offset + 1);
593
				if ($offset === false) {
594
					return '';
595
				}
596
			}
597
			return strrev(substr($reversedfilename, 0, $offset));
598
		}
599
		return '';
600
	}
601
602
	/**
603
	 * @param int $seconds
604
	 *
605
	 * @return string
606
	 */
607
	public static function PlaytimeString($seconds) {
608
		$sign = (($seconds < 0) ? '-' : '');
609
		$seconds = round(abs($seconds));
610
		$H = (int) floor( $seconds                            / 3600);
611
		$M = (int) floor(($seconds - (3600 * $H)            ) /   60);
612
		$S = (int) round( $seconds - (3600 * $H) - (60 * $M)        );
613
		return $sign.($H ? $H.':' : '').($H ? str_pad($M, 2, '0', STR_PAD_LEFT) : intval($M)).':'.str_pad($S, 2, 0, STR_PAD_LEFT);
614
	}
615
616
	/**
617
	 * @param int $macdate
618
	 *
619
	 * @return int|float
620
	 */
621
	public static function DateMac2Unix($macdate) {
622
		// Macintosh timestamp: seconds since 00:00h January 1, 1904
623
		// UNIX timestamp:      seconds since 00:00h January 1, 1970
624
		return self::CastAsInt($macdate - 2082844800);
625
	}
626
627
	/**
628
	 * @param string $rawdata
629
	 *
630
	 * @return float
631
	 */
632
	public static function FixedPoint8_8($rawdata) {
633
		return self::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (self::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8));
634
	}
635
636
	/**
637
	 * @param string $rawdata
638
	 *
639
	 * @return float
640
	 */
641
	public static function FixedPoint16_16($rawdata) {
642
		return self::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (self::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16));
643
	}
644
645
	/**
646
	 * @param string $rawdata
647
	 *
648
	 * @return float
649
	 */
650
	public static function FixedPoint2_30($rawdata) {
651
		$binarystring = self::BigEndian2Bin($rawdata);
652
		return self::Bin2Dec(substr($binarystring, 0, 2)) + (float) (self::Bin2Dec(substr($binarystring, 2, 30)) / pow(2, 30));
653
	}
654
655
656
	/**
657
	 * @param string $ArrayPath
658
	 * @param string $Separator
659
	 * @param mixed $Value
660
	 *
661
	 * @return array
662
	 */
663
	public static function CreateDeepArray($ArrayPath, $Separator, $Value) {
664
		// assigns $Value to a nested array path:
665
		//   $foo = self::CreateDeepArray('/path/to/my', '/', 'file.txt')
666
		// is the same as:
667
		//   $foo = array('path'=>array('to'=>'array('my'=>array('file.txt'))));
668
		// or
669
		//   $foo['path']['to']['my'] = 'file.txt';
670
		$ArrayPath = ltrim($ArrayPath, $Separator);
671 View Code Duplication
		if (($pos = strpos($ArrayPath, $Separator)) !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
672
			$ReturnedArray[substr($ArrayPath, 0, $pos)] = self::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$ReturnedArray was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ReturnedArray = array(); before regardless.

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

Let’s take a look at an example:

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

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

    // do something with $myArray
}

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

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

Loading history...
673
		} else {
674
			$ReturnedArray[$ArrayPath] = $Value;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$ReturnedArray was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ReturnedArray = array(); before regardless.

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

Let’s take a look at an example:

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

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

    // do something with $myArray
}

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

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

Loading history...
675
		}
676
		return $ReturnedArray;
677
	}
678
679
	/**
680
	 * @param array $arraydata
681
	 * @param bool  $returnkey
682
	 *
683
	 * @return int|false
684
	 */
685 View Code Duplication
	public static function array_max($arraydata, $returnkey=false) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
686
		$maxvalue = false;
687
		$maxkey = false;
688
		foreach ($arraydata as $key => $value) {
689
			if (!is_array($value)) {
690
				if ($value > $maxvalue) {
691
					$maxvalue = $value;
692
					$maxkey = $key;
693
				}
694
			}
695
		}
696
		return ($returnkey ? $maxkey : $maxvalue);
697
	}
698
699
	/**
700
	 * @param array $arraydata
701
	 * @param bool  $returnkey
702
	 *
703
	 * @return int|false
704
	 */
705 View Code Duplication
	public static function array_min($arraydata, $returnkey=false) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
706
		$minvalue = false;
707
		$minkey = false;
708
		foreach ($arraydata as $key => $value) {
709
			if (!is_array($value)) {
710
				if ($value > $minvalue) {
711
					$minvalue = $value;
712
					$minkey = $key;
713
				}
714
			}
715
		}
716
		return ($returnkey ? $minkey : $minvalue);
717
	}
718
719
	/**
720
	 * @param string $XMLstring
721
	 *
722
	 * @return array|false
723
	 */
724
	public static function XML2array($XMLstring) {
725
		if (function_exists('simplexml_load_string') && function_exists('libxml_disable_entity_loader')) {
726
			// http://websec.io/2012/08/27/Preventing-XEE-in-PHP.html
727
			// https://core.trac.wordpress.org/changeset/29378
728
			$loader = libxml_disable_entity_loader(true);
729
			$XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', LIBXML_NOENT);
730
			$return = self::SimpleXMLelement2array($XMLobject);
731
			libxml_disable_entity_loader($loader);
732
			return $return;
733
		}
734
		return false;
735
	}
736
737
	/**
738
	* @param SimpleXMLElement|array $XMLobject
739
	*
740
	* @return array
741
	*/
742
	public static function SimpleXMLelement2array($XMLobject) {
743
		if (!is_object($XMLobject) && !is_array($XMLobject)) {
744
			return $XMLobject;
745
		}
746
		$XMLarray = $XMLobject instanceof SimpleXMLElement ? get_object_vars($XMLobject) : $XMLobject;
747
		foreach ($XMLarray as $key => $value) {
748
			$XMLarray[$key] = self::SimpleXMLelement2array($value);
749
		}
750
		return $XMLarray;
751
	}
752
753
	/**
754
	 * self::md5_data() - returns md5sum for a file from startuing position to absolute end position
755
	 *
756
	 * @author Allan Hansen <ahØartemis*dk>
757
	 *
758
	 * @param string $file
759
	 * @param int    $offset
760
	 * @param int    $end
761
	 * @param string $algorithm
762
	 *
763
	 * @return string|false
764
	 * @throws Exception
765
	 * @throws getid3_exception
766
	 */
767
	public static function hash_data($file, $offset, $end, $algorithm) {
768
		static $tempdir = '';
769
		$windows_call = null;
0 ignored issues
show
Unused Code introduced by
$windows_call is not used, you could remove the assignment.

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

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

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

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

Loading history...
770
		$unix_call = null;
0 ignored issues
show
Unused Code introduced by
$unix_call is not used, you could remove the assignment.

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

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

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

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

Loading history...
771
		$hash_length = null;
0 ignored issues
show
Unused Code introduced by
$hash_length is not used, you could remove the assignment.

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

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

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

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

Loading history...
772
		$hash_function = null;
0 ignored issues
show
Unused Code introduced by
$hash_function is not used, you could remove the assignment.

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

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

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

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

Loading history...
773
		if (!self::intValueSupported($end)) {
774
			return false;
775
		}
776
		switch ($algorithm) {
777
			case 'md5':
778
				$hash_function = 'md5_file';
779
				$unix_call     = 'md5sum';
780
				$windows_call  = 'md5sum.exe';
781
				$hash_length   = 32;
782
				break;
783
784
			case 'sha1':
785
				$hash_function = 'sha1_file';
786
				$unix_call     = 'sha1sum';
787
				$windows_call  = 'sha1sum.exe';
788
				$hash_length   = 40;
789
				break;
790
791
			default:
792
				throw new Exception('Invalid algorithm ('.$algorithm.') in self::hash_data()');
793
				break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
794
		}
795
		$size = $end - $offset;
796
		while (true) {
797
			if (GETID3_OS_ISWINDOWS) {
798
799
				// It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data
800
				// Fall back to create-temp-file method:
801
				if ($algorithm == 'sha1') {
802
					break;
803
				}
804
805
				$RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call);
806
				foreach ($RequiredFiles as $required_file) {
807
					if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
808
						// helper apps not available - fall back to old method
809
						break 2;
810
					}
811
				}
812
				$commandline  = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' '.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).' | ';
813
				$commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | ';
814
				$commandline .= GETID3_HELPERAPPSDIR.$windows_call;
815
816
			} else {
817
818
				$commandline  = 'head -c'.$end.' '.escapeshellarg($file).' | ';
819
				$commandline .= 'tail -c'.$size.' | ';
820
				$commandline .= $unix_call;
821
822
			}
823
			if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
824
				//throw new Exception('PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm');
825
				break;
826
			}
827
			return substr(`$commandline`, 0, $hash_length);
828
		}
829
830
		if (empty($tempdir)) {
831
			// yes this is ugly, feel free to suggest a better way
832
			require_once(dirname(__FILE__).'/getid3.php');
833
			$getid3_temp = new getID3();
834
			$tempdir = $getid3_temp->tempdir;
835
			unset($getid3_temp);
836
		}
837
		// try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir
838
		if (($data_filename = tempnam($tempdir, 'gI3')) === false) {
839
			// can't find anywhere to create a temp file, just fail
840
			return false;
841
		}
842
843
		// Init
844
		$result = false;
845
846
		// copy parts of file
847
		try {
848
			self::CopyFileParts($file, $data_filename, $offset, $end - $offset);
849
			$result = $hash_function($data_filename);
850
		} catch (Exception $e) {
851
			throw new Exception('self::CopyFileParts() failed in getid_lib::hash_data(): '.$e->getMessage());
852
		}
853
		unlink($data_filename);
854
		return $result;
855
	}
856
857
	/**
858
	 * @param string $filename_source
859
	 * @param string $filename_dest
860
	 * @param int    $offset
861
	 * @param int    $length
862
	 *
863
	 * @return bool
864
	 * @throws Exception
865
	 */
866
	public static function CopyFileParts($filename_source, $filename_dest, $offset, $length) {
867
		if (!self::intValueSupported($offset + $length)) {
868
			throw new Exception('cannot copy file portion, it extends beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit');
869
		}
870
		if (is_readable($filename_source) && is_file($filename_source) && ($fp_src = fopen($filename_source, 'rb'))) {
871
			if (($fp_dest = fopen($filename_dest, 'wb'))) {
872
				if (fseek($fp_src, $offset) == 0) {
873
					$byteslefttowrite = $length;
874
					while (($byteslefttowrite > 0) && ($buffer = fread($fp_src, min($byteslefttowrite, getID3::FREAD_BUFFER_SIZE)))) {
875
						$byteswritten = fwrite($fp_dest, $buffer, $byteslefttowrite);
876
						$byteslefttowrite -= $byteswritten;
877
					}
878
					fclose($fp_dest);
879
					return true;
880
				} else {
881
					fclose($fp_src);
882
					throw new Exception('failed to seek to offset '.$offset.' in '.$filename_source);
883
				}
884
			} else {
885
				throw new Exception('failed to create file for writing '.$filename_dest);
886
			}
887
		} else {
888
			throw new Exception('failed to open file for reading '.$filename_source);
889
		}
890
	}
891
892
	/**
893
	 * @param int $charval
894
	 *
895
	 * @return string
896
	 */
897
	public static function iconv_fallback_int_utf8($charval) {
898
		if ($charval < 128) {
899
			// 0bbbbbbb
900
			$newcharstring = chr($charval);
901
		} elseif ($charval < 2048) {
902
			// 110bbbbb 10bbbbbb
903
			$newcharstring  = chr(($charval >>   6) | 0xC0);
904
			$newcharstring .= chr(($charval & 0x3F) | 0x80);
905
		} elseif ($charval < 65536) {
906
			// 1110bbbb 10bbbbbb 10bbbbbb
907
			$newcharstring  = chr(($charval >>  12) | 0xE0);
908
			$newcharstring .= chr(($charval >>   6) | 0xC0);
909
			$newcharstring .= chr(($charval & 0x3F) | 0x80);
910
		} else {
911
			// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
912
			$newcharstring  = chr(($charval >>  18) | 0xF0);
913
			$newcharstring .= chr(($charval >>  12) | 0xC0);
914
			$newcharstring .= chr(($charval >>   6) | 0xC0);
915
			$newcharstring .= chr(($charval & 0x3F) | 0x80);
916
		}
917
		return $newcharstring;
918
	}
919
920
	/**
921
	 * ISO-8859-1 => UTF-8
922
	 *
923
	 * @param string $string
924
	 * @param bool   $bom
925
	 *
926
	 * @return string
927
	 */
928
	public static function iconv_fallback_iso88591_utf8($string, $bom=false) {
929
		if (function_exists('utf8_encode')) {
930
			return utf8_encode($string);
931
		}
932
		// utf8_encode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
933
		$newcharstring = '';
934
		if ($bom) {
935
			$newcharstring .= "\xEF\xBB\xBF";
936
		}
937
		for ($i = 0; $i < strlen($string); $i++) {
938
			$charval = ord($string{$i});
939
			$newcharstring .= self::iconv_fallback_int_utf8($charval);
940
		}
941
		return $newcharstring;
942
	}
943
944
	/**
945
	 * ISO-8859-1 => UTF-16BE
946
	 *
947
	 * @param string $string
948
	 * @param bool   $bom
949
	 *
950
	 * @return string
951
	 */
952 View Code Duplication
	public static function iconv_fallback_iso88591_utf16be($string, $bom=false) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
953
		$newcharstring = '';
954
		if ($bom) {
955
			$newcharstring .= "\xFE\xFF";
956
		}
957
		for ($i = 0; $i < strlen($string); $i++) {
958
			$newcharstring .= "\x00".$string{$i};
959
		}
960
		return $newcharstring;
961
	}
962
963
	/**
964
	 * ISO-8859-1 => UTF-16LE
965
	 *
966
	 * @param string $string
967
	 * @param bool   $bom
968
	 *
969
	 * @return string
970
	 */
971 View Code Duplication
	public static function iconv_fallback_iso88591_utf16le($string, $bom=false) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
972
		$newcharstring = '';
973
		if ($bom) {
974
			$newcharstring .= "\xFF\xFE";
975
		}
976
		for ($i = 0; $i < strlen($string); $i++) {
977
			$newcharstring .= $string{$i}."\x00";
978
		}
979
		return $newcharstring;
980
	}
981
982
	/**
983
	 * ISO-8859-1 => UTF-16LE (BOM)
984
	 *
985
	 * @param string $string
986
	 *
987
	 * @return string
988
	 */
989
	public static function iconv_fallback_iso88591_utf16($string) {
990
		return self::iconv_fallback_iso88591_utf16le($string, true);
991
	}
992
993
	/**
994
	 * UTF-8 => ISO-8859-1
995
	 *
996
	 * @param string $string
997
	 *
998
	 * @return string
999
	 */
1000
	public static function iconv_fallback_utf8_iso88591($string) {
1001
		if (function_exists('utf8_decode')) {
1002
			return utf8_decode($string);
1003
		}
1004
		// utf8_decode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
1005
		$newcharstring = '';
1006
		$offset = 0;
1007
		$stringlength = strlen($string);
1008
		while ($offset < $stringlength) {
1009
			if ((ord($string{$offset}) | 0x07) == 0xF7) {
1010
				// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
1011
				$charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
1012
						   ((ord($string{($offset + 1)}) & 0x3F) << 12) &
1013
						   ((ord($string{($offset + 2)}) & 0x3F) <<  6) &
1014
							(ord($string{($offset + 3)}) & 0x3F);
1015
				$offset += 4;
1016
			} elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
1017
				// 1110bbbb 10bbbbbb 10bbbbbb
1018
				$charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
1019
						   ((ord($string{($offset + 1)}) & 0x3F) <<  6) &
1020
							(ord($string{($offset + 2)}) & 0x3F);
1021
				$offset += 3;
1022
			} elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
1023
				// 110bbbbb 10bbbbbb
1024
				$charval = ((ord($string{($offset + 0)}) & 0x1F) <<  6) &
1025
							(ord($string{($offset + 1)}) & 0x3F);
1026
				$offset += 2;
1027
			} elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
1028
				// 0bbbbbbb
1029
				$charval = ord($string{$offset});
1030
				$offset += 1;
1031
			} else {
1032
				// error? throw some kind of warning here?
1033
				$charval = false;
1034
				$offset += 1;
1035
			}
1036
			if ($charval !== false) {
1037
				$newcharstring .= (($charval < 256) ? chr($charval) : '?');
1038
			}
1039
		}
1040
		return $newcharstring;
1041
	}
1042
1043
	/**
1044
	 * UTF-8 => UTF-16BE
1045
	 *
1046
	 * @param string $string
1047
	 * @param bool   $bom
1048
	 *
1049
	 * @return string
1050
	 */
1051 View Code Duplication
	public static function iconv_fallback_utf8_utf16be($string, $bom=false) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1052
		$newcharstring = '';
1053
		if ($bom) {
1054
			$newcharstring .= "\xFE\xFF";
1055
		}
1056
		$offset = 0;
1057
		$stringlength = strlen($string);
1058
		while ($offset < $stringlength) {
1059
			if ((ord($string{$offset}) | 0x07) == 0xF7) {
1060
				// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
1061
				$charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
1062
						   ((ord($string{($offset + 1)}) & 0x3F) << 12) &
1063
						   ((ord($string{($offset + 2)}) & 0x3F) <<  6) &
1064
							(ord($string{($offset + 3)}) & 0x3F);
1065
				$offset += 4;
1066
			} elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
1067
				// 1110bbbb 10bbbbbb 10bbbbbb
1068
				$charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
1069
						   ((ord($string{($offset + 1)}) & 0x3F) <<  6) &
1070
							(ord($string{($offset + 2)}) & 0x3F);
1071
				$offset += 3;
1072
			} elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
1073
				// 110bbbbb 10bbbbbb
1074
				$charval = ((ord($string{($offset + 0)}) & 0x1F) <<  6) &
1075
							(ord($string{($offset + 1)}) & 0x3F);
1076
				$offset += 2;
1077
			} elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
1078
				// 0bbbbbbb
1079
				$charval = ord($string{$offset});
1080
				$offset += 1;
1081
			} else {
1082
				// error? throw some kind of warning here?
1083
				$charval = false;
1084
				$offset += 1;
1085
			}
1086
			if ($charval !== false) {
1087
				$newcharstring .= (($charval < 65536) ? self::BigEndian2String($charval, 2) : "\x00".'?');
1088
			}
1089
		}
1090
		return $newcharstring;
1091
	}
1092
1093
	/**
1094
	 * UTF-8 => UTF-16LE
1095
	 *
1096
	 * @param string $string
1097
	 * @param bool   $bom
1098
	 *
1099
	 * @return string
1100
	 */
1101 View Code Duplication
	public static function iconv_fallback_utf8_utf16le($string, $bom=false) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1102
		$newcharstring = '';
1103
		if ($bom) {
1104
			$newcharstring .= "\xFF\xFE";
1105
		}
1106
		$offset = 0;
1107
		$stringlength = strlen($string);
1108
		while ($offset < $stringlength) {
1109
			if ((ord($string{$offset}) | 0x07) == 0xF7) {
1110
				// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
1111
				$charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
1112
						   ((ord($string{($offset + 1)}) & 0x3F) << 12) &
1113
						   ((ord($string{($offset + 2)}) & 0x3F) <<  6) &
1114
							(ord($string{($offset + 3)}) & 0x3F);
1115
				$offset += 4;
1116
			} elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
1117
				// 1110bbbb 10bbbbbb 10bbbbbb
1118
				$charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
1119
						   ((ord($string{($offset + 1)}) & 0x3F) <<  6) &
1120
							(ord($string{($offset + 2)}) & 0x3F);
1121
				$offset += 3;
1122
			} elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
1123
				// 110bbbbb 10bbbbbb
1124
				$charval = ((ord($string{($offset + 0)}) & 0x1F) <<  6) &
1125
							(ord($string{($offset + 1)}) & 0x3F);
1126
				$offset += 2;
1127
			} elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
1128
				// 0bbbbbbb
1129
				$charval = ord($string{$offset});
1130
				$offset += 1;
1131
			} else {
1132
				// error? maybe throw some warning here?
1133
				$charval = false;
1134
				$offset += 1;
1135
			}
1136
			if ($charval !== false) {
1137
				$newcharstring .= (($charval < 65536) ? self::LittleEndian2String($charval, 2) : '?'."\x00");
1138
			}
1139
		}
1140
		return $newcharstring;
1141
	}
1142
1143
	/**
1144
	 * UTF-8 => UTF-16LE (BOM)
1145
	 *
1146
	 * @param string $string
1147
	 *
1148
	 * @return string
1149
	 */
1150
	public static function iconv_fallback_utf8_utf16($string) {
1151
		return self::iconv_fallback_utf8_utf16le($string, true);
1152
	}
1153
1154
	/**
1155
	 * UTF-16BE => UTF-8
1156
	 *
1157
	 * @param string $string
1158
	 *
1159
	 * @return string
1160
	 */
1161 View Code Duplication
	public static function iconv_fallback_utf16be_utf8($string) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1162
		if (substr($string, 0, 2) == "\xFE\xFF") {
1163
			// strip BOM
1164
			$string = substr($string, 2);
1165
		}
1166
		$newcharstring = '';
1167
		for ($i = 0; $i < strlen($string); $i += 2) {
1168
			$charval = self::BigEndian2Int(substr($string, $i, 2));
1169
			$newcharstring .= self::iconv_fallback_int_utf8($charval);
0 ignored issues
show
Bug introduced by
It seems like $charval defined by self::BigEndian2Int(substr($string, $i, 2)) on line 1168 can also be of type double or false; however, getid3_lib::iconv_fallback_int_utf8() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1170
		}
1171
		return $newcharstring;
1172
	}
1173
1174
	/**
1175
	 * UTF-16LE => UTF-8
1176
	 *
1177
	 * @param string $string
1178
	 *
1179
	 * @return string
1180
	 */
1181 View Code Duplication
	public static function iconv_fallback_utf16le_utf8($string) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1182
		if (substr($string, 0, 2) == "\xFF\xFE") {
1183
			// strip BOM
1184
			$string = substr($string, 2);
1185
		}
1186
		$newcharstring = '';
1187
		for ($i = 0; $i < strlen($string); $i += 2) {
1188
			$charval = self::LittleEndian2Int(substr($string, $i, 2));
1189
			$newcharstring .= self::iconv_fallback_int_utf8($charval);
0 ignored issues
show
Bug introduced by
It seems like $charval defined by self::LittleEndian2Int(substr($string, $i, 2)) on line 1188 can also be of type double or false; however, getid3_lib::iconv_fallback_int_utf8() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1190
		}
1191
		return $newcharstring;
1192
	}
1193
1194
	/**
1195
	 * UTF-16BE => ISO-8859-1
1196
	 *
1197
	 * @param string $string
1198
	 *
1199
	 * @return string
1200
	 */
1201 View Code Duplication
	public static function iconv_fallback_utf16be_iso88591($string) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1202
		if (substr($string, 0, 2) == "\xFE\xFF") {
1203
			// strip BOM
1204
			$string = substr($string, 2);
1205
		}
1206
		$newcharstring = '';
1207
		for ($i = 0; $i < strlen($string); $i += 2) {
1208
			$charval = self::BigEndian2Int(substr($string, $i, 2));
1209
			$newcharstring .= (($charval < 256) ? chr($charval) : '?');
1210
		}
1211
		return $newcharstring;
1212
	}
1213
1214
	/**
1215
	 * UTF-16LE => ISO-8859-1
1216
	 *
1217
	 * @param string $string
1218
	 *
1219
	 * @return string
1220
	 */
1221 View Code Duplication
	public static function iconv_fallback_utf16le_iso88591($string) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1222
		if (substr($string, 0, 2) == "\xFF\xFE") {
1223
			// strip BOM
1224
			$string = substr($string, 2);
1225
		}
1226
		$newcharstring = '';
1227
		for ($i = 0; $i < strlen($string); $i += 2) {
1228
			$charval = self::LittleEndian2Int(substr($string, $i, 2));
1229
			$newcharstring .= (($charval < 256) ? chr($charval) : '?');
1230
		}
1231
		return $newcharstring;
1232
	}
1233
1234
	/**
1235
	 * UTF-16 (BOM) => ISO-8859-1
1236
	 *
1237
	 * @param string $string
1238
	 *
1239
	 * @return string
1240
	 */
1241 View Code Duplication
	public static function iconv_fallback_utf16_iso88591($string) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1242
		$bom = substr($string, 0, 2);
1243
		if ($bom == "\xFE\xFF") {
1244
			return self::iconv_fallback_utf16be_iso88591(substr($string, 2));
1245
		} elseif ($bom == "\xFF\xFE") {
1246
			return self::iconv_fallback_utf16le_iso88591(substr($string, 2));
1247
		}
1248
		return $string;
1249
	}
1250
1251
	/**
1252
	 * UTF-16 (BOM) => UTF-8
1253
	 *
1254
	 * @param string $string
1255
	 *
1256
	 * @return string
1257
	 */
1258 View Code Duplication
	public static function iconv_fallback_utf16_utf8($string) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1259
		$bom = substr($string, 0, 2);
1260
		if ($bom == "\xFE\xFF") {
1261
			return self::iconv_fallback_utf16be_utf8(substr($string, 2));
1262
		} elseif ($bom == "\xFF\xFE") {
1263
			return self::iconv_fallback_utf16le_utf8(substr($string, 2));
1264
		}
1265
		return $string;
1266
	}
1267
1268
	/**
1269
	 * @param string $in_charset
1270
	 * @param string $out_charset
1271
	 * @param string $string
1272
	 *
1273
	 * @return string
1274
	 * @throws Exception
1275
	 */
1276
	public static function iconv_fallback($in_charset, $out_charset, $string) {
1277
1278
		if ($in_charset == $out_charset) {
1279
			return $string;
1280
		}
1281
1282
		// mb_convert_encoding() available
1283
		if (function_exists('mb_convert_encoding')) {
1284 View Code Duplication
			if ($converted_string = @mb_convert_encoding($string, $out_charset, $in_charset)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1285
				switch ($out_charset) {
1286
					case 'ISO-8859-1':
1287
						$converted_string = rtrim($converted_string, "\x00");
1288
						break;
1289
				}
1290
				return $converted_string;
1291
			}
1292
			return $string;
1293
		}
1294
		// iconv() available
1295
		else if (function_exists('iconv')) {
1296 View Code Duplication
			if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1297
				switch ($out_charset) {
1298
					case 'ISO-8859-1':
1299
						$converted_string = rtrim($converted_string, "\x00");
1300
						break;
1301
				}
1302
				return $converted_string;
1303
			}
1304
1305
			// iconv() may sometimes fail with "illegal character in input string" error message
1306
			// and return an empty string, but returning the unconverted string is more useful
1307
			return $string;
1308
		}
1309
1310
1311
		// neither mb_convert_encoding or iconv() is available
1312
		static $ConversionFunctionList = array();
1313
		if (empty($ConversionFunctionList)) {
1314
			$ConversionFunctionList['ISO-8859-1']['UTF-8']    = 'iconv_fallback_iso88591_utf8';
1315
			$ConversionFunctionList['ISO-8859-1']['UTF-16']   = 'iconv_fallback_iso88591_utf16';
1316
			$ConversionFunctionList['ISO-8859-1']['UTF-16BE'] = 'iconv_fallback_iso88591_utf16be';
1317
			$ConversionFunctionList['ISO-8859-1']['UTF-16LE'] = 'iconv_fallback_iso88591_utf16le';
1318
			$ConversionFunctionList['UTF-8']['ISO-8859-1']    = 'iconv_fallback_utf8_iso88591';
1319
			$ConversionFunctionList['UTF-8']['UTF-16']        = 'iconv_fallback_utf8_utf16';
1320
			$ConversionFunctionList['UTF-8']['UTF-16BE']      = 'iconv_fallback_utf8_utf16be';
1321
			$ConversionFunctionList['UTF-8']['UTF-16LE']      = 'iconv_fallback_utf8_utf16le';
1322
			$ConversionFunctionList['UTF-16']['ISO-8859-1']   = 'iconv_fallback_utf16_iso88591';
1323
			$ConversionFunctionList['UTF-16']['UTF-8']        = 'iconv_fallback_utf16_utf8';
1324
			$ConversionFunctionList['UTF-16LE']['ISO-8859-1'] = 'iconv_fallback_utf16le_iso88591';
1325
			$ConversionFunctionList['UTF-16LE']['UTF-8']      = 'iconv_fallback_utf16le_utf8';
1326
			$ConversionFunctionList['UTF-16BE']['ISO-8859-1'] = 'iconv_fallback_utf16be_iso88591';
1327
			$ConversionFunctionList['UTF-16BE']['UTF-8']      = 'iconv_fallback_utf16be_utf8';
1328
		}
1329
		if (isset($ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)])) {
1330
			$ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)];
1331
			return self::$ConversionFunction($string);
1332
		}
1333
		throw new Exception('PHP does not has mb_convert_encoding() or iconv() support - cannot convert from '.$in_charset.' to '.$out_charset);
1334
	}
1335
1336
	/**
1337
	 * @param mixed  $data
1338
	 * @param string $charset
1339
	 *
1340
	 * @return mixed
1341
	 */
1342
	public static function recursiveMultiByteCharString2HTML($data, $charset='ISO-8859-1') {
1343
		if (is_string($data)) {
1344
			return self::MultiByteCharString2HTML($data, $charset);
1345
		} elseif (is_array($data)) {
1346
			$return_data = array();
1347
			foreach ($data as $key => $value) {
1348
				$return_data[$key] = self::recursiveMultiByteCharString2HTML($value, $charset);
1349
			}
1350
			return $return_data;
1351
		}
1352
		// integer, float, objects, resources, etc
1353
		return $data;
1354
	}
1355
1356
	/**
1357
	 * @param string|int|float $string
1358
	 * @param string           $charset
1359
	 *
1360
	 * @return string
1361
	 */
1362
	public static function MultiByteCharString2HTML($string, $charset='ISO-8859-1') {
1363
		$string = (string) $string; // in case trying to pass a numeric (float, int) string, would otherwise return an empty string
1364
		$HTMLstring = '';
1365
1366
		switch (strtolower($charset)) {
1367
			case '1251':
1368
			case '1252':
1369
			case '866':
1370
			case '932':
1371
			case '936':
1372
			case '950':
1373
			case 'big5':
1374
			case 'big5-hkscs':
1375
			case 'cp1251':
1376
			case 'cp1252':
1377
			case 'cp866':
1378
			case 'euc-jp':
1379
			case 'eucjp':
1380
			case 'gb2312':
1381
			case 'ibm866':
1382
			case 'iso-8859-1':
1383
			case 'iso-8859-15':
1384
			case 'iso8859-1':
1385
			case 'iso8859-15':
1386
			case 'koi8-r':
1387
			case 'koi8-ru':
1388
			case 'koi8r':
1389
			case 'shift_jis':
1390
			case 'sjis':
1391
			case 'win-1251':
1392
			case 'windows-1251':
1393
			case 'windows-1252':
1394
				$HTMLstring = htmlentities($string, ENT_COMPAT, $charset);
1395
				break;
1396
1397
			case 'utf-8':
1398
				$strlen = strlen($string);
1399
				for ($i = 0; $i < $strlen; $i++) {
1400
					$char_ord_val = ord($string{$i});
1401
					$charval = 0;
1402
					if ($char_ord_val < 0x80) {
1403
						$charval = $char_ord_val;
1404
					} elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F  &&  $i+3 < $strlen) {
1405
						$charval  = (($char_ord_val & 0x07) << 18);
1406
						$charval += ((ord($string{++$i}) & 0x3F) << 12);
1407
						$charval += ((ord($string{++$i}) & 0x3F) << 6);
1408
						$charval +=  (ord($string{++$i}) & 0x3F);
1409
					} elseif ((($char_ord_val & 0xE0) >> 5) == 0x07  &&  $i+2 < $strlen) {
1410
						$charval  = (($char_ord_val & 0x0F) << 12);
1411
						$charval += ((ord($string{++$i}) & 0x3F) << 6);
1412
						$charval +=  (ord($string{++$i}) & 0x3F);
1413
					} elseif ((($char_ord_val & 0xC0) >> 6) == 0x03  &&  $i+1 < $strlen) {
1414
						$charval  = (($char_ord_val & 0x1F) << 6);
1415
						$charval += (ord($string{++$i}) & 0x3F);
1416
					}
1417
					if (($charval >= 32) && ($charval <= 127)) {
1418
						$HTMLstring .= htmlentities(chr($charval));
1419
					} else {
1420
						$HTMLstring .= '&#'.$charval.';';
1421
					}
1422
				}
1423
				break;
1424
1425 View Code Duplication
			case 'utf-16le':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1426
				for ($i = 0; $i < strlen($string); $i += 2) {
1427
					$charval = self::LittleEndian2Int(substr($string, $i, 2));
1428
					if (($charval >= 32) && ($charval <= 127)) {
1429
						$HTMLstring .= chr($charval);
1430
					} else {
1431
						$HTMLstring .= '&#'.$charval.';';
1432
					}
1433
				}
1434
				break;
1435
1436 View Code Duplication
			case 'utf-16be':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1437
				for ($i = 0; $i < strlen($string); $i += 2) {
1438
					$charval = self::BigEndian2Int(substr($string, $i, 2));
1439
					if (($charval >= 32) && ($charval <= 127)) {
1440
						$HTMLstring .= chr($charval);
1441
					} else {
1442
						$HTMLstring .= '&#'.$charval.';';
1443
					}
1444
				}
1445
				break;
1446
1447
			default:
1448
				$HTMLstring = 'ERROR: Character set "'.$charset.'" not supported in MultiByteCharString2HTML()';
1449
				break;
1450
		}
1451
		return $HTMLstring;
1452
	}
1453
1454
	/**
1455
	 * @param int $namecode
1456
	 *
1457
	 * @return string
1458
	 */
1459 View Code Duplication
	public static function RGADnameLookup($namecode) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1460
		static $RGADname = array();
1461
		if (empty($RGADname)) {
1462
			$RGADname[0] = 'not set';
1463
			$RGADname[1] = 'Track Gain Adjustment';
1464
			$RGADname[2] = 'Album Gain Adjustment';
1465
		}
1466
1467
		return (isset($RGADname[$namecode]) ? $RGADname[$namecode] : '');
1468
	}
1469
1470
	/**
1471
	 * @param int $originatorcode
1472
	 *
1473
	 * @return string
1474
	 */
1475
	public static function RGADoriginatorLookup($originatorcode) {
1476
		static $RGADoriginator = array();
1477
		if (empty($RGADoriginator)) {
1478
			$RGADoriginator[0] = 'unspecified';
1479
			$RGADoriginator[1] = 'pre-set by artist/producer/mastering engineer';
1480
			$RGADoriginator[2] = 'set by user';
1481
			$RGADoriginator[3] = 'determined automatically';
1482
		}
1483
1484
		return (isset($RGADoriginator[$originatorcode]) ? $RGADoriginator[$originatorcode] : '');
1485
	}
1486
1487
	/**
1488
	 * @param int $rawadjustment
1489
	 * @param int $signbit
1490
	 *
1491
	 * @return float
1492
	 */
1493
	public static function RGADadjustmentLookup($rawadjustment, $signbit) {
1494
		$adjustment = (float) $rawadjustment / 10;
1495
		if ($signbit == 1) {
1496
			$adjustment *= -1;
1497
		}
1498
		return $adjustment;
1499
	}
1500
1501
	/**
1502
	 * @param int $namecode
1503
	 * @param int $originatorcode
1504
	 * @param int $replaygain
1505
	 *
1506
	 * @return string
1507
	 */
1508
	public static function RGADgainString($namecode, $originatorcode, $replaygain) {
1509
		if ($replaygain < 0) {
1510
			$signbit = '1';
1511
		} else {
1512
			$signbit = '0';
1513
		}
1514
		$storedreplaygain = intval(round($replaygain * 10));
1515
		$gainstring  = str_pad(decbin($namecode), 3, '0', STR_PAD_LEFT);
1516
		$gainstring .= str_pad(decbin($originatorcode), 3, '0', STR_PAD_LEFT);
1517
		$gainstring .= $signbit;
1518
		$gainstring .= str_pad(decbin($storedreplaygain), 9, '0', STR_PAD_LEFT);
1519
1520
		return $gainstring;
1521
	}
1522
1523
	/**
1524
	 * @param float $amplitude
1525
	 *
1526
	 * @return float
1527
	 */
1528
	public static function RGADamplitude2dB($amplitude) {
1529
		return 20 * log10($amplitude);
1530
	}
1531
1532
	/**
1533
	 * @param string $imgData
1534
	 * @param array  $imageinfo
1535
	 *
1536
	 * @return array|false
1537
	 */
1538
	public static function GetDataImageSize($imgData, &$imageinfo=array()) {
1539
		static $tempdir = '';
1540
		if (empty($tempdir)) {
1541
			if (function_exists('sys_get_temp_dir')) {
1542
				$tempdir = sys_get_temp_dir(); // https://github.com/JamesHeinrich/getID3/issues/52
1543
			}
1544
1545
			// yes this is ugly, feel free to suggest a better way
1546
			if (include_once(dirname(__FILE__).'/getid3.php')) {
1547
				if ($getid3_temp = new getID3()) {
1548
					if ($getid3_temp_tempdir = $getid3_temp->tempdir) {
1549
						$tempdir = $getid3_temp_tempdir;
1550
					}
1551
					unset($getid3_temp, $getid3_temp_tempdir);
1552
				}
1553
			}
1554
		}
1555
		$GetDataImageSize = false;
1556
		if ($tempfilename = tempnam($tempdir, 'gI3')) {
1557
			if (is_writable($tempfilename) && is_file($tempfilename) && ($tmp = fopen($tempfilename, 'wb'))) {
1558
				fwrite($tmp, $imgData);
1559
				fclose($tmp);
1560
				$GetDataImageSize = @getimagesize($tempfilename, $imageinfo);
1561
				if (($GetDataImageSize === false) || !isset($GetDataImageSize[0]) || !isset($GetDataImageSize[1])) {
1562
					return false;
1563
				}
1564
				$GetDataImageSize['height'] = $GetDataImageSize[0];
1565
				$GetDataImageSize['width']  = $GetDataImageSize[1];
1566
			}
1567
			unlink($tempfilename);
1568
		}
1569
		return $GetDataImageSize;
1570
	}
1571
1572
	/**
1573
	 * @param string $mime_type
1574
	 *
1575
	 * @return string
1576
	 */
1577
	public static function ImageExtFromMime($mime_type) {
1578
		// temporary way, works OK for now, but should be reworked in the future
1579
		return str_replace(array('image/', 'x-', 'jpeg'), array('', '', 'jpg'), $mime_type);
1580
	}
1581
1582
	/**
1583
	 * @param array $ThisFileInfo
1584
	 *
1585
	 * @return bool
1586
	 */
1587
	public static function CopyTagsToComments(&$ThisFileInfo) {
1588
1589
		// Copy all entries from ['tags'] into common ['comments']
1590
		if (!empty($ThisFileInfo['tags'])) {
1591
			foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) {
1592
				foreach ($tagarray as $tagname => $tagdata) {
1593
					foreach ($tagdata as $key => $value) {
1594
						if (!empty($value)) {
1595
							if (empty($ThisFileInfo['comments'][$tagname])) {
1596
1597
								// fall through and append value
1598
1599
							} elseif ($tagtype == 'id3v1') {
1600
1601
								$newvaluelength = strlen(trim($value));
1602
								foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
1603
									$oldvaluelength = strlen(trim($existingvalue));
1604
									if (($newvaluelength <= $oldvaluelength) && (substr($existingvalue, 0, $newvaluelength) == trim($value))) {
1605
										// new value is identical but shorter-than (or equal-length to) one already in comments - skip
1606
										break 2;
1607
									}
1608
								}
1609
1610
							} elseif (!is_array($value)) {
1611
1612
								$newvaluelength = strlen(trim($value));
1613
								foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
1614
									$oldvaluelength = strlen(trim($existingvalue));
1615
									if ((strlen($existingvalue) > 10) && ($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) {
1616
										$ThisFileInfo['comments'][$tagname][$existingkey] = trim($value);
1617
										//break 2;
1618
										break;
1619
									}
1620
								}
1621
1622
							}
1623
							if (is_array($value) || empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) {
1624
								$value = (is_string($value) ? trim($value) : $value);
1625
								if (!is_int($key) && !ctype_digit($key)) {
1626
									$ThisFileInfo['comments'][$tagname][$key] = $value;
1627
								} else {
1628
									if (isset($ThisFileInfo['comments'][$tagname])) {
1629
										$ThisFileInfo['comments'][$tagname] = array($value);
1630
									} else {
1631
										$ThisFileInfo['comments'][$tagname][] = $value;
1632
									}
1633
								}
1634
							}
1635
						}
1636
					}
1637
				}
1638
			}
1639
1640
			// attempt to standardize spelling of returned keys
1641
			$StandardizeFieldNames = array(
1642
				'tracknumber' => 'track_number',
1643
				'track'       => 'track_number',
1644
			);
1645
			foreach ($StandardizeFieldNames as $badkey => $goodkey) {
1646
				if (array_key_exists($badkey, $ThisFileInfo['comments']) && !array_key_exists($goodkey, $ThisFileInfo['comments'])) {
1647
					$ThisFileInfo['comments'][$goodkey] = $ThisFileInfo['comments'][$badkey];
1648
					unset($ThisFileInfo['comments'][$badkey]);
1649
				}
1650
			}
1651
1652
			// Copy to ['comments_html']
1653
			if (!empty($ThisFileInfo['comments'])) {
1654
				foreach ($ThisFileInfo['comments'] as $field => $values) {
1655
					if ($field == 'picture') {
1656
						// pictures can take up a lot of space, and we don't need multiple copies of them
1657
						// let there be a single copy in [comments][picture], and not elsewhere
1658
						continue;
1659
					}
1660
					foreach ($values as $index => $value) {
1661
						if (is_array($value)) {
1662
							$ThisFileInfo['comments_html'][$field][$index] = $value;
1663
						} else {
1664
							$ThisFileInfo['comments_html'][$field][$index] = str_replace('&#0;', '', self::MultiByteCharString2HTML($value, $ThisFileInfo['encoding']));
1665
						}
1666
					}
1667
				}
1668
			}
1669
1670
		}
1671
		return true;
1672
	}
1673
1674
	/**
1675
	 * @param string $key
1676
	 * @param int    $begin
1677
	 * @param int    $end
1678
	 * @param string $file
1679
	 * @param string $name
1680
	 *
1681
	 * @return string
1682
	 */
1683
	public static function EmbeddedLookup($key, $begin, $end, $file, $name) {
1684
1685
		// Cached
1686
		static $cache;
1687
		if (isset($cache[$file][$name])) {
1688
			return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : '');
1689
		}
1690
1691
		// Init
1692
		$keylength  = strlen($key);
0 ignored issues
show
Unused Code introduced by
$keylength is not used, you could remove the assignment.

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

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

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

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

Loading history...
1693
		$line_count = $end - $begin - 7;
1694
1695
		// Open php file
1696
		$fp = fopen($file, 'r');
1697
1698
		// Discard $begin lines
1699
		for ($i = 0; $i < ($begin + 3); $i++) {
1700
			fgets($fp, 1024);
1701
		}
1702
1703
		// Loop thru line
1704
		while (0 < $line_count--) {
1705
1706
			// Read line
1707
			$line = ltrim(fgets($fp, 1024), "\t ");
1708
1709
			// METHOD A: only cache the matching key - less memory but slower on next lookup of not-previously-looked-up key
1710
			//$keycheck = substr($line, 0, $keylength);
1711
			//if ($key == $keycheck)  {
1712
			//	$cache[$file][$name][$keycheck] = substr($line, $keylength + 1);
1713
			//	break;
1714
			//}
1715
1716
			// METHOD B: cache all keys in this lookup - more memory but faster on next lookup of not-previously-looked-up key
1717
			//$cache[$file][$name][substr($line, 0, $keylength)] = trim(substr($line, $keylength + 1));
1718
			$explodedLine = explode("\t", $line, 2);
1719
			$ThisKey   = (isset($explodedLine[0]) ? $explodedLine[0] : '');
1720
			$ThisValue = (isset($explodedLine[1]) ? $explodedLine[1] : '');
1721
			$cache[$file][$name][$ThisKey] = trim($ThisValue);
1722
		}
1723
1724
		// Close and return
1725
		fclose($fp);
1726
		return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : '');
1727
	}
1728
1729
	/**
1730
	 * @param string $filename
1731
	 * @param string $sourcefile
1732
	 * @param bool   $DieOnFailure
1733
	 *
1734
	 * @return bool
1735
	 * @throws Exception
1736
	 */
1737
	public static function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) {
1738
		global $GETID3_ERRORARRAY;
1739
1740
		if (file_exists($filename)) {
1741
			if (include_once($filename)) {
1742
				return true;
1743
			} else {
1744
				$diemessage = basename($sourcefile).' depends on '.$filename.', which has errors';
1745
			}
1746
		} else {
1747
			$diemessage = basename($sourcefile).' depends on '.$filename.', which is missing';
1748
		}
1749
		if ($DieOnFailure) {
1750
			throw new Exception($diemessage);
1751
		} else {
1752
			$GETID3_ERRORARRAY[] = $diemessage;
1753
		}
1754
		return false;
1755
	}
1756
1757
	/**
1758
	 * @param string $string
1759
	 *
1760
	 * @return string
1761
	 */
1762
	public static function trimNullByte($string) {
1763
		return trim($string, "\x00");
1764
	}
1765
1766
	/**
1767
	 * @param string $path
1768
	 *
1769
	 * @return float|bool
1770
	 */
1771
	public static function getFileSizeSyscall($path) {
1772
		$filesize = false;
1773
1774
		if (GETID3_OS_ISWINDOWS) {
1775
			if (class_exists('COM')) { // From PHP 5.3.15 and 5.4.5, COM and DOTNET is no longer built into the php core.you have to add COM support in php.ini:
1776
				$filesystem = new COM('Scripting.FileSystemObject');
0 ignored issues
show
Unused Code introduced by
The call to com::__construct() has too many arguments starting with 'Scripting.FileSystemObject'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1777
				$file = $filesystem->GetFile($path);
1778
				$filesize = $file->Size();
1779
				unset($filesystem, $file);
1780
			} else {
1781
				$commandline = 'for %I in ('.escapeshellarg($path).') do @echo %~zI';
1782
			}
1783
		} else {
1784
			$commandline = 'ls -l '.escapeshellarg($path).' | awk \'{print $5}\'';
1785
		}
1786
		if (isset($commandline)) {
1787
			$output = trim(`$commandline`);
1788
			if (ctype_digit($output)) {
1789
				$filesize = (float) $output;
1790
			}
1791
		}
1792
		return $filesize;
1793
	}
1794
1795
	/**
1796
	 * @param string $filename
1797
	 *
1798
	 * @return string|false
1799
	 */
1800
	public static function truepath($filename) {
1801
		// 2017-11-08: this could use some improvement, patches welcome
1802
		if (preg_match('#^(\\\\\\\\|//)[a-z0-9]#i', $filename, $matches)) {
1803
			// PHP's built-in realpath function does not work on UNC Windows shares
1804
			$goodpath = array();
1805
			foreach (explode('/', str_replace('\\', '/', $filename)) as $part) {
1806
				if ($part == '.') {
1807
					continue;
1808
				}
1809
				if ($part == '..') {
1810
					if (count($goodpath)) {
1811
						array_pop($goodpath);
1812
					} else {
1813
						// cannot step above this level, already at top level
1814
						return false;
1815
					}
1816
				} else {
1817
					$goodpath[] = $part;
1818
				}
1819
			}
1820
			return implode(DIRECTORY_SEPARATOR, $goodpath);
1821
		}
1822
		return realpath($filename);
1823
	}
1824
1825
	/**
1826
	 * Workaround for Bug #37268 (https://bugs.php.net/bug.php?id=37268)
1827
	 *
1828
	 * @param string $path A path.
1829
	 * @param string $suffix If the name component ends in suffix this will also be cut off.
1830
	 *
1831
	 * @return string
1832
	 */
1833
	public static function mb_basename($path, $suffix = null) {
1834
		$splited = preg_split('#/#', rtrim($path, '/ '));
1835
		return substr(basename('X'.$splited[count($splited) - 1], $suffix), 1);
1836
	}
1837
1838
}
1839