| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 3 ///////////////////////////////////////////////////////////////// 4 /// getID3() by James Heinrich <info@getid3.org> // 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 if (!defined('GETID3_LIBXML_OPTIONS') && defined('LIBXML_VERSION')) { 15 if (LIBXML_VERSION >= 20621) { 16 define('GETID3_LIBXML_OPTIONS', LIBXML_NOENT | LIBXML_NONET | LIBXML_NOWARNING | LIBXML_COMPACT); 17 } else { 18 define('GETID3_LIBXML_OPTIONS', LIBXML_NOENT | LIBXML_NONET | LIBXML_NOWARNING); 19 } 20 } 21 22 class getid3_lib 23 { 24 /** 25 * @param string $string 26 * @param bool $hex 27 * @param bool $spaces 28 * @param string|bool $htmlencoding 29 * 30 * @return string 31 */ 32 public static function PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8') { 33 $returnstring = ''; 34 for ($i = 0; $i < strlen($string); $i++) { 35 if ($hex) { 36 $returnstring .= str_pad(dechex(ord($string[$i])), 2, '0', STR_PAD_LEFT); 37 } else { 38 $returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string[$i]) ? $string[$i] : 'ยค'); 39 } 40 if ($spaces) { 41 $returnstring .= ' '; 42 } 43 } 44 if (!empty($htmlencoding)) { 45 if ($htmlencoding === true) { 46 $htmlencoding = 'UTF-8'; // prior to getID3 v1.9.0 the function's 4th parameter was boolean 47 } 48 $returnstring = htmlentities($returnstring, ENT_QUOTES, $htmlencoding); 49 } 50 return $returnstring; 51 } 52 53 /** 54 * Truncates a floating-point number at the decimal point. 55 * 56 * @param float $floatnumber 57 * 58 * @return float|int returns int (if possible, otherwise float) 59 */ 60 public static function trunc($floatnumber) { 61 if ($floatnumber >= 1) { 62 $truncatednumber = floor($floatnumber); 63 } elseif ($floatnumber <= -1) { 64 $truncatednumber = ceil($floatnumber); 65 } else { 66 $truncatednumber = 0; 67 } 68 if (self::intValueSupported($truncatednumber)) { 69 $truncatednumber = (int) $truncatednumber; 70 } 71 return $truncatednumber; 72 } 73 74 /** 75 * @param int|null $variable 76 * @param-out int $variable 77 * @param int $increment 78 * 79 * @return bool 80 */ 81 public static function safe_inc(&$variable, $increment=1) { 82 if (isset($variable)) { 83 $variable += $increment; 84 } else { 85 $variable = $increment; 86 } 87 return true; 88 } 89 90 /** 91 * @param int|float $floatnum 92 * 93 * @return int|float 94 */ 95 public static function CastAsInt($floatnum) { 96 // convert to float if not already 97 $floatnum = (float) $floatnum; 98 99 // convert a float to type int, only if possible 100 if (self::trunc($floatnum) == $floatnum) { 101 // it's not floating point 102 if (self::intValueSupported($floatnum)) { 103 // it's within int range 104 $floatnum = (int) $floatnum; 105 } 106 } 107 return $floatnum; 108 } 109 110 /** 111 * @param int $num 112 * 113 * @return bool 114 */ 115 public static function intValueSupported($num) { 116 // check if integers are 64-bit 117 static $hasINT64 = null; 118 if ($hasINT64 === null) { // 10x faster than is_null() 119 /** @var int|float|object $bigInt */ 120 $bigInt = pow(2, 31); 121 $hasINT64 = is_int($bigInt); // 32-bit int are limited to (2^31)-1 122 if (!$hasINT64 && !defined('PHP_INT_MIN')) { 123 define('PHP_INT_MIN', ~PHP_INT_MAX); 124 } 125 } 126 // if integers are 64-bit - no other check required 127 if ($hasINT64 || (($num <= PHP_INT_MAX) && ($num >= PHP_INT_MIN))) { 128 return true; 129 } 130 return false; 131 } 132 133 /** 134 * Perform a division, guarding against division by zero 135 * 136 * @param float|int $numerator 137 * @param float|int $denominator 138 * @param float|int $fallback 139 * @return float|int 140 */ 141 public static function SafeDiv($numerator, $denominator, $fallback = 0) { 142 return $denominator ? $numerator / $denominator : $fallback; 143 } 144 145 /** 146 * @param string $fraction 147 * 148 * @return float 149 */ 150 public static function DecimalizeFraction($fraction) { 151 list($numerator, $denominator) = explode('/', $fraction); 152 return (int) $numerator / ($denominator ? $denominator : 1); 153 } 154 155 /** 156 * @param string $binarynumerator 157 * 158 * @return float 159 */ 160 public static function DecimalBinary2Float($binarynumerator) { 161 $numerator = self::Bin2Dec($binarynumerator); 162 $denominator = self::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator))); 163 return ($numerator / $denominator); 164 } 165 166 /** 167 * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html 168 * 169 * @param string $binarypointnumber 170 * @param int $maxbits 171 * 172 * @return array 173 */ 174 public static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) { 175 if (strpos($binarypointnumber, '.') === false) { 176 $binarypointnumber = '0.'.$binarypointnumber; 177 } elseif ($binarypointnumber[0] == '.') { 178 $binarypointnumber = '0'.$binarypointnumber; 179 } 180 $exponent = 0; 181 while (($binarypointnumber[0] != '1') || (substr($binarypointnumber, 1, 1) != '.')) { 182 if (substr($binarypointnumber, 1, 1) == '.') { 183 $exponent--; 184 $binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3); 185 } else { 186 $pointpos = strpos($binarypointnumber, '.'); 187 $exponent += ($pointpos - 1); 188 $binarypointnumber = str_replace('.', '', $binarypointnumber); 189 $binarypointnumber = $binarypointnumber[0].'.'.substr($binarypointnumber, 1); 190 } 191 } 192 $binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT); 193 return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent); 194 } 195 196 /** 197 * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html 198 * 199 * @param float $floatvalue 200 * 201 * @return string 202 */ 203 public static function Float2BinaryDecimal($floatvalue) { 204 $maxbits = 128; // to how many bits of precision should the calculations be taken? 205 $intpart = self::trunc($floatvalue); 206 $floatpart = abs($floatvalue - $intpart); 207 $pointbitstring = ''; 208 while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) { 209 $floatpart *= 2; 210 $pointbitstring .= (string) self::trunc($floatpart); 211 $floatpart -= self::trunc($floatpart); 212 } 213 $binarypointnumber = decbin($intpart).'.'.$pointbitstring; 214 return $binarypointnumber; 215 } 216 217 /** 218 * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html 219 * 220 * @param float $floatvalue 221 * @param int $bits 222 * 223 * @return string|false 224 */ 225 public static function Float2String($floatvalue, $bits) { 226 $exponentbits = 0; 227 $fractionbits = 0; 228 switch ($bits) { 229 case 32: 230 $exponentbits = 8; 231 $fractionbits = 23; 232 break; 233 234 case 64: 235 $exponentbits = 11; 236 $fractionbits = 52; 237 break; 238 239 default: 240 return false; 241 } 242 if ($floatvalue >= 0) { 243 $signbit = '0'; 244 } else { 245 $signbit = '1'; 246 } 247 $normalizedbinary = self::NormalizeBinaryPoint(self::Float2BinaryDecimal($floatvalue), $fractionbits); 248 $biasedexponent = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent 249 $exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT); 250 $fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT); 251 252 return self::BigEndian2String(self::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false); 253 } 254 255 /** 256 * @param string $byteword 257 * 258 * @return float|false 259 */ 260 public static function LittleEndian2Float($byteword) { 261 return self::BigEndian2Float(strrev($byteword)); 262 } 263 264 /** 265 * ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic 266 * 267 * @link https://web.archive.org/web/20120325162206/http://www.psc.edu/general/software/packages/ieee/ieee.php 268 * @link http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html 269 * 270 * @param string $byteword 271 * 272 * @return float|false 273 */ 274 public static function BigEndian2Float($byteword) { 275 $bitword = self::BigEndian2Bin($byteword); 276 if (!$bitword) { 277 return 0; 278 } 279 $signbit = $bitword[0]; 280 $floatvalue = 0; 281 $exponentbits = 0; 282 $fractionbits = 0; 283 284 switch (strlen($byteword) * 8) { 285 case 32: 286 $exponentbits = 8; 287 $fractionbits = 23; 288 break; 289 290 case 64: 291 $exponentbits = 11; 292 $fractionbits = 52; 293 break; 294 295 case 80: 296 // 80-bit Apple SANE format 297 // http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/ 298 $exponentstring = substr($bitword, 1, 15); 299 $isnormalized = intval($bitword[16]); 300 $fractionstring = substr($bitword, 17, 63); 301 $exponent = pow(2, self::Bin2Dec($exponentstring) - 16383); 302 $fraction = $isnormalized + self::DecimalBinary2Float($fractionstring); 303 $floatvalue = $exponent * $fraction; 304 if ($signbit == '1') { 305 $floatvalue *= -1; 306 } 307 return $floatvalue; 308 309 default: 310 return false; 311 } 312 $exponentstring = substr($bitword, 1, $exponentbits); 313 $fractionstring = substr($bitword, $exponentbits + 1, $fractionbits); 314 $exponent = self::Bin2Dec($exponentstring); 315 $fraction = self::Bin2Dec($fractionstring); 316 317 if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) { 318 // Not a Number 319 $floatvalue = NAN; 320 } elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) { 321 if ($signbit == '1') { 322 $floatvalue = -INF; 323 } else { 324 $floatvalue = INF; 325 } 326 } elseif (($exponent == 0) && ($fraction == 0)) { 327 if ($signbit == '1') { 328 $floatvalue = -0.0; 329 } else { 330 $floatvalue = 0.0; 331 } 332 } elseif (($exponent == 0) && ($fraction != 0)) { 333 // These are 'unnormalized' values 334 $floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * self::DecimalBinary2Float($fractionstring); 335 if ($signbit == '1') { 336 $floatvalue *= -1; 337 } 338 } elseif ($exponent != 0) { 339 $floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + self::DecimalBinary2Float($fractionstring)); 340 if ($signbit == '1') { 341 $floatvalue *= -1; 342 } 343 } 344 return (float) $floatvalue; 345 } 346 347 /** 348 * @param string $byteword 349 * @param bool $synchsafe 350 * @param bool $signed 351 * 352 * @return int|float|false 353 * @throws Exception 354 */ 355 public static function BigEndian2Int($byteword, $synchsafe=false, $signed=false) { 356 $intvalue = 0; 357 $bytewordlen = strlen($byteword); 358 if ($bytewordlen == 0) { 359 return false; 360 } 361 for ($i = 0; $i < $bytewordlen; $i++) { 362 if ($synchsafe) { // disregard MSB, effectively 7-bit bytes 363 //$intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7); // faster, but runs into problems past 2^31 on 32-bit systems 364 $intvalue += (ord($byteword[$i]) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7); 365 } else { 366 $intvalue += ord($byteword[$i]) * pow(256, ($bytewordlen - 1 - $i)); 367 } 368 } 369 if ($signed && !$synchsafe) { 370 // synchsafe ints are not allowed to be signed 371 if ($bytewordlen <= PHP_INT_SIZE) { 372 $signMaskBit = 0x80 << (8 * ($bytewordlen - 1)); 373 if ($intvalue & $signMaskBit) { 374 $intvalue = 0 - ($intvalue & ($signMaskBit - 1)); 375 } 376 } else { 377 throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits ('.strlen($byteword).') in self::BigEndian2Int()'); 378 } 379 } 380 return self::CastAsInt($intvalue); 381 } 382 383 /** 384 * @param string $byteword 385 * @param bool $signed 386 * 387 * @return int|float|false 388 */ 389 public static function LittleEndian2Int($byteword, $signed=false) { 390 return self::BigEndian2Int(strrev($byteword), false, $signed); 391 } 392 393 /** 394 * @param string $byteword 395 * 396 * @return string 397 */ 398 public static function LittleEndian2Bin($byteword) { 399 return self::BigEndian2Bin(strrev($byteword)); 400 } 401 402 /** 403 * @param string $byteword 404 * 405 * @return string 406 */ 407 public static function BigEndian2Bin($byteword) { 408 $binvalue = ''; 409 $bytewordlen = strlen($byteword); 410 for ($i = 0; $i < $bytewordlen; $i++) { 411 $binvalue .= str_pad(decbin(ord($byteword[$i])), 8, '0', STR_PAD_LEFT); 412 } 413 return $binvalue; 414 } 415 416 /** 417 * @param int $number 418 * @param int $minbytes 419 * @param bool $synchsafe 420 * @param bool $signed 421 * 422 * @return string 423 * @throws Exception 424 */ 425 public static function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) { 426 if ($number < 0) { 427 throw new Exception('ERROR: self::BigEndian2String() does not support negative numbers'); 428 } 429 $maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF); 430 $intstring = ''; 431 if ($signed) { 432 if ($minbytes > PHP_INT_SIZE) { 433 throw new Exception('ERROR: Cannot have signed integers larger than '.(8 * PHP_INT_SIZE).'-bits in self::BigEndian2String()'); 434 } 435 $number = $number & (0x80 << (8 * ($minbytes - 1))); 436 } 437 while ($number != 0) { 438 $quotient = ($number / ($maskbyte + 1)); 439 $intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring; 440 $number = floor($quotient); 441 } 442 return str_pad($intstring, $minbytes, "\x00", STR_PAD_LEFT); 443 } 444 445 /** 446 * @param int|string $number 447 * 448 * @return string 449 */ 450 public static function Dec2Bin($number) { 451 if (!is_numeric($number)) { 452 // https://github.com/JamesHeinrich/getID3/issues/299 453 trigger_error('TypeError: Dec2Bin(): Argument #1 ($number) must be numeric, '.gettype($number).' given', E_USER_WARNING); 454 return ''; 455 } 456 $bytes = array(); 457 while ($number >= 256) { 458 $bytes[] = (int) (($number / 256) - (floor($number / 256))) * 256; 459 $number = floor($number / 256); 460 } 461 $bytes[] = (int) $number; 462 $binstring = ''; 463 foreach ($bytes as $i => $byte) { 464 $binstring = (($i == count($bytes) - 1) ? decbin($byte) : str_pad(decbin($byte), 8, '0', STR_PAD_LEFT)).$binstring; 465 } 466 return $binstring; 467 } 468 469 /** 470 * @param string $binstring 471 * @param bool $signed 472 * 473 * @return int|float 474 */ 475 public static function Bin2Dec($binstring, $signed=false) { 476 $signmult = 1; 477 if ($signed) { 478 if ($binstring[0] == '1') { 479 $signmult = -1; 480 } 481 $binstring = substr($binstring, 1); 482 } 483 $decvalue = 0; 484 for ($i = 0; $i < strlen($binstring); $i++) { 485 $decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i); 486 } 487 return self::CastAsInt($decvalue * $signmult); 488 } 489 490 /** 491 * @param string $binstring 492 * 493 * @return string 494 */ 495 public static function Bin2String($binstring) { 496 // return 'hi' for input of '0110100001101001' 497 $string = ''; 498 $binstringreversed = strrev($binstring); 499 for ($i = 0; $i < strlen($binstringreversed); $i += 8) { 500 $string = chr(self::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string; 501 } 502 return $string; 503 } 504 505 /** 506 * @param int $number 507 * @param int $minbytes 508 * @param bool $synchsafe 509 * 510 * @return string 511 */ 512 public static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) { 513 $intstring = ''; 514 while ($number > 0) { 515 if ($synchsafe) { 516 $intstring = $intstring.chr($number & 127); 517 $number >>= 7; 518 } else { 519 $intstring = $intstring.chr($number & 255); 520 $number >>= 8; 521 } 522 } 523 return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT); 524 } 525 526 /** 527 * @param mixed $array1 528 * @param mixed $array2 529 * 530 * @return array|false 531 */ 532 public static function array_merge_clobber($array1, $array2) { 533 // written by kcรhireability*com 534 // taken from http://www.php.net/manual/en/function.array-merge-recursive.php 535 if (!is_array($array1) || !is_array($array2)) { 536 return false; 537 } 538 $newarray = $array1; 539 foreach ($array2 as $key => $val) { 540 if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) { 541 $newarray[$key] = self::array_merge_clobber($newarray[$key], $val); 542 } else { 543 $newarray[$key] = $val; 544 } 545 } 546 return $newarray; 547 } 548 549 /** 550 * @param mixed $array1 551 * @param mixed $array2 552 * 553 * @return array|false 554 */ 555 public static function array_merge_noclobber($array1, $array2) { 556 if (!is_array($array1) || !is_array($array2)) { 557 return false; 558 } 559 $newarray = $array1; 560 foreach ($array2 as $key => $val) { 561 if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) { 562 $newarray[$key] = self::array_merge_noclobber($newarray[$key], $val); 563 } elseif (!isset($newarray[$key])) { 564 $newarray[$key] = $val; 565 } 566 } 567 return $newarray; 568 } 569 570 /** 571 * @param mixed $array1 572 * @param mixed $array2 573 * 574 * @return array|false|null 575 */ 576 public static function flipped_array_merge_noclobber($array1, $array2) { 577 if (!is_array($array1) || !is_array($array2)) { 578 return false; 579 } 580 # naturally, this only works non-recursively 581 $newarray = array_flip($array1); 582 foreach (array_flip($array2) as $key => $val) { 583 if (!isset($newarray[$key])) { 584 $newarray[$key] = count($newarray); 585 } 586 } 587 return array_flip($newarray); 588 } 589 590 /** 591 * @param array $theArray 592 * 593 * @return bool 594 */ 595 public static function ksort_recursive(&$theArray) { 596 ksort($theArray); 597 foreach ($theArray as $key => $value) { 598 if (is_array($value)) { 599 self::ksort_recursive($theArray[$key]); 600 } 601 } 602 return true; 603 } 604 605 /** 606 * @param string $filename 607 * @param int $numextensions 608 * 609 * @return string 610 */ 611 public static function fileextension($filename, $numextensions=1) { 612 if (strstr($filename, '.')) { 613 $reversedfilename = strrev($filename); 614 $offset = 0; 615 for ($i = 0; $i < $numextensions; $i++) { 616 $offset = strpos($reversedfilename, '.', $offset + 1); 617 if ($offset === false) { 618 return ''; 619 } 620 } 621 return strrev(substr($reversedfilename, 0, $offset)); 622 } 623 return ''; 624 } 625 626 /** 627 * @param int $seconds 628 * 629 * @return string 630 */ 631 public static function PlaytimeString($seconds) { 632 $sign = (($seconds < 0) ? '-' : ''); 633 $seconds = round(abs($seconds)); 634 $H = (int) floor( $seconds / 3600); 635 $M = (int) floor(($seconds - (3600 * $H) ) / 60); 636 $S = (int) round( $seconds - (3600 * $H) - (60 * $M) ); 637 return $sign.($H ? $H.':' : '').($H ? str_pad($M, 2, '0', STR_PAD_LEFT) : intval($M)).':'.str_pad($S, 2, 0, STR_PAD_LEFT); 638 } 639 640 /** 641 * @param int $macdate 642 * 643 * @return int|float 644 */ 645 public static function DateMac2Unix($macdate) { 646 // Macintosh timestamp: seconds since 00:00h January 1, 1904 647 // UNIX timestamp: seconds since 00:00h January 1, 1970 648 return self::CastAsInt($macdate - 2082844800); 649 } 650 651 /** 652 * @param string $rawdata 653 * 654 * @return float 655 */ 656 public static function FixedPoint8_8($rawdata) { 657 return self::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (self::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8)); 658 } 659 660 /** 661 * @param string $rawdata 662 * 663 * @return float 664 */ 665 public static function FixedPoint16_16($rawdata) { 666 return self::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (self::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16)); 667 } 668 669 /** 670 * @param string $rawdata 671 * 672 * @return float 673 */ 674 public static function FixedPoint2_30($rawdata) { 675 $binarystring = self::BigEndian2Bin($rawdata); 676 return self::Bin2Dec(substr($binarystring, 0, 2)) + (float) (self::Bin2Dec(substr($binarystring, 2, 30)) / pow(2, 30)); 677 } 678 679 680 /** 681 * @param string $ArrayPath 682 * @param string $Separator 683 * @param mixed $Value 684 * 685 * @return array 686 */ 687 public static function CreateDeepArray($ArrayPath, $Separator, $Value) { 688 // assigns $Value to a nested array path: 689 // $foo = self::CreateDeepArray('/path/to/my', '/', 'file.txt') 690 // is the same as: 691 // $foo = array('path'=>array('to'=>'array('my'=>array('file.txt')))); 692 // or 693 // $foo['path']['to']['my'] = 'file.txt'; 694 $ArrayPath = ltrim($ArrayPath, $Separator); 695 $ReturnedArray = array(); 696 if (($pos = strpos($ArrayPath, $Separator)) !== false) { 697 $ReturnedArray[substr($ArrayPath, 0, $pos)] = self::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value); 698 } else { 699 $ReturnedArray[$ArrayPath] = $Value; 700 } 701 return $ReturnedArray; 702 } 703 704 /** 705 * @param array $arraydata 706 * @param bool $returnkey 707 * 708 * @return int|false 709 */ 710 public static function array_max($arraydata, $returnkey=false) { 711 $maxvalue = false; 712 $maxkey = false; 713 foreach ($arraydata as $key => $value) { 714 if (!is_array($value)) { 715 if (($maxvalue === false) || ($value > $maxvalue)) { 716 $maxvalue = $value; 717 $maxkey = $key; 718 } 719 } 720 } 721 return ($returnkey ? $maxkey : $maxvalue); 722 } 723 724 /** 725 * @param array $arraydata 726 * @param bool $returnkey 727 * 728 * @return int|false 729 */ 730 public static function array_min($arraydata, $returnkey=false) { 731 $minvalue = false; 732 $minkey = false; 733 foreach ($arraydata as $key => $value) { 734 if (!is_array($value)) { 735 if (($minvalue === false) || ($value < $minvalue)) { 736 $minvalue = $value; 737 $minkey = $key; 738 } 739 } 740 } 741 return ($returnkey ? $minkey : $minvalue); 742 } 743 744 /** 745 * @param string $XMLstring 746 * 747 * @return array|false 748 */ 749 public static function XML2array($XMLstring) { 750 if (function_exists('simplexml_load_string')) { 751 if (PHP_VERSION_ID < 80000) { 752 if (function_exists('libxml_disable_entity_loader')) { 753 // http://websec.io/2012/08/27/Preventing-XEE-in-PHP.html 754 // https://core.trac.wordpress.org/changeset/29378 755 // This function has been deprecated in PHP 8.0 because in libxml 2.9.0, external entity loading is 756 // disabled by default, but is still needed when LIBXML_NOENT is used. 757 $loader = @libxml_disable_entity_loader(true); 758 $XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', GETID3_LIBXML_OPTIONS); 759 $return = self::SimpleXMLelement2array($XMLobject); 760 @libxml_disable_entity_loader($loader); 761 return $return; 762 } 763 } else { 764 $allow = false; 765 if (defined('LIBXML_VERSION') && (LIBXML_VERSION >= 20900)) { 766 // https://www.php.net/manual/en/function.libxml-disable-entity-loader.php 767 // "as of libxml 2.9.0 entity substitution is disabled by default, so there is no need to disable the loading 768 // of external entities, unless there is the need to resolve internal entity references with LIBXML_NOENT." 769 $allow = true; 770 } elseif (function_exists('libxml_set_external_entity_loader')) { 771 libxml_set_external_entity_loader(function () { return null; }); // https://www.zend.com/blog/cve-2023-3823 772 $allow = true; 773 } 774 if ($allow) { 775 $XMLobject = simplexml_load_string($XMLstring, 'SimpleXMLElement', GETID3_LIBXML_OPTIONS); 776 $return = self::SimpleXMLelement2array($XMLobject); 777 return $return; 778 } 779 } 780 } 781 return false; 782 } 783 784 /** 785 * @param SimpleXMLElement|array|mixed $XMLobject 786 * 787 * @return mixed 788 */ 789 public static function SimpleXMLelement2array($XMLobject) { 790 if (!is_object($XMLobject) && !is_array($XMLobject)) { 791 return $XMLobject; 792 } 793 $XMLarray = $XMLobject instanceof SimpleXMLElement ? get_object_vars($XMLobject) : $XMLobject; 794 foreach ($XMLarray as $key => $value) { 795 $XMLarray[$key] = self::SimpleXMLelement2array($value); 796 } 797 return $XMLarray; 798 } 799 800 /** 801 * Returns checksum for a file from starting position to absolute end position. 802 * 803 * @param string $file 804 * @param int $offset 805 * @param int $end 806 * @param string $algorithm 807 * 808 * @return string|false 809 * @throws getid3_exception 810 */ 811 public static function hash_data($file, $offset, $end, $algorithm) { 812 if (!self::intValueSupported($end)) { 813 return false; 814 } 815 if (!in_array($algorithm, array('md5', 'sha1'))) { 816 throw new getid3_exception('Invalid algorithm ('.$algorithm.') in self::hash_data()'); 817 } 818 819 $size = $end - $offset; 820 821 $fp = fopen($file, 'rb'); 822 fseek($fp, $offset); 823 $ctx = hash_init($algorithm); 824 while ($size > 0) { 825 $buffer = fread($fp, min($size, getID3::FREAD_BUFFER_SIZE)); 826 hash_update($ctx, $buffer); 827 $size -= getID3::FREAD_BUFFER_SIZE; 828 } 829 $hash = hash_final($ctx); 830 fclose($fp); 831 832 return $hash; 833 } 834 835 /** 836 * @param string $filename_source 837 * @param string $filename_dest 838 * @param int $offset 839 * @param int $length 840 * 841 * @return bool 842 * @throws Exception 843 * 844 * @deprecated Unused, may be removed in future versions of getID3 845 */ 846 public static function CopyFileParts($filename_source, $filename_dest, $offset, $length) { 847 if (!self::intValueSupported($offset + $length)) { 848 throw new Exception('cannot copy file portion, it extends beyond the '.round(PHP_INT_MAX / 1073741824).'GB limit'); 849 } 850 if (is_readable($filename_source) && is_file($filename_source) && ($fp_src = fopen($filename_source, 'rb'))) { 851 if (($fp_dest = fopen($filename_dest, 'wb'))) { 852 if (fseek($fp_src, $offset) == 0) { 853 $byteslefttowrite = $length; 854 while (($byteslefttowrite > 0) && ($buffer = fread($fp_src, min($byteslefttowrite, getID3::FREAD_BUFFER_SIZE)))) { 855 $byteswritten = fwrite($fp_dest, $buffer, $byteslefttowrite); 856 $byteslefttowrite -= $byteswritten; 857 } 858 fclose($fp_dest); 859 return true; 860 } else { 861 fclose($fp_src); 862 throw new Exception('failed to seek to offset '.$offset.' in '.$filename_source); 863 } 864 } else { 865 throw new Exception('failed to create file for writing '.$filename_dest); 866 } 867 } else { 868 throw new Exception('failed to open file for reading '.$filename_source); 869 } 870 } 871 872 /** 873 * @param int $charval 874 * 875 * @return string 876 */ 877 public static function iconv_fallback_int_utf8($charval) { 878 if ($charval < 128) { 879 // 0bbbbbbb 880 $newcharstring = chr($charval); 881 } elseif ($charval < 2048) { 882 // 110bbbbb 10bbbbbb 883 $newcharstring = chr(($charval >> 6) | 0xC0); 884 $newcharstring .= chr(($charval & 0x3F) | 0x80); 885 } elseif ($charval < 65536) { 886 // 1110bbbb 10bbbbbb 10bbbbbb 887 $newcharstring = chr(($charval >> 12) | 0xE0); 888 $newcharstring .= chr(($charval >> 6) | 0xC0); 889 $newcharstring .= chr(($charval & 0x3F) | 0x80); 890 } else { 891 // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb 892 $newcharstring = chr(($charval >> 18) | 0xF0); 893 $newcharstring .= chr(($charval >> 12) | 0xC0); 894 $newcharstring .= chr(($charval >> 6) | 0xC0); 895 $newcharstring .= chr(($charval & 0x3F) | 0x80); 896 } 897 return $newcharstring; 898 } 899 900 /** 901 * ISO-8859-1 => UTF-8 902 * 903 * @param string $string 904 * @param bool $bom 905 * 906 * @return string 907 */ 908 public static function iconv_fallback_iso88591_utf8($string, $bom=false) { 909 $newcharstring = ''; 910 if ($bom) { 911 $newcharstring .= "\xEF\xBB\xBF"; 912 } 913 for ($i = 0; $i < strlen($string); $i++) { 914 $charval = ord($string[$i]); 915 $newcharstring .= self::iconv_fallback_int_utf8($charval); 916 } 917 return $newcharstring; 918 } 919 920 /** 921 * ISO-8859-1 => UTF-16BE 922 * 923 * @param string $string 924 * @param bool $bom 925 * 926 * @return string 927 */ 928 public static function iconv_fallback_iso88591_utf16be($string, $bom=false) { 929 $newcharstring = ''; 930 if ($bom) { 931 $newcharstring .= "\xFE\xFF"; 932 } 933 for ($i = 0; $i < strlen($string); $i++) { 934 $newcharstring .= "\x00".$string[$i]; 935 } 936 return $newcharstring; 937 } 938 939 /** 940 * ISO-8859-1 => UTF-16LE 941 * 942 * @param string $string 943 * @param bool $bom 944 * 945 * @return string 946 */ 947 public static function iconv_fallback_iso88591_utf16le($string, $bom=false) { 948 $newcharstring = ''; 949 if ($bom) { 950 $newcharstring .= "\xFF\xFE"; 951 } 952 for ($i = 0; $i < strlen($string); $i++) { 953 $newcharstring .= $string[$i]."\x00"; 954 } 955 return $newcharstring; 956 } 957 958 /** 959 * ISO-8859-1 => UTF-16LE (BOM) 960 * 961 * @param string $string 962 * 963 * @return string 964 */ 965 public static function iconv_fallback_iso88591_utf16($string) { 966 return self::iconv_fallback_iso88591_utf16le($string, true); 967 } 968 969 /** 970 * UTF-8 => ISO-8859-1 971 * 972 * @param string $string 973 * 974 * @return string 975 */ 976 public static function iconv_fallback_utf8_iso88591($string) { 977 $newcharstring = ''; 978 $offset = 0; 979 $stringlength = strlen($string); 980 while ($offset < $stringlength) { 981 if ((ord($string[$offset]) | 0x07) == 0xF7) { 982 // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb 983 $charval = ((ord($string[($offset + 0)]) & 0x07) << 18) & 984 ((ord($string[($offset + 1)]) & 0x3F) << 12) & 985 ((ord($string[($offset + 2)]) & 0x3F) << 6) & 986 (ord($string[($offset + 3)]) & 0x3F); 987 $offset += 4; 988 } elseif ((ord($string[$offset]) | 0x0F) == 0xEF) { 989 // 1110bbbb 10bbbbbb 10bbbbbb 990 $charval = ((ord($string[($offset + 0)]) & 0x0F) << 12) & 991 ((ord($string[($offset + 1)]) & 0x3F) << 6) & 992 (ord($string[($offset + 2)]) & 0x3F); 993 $offset += 3; 994 } elseif ((ord($string[$offset]) | 0x1F) == 0xDF) { 995 // 110bbbbb 10bbbbbb 996 $charval = ((ord($string[($offset + 0)]) & 0x1F) << 6) & 997 (ord($string[($offset + 1)]) & 0x3F); 998 $offset += 2; 999 } elseif ((ord($string[$offset]) | 0x7F) == 0x7F) { 1000 // 0bbbbbbb 1001 $charval = ord($string[$offset]); 1002 $offset += 1; 1003 } else { 1004 // error? throw some kind of warning here? 1005 $charval = false; 1006 $offset += 1; 1007 } 1008 if ($charval !== false) { 1009 $newcharstring .= (($charval < 256) ? chr($charval) : '?'); 1010 } 1011 } 1012 return $newcharstring; 1013 } 1014 1015 /** 1016 * UTF-8 => UTF-16BE 1017 * 1018 * @param string $string 1019 * @param bool $bom 1020 * 1021 * @return string 1022 */ 1023 public static function iconv_fallback_utf8_utf16be($string, $bom=false) { 1024 $newcharstring = ''; 1025 if ($bom) { 1026 $newcharstring .= "\xFE\xFF"; 1027 } 1028 $offset = 0; 1029 $stringlength = strlen($string); 1030 while ($offset < $stringlength) { 1031 if ((ord($string[$offset]) | 0x07) == 0xF7) { 1032 // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb 1033 $charval = ((ord($string[($offset + 0)]) & 0x07) << 18) & 1034 ((ord($string[($offset + 1)]) & 0x3F) << 12) & 1035 ((ord($string[($offset + 2)]) & 0x3F) << 6) & 1036 (ord($string[($offset + 3)]) & 0x3F); 1037 $offset += 4; 1038 } elseif ((ord($string[$offset]) | 0x0F) == 0xEF) { 1039 // 1110bbbb 10bbbbbb 10bbbbbb 1040 $charval = ((ord($string[($offset + 0)]) & 0x0F) << 12) & 1041 ((ord($string[($offset + 1)]) & 0x3F) << 6) & 1042 (ord($string[($offset + 2)]) & 0x3F); 1043 $offset += 3; 1044 } elseif ((ord($string[$offset]) | 0x1F) == 0xDF) { 1045 // 110bbbbb 10bbbbbb 1046 $charval = ((ord($string[($offset + 0)]) & 0x1F) << 6) & 1047 (ord($string[($offset + 1)]) & 0x3F); 1048 $offset += 2; 1049 } elseif ((ord($string[$offset]) | 0x7F) == 0x7F) { 1050 // 0bbbbbbb 1051 $charval = ord($string[$offset]); 1052 $offset += 1; 1053 } else { 1054 // error? throw some kind of warning here? 1055 $charval = false; 1056 $offset += 1; 1057 } 1058 if ($charval !== false) { 1059 $newcharstring .= (($charval < 65536) ? self::BigEndian2String($charval, 2) : "\x00".'?'); 1060 } 1061 } 1062 return $newcharstring; 1063 } 1064 1065 /** 1066 * UTF-8 => UTF-16LE 1067 * 1068 * @param string $string 1069 * @param bool $bom 1070 * 1071 * @return string 1072 */ 1073 public static function iconv_fallback_utf8_utf16le($string, $bom=false) { 1074 $newcharstring = ''; 1075 if ($bom) { 1076 $newcharstring .= "\xFF\xFE"; 1077 } 1078 $offset = 0; 1079 $stringlength = strlen($string); 1080 while ($offset < $stringlength) { 1081 if ((ord($string[$offset]) | 0x07) == 0xF7) { 1082 // 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb 1083 $charval = ((ord($string[($offset + 0)]) & 0x07) << 18) & 1084 ((ord($string[($offset + 1)]) & 0x3F) << 12) & 1085 ((ord($string[($offset + 2)]) & 0x3F) << 6) & 1086 (ord($string[($offset + 3)]) & 0x3F); 1087 $offset += 4; 1088 } elseif ((ord($string[$offset]) | 0x0F) == 0xEF) { 1089 // 1110bbbb 10bbbbbb 10bbbbbb 1090 $charval = ((ord($string[($offset + 0)]) & 0x0F) << 12) & 1091 ((ord($string[($offset + 1)]) & 0x3F) << 6) & 1092 (ord($string[($offset + 2)]) & 0x3F); 1093 $offset += 3; 1094 } elseif ((ord($string[$offset]) | 0x1F) == 0xDF) { 1095 // 110bbbbb 10bbbbbb 1096 $charval = ((ord($string[($offset + 0)]) & 0x1F) << 6) & 1097 (ord($string[($offset + 1)]) & 0x3F); 1098 $offset += 2; 1099 } elseif ((ord($string[$offset]) | 0x7F) == 0x7F) { 1100 // 0bbbbbbb 1101 $charval = ord($string[$offset]); 1102 $offset += 1; 1103 } else { 1104 // error? maybe throw some warning here? 1105 $charval = false; 1106 $offset += 1; 1107 } 1108 if ($charval !== false) { 1109 $newcharstring .= (($charval < 65536) ? self::LittleEndian2String($charval, 2) : '?'."\x00"); 1110 } 1111 } 1112 return $newcharstring; 1113 } 1114 1115 /** 1116 * UTF-8 => UTF-16LE (BOM) 1117 * 1118 * @param string $string 1119 * 1120 * @return string 1121 */ 1122 public static function iconv_fallback_utf8_utf16($string) { 1123 return self::iconv_fallback_utf8_utf16le($string, true); 1124 } 1125 1126 /** 1127 * UTF-16BE => UTF-8 1128 * 1129 * @param string $string 1130 * 1131 * @return string 1132 */ 1133 public static function iconv_fallback_utf16be_utf8($string) { 1134 if (substr($string, 0, 2) == "\xFE\xFF") { 1135 // strip BOM 1136 $string = substr($string, 2); 1137 } 1138 $newcharstring = ''; 1139 for ($i = 0; $i < strlen($string); $i += 2) { 1140 $charval = self::BigEndian2Int(substr($string, $i, 2)); 1141 $newcharstring .= self::iconv_fallback_int_utf8($charval); 1142 } 1143 return $newcharstring; 1144 } 1145 1146 /** 1147 * UTF-16LE => UTF-8 1148 * 1149 * @param string $string 1150 * 1151 * @return string 1152 */ 1153 public static function iconv_fallback_utf16le_utf8($string) { 1154 if (substr($string, 0, 2) == "\xFF\xFE") { 1155 // strip BOM 1156 $string = substr($string, 2); 1157 } 1158 $newcharstring = ''; 1159 for ($i = 0; $i < strlen($string); $i += 2) { 1160 $charval = self::LittleEndian2Int(substr($string, $i, 2)); 1161 $newcharstring .= self::iconv_fallback_int_utf8($charval); 1162 } 1163 return $newcharstring; 1164 } 1165 1166 /** 1167 * UTF-16BE => ISO-8859-1 1168 * 1169 * @param string $string 1170 * 1171 * @return string 1172 */ 1173 public static function iconv_fallback_utf16be_iso88591($string) { 1174 if (substr($string, 0, 2) == "\xFE\xFF") { 1175 // strip BOM 1176 $string = substr($string, 2); 1177 } 1178 $newcharstring = ''; 1179 for ($i = 0; $i < strlen($string); $i += 2) { 1180 $charval = self::BigEndian2Int(substr($string, $i, 2)); 1181 $newcharstring .= (($charval < 256) ? chr($charval) : '?'); 1182 } 1183 return $newcharstring; 1184 } 1185 1186 /** 1187 * UTF-16LE => ISO-8859-1 1188 * 1189 * @param string $string 1190 * 1191 * @return string 1192 */ 1193 public static function iconv_fallback_utf16le_iso88591($string) { 1194 if (substr($string, 0, 2) == "\xFF\xFE") { 1195 // strip BOM 1196 $string = substr($string, 2); 1197 } 1198 $newcharstring = ''; 1199 for ($i = 0; $i < strlen($string); $i += 2) { 1200 $charval = self::LittleEndian2Int(substr($string, $i, 2)); 1201 $newcharstring .= (($charval < 256) ? chr($charval) : '?'); 1202 } 1203 return $newcharstring; 1204 } 1205 1206 /** 1207 * UTF-16 (BOM) => ISO-8859-1 1208 * 1209 * @param string $string 1210 * 1211 * @return string 1212 */ 1213 public static function iconv_fallback_utf16_iso88591($string) { 1214 $bom = substr($string, 0, 2); 1215 if ($bom == "\xFE\xFF") { 1216 return self::iconv_fallback_utf16be_iso88591(substr($string, 2)); 1217 } elseif ($bom == "\xFF\xFE") { 1218 return self::iconv_fallback_utf16le_iso88591(substr($string, 2)); 1219 } 1220 return $string; 1221 } 1222 1223 /** 1224 * UTF-16 (BOM) => UTF-8 1225 * 1226 * @param string $string 1227 * 1228 * @return string 1229 */ 1230 public static function iconv_fallback_utf16_utf8($string) { 1231 $bom = substr($string, 0, 2); 1232 if ($bom == "\xFE\xFF") { 1233 return self::iconv_fallback_utf16be_utf8(substr($string, 2)); 1234 } elseif ($bom == "\xFF\xFE") { 1235 return self::iconv_fallback_utf16le_utf8(substr($string, 2)); 1236 } 1237 return $string; 1238 } 1239 1240 /** 1241 * @param string $in_charset 1242 * @param string $out_charset 1243 * @param string $string 1244 * 1245 * @return string 1246 * @throws Exception 1247 */ 1248 public static function iconv_fallback($in_charset, $out_charset, $string) { 1249 1250 if ($in_charset == $out_charset) { 1251 return $string; 1252 } 1253 1254 // mb_convert_encoding() available 1255 if (function_exists('mb_convert_encoding')) { 1256 if ((strtoupper($in_charset) == 'UTF-16') && (substr($string, 0, 2) != "\xFE\xFF") && (substr($string, 0, 2) != "\xFF\xFE")) { 1257 // if BOM missing, mb_convert_encoding will mishandle the conversion, assume UTF-16BE and prepend appropriate BOM 1258 $string = "\xFF\xFE".$string; 1259 } 1260 if ((strtoupper($in_charset) == 'UTF-16') && (strtoupper($out_charset) == 'UTF-8')) { 1261 if (($string == "\xFF\xFE") || ($string == "\xFE\xFF")) { 1262 // if string consists of only BOM, mb_convert_encoding will return the BOM unmodified 1263 return ''; 1264 } 1265 } 1266 if ($converted_string = @mb_convert_encoding($string, $out_charset, $in_charset)) { 1267 switch ($out_charset) { 1268 case 'ISO-8859-1': 1269 $converted_string = rtrim($converted_string, "\x00"); 1270 break; 1271 } 1272 return $converted_string; 1273 } 1274 return $string; 1275 1276 // iconv() available 1277 } elseif (function_exists('iconv')) { 1278 if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) { 1279 switch ($out_charset) { 1280 case 'ISO-8859-1': 1281 $converted_string = rtrim($converted_string, "\x00"); 1282 break; 1283 } 1284 return $converted_string; 1285 } 1286 1287 // iconv() may sometimes fail with "illegal character in input string" error message 1288 // and return an empty string, but returning the unconverted string is more useful 1289 return $string; 1290 } 1291 1292 1293 // neither mb_convert_encoding or iconv() is available 1294 static $ConversionFunctionList = array(); 1295 if (empty($ConversionFunctionList)) { 1296 $ConversionFunctionList['ISO-8859-1']['UTF-8'] = 'iconv_fallback_iso88591_utf8'; 1297 $ConversionFunctionList['ISO-8859-1']['UTF-16'] = 'iconv_fallback_iso88591_utf16'; 1298 $ConversionFunctionList['ISO-8859-1']['UTF-16BE'] = 'iconv_fallback_iso88591_utf16be'; 1299 $ConversionFunctionList['ISO-8859-1']['UTF-16LE'] = 'iconv_fallback_iso88591_utf16le'; 1300 $ConversionFunctionList['UTF-8']['ISO-8859-1'] = 'iconv_fallback_utf8_iso88591'; 1301 $ConversionFunctionList['UTF-8']['UTF-16'] = 'iconv_fallback_utf8_utf16'; 1302 $ConversionFunctionList['UTF-8']['UTF-16BE'] = 'iconv_fallback_utf8_utf16be'; 1303 $ConversionFunctionList['UTF-8']['UTF-16LE'] = 'iconv_fallback_utf8_utf16le'; 1304 $ConversionFunctionList['UTF-16']['ISO-8859-1'] = 'iconv_fallback_utf16_iso88591'; 1305 $ConversionFunctionList['UTF-16']['UTF-8'] = 'iconv_fallback_utf16_utf8'; 1306 $ConversionFunctionList['UTF-16LE']['ISO-8859-1'] = 'iconv_fallback_utf16le_iso88591'; 1307 $ConversionFunctionList['UTF-16LE']['UTF-8'] = 'iconv_fallback_utf16le_utf8'; 1308 $ConversionFunctionList['UTF-16BE']['ISO-8859-1'] = 'iconv_fallback_utf16be_iso88591'; 1309 $ConversionFunctionList['UTF-16BE']['UTF-8'] = 'iconv_fallback_utf16be_utf8'; 1310 } 1311 if (isset($ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)])) { 1312 $ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)]; 1313 return self::$ConversionFunction($string); 1314 } 1315 throw new Exception('PHP does not has mb_convert_encoding() or iconv() support - cannot convert from '.$in_charset.' to '.$out_charset); 1316 } 1317 1318 /** 1319 * @param mixed $data 1320 * @param string $charset 1321 * 1322 * @return mixed 1323 */ 1324 public static function recursiveMultiByteCharString2HTML($data, $charset='ISO-8859-1') { 1325 if (is_string($data)) { 1326 return self::MultiByteCharString2HTML($data, $charset); 1327 } elseif (is_array($data)) { 1328 $return_data = array(); 1329 foreach ($data as $key => $value) { 1330 $return_data[$key] = self::recursiveMultiByteCharString2HTML($value, $charset); 1331 } 1332 return $return_data; 1333 } 1334 // integer, float, objects, resources, etc 1335 return $data; 1336 } 1337 1338 /** 1339 * @param string|int|float $string 1340 * @param string $charset 1341 * 1342 * @return string 1343 */ 1344 public static function MultiByteCharString2HTML($string, $charset='ISO-8859-1') { 1345 $string = (string) $string; // in case trying to pass a numeric (float, int) string, would otherwise return an empty string 1346 $HTMLstring = ''; 1347 1348 switch (strtolower($charset)) { 1349 case '1251': 1350 case '1252': 1351 case '866': 1352 case '932': 1353 case '936': 1354 case '950': 1355 case 'big5': 1356 case 'big5-hkscs': 1357 case 'cp1251': 1358 case 'cp1252': 1359 case 'cp866': 1360 case 'euc-jp': 1361 case 'eucjp': 1362 case 'gb2312': 1363 case 'ibm866': 1364 case 'iso-8859-1': 1365 case 'iso-8859-15': 1366 case 'iso8859-1': 1367 case 'iso8859-15': 1368 case 'koi8-r': 1369 case 'koi8-ru': 1370 case 'koi8r': 1371 case 'shift_jis': 1372 case 'sjis': 1373 case 'win-1251': 1374 case 'windows-1251': 1375 case 'windows-1252': 1376 $HTMLstring = htmlentities($string, ENT_COMPAT, $charset); 1377 break; 1378 1379 case 'utf-8': 1380 $strlen = strlen($string); 1381 for ($i = 0; $i < $strlen; $i++) { 1382 $char_ord_val = ord($string[$i]); 1383 $charval = 0; 1384 if ($char_ord_val < 0x80) { 1385 $charval = $char_ord_val; 1386 } elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F && $i+3 < $strlen) { 1387 $charval = (($char_ord_val & 0x07) << 18); 1388 $charval += ((ord($string[++$i]) & 0x3F) << 12); 1389 $charval += ((ord($string[++$i]) & 0x3F) << 6); 1390 $charval += (ord($string[++$i]) & 0x3F); 1391 } elseif ((($char_ord_val & 0xE0) >> 5) == 0x07 && $i+2 < $strlen) { 1392 $charval = (($char_ord_val & 0x0F) << 12); 1393 $charval += ((ord($string[++$i]) & 0x3F) << 6); 1394 $charval += (ord($string[++$i]) & 0x3F); 1395 } elseif ((($char_ord_val & 0xC0) >> 6) == 0x03 && $i+1 < $strlen) { 1396 $charval = (($char_ord_val & 0x1F) << 6); 1397 $charval += (ord($string[++$i]) & 0x3F); 1398 } 1399 if (($charval >= 32) && ($charval <= 127)) { 1400 $HTMLstring .= htmlentities(chr($charval)); 1401 } else { 1402 $HTMLstring .= '&#'.$charval.';'; 1403 } 1404 } 1405 break; 1406 1407 case 'utf-16le': 1408 for ($i = 0; $i < strlen($string); $i += 2) { 1409 $charval = self::LittleEndian2Int(substr($string, $i, 2)); 1410 if (($charval >= 32) && ($charval <= 127)) { 1411 $HTMLstring .= chr($charval); 1412 } else { 1413 $HTMLstring .= '&#'.$charval.';'; 1414 } 1415 } 1416 break; 1417 1418 case 'utf-16be': 1419 for ($i = 0; $i < strlen($string); $i += 2) { 1420 $charval = self::BigEndian2Int(substr($string, $i, 2)); 1421 if (($charval >= 32) && ($charval <= 127)) { 1422 $HTMLstring .= chr($charval); 1423 } else { 1424 $HTMLstring .= '&#'.$charval.';'; 1425 } 1426 } 1427 break; 1428 1429 default: 1430 $HTMLstring = 'ERROR: Character set "'.$charset.'" not supported in MultiByteCharString2HTML()'; 1431 break; 1432 } 1433 return $HTMLstring; 1434 } 1435 1436 /** 1437 * @param int $namecode 1438 * 1439 * @return string 1440 */ 1441 public static function RGADnameLookup($namecode) { 1442 static $RGADname = array(); 1443 if (empty($RGADname)) { 1444 $RGADname[0] = 'not set'; 1445 $RGADname[1] = 'Track Gain Adjustment'; 1446 $RGADname[2] = 'Album Gain Adjustment'; 1447 } 1448 1449 return (isset($RGADname[$namecode]) ? $RGADname[$namecode] : ''); 1450 } 1451 1452 /** 1453 * @param int $originatorcode 1454 * 1455 * @return string 1456 */ 1457 public static function RGADoriginatorLookup($originatorcode) { 1458 static $RGADoriginator = array(); 1459 if (empty($RGADoriginator)) { 1460 $RGADoriginator[0] = 'unspecified'; 1461 $RGADoriginator[1] = 'pre-set by artist/producer/mastering engineer'; 1462 $RGADoriginator[2] = 'set by user'; 1463 $RGADoriginator[3] = 'determined automatically'; 1464 } 1465 1466 return (isset($RGADoriginator[$originatorcode]) ? $RGADoriginator[$originatorcode] : ''); 1467 } 1468 1469 /** 1470 * @param int $rawadjustment 1471 * @param int $signbit 1472 * 1473 * @return float 1474 */ 1475 public static function RGADadjustmentLookup($rawadjustment, $signbit) { 1476 $adjustment = (float) $rawadjustment / 10; 1477 if ($signbit == 1) { 1478 $adjustment *= -1; 1479 } 1480 return $adjustment; 1481 } 1482 1483 /** 1484 * @param int $namecode 1485 * @param int $originatorcode 1486 * @param int $replaygain 1487 * 1488 * @return string 1489 */ 1490 public static function RGADgainString($namecode, $originatorcode, $replaygain) { 1491 if ($replaygain < 0) { 1492 $signbit = '1'; 1493 } else { 1494 $signbit = '0'; 1495 } 1496 $storedreplaygain = intval(round($replaygain * 10)); 1497 $gainstring = str_pad(decbin($namecode), 3, '0', STR_PAD_LEFT); 1498 $gainstring .= str_pad(decbin($originatorcode), 3, '0', STR_PAD_LEFT); 1499 $gainstring .= $signbit; 1500 $gainstring .= str_pad(decbin($storedreplaygain), 9, '0', STR_PAD_LEFT); 1501 1502 return $gainstring; 1503 } 1504 1505 /** 1506 * @param float $amplitude 1507 * 1508 * @return float 1509 */ 1510 public static function RGADamplitude2dB($amplitude) { 1511 return 20 * log10($amplitude); 1512 } 1513 1514 /** 1515 * @param string $imgData 1516 * @param array $imageinfo 1517 * 1518 * @return array|false 1519 */ 1520 public static function GetDataImageSize($imgData, &$imageinfo=array()) { 1521 if (PHP_VERSION_ID >= 50400) { 1522 $GetDataImageSize = @getimagesizefromstring($imgData, $imageinfo); 1523 if ($GetDataImageSize === false) { 1524 return false; 1525 } 1526 $GetDataImageSize['height'] = $GetDataImageSize[0]; 1527 $GetDataImageSize['width'] = $GetDataImageSize[1]; 1528 return $GetDataImageSize; 1529 } 1530 static $tempdir = ''; 1531 if (empty($tempdir)) { 1532 if (function_exists('sys_get_temp_dir')) { 1533 $tempdir = sys_get_temp_dir(); // https://github.com/JamesHeinrich/getID3/issues/52 1534 } 1535 1536 // yes this is ugly, feel free to suggest a better way 1537 if (include_once(dirname(__FILE__).'/getid3.php')) { 1538 $getid3_temp = new getID3(); 1539 if ($getid3_temp_tempdir = $getid3_temp->tempdir) { 1540 $tempdir = $getid3_temp_tempdir; 1541 } 1542 unset($getid3_temp, $getid3_temp_tempdir); 1543 } 1544 } 1545 $GetDataImageSize = false; 1546 if ($tempfilename = tempnam($tempdir, 'gI3')) { 1547 if (is_writable($tempfilename) && is_file($tempfilename) && ($tmp = fopen($tempfilename, 'wb'))) { 1548 fwrite($tmp, $imgData); 1549 fclose($tmp); 1550 $GetDataImageSize = @getimagesize($tempfilename, $imageinfo); 1551 if ($GetDataImageSize === false) { 1552 return false; 1553 } 1554 $GetDataImageSize['height'] = $GetDataImageSize[0]; 1555 $GetDataImageSize['width'] = $GetDataImageSize[1]; 1556 } 1557 unlink($tempfilename); 1558 } 1559 return $GetDataImageSize; 1560 } 1561 1562 /** 1563 * @param string $mime_type 1564 * 1565 * @return string 1566 */ 1567 public static function ImageExtFromMime($mime_type) { 1568 // temporary way, works OK for now, but should be reworked in the future 1569 return str_replace(array('image/', 'x-', 'jpeg'), array('', '', 'jpg'), $mime_type); 1570 } 1571 1572 /** 1573 * @param array $ThisFileInfo 1574 * @param bool $option_tags_html default true (just as in the main getID3 class) 1575 * 1576 * @return bool 1577 */ 1578 public static function CopyTagsToComments(&$ThisFileInfo, $option_tags_html=true) { 1579 // Copy all entries from ['tags'] into common ['comments'] 1580 if (!empty($ThisFileInfo['tags'])) { 1581 1582 // Some tag types can only support limited character sets and may contain data in non-standard encoding (usually ID3v1) 1583 // and/or poorly-transliterated tag values that are also in tag formats that do support full-range character sets 1584 // To make the output more user-friendly, process the potentially-problematic tag formats last to enhance the chance that 1585 // the first entries in [comments] are the most correct and the "bad" ones (if any) come later. 1586 // https://github.com/JamesHeinrich/getID3/issues/338 1587 $processLastTagTypes = array('id3v1','riff'); 1588 foreach ($processLastTagTypes as $processLastTagType) { 1589 if (isset($ThisFileInfo['tags'][$processLastTagType])) { 1590 // bubble ID3v1 to the end, if present to aid in detecting bad ID3v1 encodings 1591 $temp = $ThisFileInfo['tags'][$processLastTagType]; 1592 unset($ThisFileInfo['tags'][$processLastTagType]); 1593 $ThisFileInfo['tags'][$processLastTagType] = $temp; 1594 unset($temp); 1595 } 1596 } 1597 foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) { 1598 foreach ($tagarray as $tagname => $tagdata) { 1599 foreach ($tagdata as $key => $value) { 1600 if (!empty($value)) { 1601 if (empty($ThisFileInfo['comments'][$tagname])) { 1602 1603 // fall through and append value 1604 1605 } elseif ($tagtype == 'id3v1') { 1606 1607 $newvaluelength = strlen(trim($value)); 1608 foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) { 1609 $oldvaluelength = strlen(trim($existingvalue)); 1610 if (($newvaluelength <= $oldvaluelength) && (substr($existingvalue, 0, $newvaluelength) == trim($value))) { 1611 // new value is identical but shorter-than (or equal-length to) one already in comments - skip 1612 break 2; 1613 } 1614 1615 if (function_exists('mb_convert_encoding')) { 1616 if (trim($value) == trim(substr(mb_convert_encoding($existingvalue, $ThisFileInfo['id3v1']['encoding'], $ThisFileInfo['encoding']), 0, 30))) { 1617 // value stored in ID3v1 appears to be probably the multibyte value transliterated (badly) into ISO-8859-1 in ID3v1. 1618 // As an example, Foobar2000 will do this if you tag a file with Chinese or Arabic or Cyrillic or something that doesn't fit into ISO-8859-1 the ID3v1 will consist of mostly "?" characters, one per multibyte unrepresentable character 1619 break 2; 1620 } 1621 } 1622 } 1623 1624 } elseif (!is_array($value)) { 1625 1626 $newvaluelength = strlen(trim($value)); 1627 $newvaluelengthMB = mb_strlen(trim($value)); 1628 foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) { 1629 $oldvaluelength = strlen(trim($existingvalue)); 1630 $oldvaluelengthMB = mb_strlen(trim($existingvalue)); 1631 if (($newvaluelengthMB == $oldvaluelengthMB) && ($existingvalue == getid3_lib::iconv_fallback('UTF-8', 'ASCII', $value))) { 1632 // https://github.com/JamesHeinrich/getID3/issues/338 1633 // check for tags containing extended characters that may have been forced into limited-character storage (e.g. UTF8 values into ASCII) 1634 // which will usually display unrepresentable characters as "?" 1635 $ThisFileInfo['comments'][$tagname][$existingkey] = trim($value); 1636 break; 1637 } 1638 if ((strlen($existingvalue) > 10) && ($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) { 1639 $ThisFileInfo['comments'][$tagname][$existingkey] = trim($value); 1640 break; 1641 } 1642 } 1643 1644 } 1645 if (is_array($value) || empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) { 1646 $value = (is_string($value) ? trim($value) : $value); 1647 if (!is_int($key) && !ctype_digit($key)) { 1648 $ThisFileInfo['comments'][$tagname][$key] = $value; 1649 } else { 1650 if (!isset($ThisFileInfo['comments'][$tagname])) { 1651 $ThisFileInfo['comments'][$tagname] = array($value); 1652 } else { 1653 $ThisFileInfo['comments'][$tagname][] = $value; 1654 } 1655 } 1656 } 1657 } 1658 } 1659 } 1660 } 1661 1662 // attempt to standardize spelling of returned keys 1663 if (!empty($ThisFileInfo['comments'])) { 1664 $StandardizeFieldNames = array( 1665 'tracknumber' => 'track_number', 1666 'track' => 'track_number', 1667 ); 1668 foreach ($StandardizeFieldNames as $badkey => $goodkey) { 1669 if (array_key_exists($badkey, $ThisFileInfo['comments']) && !array_key_exists($goodkey, $ThisFileInfo['comments'])) { 1670 $ThisFileInfo['comments'][$goodkey] = $ThisFileInfo['comments'][$badkey]; 1671 unset($ThisFileInfo['comments'][$badkey]); 1672 } 1673 } 1674 } 1675 1676 if ($option_tags_html) { 1677 // Copy ['comments'] to ['comments_html'] 1678 if (!empty($ThisFileInfo['comments'])) { 1679 foreach ($ThisFileInfo['comments'] as $field => $values) { 1680 if ($field == 'picture') { 1681 // pictures can take up a lot of space, and we don't need multiple copies of them 1682 // let there be a single copy in [comments][picture], and not elsewhere 1683 continue; 1684 } 1685 foreach ($values as $index => $value) { 1686 if (is_array($value)) { 1687 $ThisFileInfo['comments_html'][$field][$index] = $value; 1688 } else { 1689 $ThisFileInfo['comments_html'][$field][$index] = str_replace('�', '', self::MultiByteCharString2HTML($value, $ThisFileInfo['encoding'])); 1690 } 1691 } 1692 } 1693 } 1694 } 1695 1696 } 1697 return true; 1698 } 1699 1700 /** 1701 * @param string $key 1702 * @param int $begin 1703 * @param int $end 1704 * @param string $file 1705 * @param string $name 1706 * 1707 * @return string 1708 */ 1709 public static function EmbeddedLookup($key, $begin, $end, $file, $name) { 1710 1711 // Cached 1712 static $cache; 1713 if (isset($cache[$file][$name])) { 1714 return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : ''); 1715 } 1716 1717 // Init 1718 $keylength = strlen($key); 1719 $line_count = $end - $begin - 7; 1720 1721 // Open php file 1722 $fp = fopen($file, 'r'); 1723 1724 // Discard $begin lines 1725 for ($i = 0; $i < ($begin + 3); $i++) { 1726 fgets($fp, 1024); 1727 } 1728 1729 // Loop thru line 1730 while (0 < $line_count--) { 1731 1732 // Read line 1733 $line = ltrim(fgets($fp, 1024), "\t "); 1734 1735 // METHOD A: only cache the matching key - less memory but slower on next lookup of not-previously-looked-up key 1736 //$keycheck = substr($line, 0, $keylength); 1737 //if ($key == $keycheck) { 1738 // $cache[$file][$name][$keycheck] = substr($line, $keylength + 1); 1739 // break; 1740 //} 1741 1742 // METHOD B: cache all keys in this lookup - more memory but faster on next lookup of not-previously-looked-up key 1743 //$cache[$file][$name][substr($line, 0, $keylength)] = trim(substr($line, $keylength + 1)); 1744 $explodedLine = explode("\t", $line, 2); 1745 $ThisKey = $explodedLine[0]; 1746 $ThisValue = (isset($explodedLine[1]) ? $explodedLine[1] : ''); 1747 $cache[$file][$name][$ThisKey] = trim($ThisValue); 1748 } 1749 1750 // Close and return 1751 fclose($fp); 1752 return (isset($cache[$file][$name][$key]) ? $cache[$file][$name][$key] : ''); 1753 } 1754 1755 /** 1756 * @param string $filename 1757 * @param string $sourcefile 1758 * @param bool $DieOnFailure 1759 * 1760 * @return bool 1761 * @throws Exception 1762 */ 1763 public static function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) { 1764 global $GETID3_ERRORARRAY; 1765 1766 if (file_exists($filename)) { 1767 if (include_once($filename)) { 1768 return true; 1769 } else { 1770 $diemessage = basename($sourcefile).' depends on '.$filename.', which has errors'; 1771 } 1772 } else { 1773 $diemessage = basename($sourcefile).' depends on '.$filename.', which is missing'; 1774 } 1775 if ($DieOnFailure) { 1776 throw new Exception($diemessage); 1777 } else { 1778 $GETID3_ERRORARRAY[] = $diemessage; 1779 } 1780 return false; 1781 } 1782 1783 /** 1784 * @param string $string 1785 * 1786 * @return string 1787 */ 1788 public static function trimNullByte($string) { 1789 return trim($string, "\x00"); 1790 } 1791 1792 /** 1793 * @param string $path 1794 * 1795 * @return float|bool 1796 */ 1797 public static function getFileSizeSyscall($path) { 1798 $commandline = null; 1799 $filesize = false; 1800 1801 if (GETID3_OS_ISWINDOWS) { 1802 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: 1803 $filesystem = new COM('Scripting.FileSystemObject'); 1804 $file = $filesystem->GetFile($path); 1805 $filesize = $file->Size(); 1806 unset($filesystem, $file); 1807 } else { 1808 $commandline = 'for %I in ('.escapeshellarg($path).') do @echo %~zI'; 1809 } 1810 } else { 1811 $commandline = 'ls -l '.escapeshellarg($path).' | awk \'{print $5}\''; 1812 } 1813 if (isset($commandline)) { 1814 $output = trim(shell_exec($commandline)); 1815 if (ctype_digit($output)) { 1816 $filesize = (float) $output; 1817 } 1818 } 1819 return $filesize; 1820 } 1821 1822 /** 1823 * @param string $filename 1824 * 1825 * @return string|false 1826 */ 1827 public static function truepath($filename) { 1828 // 2017-11-08: this could use some improvement, patches welcome 1829 if (preg_match('#^(\\\\\\\\|//)[a-z0-9]#i', $filename, $matches)) { 1830 // PHP's built-in realpath function does not work on UNC Windows shares 1831 $goodpath = array(); 1832 foreach (explode('/', str_replace('\\', '/', $filename)) as $part) { 1833 if ($part == '.') { 1834 continue; 1835 } 1836 if ($part == '..') { 1837 if (count($goodpath)) { 1838 array_pop($goodpath); 1839 } else { 1840 // cannot step above this level, already at top level 1841 return false; 1842 } 1843 } else { 1844 $goodpath[] = $part; 1845 } 1846 } 1847 return implode(DIRECTORY_SEPARATOR, $goodpath); 1848 } 1849 return realpath($filename); 1850 } 1851 1852 /** 1853 * Workaround for Bug #37268 (https://bugs.php.net/bug.php?id=37268) 1854 * 1855 * @param string $path A path. 1856 * @param string $suffix If the name component ends in suffix this will also be cut off. 1857 * 1858 * @return string 1859 */ 1860 public static function mb_basename($path, $suffix = '') { 1861 $splited = preg_split('#/#', rtrim($path, '/ ')); 1862 return substr(basename('X'.$splited[count($splited) - 1], $suffix), 1); 1863 } 1864 1865 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Wed Apr 15 08:20:10 2026 | Cross-referenced by PHPXref |