From 458335dc9fed1ce958fd8fd30e7aee81d425e633 Mon Sep 17 00:00:00 2001 From: svollbehr Date: Tue, 1 Apr 2008 10:38:12 +0000 Subject: [PATCH] Add basic ID3v2 write support, add unit tests git-svn-id: http://php-reader.googlecode.com/svn/trunk@64 51a70ab9-7547-0410-9469-37e369ee0574 --- src/ID3/Encoding.php | 7 + src/ID3/ExtendedHeader.php | 68 +++++- src/ID3/Frame.php | 55 +++-- src/ID3/Frame/AbstractLink.php | 24 +- src/ID3/Frame/AbstractText.php | 63 +++++- src/ID3/Header.php | 50 ++++- src/ID3/Language.php | 7 + src/ID3/Timing.php | 7 + src/ID3v1.php | 4 +- src/ID3v2.php | 178 +++++++++++++-- src/Transform.php | 4 +- tests/TestAll.php | 115 +++++----- tests/TestID3v1.php | 388 ++++++++++++++++----------------- tests/TestID3v2.php | 123 +++++++++++ tests/TestTransform.php | 332 ++++++++++++++-------------- 15 files changed, 950 insertions(+), 475 deletions(-) create mode 100644 tests/TestID3v2.php diff --git a/src/ID3/Encoding.php b/src/ID3/Encoding.php index c0966d1..ad512b4 100644 --- a/src/ID3/Encoding.php +++ b/src/ID3/Encoding.php @@ -69,4 +69,11 @@ interface ID3_Encoding * @return integer */ public function getEncoding(); + + /** + * Sets the text encoding. + * + * @param integer $encoding The text encoding. + */ + public function setEncoding($encoding); } diff --git a/src/ID3/ExtendedHeader.php b/src/ID3/ExtendedHeader.php index a7648a0..c299074 100644 --- a/src/ID3/ExtendedHeader.php +++ b/src/ID3/ExtendedHeader.php @@ -36,7 +36,7 @@ */ /**#@+ @ignore */ -require_once("Object.php"); +require_once("ID3/Object.php"); /**#@-*/ /** @@ -96,7 +96,7 @@ final class ID3_ExtendedHeader extends ID3_Object { parent::__construct($reader); - $offset = $this->_reader->offset; + $offset = $this->_reader->getOffset(); $this->_size = $this->decodeSynchsafe32($this->_reader->readUInt32BE()); $this->_reader->skip(1); $this->_flags = $this->_reader->readInt8(); @@ -105,7 +105,7 @@ final class ID3_ExtendedHeader extends ID3_Object $this->_reader->skip(1); if ($this->hasFlag(self::CRC32)) { $this->_reader->skip(1); - $this->_crc = Transform::getInt32BE + $this->_crc = Transform::fromInt32BE (($this->_reader->read(1) << 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->_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; } + /** + * 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. * * @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 @@ -189,4 +221,30 @@ final class ID3_ExtendedHeader extends ID3_Object * @return integer */ 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) : ""); + } } diff --git a/src/ID3/Frame.php b/src/ID3/Frame.php index 9af49f0..01e32ed 100644 --- a/src/ID3/Frame.php +++ b/src/ID3/Frame.php @@ -36,7 +36,7 @@ */ /**#@+ @ignore */ -require_once("Object.php"); +require_once("ID3/Object.php"); /**#@-*/ /** @@ -117,17 +117,17 @@ class ID3_Frame extends ID3_Object private $_identifier; /** @var integer */ - private $_size; + private $_size = 0; /** @var integer */ - private $_flags; + private $_flags = 0; /** - * Raw content read from the frame. + * Raw content of the frame. * * @var string */ - protected $_data; + protected $_data = ""; /** * Constructs the class with given parameters and reads object related data @@ -136,14 +136,18 @@ class ID3_Frame extends ID3_Object * @todo Only limited subset of flags are processed. * @param Reader $reader The reader object. */ - public function __construct($reader) + public function __construct($reader = null) { parent::__construct($reader); - - $this->_identifier = $this->_reader->readString8(4); - $this->_size = $this->decodeSynchsafe32($this->_reader->readUInt32BE()); - $this->_flags = $this->_reader->readUInt16BE(); - $this->_data = $this->_reader->read($this->_size); + + if ($reader === null) { + $this->_identifier = substr(get_class($this), -4); + } else { + $this->_identifier = $this->_reader->readString8(4); + $this->_size = $this->decodeSynchsafe32($this->_reader->readUInt32BE()); + $this->_flags = $this->_reader->readUInt16BE(); + $this->_data = $this->_reader->read($this->_size); + } } /** @@ -185,18 +189,35 @@ class ID3_Frame extends ID3_Object * * @return integer */ - public function getFlags($flags) - { - return $this->_flags; - } + public function getFlags($flags) { return $this->_flags; } /** * Sets the frame 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; } } diff --git a/src/ID3/Frame/AbstractLink.php b/src/ID3/Frame/AbstractLink.php index 5120923..51694c6 100644 --- a/src/ID3/Frame/AbstractLink.php +++ b/src/ID3/Frame/AbstractLink.php @@ -59,10 +59,12 @@ abstract class ID3_Frame_AbstractLink extends ID3_Frame * * @param Reader $reader The reader object. */ - public function __construct($reader) + public function __construct($reader = null) { parent::__construct($reader); - $this->_link = preg_split("/\\x00/", $this->_data, 1); + + if ($reader !== null) + $this->_link = preg_split("/\\x00/", $this->_data, 1); } /** @@ -71,4 +73,22 @@ abstract class ID3_Frame_AbstractLink extends ID3_Frame * @return string */ 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(); + } } diff --git a/src/ID3/Frame/AbstractText.php b/src/ID3/Frame/AbstractText.php index dae85dd..5aebf38 100644 --- a/src/ID3/Frame/AbstractText.php +++ b/src/ID3/Frame/AbstractText.php @@ -54,7 +54,7 @@ abstract class ID3_Frame_AbstractText extends ID3_Frame implements ID3_Encoding { /** @var integer */ - private $_encoding; + private $_encoding = ID3_Encoding::UTF8; /** @var string */ private $_text; @@ -64,10 +64,13 @@ abstract class ID3_Frame_AbstractText extends ID3_Frame * * @param Reader $reader The reader object. */ - public function __construct($reader) + public function __construct($reader = null) { parent::__construct($reader); - + + if ($reader === null) + return; + $this->_encoding = ord($this->_data{0}); $this->_data = substr($this->_data, 1); switch ($this->_encoding) { @@ -91,11 +94,61 @@ abstract class ID3_Frame_AbstractText extends ID3_Frame * @return integer */ 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. * * @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(); + } } diff --git a/src/ID3/Header.php b/src/ID3/Header.php index 8c3f75a..c7ce031 100644 --- a/src/ID3/Header.php +++ b/src/ID3/Header.php @@ -36,7 +36,7 @@ */ /**#@+ @ignore */ -require_once("Object.php"); +require_once("ID3/Object.php"); /**#@-*/ /** @@ -72,13 +72,13 @@ final class ID3_Header extends ID3_Object const FOOTER = 32; /** @var integer */ - private $_version; + private $_version = 4; /** @var integer */ - private $_revision; + private $_revision = 0; /** @var integer */ - private $_flags; + private $_flags = 0; /** @var integer */ private $_size; @@ -89,10 +89,13 @@ final class ID3_Header extends ID3_Object * * @param Reader $reader The reader object. */ - public function __construct($reader) + public function __construct($reader = null) { parent::__construct($reader); - + + if ($reader === null) + return; + $this->_version = $this->_reader->readInt8(); $this->_revision = $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; } + /** + * 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. * * @return integer */ 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)); + } } diff --git a/src/ID3/Language.php b/src/ID3/Language.php index 785496f..e4f6336 100644 --- a/src/ID3/Language.php +++ b/src/ID3/Language.php @@ -59,4 +59,11 @@ interface ID3_Language * @return string */ public function getLanguage(); + + /** + * Sets the text language code. + * + * @param string $language The text language code. + */ + public function setLanguage($language); } diff --git a/src/ID3/Timing.php b/src/ID3/Timing.php index bbcccd0..7eafee2 100644 --- a/src/ID3/Timing.php +++ b/src/ID3/Timing.php @@ -63,4 +63,11 @@ interface ID3_Timing * @return integer */ public function getFormat(); + + /** + * Sets the timing format. + * + * @param integer $format The timing format. + */ + public function setFormat($format); } diff --git a/src/ID3v1.php b/src/ID3v1.php index 29a7b45..9128d5c 100644 --- a/src/ID3v1.php +++ b/src/ID3v1.php @@ -123,9 +123,9 @@ final class ID3v1 return; $this->_reader = new Reader($filename); - if ($this->_reader->size < 128) + if ($this->_reader->getSize() < 128) return; - $this->_reader->offset = -128; + $this->_reader->setOffset(-128); if ($this->_reader->read(3) != "TAG") { $this->_reader = false; // reset reader, see write return; diff --git a/src/ID3v2.php b/src/ID3v2.php index 7e89642..6990265 100644 --- a/src/ID3v2.php +++ b/src/ID3v2.php @@ -99,7 +99,7 @@ final class ID3v2 private $_extendedHeader; /** @var ID3_Header */ - private $_footer = null; + private $_footer; /** @var Array */ private $_frames = array(); @@ -111,7 +111,7 @@ final class ID3v2 * Constructs the ID3v2 class with given file and options. * * @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 Array $options The options array. */ @@ -123,8 +123,10 @@ final class ID3v2 } if (($this->_filename = $filename) === false || - file_exists($filename) === false) + file_exists($filename) === false) { + $this->_header = new ID3_Header(); return; + } $this->_reader = new Reader($filename); @@ -137,12 +139,8 @@ final class ID3v2 ("File does not contain ID3v2 tag of supported version: " . $filename); if ($this->_header->hasFlag(ID3_Header::EXTENDEDHEADER)) $this->_extendedHeader = new ID3_ExtendedHeader($this->_reader); - if ($this->_header->hasFlag(ID3_Header::FOOTER)) { - $offset = $this->_reader->offset; - $this->_reader->offset = $this->_header->getSize() + 10; - $this->_footer = new ID3_Header($this->_reader); - $this->_reader->offset = $offset; - } + if ($this->_header->hasFlag(ID3_Header::FOOTER)) + $this->_footer = &$this->_header; // skip footer, and rather copy header while ($frame = $this->nextFrame()) { if (!isset($this->_frames[$frame->identifier])) @@ -166,7 +164,8 @@ final class ID3v2 */ public function hasExtendedHeader() { - return $this->_header->hasFlag(ID3_Header::EXTENDEDHEADER); + if ($this->_header) + return $this->_header->hasFlag(ID3_Header::EXTENDEDHEADER); } /** @@ -182,6 +181,20 @@ final class ID3v2 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 true if * there are frames left in the tag, false otherwise. @@ -190,16 +203,16 @@ final class ID3v2 */ protected function hasFrames() { - $offset = $this->_reader->offset; + $offset = $this->_reader->getOffset(); // 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)) return false; // Return false if we reached the last frame, true otherwise $res = $this->_reader->readUInt32BE() != 0; - $this->_reader->offset = $offset; + $this->_reader->setOffset($offset); return $res; } @@ -214,10 +227,11 @@ final class ID3v2 { $frame = false; if ($this->hasFrames()) { - $offset = $this->_reader->offset; + $offset = $this->_reader->getOffset(); $identifier = $this->_reader->readString8(4); - $this->_reader->offset = $offset; - if (file_exists($filename = "ID3/Frame/" . $identifier . ".php")) + $this->_reader->setOffset($offset); + if (@fopen($filename = "ID3/Frame/" . + strtoupper($identifier) . ".php", "r", true) !== false) require_once($filename); if (class_exists($classname = "ID3_Frame_" . $identifier)) $frame = new $classname($this->_reader); @@ -276,6 +290,19 @@ final class ID3v2 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 * true if the footer is present, false otherwise. @@ -299,18 +326,131 @@ final class ID3v2 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 * 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. * @return mixed */ public function __get($name) { - if (isset($this->_frames[$name])) - return $this->_frames[$name][0]; + if (isset($this->_frames[strtoupper($name)])) + return $this->_frames[strtoupper($name)][0]; if (method_exists($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() : ""); } } diff --git a/src/Transform.php b/src/Transform.php index ed410cb..59cddcb 100644 --- a/src/Transform.php +++ b/src/Transform.php @@ -332,9 +332,9 @@ final class Transform public static function fromString16($value) { if ($value{0} == 0xfe && $value{1} = 0xff) - return self::fromString16LE(substr($value, 2)); - else return self::fromString16BE(substr($value, 2)); + else + return self::fromString16LE(substr($value, 2)); } /** diff --git a/tests/TestAll.php b/tests/TestAll.php index 0275732..c635446 100644 --- a/tests/TestAll.php +++ b/tests/TestAll.php @@ -1,57 +1,58 @@ -addTestSuite(substr($file, 0, -4)); - } -} -closedir($dir); - -PHPUnit_TextUI_TestRunner::run($suite); -/**#@-*/ +addTestSuite(substr($file, 0, -4)); + } +} +closedir($dir); + +PHPUnit_TextUI_TestRunner::run($suite); +/**#@-*/ diff --git a/tests/TestID3v1.php b/tests/TestID3v1.php index 1f7c7de..5c58950 100644 --- a/tests/TestID3v1.php +++ b/tests/TestID3v1.php @@ -1,194 +1,194 @@ - - * @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 TestID3v1 extends PHPUnit_Framework_TestCase -{ - function testTagCreate() - { - $id3 = new ID3v1(); - - $id3->title = "Title 1"; - $this->assertEquals("Title 1", $id3->title); - - $id3->artist = "Artist 1"; - $this->assertEquals("Artist 1", $id3->artist); - - $id3->album = "Album 1"; - $this->assertEquals("Album 1", $id3->album); - - $id3->year = "2008"; - $this->assertEquals("2008", $id3->year); - - $id3->comment = "Comment 1"; - $this->assertEquals("Comment 1", $id3->comment); - - $id3->track = 30; - $this->assertEquals(30, $id3->track); - - $id3->genre = array_search("Classical", ID3v1::$genres); - $this->assertEquals("Classical", $id3->genre); - - $id3->write("id3v1.tag"); - } - - function testTagReadAfterCreate() - { - $id3 = new ID3v1("id3v1.tag"); - $this->assertEquals("Title 1", $id3->title); - $this->assertEquals("Artist 1", $id3->artist); - $this->assertEquals("Album 1", $id3->album); - $this->assertEquals("2008", $id3->year); - $this->assertEquals("Comment 1", $id3->comment); - $this->assertEquals(30, $id3->track); - $this->assertEquals("Classical", $id3->genre); - } - - function testTagChange() - { - $id3 = new ID3v1("id3v1.tag"); - - $id3->title = "Title 2"; - $this->assertEquals("Title 2", $id3->title); - - $id3->artist = "Artist 2"; - $this->assertEquals("Artist 2", $id3->artist); - - $id3->album = "Album 2"; - $this->assertEquals("Album 2", $id3->album); - - $id3->year = "2045"; - $this->assertEquals("2045", $id3->year); - - $id3->comment = "Comment 2"; - $this->assertEquals("Comment 2", $id3->comment); - - $id3->track = 10; - $this->assertEquals(10, $id3->track); - - $id3->genre = array_search("Trance", ID3v1::$genres); - $this->assertEquals("Trance", $id3->genre); - - $id3->write(); - } - - function testTagReadAfterChange() - { - $id3 = new ID3v1("id3v1.tag"); - $this->assertEquals("Title 2", $id3->title); - $this->assertEquals("Artist 2", $id3->artist); - $this->assertEquals("Album 2", $id3->album); - $this->assertEquals("2045", $id3->year); - $this->assertEquals("Comment 2", $id3->comment); - $this->assertEquals(10, $id3->track); - $this->assertEquals("Trance", $id3->genre); - } - - function testTagReplace() - { - $id3 = new ID3v1(); - - $id3->title = "Title 3"; - $this->assertEquals("Title 3", $id3->title); - $this->assertEquals("Unknown", $id3->genre); - - $id3->write("id3v1.tag"); - } - - function testTagReadAfterReplace() - { - $id3 = new ID3v1("id3v1.tag"); - $this->assertEquals("Title 3", $id3->title); - $this->assertEquals("", $id3->artist); - $this->assertEquals("", $id3->album); - $this->assertEquals("", $id3->year); - $this->assertEquals("", $id3->comment); - $this->assertEquals("", $id3->track); - $this->assertEquals("Unknown", $id3->genre); - } - - function testTagCreateVersion10() - { - $id3 = new ID3v1(); - - $id3->title = "Title 4"; - $this->assertEquals("Title 4", $id3->title); - - $id3->artist = "Artist 4"; - $this->assertEquals("Artist 4", $id3->artist); - - $id3->album = "Album 4"; - $this->assertEquals("Album 4", $id3->album); - - $id3->year = "2020"; - $this->assertEquals("2020", $id3->year); - - $id3->comment = "A comment field with 30 chars."; - $this->assertEquals("A comment field with 30 chars.", $id3->comment); - - $id3->genre = array_search("Classical", ID3v1::$genres); - $this->assertEquals("Classical", $id3->genre); - - $id3->write("id3v1.tag"); - } - - function testTagReadAfterCreateVersion10() - { - $id3 = new ID3v1("id3v1.tag"); - $this->assertEquals("Title 4", $id3->title); - $this->assertEquals("Artist 4", $id3->artist); - $this->assertEquals("Album 4", $id3->album); - $this->assertEquals("2020", $id3->year); - $this->assertEquals("A comment field with 30 chars.", $id3->comment); - $this->assertEquals("", $id3->track); - $this->assertEquals("Classical", $id3->genre); - } -} + + * @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 TestID3v1 extends PHPUnit_Framework_TestCase +{ + function testTagCreate() + { + $id3 = new ID3v1(); + + $id3->title = "Title 1"; + $this->assertEquals("Title 1", $id3->title); + + $id3->artist = "Artist 1"; + $this->assertEquals("Artist 1", $id3->artist); + + $id3->album = "Album 1"; + $this->assertEquals("Album 1", $id3->album); + + $id3->year = "2008"; + $this->assertEquals("2008", $id3->year); + + $id3->comment = "Comment 1"; + $this->assertEquals("Comment 1", $id3->comment); + + $id3->track = 30; + $this->assertEquals(30, $id3->track); + + $id3->genre = array_search("Classical", ID3v1::$genres); + $this->assertEquals("Classical", $id3->genre); + + $id3->write("id3v1.tag"); + } + + function testTagReadAfterCreate() + { + $id3 = new ID3v1("id3v1.tag"); + $this->assertEquals("Title 1", $id3->title); + $this->assertEquals("Artist 1", $id3->artist); + $this->assertEquals("Album 1", $id3->album); + $this->assertEquals("2008", $id3->year); + $this->assertEquals("Comment 1", $id3->comment); + $this->assertEquals(30, $id3->track); + $this->assertEquals("Classical", $id3->genre); + } + + function testTagChange() + { + $id3 = new ID3v1("id3v1.tag"); + + $id3->title = "Title 2"; + $this->assertEquals("Title 2", $id3->title); + + $id3->artist = "Artist 2"; + $this->assertEquals("Artist 2", $id3->artist); + + $id3->album = "Album 2"; + $this->assertEquals("Album 2", $id3->album); + + $id3->year = "2045"; + $this->assertEquals("2045", $id3->year); + + $id3->comment = "Comment 2"; + $this->assertEquals("Comment 2", $id3->comment); + + $id3->track = 10; + $this->assertEquals(10, $id3->track); + + $id3->genre = array_search("Trance", ID3v1::$genres); + $this->assertEquals("Trance", $id3->genre); + + $id3->write(); + } + + function testTagReadAfterChange() + { + $id3 = new ID3v1("id3v1.tag"); + $this->assertEquals("Title 2", $id3->title); + $this->assertEquals("Artist 2", $id3->artist); + $this->assertEquals("Album 2", $id3->album); + $this->assertEquals("2045", $id3->year); + $this->assertEquals("Comment 2", $id3->comment); + $this->assertEquals(10, $id3->track); + $this->assertEquals("Trance", $id3->genre); + } + + function testTagReplace() + { + $id3 = new ID3v1(); + + $id3->title = "Title 3"; + $this->assertEquals("Title 3", $id3->title); + $this->assertEquals("Unknown", $id3->genre); + + $id3->write("id3v1.tag"); + } + + function testTagReadAfterReplace() + { + $id3 = new ID3v1("id3v1.tag"); + $this->assertEquals("Title 3", $id3->title); + $this->assertEquals("", $id3->artist); + $this->assertEquals("", $id3->album); + $this->assertEquals("", $id3->year); + $this->assertEquals("", $id3->comment); + $this->assertEquals("", $id3->track); + $this->assertEquals("Unknown", $id3->genre); + } + + function testTagCreateVersion10() + { + $id3 = new ID3v1(); + + $id3->title = "Title 4"; + $this->assertEquals("Title 4", $id3->title); + + $id3->artist = "Artist 4"; + $this->assertEquals("Artist 4", $id3->artist); + + $id3->album = "Album 4"; + $this->assertEquals("Album 4", $id3->album); + + $id3->year = "2020"; + $this->assertEquals("2020", $id3->year); + + $id3->comment = "A comment field with 30 chars."; + $this->assertEquals("A comment field with 30 chars.", $id3->comment); + + $id3->genre = array_search("Classical", ID3v1::$genres); + $this->assertEquals("Classical", $id3->genre); + + $id3->write("id3v1.tag"); + } + + function testTagReadAfterCreateVersion10() + { + $id3 = new ID3v1("id3v1.tag"); + $this->assertEquals("Title 4", $id3->title); + $this->assertEquals("Artist 4", $id3->artist); + $this->assertEquals("Album 4", $id3->album); + $this->assertEquals("2020", $id3->year); + $this->assertEquals("A comment field with 30 chars.", $id3->comment); + $this->assertEquals("", $id3->track); + $this->assertEquals("Classical", $id3->genre); + } +} diff --git a/tests/TestID3v2.php b/tests/TestID3v2.php new file mode 100644 index 0000000..f5eedad --- /dev/null +++ b/tests/TestID3v2.php @@ -0,0 +1,123 @@ + + * @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); + } +} diff --git a/tests/TestTransform.php b/tests/TestTransform.php index 0962f9f..94740d3 100644 --- a/tests/TestTransform.php +++ b/tests/TestTransform.php @@ -1,166 +1,166 @@ - - * @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 TestTransform extends PHPUnit_Framework_TestCase -{ - function testInt64LE() - { - $this->assertEquals - (0x7fffffffffffffff, Transform::fromInt64BE - (Transform::toInt64BE(0x7fffffffffffffff))); - } - - function testInt64BE() - { - $this->assertEquals - (0x7fffffffffffffff, Transform::fromInt64BE - (Transform::toInt64BE(0x7fffffffffffffff))); - } - - function testInt32() - { - $this->assertEquals - (0x7fffffff, Transform::fromInt32(Transform::toInt32(0x7fffffff))); - } - - function testUInt32LE() - { - $this->assertEquals - ("78563412", Transform::fromHHex(Transform::toUInt32LE(0x12345678))); - $this->assertEquals - (0xffffffff, Transform::fromUInt32LE(Transform::toUInt32LE(0xffffffff))); - } - - function testUInt32BE() - { - $this->assertEquals - ("12345678", Transform::fromHHex(Transform::toUInt32BE(0x12345678))); - $this->assertEquals - (0xffffffff, Transform::fromUInt32BE(Transform::toUInt32BE(0xffffffff))); - } - - function testInt16() - { - $this->assertEquals - (0x7fff, Transform::fromInt16(Transform::toInt16(0x7fff))); - } - - function testUInt16LE() - { - $this->assertEquals - ("fffe", Transform::fromHHex(Transform::toUInt16LE(0xfeff))); - $this->assertEquals - (0xffff, Transform::fromUInt16LE(Transform::toUInt16LE(0xffff))); - } - - function testUInt16BE() - { - $this->assertEquals - ("feff", Transform::fromHHex(Transform::toUInt16BE(0xfeff))); - $this->assertEquals - (0xffff, Transform::fromUInt16BE(Transform::toUInt16BE(0xffff))); - } - - function testInt8() - { - $this->assertEquals(0x7f, Transform::fromInt8(Transform::toInt8(0x7f))); - } - - function testString16() - { - $this->assertEquals("00e4", Transform::fromHHex - (Transform::fromString16(Transform::toString16("\xff\xfe\xe4\x00")))); - $this->assertEquals("e400", Transform::fromHHex - (Transform::fromString16(Transform::toString16("\xfe\xff\x00\xe4")))); - } - - function testString16LE() - { - $this->assertEquals - ("fffe", Transform::fromHHex(Transform::toString16LE("\xff\xfe"))); - $this->assertEquals - ("\0T\0h\0i\0s\0 \0i\0s\0 \0a\0 \0t\0e\0s\0t\0.", - Transform::fromString16LE(Transform::toString16LE - ("\0T\0h\0i\0s\0 \0i\0s\0 \0a\0 \0t\0e\0s\0t\0."))); - } - - function testString16BE() - { - $this->assertEquals - ("feff", Transform::fromHHex(Transform::toString16BE("\xff\xfe"))); - $this->assertEquals - ("\0T\0h\0i\0s\0 \0i\0s\0 \0a\0 \0t\0e\0s\0t\0.", - Transform::fromString16BE(Transform::toString16BE - ("\0T\0h\0i\0s\0 \0i\0s\0 \0a\0 \0t\0e\0s\0t\0."))); - } - - function testHHex() - { - $this->assertEquals("6c34", bin2hex(Transform::toHHex("6c34"))); - $this->assertEquals("6c34", Transform::fromHHex(Transform::toHHex("6c34"))); - } - - function testLHex() - { - $this->assertEquals("c643", bin2hex(Transform::toLHex("6c34"))); - $this->assertEquals("6c34", Transform::fromLHex(Transform::toLHex("6c34"))); - } - - function testGUID() - { - $this->assertEquals - ("75b22630-668e-11cf-a6d9-00aa0062ce6c", - Transform::fromGUID(Transform::toGUID - ("75b22630-668e-11cf-a6d9-00aa0062ce6c"))); - } - -} + + * @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 TestTransform extends PHPUnit_Framework_TestCase +{ + function testInt64LE() + { + $this->assertEquals + (0x7fffffffffffffff, Transform::fromInt64BE + (Transform::toInt64BE(0x7fffffffffffffff))); + } + + function testInt64BE() + { + $this->assertEquals + (0x7fffffffffffffff, Transform::fromInt64BE + (Transform::toInt64BE(0x7fffffffffffffff))); + } + + function testInt32() + { + $this->assertEquals + (0x7fffffff, Transform::fromInt32(Transform::toInt32(0x7fffffff))); + } + + function testUInt32LE() + { + $this->assertEquals + ("78563412", Transform::fromHHex(Transform::toUInt32LE(0x12345678))); + $this->assertEquals + (0xffffffff, Transform::fromUInt32LE(Transform::toUInt32LE(0xffffffff))); + } + + function testUInt32BE() + { + $this->assertEquals + ("12345678", Transform::fromHHex(Transform::toUInt32BE(0x12345678))); + $this->assertEquals + (0xffffffff, Transform::fromUInt32BE(Transform::toUInt32BE(0xffffffff))); + } + + function testInt16() + { + $this->assertEquals + (0x7fff, Transform::fromInt16(Transform::toInt16(0x7fff))); + } + + function testUInt16LE() + { + $this->assertEquals + ("fffe", Transform::fromHHex(Transform::toUInt16LE(0xfeff))); + $this->assertEquals + (0xffff, Transform::fromUInt16LE(Transform::toUInt16LE(0xffff))); + } + + function testUInt16BE() + { + $this->assertEquals + ("feff", Transform::fromHHex(Transform::toUInt16BE(0xfeff))); + $this->assertEquals + (0xffff, Transform::fromUInt16BE(Transform::toUInt16BE(0xffff))); + } + + function testInt8() + { + $this->assertEquals(0x7f, Transform::fromInt8(Transform::toInt8(0x7f))); + } + + function testString16() + { + $this->assertEquals("00e4", Transform::fromHHex + (Transform::fromString16(Transform::toString16("\xff\xfe\x00\xe4")))); + $this->assertEquals("00e4", Transform::fromHHex + (Transform::fromString16(Transform::toString16("\xfe\xff\x00\xe4")))); + } + + function testString16LE() + { + $this->assertEquals + ("fffe", Transform::fromHHex(Transform::toString16LE("\xff\xfe"))); + $this->assertEquals + ("\0T\0h\0i\0s\0 \0i\0s\0 \0a\0 \0t\0e\0s\0t\0.", + Transform::fromString16LE(Transform::toString16LE + ("\0T\0h\0i\0s\0 \0i\0s\0 \0a\0 \0t\0e\0s\0t\0."))); + } + + function testString16BE() + { + $this->assertEquals + ("feff", Transform::fromHHex(Transform::toString16BE("\xff\xfe"))); + $this->assertEquals + ("\0T\0h\0i\0s\0 \0i\0s\0 \0a\0 \0t\0e\0s\0t\0.", + Transform::fromString16BE(Transform::toString16BE + ("\0T\0h\0i\0s\0 \0i\0s\0 \0a\0 \0t\0e\0s\0t\0."))); + } + + function testHHex() + { + $this->assertEquals("6c34", bin2hex(Transform::toHHex("6c34"))); + $this->assertEquals("6c34", Transform::fromHHex(Transform::toHHex("6c34"))); + } + + function testLHex() + { + $this->assertEquals("c643", bin2hex(Transform::toLHex("6c34"))); + $this->assertEquals("6c34", Transform::fromLHex(Transform::toLHex("6c34"))); + } + + function testGUID() + { + $this->assertEquals + ("75b22630-668e-11cf-a6d9-00aa0062ce6c", + Transform::fromGUID(Transform::toGUID + ("75b22630-668e-11cf-a6d9-00aa0062ce6c"))); + } + +}