[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ID3/ -> getid3.lib.php (source)

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


Generated : Thu Apr 18 08:20:02 2024 Cross-referenced by PHPXref