Add basic ID3v2 write support, add unit tests
git-svn-id: http://php-reader.googlecode.com/svn/trunk@64 51a70ab9-7547-0410-9469-37e369ee0574
This commit is contained in:
@@ -69,4 +69,11 @@ interface ID3_Encoding
|
|||||||
* @return integer
|
* @return integer
|
||||||
*/
|
*/
|
||||||
public function getEncoding();
|
public function getEncoding();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the text encoding.
|
||||||
|
*
|
||||||
|
* @param integer $encoding The text encoding.
|
||||||
|
*/
|
||||||
|
public function setEncoding($encoding);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**#@+ @ignore */
|
/**#@+ @ignore */
|
||||||
require_once("Object.php");
|
require_once("ID3/Object.php");
|
||||||
/**#@-*/
|
/**#@-*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,7 +96,7 @@ final class ID3_ExtendedHeader extends ID3_Object
|
|||||||
{
|
{
|
||||||
parent::__construct($reader);
|
parent::__construct($reader);
|
||||||
|
|
||||||
$offset = $this->_reader->offset;
|
$offset = $this->_reader->getOffset();
|
||||||
$this->_size = $this->decodeSynchsafe32($this->_reader->readUInt32BE());
|
$this->_size = $this->decodeSynchsafe32($this->_reader->readUInt32BE());
|
||||||
$this->_reader->skip(1);
|
$this->_reader->skip(1);
|
||||||
$this->_flags = $this->_reader->readInt8();
|
$this->_flags = $this->_reader->readInt8();
|
||||||
@@ -105,7 +105,7 @@ final class ID3_ExtendedHeader extends ID3_Object
|
|||||||
$this->_reader->skip(1);
|
$this->_reader->skip(1);
|
||||||
if ($this->hasFlag(self::CRC32)) {
|
if ($this->hasFlag(self::CRC32)) {
|
||||||
$this->_reader->skip(1);
|
$this->_reader->skip(1);
|
||||||
$this->_crc = Transform::getInt32BE
|
$this->_crc = Transform::fromInt32BE
|
||||||
(($this->_reader->read(1) << 4) &
|
(($this->_reader->read(1) << 4) &
|
||||||
$this->decodeSynchsafe32($this->_reader->read(4)));
|
$this->decodeSynchsafe32($this->_reader->read(4)));
|
||||||
}
|
}
|
||||||
@@ -114,7 +114,7 @@ final class ID3_ExtendedHeader extends ID3_Object
|
|||||||
$this->_restrictions = $this->_reader->readInt8(1);
|
$this->_restrictions = $this->_reader->readInt8(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_reader->skip($this->_size - $this->_reader->offset - $offset);
|
$this->_reader->skip($this->_size - $this->_reader->getOffset() - $offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -133,12 +133,44 @@ final class ID3_ExtendedHeader extends ID3_Object
|
|||||||
*/
|
*/
|
||||||
public function hasFlag($flag) { return ($this->_flags & $flag) == $flag; }
|
public function hasFlag($flag) { return ($this->_flags & $flag) == $flag; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the flags byte.
|
||||||
|
*
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
public function getFlags($flags) { return $this->_flags; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the flags byte.
|
||||||
|
*
|
||||||
|
* @param integer $flags The flags byte.
|
||||||
|
*/
|
||||||
|
public function setFlags($flags) { $this->_flags = $flags; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the CRC-32 data.
|
* Returns the CRC-32 data.
|
||||||
*
|
*
|
||||||
* @return integer
|
* @return integer
|
||||||
*/
|
*/
|
||||||
public function getCRC() { return $this->_crc; }
|
public function getCrc()
|
||||||
|
{
|
||||||
|
if ($this->hasFlag(self::CRC32))
|
||||||
|
return $this->_crc;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the CRC-32 should be generated upon tag write.
|
||||||
|
*
|
||||||
|
* @param boolean $useCrc Whether CRC-32 should be generated
|
||||||
|
*/
|
||||||
|
public function setCrc($useCrc)
|
||||||
|
{
|
||||||
|
if ($useCrc)
|
||||||
|
$this->setFlags($this->getFlags() | self::CDC32);
|
||||||
|
else
|
||||||
|
$this->setFlags($this->getFlags() & ~self::CDC32);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the restrictions. For some applications it might be desired to
|
* Returns the restrictions. For some applications it might be desired to
|
||||||
@@ -189,4 +221,30 @@ final class ID3_ExtendedHeader extends ID3_Object
|
|||||||
* @return integer
|
* @return integer
|
||||||
*/
|
*/
|
||||||
public function getRestrictions() { return $this->_restrictions; }
|
public function getRestrictions() { return $this->_restrictions; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the restrictions byte. See {@link #getRestrictions} for more.
|
||||||
|
*
|
||||||
|
* @param integer $restrictions The restrictions byte.
|
||||||
|
*/
|
||||||
|
public function setRestrictions($restrictions)
|
||||||
|
{
|
||||||
|
$this->_restrictions = $restrictions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the header raw data.
|
||||||
|
*
|
||||||
|
* @todo CRC must use safesynch
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function toString()
|
||||||
|
{
|
||||||
|
return Transform::toUInt32BE($this->encodeSynchsafe32($this->_size)) .
|
||||||
|
Transform::toInt8(1) . Transform::toInt8($this->_flags) .
|
||||||
|
($this->hasFlag(self::UPDATE) ? "\0" : "") .
|
||||||
|
($this->hasFlag(self::CRC32) ? Transform::toInt8(5) . $this->_crc : "") .
|
||||||
|
($this->hasFlag(self::RESTRICTED) ?
|
||||||
|
Transform::toInt8(1) . Transform::toInt8($this->_restrictions) : "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**#@+ @ignore */
|
/**#@+ @ignore */
|
||||||
require_once("Object.php");
|
require_once("ID3/Object.php");
|
||||||
/**#@-*/
|
/**#@-*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,17 +117,17 @@ class ID3_Frame extends ID3_Object
|
|||||||
private $_identifier;
|
private $_identifier;
|
||||||
|
|
||||||
/** @var integer */
|
/** @var integer */
|
||||||
private $_size;
|
private $_size = 0;
|
||||||
|
|
||||||
/** @var integer */
|
/** @var integer */
|
||||||
private $_flags;
|
private $_flags = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raw content read from the frame.
|
* Raw content of the frame.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $_data;
|
protected $_data = "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the class with given parameters and reads object related data
|
* Constructs the class with given parameters and reads object related data
|
||||||
@@ -136,15 +136,19 @@ class ID3_Frame extends ID3_Object
|
|||||||
* @todo Only limited subset of flags are processed.
|
* @todo Only limited subset of flags are processed.
|
||||||
* @param Reader $reader The reader object.
|
* @param Reader $reader The reader object.
|
||||||
*/
|
*/
|
||||||
public function __construct($reader)
|
public function __construct($reader = null)
|
||||||
{
|
{
|
||||||
parent::__construct($reader);
|
parent::__construct($reader);
|
||||||
|
|
||||||
|
if ($reader === null) {
|
||||||
|
$this->_identifier = substr(get_class($this), -4);
|
||||||
|
} else {
|
||||||
$this->_identifier = $this->_reader->readString8(4);
|
$this->_identifier = $this->_reader->readString8(4);
|
||||||
$this->_size = $this->decodeSynchsafe32($this->_reader->readUInt32BE());
|
$this->_size = $this->decodeSynchsafe32($this->_reader->readUInt32BE());
|
||||||
$this->_flags = $this->_reader->readUInt16BE();
|
$this->_flags = $this->_reader->readUInt16BE();
|
||||||
$this->_data = $this->_reader->read($this->_size);
|
$this->_data = $this->_reader->read($this->_size);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the frame identifier string.
|
* Returns the frame identifier string.
|
||||||
@@ -185,18 +189,35 @@ class ID3_Frame extends ID3_Object
|
|||||||
*
|
*
|
||||||
* @return integer
|
* @return integer
|
||||||
*/
|
*/
|
||||||
public function getFlags($flags)
|
public function getFlags($flags) { return $this->_flags; }
|
||||||
{
|
|
||||||
return $this->_flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the frame flags byte.
|
* Sets the frame flags byte.
|
||||||
*
|
*
|
||||||
* @param string $flags The flags byte.
|
* @param string $flags The flags byte.
|
||||||
*/
|
*/
|
||||||
public function setFlags($flags)
|
public function setFlags($flags) { $this->_flags = $flags; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the frame raw data.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function setData($data)
|
||||||
{
|
{
|
||||||
$this->_flags = $flags;
|
$this->_data = $data;
|
||||||
|
$this->_size = strlen($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the frame raw data.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return Transform::toString8(substr($this->_identifier, 0, 4), 4) .
|
||||||
|
Transform::toUInt32BE($this->encodeSynchsafe32($this->_size)) .
|
||||||
|
Transform::toUInt16BE($this->_flags) . $this->_data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,9 +59,11 @@ abstract class ID3_Frame_AbstractLink extends ID3_Frame
|
|||||||
*
|
*
|
||||||
* @param Reader $reader The reader object.
|
* @param Reader $reader The reader object.
|
||||||
*/
|
*/
|
||||||
public function __construct($reader)
|
public function __construct($reader = null)
|
||||||
{
|
{
|
||||||
parent::__construct($reader);
|
parent::__construct($reader);
|
||||||
|
|
||||||
|
if ($reader !== null)
|
||||||
$this->_link = preg_split("/\\x00/", $this->_data, 1);
|
$this->_link = preg_split("/\\x00/", $this->_data, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,4 +73,22 @@ abstract class ID3_Frame_AbstractLink extends ID3_Frame
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getLink() { return $this->_link; }
|
public function getLink() { return $this->_link; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the link.
|
||||||
|
*
|
||||||
|
* @param string $link The link.
|
||||||
|
*/
|
||||||
|
public function setLink($link) { $this->_link = $link; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the frame raw data.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
$this->setData($link);
|
||||||
|
return parent::__toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ abstract class ID3_Frame_AbstractText extends ID3_Frame
|
|||||||
implements ID3_Encoding
|
implements ID3_Encoding
|
||||||
{
|
{
|
||||||
/** @var integer */
|
/** @var integer */
|
||||||
private $_encoding;
|
private $_encoding = ID3_Encoding::UTF8;
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private $_text;
|
private $_text;
|
||||||
@@ -64,10 +64,13 @@ abstract class ID3_Frame_AbstractText extends ID3_Frame
|
|||||||
*
|
*
|
||||||
* @param Reader $reader The reader object.
|
* @param Reader $reader The reader object.
|
||||||
*/
|
*/
|
||||||
public function __construct($reader)
|
public function __construct($reader = null)
|
||||||
{
|
{
|
||||||
parent::__construct($reader);
|
parent::__construct($reader);
|
||||||
|
|
||||||
|
if ($reader === null)
|
||||||
|
return;
|
||||||
|
|
||||||
$this->_encoding = ord($this->_data{0});
|
$this->_encoding = ord($this->_data{0});
|
||||||
$this->_data = substr($this->_data, 1);
|
$this->_data = substr($this->_data, 1);
|
||||||
switch ($this->_encoding) {
|
switch ($this->_encoding) {
|
||||||
@@ -92,10 +95,60 @@ abstract class ID3_Frame_AbstractText extends ID3_Frame
|
|||||||
*/
|
*/
|
||||||
public function getEncoding() { return $this->_encoding; }
|
public function getEncoding() { return $this->_encoding; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the text encoding.
|
||||||
|
*
|
||||||
|
* @param integer $encoding The text encoding.
|
||||||
|
*/
|
||||||
|
public function setEncoding($encoding) { $this->_encoding = $encoding; };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first text chunk the frame contains.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getText() { return $this->_text[0]; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of texts the frame contains.
|
* Returns an array of texts the frame contains.
|
||||||
*
|
*
|
||||||
* @return Array
|
* @return Array
|
||||||
*/
|
*/
|
||||||
public function getText() { return $this->_text; }
|
public function getTexts() { return $this->_text; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the text using given encoding.
|
||||||
|
*
|
||||||
|
* @param mixed $text The test string or an array of strings.
|
||||||
|
*/
|
||||||
|
public function setText($text, $encoding = ID3_Encoding::UTF8)
|
||||||
|
{
|
||||||
|
$this->_encoding = $encoding;
|
||||||
|
$this->_text = is_array($text) ? $text : array($text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the frame raw data.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
$data = Transform::toInt8($this->_encoding);
|
||||||
|
switch ($this->_encoding) {
|
||||||
|
case self::UTF16:
|
||||||
|
$data .= Transform::toString16(implode("\0\0", $this->_text));
|
||||||
|
break;
|
||||||
|
case self::UTF16BE:
|
||||||
|
$data .= Transform::toString16BE(implode("\0\0", $this->_text));
|
||||||
|
break;
|
||||||
|
case self::UTF16LE:
|
||||||
|
$data .= Transform::toString16LE(implode("\0\0", $this->_text));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$data .= implode("\0", $this->_text);
|
||||||
|
}
|
||||||
|
$this->setData($data);
|
||||||
|
return parent::__toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**#@+ @ignore */
|
/**#@+ @ignore */
|
||||||
require_once("Object.php");
|
require_once("ID3/Object.php");
|
||||||
/**#@-*/
|
/**#@-*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,13 +72,13 @@ final class ID3_Header extends ID3_Object
|
|||||||
const FOOTER = 32;
|
const FOOTER = 32;
|
||||||
|
|
||||||
/** @var integer */
|
/** @var integer */
|
||||||
private $_version;
|
private $_version = 4;
|
||||||
|
|
||||||
/** @var integer */
|
/** @var integer */
|
||||||
private $_revision;
|
private $_revision = 0;
|
||||||
|
|
||||||
/** @var integer */
|
/** @var integer */
|
||||||
private $_flags;
|
private $_flags = 0;
|
||||||
|
|
||||||
/** @var integer */
|
/** @var integer */
|
||||||
private $_size;
|
private $_size;
|
||||||
@@ -89,10 +89,13 @@ final class ID3_Header extends ID3_Object
|
|||||||
*
|
*
|
||||||
* @param Reader $reader The reader object.
|
* @param Reader $reader The reader object.
|
||||||
*/
|
*/
|
||||||
public function __construct($reader)
|
public function __construct($reader = null)
|
||||||
{
|
{
|
||||||
parent::__construct($reader);
|
parent::__construct($reader);
|
||||||
|
|
||||||
|
if ($reader === null)
|
||||||
|
return;
|
||||||
|
|
||||||
$this->_version = $this->_reader->readInt8();
|
$this->_version = $this->_reader->readInt8();
|
||||||
$this->_revision = $this->_reader->readInt8();
|
$this->_revision = $this->_reader->readInt8();
|
||||||
$this->_flags = $this->_reader->readInt8();
|
$this->_flags = $this->_reader->readInt8();
|
||||||
@@ -122,10 +125,45 @@ final class ID3_Header extends ID3_Object
|
|||||||
*/
|
*/
|
||||||
public function hasFlag($flag) { return ($this->_flags & $flag) == $flag; }
|
public function hasFlag($flag) { return ($this->_flags & $flag) == $flag; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the flags byte.
|
||||||
|
*
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
public function getFlags($flags) { return $this->_flags; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the flags byte.
|
||||||
|
*
|
||||||
|
* @param string $flags The flags byte.
|
||||||
|
*/
|
||||||
|
public function setFlags($flags) { $this->_flags = $flags; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the tag size, excluding the header and the footer.
|
* Returns the tag size, excluding the header and the footer.
|
||||||
*
|
*
|
||||||
* @return integer
|
* @return integer
|
||||||
*/
|
*/
|
||||||
public function getSize() { return $this->_size; }
|
public function getSize() { return $this->_size; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the tag size, excluding the header and the footer. Called
|
||||||
|
* automatically upon tag generation to adjust the tag size.
|
||||||
|
*
|
||||||
|
* @param integer $size The size of the tag, in bytes.
|
||||||
|
*/
|
||||||
|
public function setSize($size) { $this->_size = $size; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the header/footer raw data without the identifier.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function __toString()
|
||||||
|
{
|
||||||
|
return Transform::toInt8($this->_version) .
|
||||||
|
Transform::toInt8($this->_revision) .
|
||||||
|
Transform::toInt8($this->_flags) .
|
||||||
|
Transform::toUInt32BE($this->encodeSynchsafe32($this->_size));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,4 +59,11 @@ interface ID3_Language
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getLanguage();
|
public function getLanguage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the text language code.
|
||||||
|
*
|
||||||
|
* @param string $language The text language code.
|
||||||
|
*/
|
||||||
|
public function setLanguage($language);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,4 +63,11 @@ interface ID3_Timing
|
|||||||
* @return integer
|
* @return integer
|
||||||
*/
|
*/
|
||||||
public function getFormat();
|
public function getFormat();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the timing format.
|
||||||
|
*
|
||||||
|
* @param integer $format The timing format.
|
||||||
|
*/
|
||||||
|
public function setFormat($format);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,9 +123,9 @@ final class ID3v1
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
$this->_reader = new Reader($filename);
|
$this->_reader = new Reader($filename);
|
||||||
if ($this->_reader->size < 128)
|
if ($this->_reader->getSize() < 128)
|
||||||
return;
|
return;
|
||||||
$this->_reader->offset = -128;
|
$this->_reader->setOffset(-128);
|
||||||
if ($this->_reader->read(3) != "TAG") {
|
if ($this->_reader->read(3) != "TAG") {
|
||||||
$this->_reader = false; // reset reader, see write
|
$this->_reader = false; // reset reader, see write
|
||||||
return;
|
return;
|
||||||
|
|||||||
176
src/ID3v2.php
176
src/ID3v2.php
@@ -99,7 +99,7 @@ final class ID3v2
|
|||||||
private $_extendedHeader;
|
private $_extendedHeader;
|
||||||
|
|
||||||
/** @var ID3_Header */
|
/** @var ID3_Header */
|
||||||
private $_footer = null;
|
private $_footer;
|
||||||
|
|
||||||
/** @var Array */
|
/** @var Array */
|
||||||
private $_frames = array();
|
private $_frames = array();
|
||||||
@@ -111,7 +111,7 @@ final class ID3v2
|
|||||||
* Constructs the ID3v2 class with given file and options.
|
* Constructs the ID3v2 class with given file and options.
|
||||||
*
|
*
|
||||||
* @todo Only limited subset of flags are processed.
|
* @todo Only limited subset of flags are processed.
|
||||||
* @todo ID3_Footer
|
* @todo Utilize the SEEK frame and search for a footer to find the tag
|
||||||
* @param string $filename The path to the file.
|
* @param string $filename The path to the file.
|
||||||
* @param Array $options The options array.
|
* @param Array $options The options array.
|
||||||
*/
|
*/
|
||||||
@@ -123,8 +123,10 @@ final class ID3v2
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (($this->_filename = $filename) === false ||
|
if (($this->_filename = $filename) === false ||
|
||||||
file_exists($filename) === false)
|
file_exists($filename) === false) {
|
||||||
|
$this->_header = new ID3_Header();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$this->_reader = new Reader($filename);
|
$this->_reader = new Reader($filename);
|
||||||
|
|
||||||
@@ -137,12 +139,8 @@ final class ID3v2
|
|||||||
("File does not contain ID3v2 tag of supported version: " . $filename);
|
("File does not contain ID3v2 tag of supported version: " . $filename);
|
||||||
if ($this->_header->hasFlag(ID3_Header::EXTENDEDHEADER))
|
if ($this->_header->hasFlag(ID3_Header::EXTENDEDHEADER))
|
||||||
$this->_extendedHeader = new ID3_ExtendedHeader($this->_reader);
|
$this->_extendedHeader = new ID3_ExtendedHeader($this->_reader);
|
||||||
if ($this->_header->hasFlag(ID3_Header::FOOTER)) {
|
if ($this->_header->hasFlag(ID3_Header::FOOTER))
|
||||||
$offset = $this->_reader->offset;
|
$this->_footer = &$this->_header; // skip footer, and rather copy header
|
||||||
$this->_reader->offset = $this->_header->getSize() + 10;
|
|
||||||
$this->_footer = new ID3_Header($this->_reader);
|
|
||||||
$this->_reader->offset = $offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ($frame = $this->nextFrame()) {
|
while ($frame = $this->nextFrame()) {
|
||||||
if (!isset($this->_frames[$frame->identifier]))
|
if (!isset($this->_frames[$frame->identifier]))
|
||||||
@@ -166,6 +164,7 @@ final class ID3v2
|
|||||||
*/
|
*/
|
||||||
public function hasExtendedHeader()
|
public function hasExtendedHeader()
|
||||||
{
|
{
|
||||||
|
if ($this->_header)
|
||||||
return $this->_header->hasFlag(ID3_Header::EXTENDEDHEADER);
|
return $this->_header->hasFlag(ID3_Header::EXTENDEDHEADER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,6 +181,20 @@ final class ID3v2
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the extended header object.
|
||||||
|
*
|
||||||
|
* @param ID3_ExtendedHeader $extendedHeader The header object
|
||||||
|
*/
|
||||||
|
public function setExtendedHeader($extendedHeader)
|
||||||
|
{
|
||||||
|
if (is_subclass_of($extendedHeader, "ID3_ExtendedHeader")) {
|
||||||
|
$this->_header->flags =
|
||||||
|
$this->_header->flags | ID3_Header::EXTENDEDHEADER;
|
||||||
|
$this->_extendedHeader = $extendedHeader;
|
||||||
|
} else throw new ID3_Exception("Invalid argument");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether there are frames left in the tag. Returns <var>true</var> if
|
* Checks whether there are frames left in the tag. Returns <var>true</var> if
|
||||||
* there are frames left in the tag, <var>false</var> otherwise.
|
* there are frames left in the tag, <var>false</var> otherwise.
|
||||||
@@ -190,16 +203,16 @@ final class ID3v2
|
|||||||
*/
|
*/
|
||||||
protected function hasFrames()
|
protected function hasFrames()
|
||||||
{
|
{
|
||||||
$offset = $this->_reader->offset;
|
$offset = $this->_reader->getOffset();
|
||||||
|
|
||||||
// Return false if we reached the end of the tag
|
// Return false if we reached the end of the tag
|
||||||
if ($offset >= $this->_header->getSize() - 10 -
|
if ($offset - 10 >= $this->_header->getSize() -
|
||||||
($this->hasFooter() ? 10 : 0))
|
($this->hasFooter() ? 10 : 0))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Return false if we reached the last frame, true otherwise
|
// Return false if we reached the last frame, true otherwise
|
||||||
$res = $this->_reader->readUInt32BE() != 0;
|
$res = $this->_reader->readUInt32BE() != 0;
|
||||||
$this->_reader->offset = $offset;
|
$this->_reader->setOffset($offset);
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,10 +227,11 @@ final class ID3v2
|
|||||||
{
|
{
|
||||||
$frame = false;
|
$frame = false;
|
||||||
if ($this->hasFrames()) {
|
if ($this->hasFrames()) {
|
||||||
$offset = $this->_reader->offset;
|
$offset = $this->_reader->getOffset();
|
||||||
$identifier = $this->_reader->readString8(4);
|
$identifier = $this->_reader->readString8(4);
|
||||||
$this->_reader->offset = $offset;
|
$this->_reader->setOffset($offset);
|
||||||
if (file_exists($filename = "ID3/Frame/" . $identifier . ".php"))
|
if (@fopen($filename = "ID3/Frame/" .
|
||||||
|
strtoupper($identifier) . ".php", "r", true) !== false)
|
||||||
require_once($filename);
|
require_once($filename);
|
||||||
if (class_exists($classname = "ID3_Frame_" . $identifier))
|
if (class_exists($classname = "ID3_Frame_" . $identifier))
|
||||||
$frame = new $classname($this->_reader);
|
$frame = new $classname($this->_reader);
|
||||||
@@ -276,6 +290,19 @@ final class ID3v2
|
|||||||
return $matches;
|
return $matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new frame to the tag and returns it.
|
||||||
|
*
|
||||||
|
* @param ID3_Frame $frame The frame to add.
|
||||||
|
* @return ID3_Frame
|
||||||
|
*/
|
||||||
|
public function addFrame($frame)
|
||||||
|
{
|
||||||
|
if (!$this->hasFrame($frame->getIdentifier()))
|
||||||
|
$this->_frames[$frame->getIdentifier()] = array();
|
||||||
|
return $this->_frames[$frame->getIdentifier()][] = $frame;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether there is a footer present in the tag. Returns
|
* Checks whether there is a footer present in the tag. Returns
|
||||||
* <var>true</var> if the footer is present, <var>false</var> otherwise.
|
* <var>true</var> if the footer is present, <var>false</var> otherwise.
|
||||||
@@ -299,18 +326,131 @@ final class ID3v2
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the tag should have a footer defined.
|
||||||
|
*
|
||||||
|
* @param boolean $useFooter Whether the tag should have a footer
|
||||||
|
*/
|
||||||
|
public function setFooter($useFooter)
|
||||||
|
{
|
||||||
|
if ($useFooter) {
|
||||||
|
$this->_header->setFlags
|
||||||
|
($this->_header->getFlags() | ID3_Header::FOOTER);
|
||||||
|
$this->_footer = &$this->_header;
|
||||||
|
} else {
|
||||||
|
/* Count footer bytes towards the tag size, so it gets removed or
|
||||||
|
overridden upon re-write */
|
||||||
|
if ($this->hasFooter())
|
||||||
|
$this->_header->setSize($this->_header->getSize() + 10);
|
||||||
|
|
||||||
|
$this->_header->setFlags
|
||||||
|
($this->_header->getFlags() & ~ID3_Header::FOOTER);
|
||||||
|
$this->_footer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the possibly altered ID3v2 tag back to the file where it was read.
|
||||||
|
* If the class was constructed without a file name, one can be provided here
|
||||||
|
* as an argument. Regardless, the write operation will override previous
|
||||||
|
* tag information, if found.
|
||||||
|
*
|
||||||
|
* If write is called without setting any frames to the tag, the tag is
|
||||||
|
* removed from the file.
|
||||||
|
*
|
||||||
|
* @param string $filename The optional path to the file.
|
||||||
|
*/
|
||||||
|
public function write($filename = false)
|
||||||
|
{
|
||||||
|
if (empty($this->_frames))
|
||||||
|
throw new ID3_Exception("Tag must contain at least one frame");
|
||||||
|
|
||||||
|
if ($filename === false && ($filename = $this->_filename) === false)
|
||||||
|
throw new ID3_Exception("No file given to write the tag to");
|
||||||
|
|
||||||
|
if (($fd = fopen
|
||||||
|
($filename, file_exists($filename) ? "r+b" : "wb")) === false)
|
||||||
|
throw new ID3_Exception("Unable to open file for writing: " . $filename);
|
||||||
|
|
||||||
|
$oldTagSize = $this->_header->getSize();
|
||||||
|
$tag = "" . $this;
|
||||||
|
$tagSize = strlen($tag);
|
||||||
|
|
||||||
|
if ($this->_reader === null || $tagSize - 10 > $oldTagSize) {
|
||||||
|
fseek($fd, 0, SEEK_END);
|
||||||
|
$oldFileSize = ftell($fd);
|
||||||
|
ftruncate($fd, $newFileSize = $tagSize - $oldTagSize + $oldFileSize);
|
||||||
|
for ($i = 1, $cur = $oldFileSize; $cur > 0; $cur -= 1024, $i++) {
|
||||||
|
fseek($fd, -(($i * 1024) + ($newFileSize - $oldFileSize)), SEEK_END);
|
||||||
|
$buffer = fread($fd, 1024);
|
||||||
|
fseek($fd, -($i * 1024), SEEK_END);
|
||||||
|
fwrite($fd, $buffer, 1024);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fseek($fd, 0);
|
||||||
|
fwrite($fd, $tag);
|
||||||
|
|
||||||
|
$this->_filename = $filename;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Magic function so that $obj->value will work. The method will attempt to
|
* Magic function so that $obj->value will work. The method will attempt to
|
||||||
* return the first frame that matches the identifier.
|
* return the first frame that matches the identifier.
|
||||||
*
|
*
|
||||||
|
* If there is no frame or field with given name, the method will attempt to
|
||||||
|
* create a frame with given identifier.
|
||||||
|
*
|
||||||
|
* If none of these work, an exception is thrown.
|
||||||
|
*
|
||||||
* @param string $name The frame or field name.
|
* @param string $name The frame or field name.
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public function __get($name) {
|
public function __get($name) {
|
||||||
if (isset($this->_frames[$name]))
|
if (isset($this->_frames[strtoupper($name)]))
|
||||||
return $this->_frames[$name][0];
|
return $this->_frames[strtoupper($name)][0];
|
||||||
if (method_exists($this, "get" . ucfirst($name)))
|
if (method_exists($this, "get" . ucfirst($name)))
|
||||||
return call_user_func(array($this, "get" . ucfirst($name)));
|
return call_user_func(array($this, "get" . ucfirst($name)));
|
||||||
else throw new ID3_Exception("Unknown frame/field: " . $name);
|
if (@fopen($filename =
|
||||||
|
"ID3/Frame/" . strtoupper($name) . ".php", "r", true) !== false)
|
||||||
|
require_once($filename);
|
||||||
|
if (class_exists($classname = "ID3_Frame_" . strtoupper($name)))
|
||||||
|
return $this->addFrame(new $classname());
|
||||||
|
throw new ID3_Exception("Unknown frame/field: " . $name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the tag raw data.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
$data = "";
|
||||||
|
if ($this->hasExtendedHeader())
|
||||||
|
$data .= $this->getExtendedHeader();
|
||||||
|
foreach ($this->_frames as $frames)
|
||||||
|
foreach ($frames as $frame)
|
||||||
|
$data .= $frame;
|
||||||
|
|
||||||
|
$datalen = strlen($data);
|
||||||
|
$padlen = 0;
|
||||||
|
|
||||||
|
/* The tag padding is calculated as follows. If the tag can be written in
|
||||||
|
the space of the previous tag, the remaining space is used for padding.
|
||||||
|
If there is no previous tag or the new tag is bigger than the space taken
|
||||||
|
by the previous tag, the padding is calculated using the following
|
||||||
|
logaritmic equation: log(0.2(x + 10)), ranging from some 300 bytes to
|
||||||
|
almost 5000 bytes given the tag length of 0..256M. */
|
||||||
|
if ($this->hasFooter() === false) {
|
||||||
|
if ($this->_reader !== null && $datalen < $this->_header->getSize())
|
||||||
|
$padlen = $this->_header->getSize() - $datalen;
|
||||||
|
else
|
||||||
|
$padlen = ceil(log(0.2 * ($datalen / 1024 + 10), 10) * 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_header->setSize($datalen + $padlen);
|
||||||
|
|
||||||
|
return "ID3" . $this->_header . str_pad($data, $datalen + $padlen, "\0") .
|
||||||
|
($this->hasFooter() ? "3DI" . $this->getFooter() : "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -332,9 +332,9 @@ final class Transform
|
|||||||
public static function fromString16($value)
|
public static function fromString16($value)
|
||||||
{
|
{
|
||||||
if ($value{0} == 0xfe && $value{1} = 0xff)
|
if ($value{0} == 0xfe && $value{1} = 0xff)
|
||||||
return self::fromString16LE(substr($value, 2));
|
|
||||||
else
|
|
||||||
return self::fromString16BE(substr($value, 2));
|
return self::fromString16BE(substr($value, 2));
|
||||||
|
else
|
||||||
|
return self::fromString16LE(substr($value, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -39,14 +39,15 @@
|
|||||||
require_once("PHPUnit/Framework.php");
|
require_once("PHPUnit/Framework.php");
|
||||||
require_once("PHPUnit/TextUI/TestRunner.php");
|
require_once("PHPUnit/TextUI/TestRunner.php");
|
||||||
|
|
||||||
|
ini_set("include_path", ini_get("include_path") . PATH_SEPARATOR . "../src/");
|
||||||
|
|
||||||
$suite = new PHPUnit_Framework_TestSuite("PHP Reader");
|
$suite = new PHPUnit_Framework_TestSuite("PHP Reader");
|
||||||
|
|
||||||
$dir = opendir(dirname(__FILE__));
|
$dir = opendir(dirname(__FILE__));
|
||||||
while (($file = readdir($dir)) !== false) {
|
while (($file = readdir($dir)) !== false) {
|
||||||
if ($file == basename(__FILE__))
|
if ($file == basename(__FILE__))
|
||||||
continue;
|
continue;
|
||||||
if (file_exists(dirname(__FILE__) . "/" . $file) &&
|
if (preg_match("/^Test.+\.php$/", $file)) {
|
||||||
preg_match("/^Test.+\.php$/", $file)) {
|
|
||||||
require_once($file);
|
require_once($file);
|
||||||
$suite->addTestSuite(substr($file, 0, -4));
|
$suite->addTestSuite(substr($file, 0, -4));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
/**#@+ @ignore */
|
/**#@+ @ignore */
|
||||||
require_once("PHPUnit/Framework.php");
|
require_once("PHPUnit/Framework.php");
|
||||||
require_once("../src/ID3v1.php");
|
require_once("ID3v1.php");
|
||||||
/**#@-*/
|
/**#@-*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
123
tests/TestID3v2.php
Normal file
123
tests/TestID3v2.php
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* PHP Reader Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008 The PHP Reader Project Workgroup. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* - Neither the name of the project workgroup nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @package php-reader
|
||||||
|
* @subpackage Tests
|
||||||
|
* @copyright Copyright (c) 2008 The PHP Reader Project Workgroup
|
||||||
|
* @license http://code.google.com/p/php-reader/wiki/License New BSD License
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**#@+ @ignore */
|
||||||
|
require_once("PHPUnit/Framework.php");
|
||||||
|
require_once("ID3v2.php");
|
||||||
|
/**#@-*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit test case for ID3v1 class.
|
||||||
|
*
|
||||||
|
* @package php-reader
|
||||||
|
* @subpackage Tests
|
||||||
|
* @author Sven Vollbehr <svollbehr@gmail.com>
|
||||||
|
* @copyright Copyright (c) 2008 The PHP Reader Project Workgroup
|
||||||
|
* @license http://code.google.com/p/php-reader/wiki/License New BSD License
|
||||||
|
* @version $Rev$
|
||||||
|
*/
|
||||||
|
final class TestID3v2 extends PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
function testTagCreate()
|
||||||
|
{
|
||||||
|
$id3 = new ID3v2();
|
||||||
|
$id3->tit2->text = "Title 1";
|
||||||
|
$this->assertEquals("Title 1", $id3->tit2->text);
|
||||||
|
$id3->tope->text = "Artist 1";
|
||||||
|
$this->assertEquals("Artist 1", $id3->tope->text);
|
||||||
|
$id3->talb->text = "Album 1";
|
||||||
|
$this->assertEquals("Album 1", $id3->talb->text);
|
||||||
|
$id3->tdrc->text = "2008";
|
||||||
|
$this->assertEquals("2008", $id3->tdrc->text);
|
||||||
|
$id3->comm->text = "Comment 1";
|
||||||
|
$this->assertEquals("Comment 1", $id3->comm->text);
|
||||||
|
$id3->trck->text = "11/13";
|
||||||
|
$this->assertEquals("11/13", $id3->trck->text);
|
||||||
|
$id3->tcon->text = "Classical";
|
||||||
|
$this->assertEquals("Classical", $id3->tcon->text);
|
||||||
|
|
||||||
|
$id3->write("id3v2.tag");
|
||||||
|
echo "create";
|
||||||
|
}
|
||||||
|
|
||||||
|
function testTagReadAfterCreate()
|
||||||
|
{
|
||||||
|
$id3 = new ID3v2("id3v2.tag");
|
||||||
|
echo "read after create";
|
||||||
|
$this->assertEquals("Title 1", $id3->tit2->text);
|
||||||
|
$this->assertEquals("Artist 1", $id3->tope->text);
|
||||||
|
$this->assertEquals("Album 1", $id3->talb->text);
|
||||||
|
$this->assertEquals("2008", $id3->tdrc->text);
|
||||||
|
$this->assertEquals("Comment 1", $id3->comm->text);
|
||||||
|
$this->assertEquals("11/13", $id3->trck->text);
|
||||||
|
$this->assertEquals("Classical", $id3->tcon->text);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testTagChange()
|
||||||
|
{
|
||||||
|
$id3 = new ID3v2("id3v2.tag");
|
||||||
|
|
||||||
|
$id3->tit2->text = "Title 2";
|
||||||
|
$this->assertEquals("Title 2", $id3->tit2->text);
|
||||||
|
$id3->tope->text = "Artist 2";
|
||||||
|
$this->assertEquals("Artist 2", $id3->tope->text);
|
||||||
|
$id3->talb->text = "Album 2";
|
||||||
|
$this->assertEquals("Album 2", $id3->talb->text);
|
||||||
|
$id3->tdrc->text = "2020";
|
||||||
|
$this->assertEquals("2020", $id3->tdrc->text);
|
||||||
|
$id3->comm->text = "Comment 2";
|
||||||
|
$this->assertEquals("Comment 2", $id3->comm->text);
|
||||||
|
$id3->trck->text = "13/13";
|
||||||
|
$this->assertEquals("13/13", $id3->trck->text);
|
||||||
|
$id3->tcon->text = "Trance";
|
||||||
|
$this->assertEquals("Trance", $id3->tcon->text);
|
||||||
|
|
||||||
|
$id3->write();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testTagReadAfterChange()
|
||||||
|
{
|
||||||
|
$id3 = new ID3v2("id3v2.tag");
|
||||||
|
$this->assertEquals("Title 2", $id3->tit2->text);
|
||||||
|
$this->assertEquals("Artist 2", $id3->tope->text);
|
||||||
|
$this->assertEquals("Album 2", $id3->talb->text);
|
||||||
|
$this->assertEquals("2020", $id3->tdrc->text);
|
||||||
|
$this->assertEquals("Comment 2", $id3->comm->text);
|
||||||
|
$this->assertEquals("13/13", $id3->trck->text);
|
||||||
|
$this->assertEquals("Trance", $id3->tcon->text);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
/**#@+ @ignore */
|
/**#@+ @ignore */
|
||||||
require_once("PHPUnit/Framework.php");
|
require_once("PHPUnit/Framework.php");
|
||||||
require_once("../src/Transform.php");
|
require_once("Transform.php");
|
||||||
/**#@-*/
|
/**#@-*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,8 +118,8 @@ final class TestTransform extends PHPUnit_Framework_TestCase
|
|||||||
function testString16()
|
function testString16()
|
||||||
{
|
{
|
||||||
$this->assertEquals("00e4", Transform::fromHHex
|
$this->assertEquals("00e4", Transform::fromHHex
|
||||||
(Transform::fromString16(Transform::toString16("\xff\xfe\xe4\x00"))));
|
(Transform::fromString16(Transform::toString16("\xff\xfe\x00\xe4"))));
|
||||||
$this->assertEquals("e400", Transform::fromHHex
|
$this->assertEquals("00e4", Transform::fromHHex
|
||||||
(Transform::fromString16(Transform::toString16("\xfe\xff\x00\xe4"))));
|
(Transform::fromString16(Transform::toString16("\xfe\xff\x00\xe4"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user