diff --git a/lib/Data/Database.php b/lib/Data/Database.php index dc374ae0..79000d9a 100644 --- a/lib/Data/Database.php +++ b/lib/Data/Database.php @@ -167,38 +167,43 @@ class Database extends AbstractData } } - $opendiscussion = $burnafterreading = false; - $attachment = $attachmentname = ''; - $meta = $paste['meta']; - unset($meta['postdate']); - $expire_date = 0; - if (array_key_exists('expire_date', $paste['meta'])) { - $expire_date = (int) $paste['meta']['expire_date']; + $expire_date = 0; + $opendiscussion = $burnafterreading = false; + $attachment = $attachmentname = null; + $meta = $paste['meta']; + $isVersion1 = array_key_exists('data', $paste); + list($createdKey) = self::_getVersionedKeys($isVersion1 ? 1 : 2); + $created = (int) $meta[$createdKey]; + unset($meta[$createdKey], $paste['meta']); + if (array_key_exists('expire_date', $meta)) { + $expire_date = (int) $meta['expire_date']; unset($meta['expire_date']); } - if (array_key_exists('opendiscussion', $paste['meta'])) { - $opendiscussion = (bool) $paste['meta']['opendiscussion']; + if (array_key_exists('opendiscussion', $meta)) { + $opendiscussion = (bool) $meta['opendiscussion']; unset($meta['opendiscussion']); } - if (array_key_exists('burnafterreading', $paste['meta'])) { - $burnafterreading = (bool) $paste['meta']['burnafterreading']; + if (array_key_exists('burnafterreading', $meta)) { + $burnafterreading = (bool) $meta['burnafterreading']; unset($meta['burnafterreading']); } - if (array_key_exists('attachment', $paste['meta'])) { - $attachment = $paste['meta']['attachment']; - unset($meta['attachment']); - } - if (array_key_exists('attachmentname', $paste['meta'])) { - $attachmentname = $paste['meta']['attachmentname']; - unset($meta['attachmentname']); + if ($isVersion1) { + if (array_key_exists('attachment', $meta)) { + $attachment = $meta['attachment']; + unset($meta['attachment']); + } + if (array_key_exists('attachmentname', $meta)) { + $attachmentname = $meta['attachmentname']; + unset($meta['attachmentname']); + } } return self::_exec( 'INSERT INTO ' . self::_sanitizeIdentifier('paste') . ' VALUES(?,?,?,?,?,?,?,?,?)', array( $pasteid, - $paste['data'], - $paste['meta']['postdate'], + $isVersion1 ? $paste['data'] : json_encode($paste), + $created, $expire_date, (int) $opendiscussion, (int) $burnafterreading, @@ -229,46 +234,51 @@ class Database extends AbstractData if (false !== $paste) { // create object - self::$_cache[$pasteid] = new stdClass; - self::$_cache[$pasteid]->data = $paste['data']; + $data = json_decode($paste['data']); + $isVersion2 = property_exists($data, 'v') && $data->v >= 2; + if ($isVersion2) { + self::$_cache[$pasteid] = $data; + list($createdKey) = self::_getVersionedKeys(2); + } else { + self::$_cache[$pasteid] = new stdClass; + self::$_cache[$pasteid]->data = $paste['data']; + list($createdKey) = self::_getVersionedKeys(1); + } $meta = json_decode($paste['meta']); if (!is_object($meta)) { $meta = new stdClass; } - // support older attachments - if (property_exists($meta, 'attachment')) { - self::$_cache[$pasteid]->attachment = $meta->attachment; - unset($meta->attachment); - if (property_exists($meta, 'attachmentname')) { - self::$_cache[$pasteid]->attachmentname = $meta->attachmentname; - unset($meta->attachmentname); + if (!$isVersion2) { + // support pre v1 attachments + if (property_exists($meta, 'attachment')) { + self::$_cache[$pasteid]->attachment = $meta->attachment; + unset($meta->attachment); + if (property_exists($meta, 'attachmentname')) { + self::$_cache[$pasteid]->attachmentname = $meta->attachmentname; + unset($meta->attachmentname); + } + } + // support v1 attachments + elseif (array_key_exists('attachment', $paste) && strlen($paste['attachment'])) { + self::$_cache[$pasteid]->attachment = $paste['attachment']; + if (array_key_exists('attachmentname', $paste) && strlen($paste['attachmentname'])) { + self::$_cache[$pasteid]->attachmentname = $paste['attachmentname']; + } } } - // support current attachments - elseif (array_key_exists('attachment', $paste) && strlen($paste['attachment'])) { - self::$_cache[$pasteid]->attachment = $paste['attachment']; - if (array_key_exists('attachmentname', $paste) && strlen($paste['attachmentname'])) { - self::$_cache[$pasteid]->attachmentname = $paste['attachmentname']; - } - } - self::$_cache[$pasteid]->meta = $meta; - self::$_cache[$pasteid]->meta->postdate = (int) $paste['postdate']; - $expire_date = (int) $paste['expiredate']; - if ( - $expire_date > 0 - ) { + + self::$_cache[$pasteid]->meta = $meta; + self::$_cache[$pasteid]->meta->$createdKey = (int) $paste['postdate']; + $expire_date = (int) $paste['expiredate']; + if ($expire_date > 0) { self::$_cache[$pasteid]->meta->expire_date = $expire_date; } - if ( - $paste['opendiscussion'] - ) { + if ($paste['opendiscussion']) { self::$_cache[$pasteid]->meta->opendiscussion = true; } - if ( - $paste['burnafterreading'] - ) { + if ($paste['burnafterreading']) { self::$_cache[$pasteid]->meta->burnafterreading = true; } } @@ -329,9 +339,19 @@ class Database extends AbstractData */ public function createComment($pasteid, $parentid, $commentid, $comment) { - foreach (array('nickname', 'vizhash') as $key) { - if (!array_key_exists($key, $comment['meta'])) { - $comment['meta'][$key] = null; + if (array_key_exists('data', $comment)) { + $version = 1; + $data = $comment['data']; + } else { + $version = 2; + $data = json_encode($comment); + } + list($createdKey, $iconKey) = self::_getVersionedKeys($version); + $meta = $comment['meta']; + unset($comment['meta']); + foreach (array('nickname', $iconKey) as $key) { + if (!array_key_exists($key, $meta)) { + $meta[$key] = null; } } return self::_exec( @@ -341,10 +361,10 @@ class Database extends AbstractData $commentid, $pasteid, $parentid, - $comment['data'], - $comment['meta']['nickname'], - $comment['meta']['vizhash'], - $comment['meta']['postdate'], + $data, + $meta['nickname'], + $meta[$iconKey], + $meta[$createdKey], ) ); } @@ -367,16 +387,24 @@ class Database extends AbstractData $comments = array(); if (count($rows)) { foreach ($rows as $row) { - $i = $this->getOpenSlot($comments, (int) $row['postdate']); - $comments[$i] = new stdClass; - $comments[$i]->id = $row['dataid']; - $comments[$i]->parentid = $row['parentid']; - $comments[$i]->data = $row['data']; - $comments[$i]->meta = new stdClass; - $comments[$i]->meta->postdate = (int) $row['postdate']; - foreach (array('nickname', 'vizhash') as $key) { - if (array_key_exists($key, $row) && !empty($row[$key])) { - $comments[$i]->meta->$key = $row[$key]; + $i = $this->getOpenSlot($comments, (int) $row['postdate']); + $data = json_decode($row['data']); + if (property_exists($data, 'v') && $data->v >= 2) { + $version = 2; + $comments[$i] = $data; + } else { + $version = 1; + $comments[$i] = new stdClass; + $comments[$i]->data = $row['data']; + } + list($createdKey, $iconKey) = self::_getVersionedKeys($version); + $comments[$i]->id = $row['dataid']; + $comments[$i]->parentid = $row['parentid']; + $comments[$i]->meta = new stdClass; + $comments[$i]->meta->$createdKey = (int) $row['postdate']; + foreach (array('nickname' => 'nickname', 'vizhash' => $iconKey) as $rowKey => $commentKey) { + if (array_key_exists($rowKey, $row) && !empty($row[$rowKey])) { + $comments[$i]->meta->$commentKey = $row[$rowKey]; } } } @@ -465,6 +493,23 @@ class Database extends AbstractData return $result; } + + /** + * get version dependent key names + * + * @access private + * @static + * @param int $version + * @return array + */ + private static function _getVersionedKeys(int $version) + { + if ($version === 1) { + return array('postdate', 'vizhash'); + } + return array('created', 'icon'); + } + /** * get table list query, depending on the database type * diff --git a/tst/Bootstrap.php b/tst/Bootstrap.php index 076b04bc..d8aa025e 100644 --- a/tst/Bootstrap.php +++ b/tst/Bootstrap.php @@ -64,11 +64,12 @@ class Helper 'zlib', ), 'plaintext', - 0, + 1, 0 ), 'meta' => array( - 'expire' => '5min', + 'expire' => '5min', + 'created' => 1344803344, ), 'v' => 2, 'ct' => 'ME5JF/YBEijp2uYMzLZozbKtWc5wfy6R59NBb7SmRig=', @@ -119,7 +120,7 @@ class Helper * @param array $meta * @return array */ - public static function getPaste($version = 2, $meta = array()) + public static function getPaste(int $version = 2, array $meta = array()) { $example = self::getPasteWithAttachment($version, $meta); // v1 has the attachment stored in a separate property @@ -136,9 +137,9 @@ class Helper * @param array $meta * @return array */ - public static function getPasteWithAttachment($version = 2, $meta = array()) + public static function getPasteWithAttachment(int $version = 2, array $meta = array()) { - $example = $version === 1 ? self::$pasteV1 : self::$pasteV2; + $example = $version === 1 ? self::$pasteV1 : self::$pasteV2; $example['meta']['salt'] = ServerSalt::generate(); $example['meta'] = array_merge($example['meta'], $meta); return $example; @@ -151,7 +152,7 @@ class Helper * @param array $meta * @return array */ - public static function getPasteAsJson($version = 2, $meta = array()) + public static function getPasteAsJson(int $version = 2, array $meta = array()) { $example = self::getPaste($version); // the JSON shouldn't contain the salt @@ -183,9 +184,9 @@ class Helper * @param array $meta * @return array */ - public static function getComment($version = 2, $meta = array()) + public static function getComment(int $version = 2, array $meta = array()) { - $example = $version === 1 ? self::$commentV1 : self::getPaste($version); + $example = $version === 1 ? self::$commentV1 : self::$pasteV2; if ($version === 2) { $example['pasteid'] = $example['parentid'] = self::getPasteId(); $example['meta']['created'] = self::$commentV1['meta']['postdate']; @@ -202,7 +203,7 @@ class Helper * @param int $version * @return array */ - public static function getCommentPost($version = 2) + public static function getCommentPost(int $version = 2) { $example = self::getComment($version); if ($version === 1) { @@ -220,7 +221,7 @@ class Helper * @param string $path * @throws Exception */ - public static function rmDir($path) + public static function rmDir(string $path) { if (is_dir($path)) { $path .= DIRECTORY_SEPARATOR; @@ -279,7 +280,7 @@ class Helper * @param string $pathToFile * @param array $values */ - public static function createIniFile($pathToFile, $values) + public static function createIniFile(string $pathToFile, array $values) { if (count($values)) { @unlink($pathToFile); @@ -322,7 +323,7 @@ class Helper * @param bool $return * @return void|string */ - public static function varExportMin($var, $return = false) + public static function varExportMin($var, bool $return = false) { if (is_array($var)) { $toImplode = array(); diff --git a/tst/Data/DatabaseTest.php b/tst/Data/DatabaseTest.php index 5b1b11e8..94d641fd 100644 --- a/tst/Data/DatabaseTest.php +++ b/tst/Data/DatabaseTest.php @@ -36,7 +36,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase $this->_model->delete(Helper::getPasteId()); // storing pastes - $paste = Helper::getPaste(array('expire_date' => 1344803344)); + $paste = Helper::getPaste(); $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist'); $this->assertTrue($this->_model->create(Helper::getPasteId(), $paste), 'store new paste'); $this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it'); @@ -44,14 +44,23 @@ class DatabaseTest extends PHPUnit_Framework_TestCase $this->assertEquals(json_decode(json_encode($paste)), $this->_model->read(Helper::getPasteId())); // storing comments - $this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist'); - $this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()) !== false, 'store comment'); - $this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after storing it'); - $comment = json_decode(json_encode(Helper::getComment())); - $comment->id = Helper::getCommentId(); - $comment->parentid = Helper::getPasteId(); + $this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'v1 comment does not yet exist'); + $this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment(1)) !== false, 'store v1 comment'); + $this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'v1 comment exists after storing it'); + $this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId()), 'v2 comment does not yet exist'); + $this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId(), Helper::getComment(2)) !== false, 'store v2 comment'); + $this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getPasteId()), 'v2 comment exists after storing it'); + $comment1 = json_decode(json_encode(Helper::getComment(1))); + $comment1->id = Helper::getCommentId(); + $comment1->parentid = Helper::getPasteId(); + $comment2 = json_decode(json_encode(Helper::getComment(2))); + $comment2->id = Helper::getPasteId(); + $comment2->parentid = Helper::getPasteId(); $this->assertEquals( - array($comment->meta->postdate => $comment), + array( + $comment1->meta->postdate => $comment1, + $comment2->meta->created . '.1' => $comment2, + ), $this->_model->readComments(Helper::getPasteId()) ); @@ -64,8 +73,9 @@ class DatabaseTest extends PHPUnit_Framework_TestCase public function testDatabaseBasedAttachmentStoreWorks() { + // this assumes a version 1 formatted paste $this->_model->delete(Helper::getPasteId()); - $original = $paste = Helper::getPasteWithAttachment(array('expire_date' => 1344803344)); + $original = $paste = Helper::getPasteWithAttachment(1, array('expire_date' => 1344803344)); $paste['meta']['burnafterreading'] = $original['meta']['burnafterreading'] = true; $paste['meta']['attachment'] = $paste['attachment']; $paste['meta']['attachmentname'] = $paste['attachmentname']; @@ -83,12 +93,12 @@ class DatabaseTest extends PHPUnit_Framework_TestCase public function testPurge() { $this->_model->delete(Helper::getPasteId()); - $expired = Helper::getPaste(array('expire_date' => 1344803344)); - $paste = Helper::getPaste(array('expire_date' => time() + 3600)); + $expired = Helper::getPaste(2, array('expire_date' => 1344803344)); + $paste = Helper::getPaste(2, array('expire_date' => time() + 3600)); $keys = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z'); $ids = array(); foreach ($keys as $key) { - $ids[$key] = substr(md5($key), 0, 16); + $ids[$key] = hash('fnv1a64', $key); $this->_model->delete($ids[$key]); $this->assertFalse($this->_model->exists($ids[$key]), "paste $key does not yet exist"); if (in_array($key, array('y', 'z'))) { @@ -243,11 +253,11 @@ class DatabaseTest extends PHPUnit_Framework_TestCase $this->_options['tbl'] = 'bar_'; $model = Database::getInstance($this->_options); - $original = $paste = Helper::getPasteWithAttachment(array('expire_date' => 1344803344)); - $paste['meta']['attachment'] = $paste['attachment']; - $paste['meta']['attachmentname'] = $paste['attachmentname']; + $original = $paste = Helper::getPasteWithAttachment(1, array('expire_date' => 1344803344)); + $meta = $paste['meta']; + $meta['attachment'] = $paste['attachment']; + $meta['attachmentname'] = $paste['attachmentname']; unset($paste['attachment'], $paste['attachmentname']); - $meta = $paste['meta']; $db = new PDO( $this->_options['dsn'], @@ -261,7 +271,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase Helper::getPasteId(), $paste['data'], $paste['meta']['postdate'], - 1344803344, + $paste['meta']['expire_date'], 0, 0, json_encode($meta),