From 22b41f3bfcbd5a54d59789c2cebfda6dc23b7dde Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 26 Mar 2017 00:01:11 +0100 Subject: Candidate implementation of defered insertion https://github.com/FreshRSS/FreshRSS/issues/530 --- app/Models/EntryDAO.php | 115 ++++++++++++++++++++++++++++++------------ app/Models/EntryDAOPGSQL.php | 26 ++++++++++ app/Models/EntryDAOSQLite.php | 39 ++++++++++---- app/Models/UserDAO.php | 10 ++-- 4 files changed, 146 insertions(+), 44 deletions(-) (limited to 'app/Models') diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index afcde3d7f..decae9307 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -88,6 +88,38 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return false; } + protected function createEntryTempTable() { + $ok = false; + $hadTransaction = $this->bd->inTransaction(); + if ($hadTransaction) { + $this->bd->commit(); + } + try { + $db = FreshRSS_Context::$system_conf->db; + require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); + Minz_Log::warning('SQL CREATE TABLE entrytmp...'); + if (defined('SQL_CREATE_TABLE_ENTRYTMP')) { + $sql = sprintf(SQL_CREATE_TABLE_ENTRYTMP, $this->prefix); + $stm = $this->bd->prepare($sql); + $ok = $stm && $stm->execute(); + } else { + global $SQL_CREATE_TABLE_ENTRYTMP; + $ok = !empty($SQL_CREATE_TABLE_ENTRYTMP); + foreach ($SQL_CREATE_TABLE_ENTRYTMP as $instruction) { + $sql = sprintf($instruction, $this->prefix); + $stm = $this->bd->prepare($sql); + $ok &= $stm && $stm->execute(); + } + } + } catch (Exception $e) { + Minz_Log::error('FreshRSS_EntryDAO::createEntryTempTable error: ' . $e->getMessage()); + } + if ($hadTransaction) { + $this->bd->beginTransaction(); + } + return $ok; + } + protected function autoUpdateDb($errorInfo) { if (isset($errorInfo[0])) { if ($errorInfo[0] === '42S22') { //ER_BAD_FIELD_ERROR @@ -97,6 +129,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return $this->addColumn($column); } } + } elseif ($errorInfo[0] === '42S02' && stripos($errorInfo[2], 'entrytmp') !== false) { //ER_BAD_TABLE_ERROR + return $this->createEntryTempTable(); //v1.7 } } if (isset($errorInfo[1])) { @@ -110,8 +144,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { private $addEntryPrepared = null; public function addEntry($valuesTmp) { - if ($this->addEntryPrepared === null) { - $sql = 'INSERT INTO `' . $this->prefix . 'entry` (id, guid, title, author, ' + if ($this->addEntryPrepared == null) { + $sql = 'INSERT INTO `' . $this->prefix . 'entrytmp` (id, guid, title, author, ' . ($this->isCompressed() ? 'content_bin' : 'content') . ', link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) ' . 'VALUES(:id, :guid, :title, :author, ' @@ -121,41 +155,43 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { . ', :is_read, :is_favorite, :id_feed, :tags)'; $this->addEntryPrepared = $this->bd->prepare($sql); } - $this->addEntryPrepared->bindParam(':id', $valuesTmp['id']); - $valuesTmp['guid'] = substr($valuesTmp['guid'], 0, 760); - $valuesTmp['guid'] = safe_ascii($valuesTmp['guid']); - $this->addEntryPrepared->bindParam(':guid', $valuesTmp['guid']); - $valuesTmp['title'] = substr($valuesTmp['title'], 0, 255); - $this->addEntryPrepared->bindParam(':title', $valuesTmp['title']); - $valuesTmp['author'] = substr($valuesTmp['author'], 0, 255); - $this->addEntryPrepared->bindParam(':author', $valuesTmp['author']); - $this->addEntryPrepared->bindParam(':content', $valuesTmp['content']); - $valuesTmp['link'] = substr($valuesTmp['link'], 0, 1023); - $valuesTmp['link'] = safe_ascii($valuesTmp['link']); - $this->addEntryPrepared->bindParam(':link', $valuesTmp['link']); - $this->addEntryPrepared->bindParam(':date', $valuesTmp['date'], PDO::PARAM_INT); - $valuesTmp['lastSeen'] = time(); - $this->addEntryPrepared->bindParam(':last_seen', $valuesTmp['lastSeen'], PDO::PARAM_INT); - $valuesTmp['is_read'] = $valuesTmp['is_read'] ? 1 : 0; - $this->addEntryPrepared->bindParam(':is_read', $valuesTmp['is_read'], PDO::PARAM_INT); - $valuesTmp['is_favorite'] = $valuesTmp['is_favorite'] ? 1 : 0; - $this->addEntryPrepared->bindParam(':is_favorite', $valuesTmp['is_favorite'], PDO::PARAM_INT); - $this->addEntryPrepared->bindParam(':id_feed', $valuesTmp['id_feed'], PDO::PARAM_INT); - $valuesTmp['tags'] = substr($valuesTmp['tags'], 0, 1023); - $this->addEntryPrepared->bindParam(':tags', $valuesTmp['tags']); - - if ($this->hasNativeHex()) { - $this->addEntryPrepared->bindParam(':hash', $valuesTmp['hash']); - } else { - $valuesTmp['hashBin'] = pack('H*', $valuesTmp['hash']); //hex2bin() is PHP5.4+ - $this->addEntryPrepared->bindParam(':hash', $valuesTmp['hashBin']); + if ($this->addEntryPrepared) { + $this->addEntryPrepared->bindParam(':id', $valuesTmp['id']); + $valuesTmp['guid'] = substr($valuesTmp['guid'], 0, 760); + $valuesTmp['guid'] = safe_ascii($valuesTmp['guid']); + $this->addEntryPrepared->bindParam(':guid', $valuesTmp['guid']); + $valuesTmp['title'] = substr($valuesTmp['title'], 0, 255); + $this->addEntryPrepared->bindParam(':title', $valuesTmp['title']); + $valuesTmp['author'] = substr($valuesTmp['author'], 0, 255); + $this->addEntryPrepared->bindParam(':author', $valuesTmp['author']); + $this->addEntryPrepared->bindParam(':content', $valuesTmp['content']); + $valuesTmp['link'] = substr($valuesTmp['link'], 0, 1023); + $valuesTmp['link'] = safe_ascii($valuesTmp['link']); + $this->addEntryPrepared->bindParam(':link', $valuesTmp['link']); + $this->addEntryPrepared->bindParam(':date', $valuesTmp['date'], PDO::PARAM_INT); + $valuesTmp['lastSeen'] = time(); + $this->addEntryPrepared->bindParam(':last_seen', $valuesTmp['lastSeen'], PDO::PARAM_INT); + $valuesTmp['is_read'] = $valuesTmp['is_read'] ? 1 : 0; + $this->addEntryPrepared->bindParam(':is_read', $valuesTmp['is_read'], PDO::PARAM_INT); + $valuesTmp['is_favorite'] = $valuesTmp['is_favorite'] ? 1 : 0; + $this->addEntryPrepared->bindParam(':is_favorite', $valuesTmp['is_favorite'], PDO::PARAM_INT); + $this->addEntryPrepared->bindParam(':id_feed', $valuesTmp['id_feed'], PDO::PARAM_INT); + $valuesTmp['tags'] = substr($valuesTmp['tags'], 0, 1023); + $this->addEntryPrepared->bindParam(':tags', $valuesTmp['tags']); + + if ($this->hasNativeHex()) { + $this->addEntryPrepared->bindParam(':hash', $valuesTmp['hash']); + } else { + $valuesTmp['hashBin'] = pack('H*', $valuesTmp['hash']); //hex2bin() is PHP5.4+ + $this->addEntryPrepared->bindParam(':hash', $valuesTmp['hashBin']); + } } - if ($this->addEntryPrepared && $this->addEntryPrepared->execute()) { return $this->bd->lastInsertId(); } else { $info = $this->addEntryPrepared == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $this->addEntryPrepared->errorInfo(); if ($this->autoUpdateDb($info)) { + $this->addEntryPrepared = null; return $this->addEntry($valuesTmp); } elseif ((int)($info[0] / 1000) !== 23) { //Filter out "SQLSTATE Class code 23: Constraint Violation" because of expected duplicate entries Minz_Log::error('SQL error addEntry: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2] @@ -165,6 +201,23 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } } + public function commitNewEntries() { + $sql = 'SET @rank=SELECT MAX(id) - COUNT(*) FROM `' . $this->prefix . 'entrytmp`; ' . //MySQL-specific + 'INSERT IGNORE INTO `' . $this->prefix . 'entry` (id, guid, title, author, content_bin, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) ' . + 'SELECT @rank:=@rank+1 AS id, guid, title, author, content_bin, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags FROM `' . $this->prefix . 'entrytmp` ORDER BY date; ' . + 'DELETE FROM `' . $this->prefix . 'entrytmp` WHERE id <= @rank;'; + $stm = $this->bd->prepare($sql); + $hadTransaction = $this->bd->inTransaction(); + if (!$hadTransaction) { + $this->bd->beginTransaction(); + } + $result = $stm ? $stm->execute() : false; + if (!$hadTransaction) { + $this->bd->commit(); + } + return $result; + } + private $updateEntryPrepared = null; public function updateEntry($valuesTmp) { diff --git a/app/Models/EntryDAOPGSQL.php b/app/Models/EntryDAOPGSQL.php index b96a62ebc..b25993c47 100644 --- a/app/Models/EntryDAOPGSQL.php +++ b/app/Models/EntryDAOPGSQL.php @@ -11,6 +11,11 @@ class FreshRSS_EntryDAOPGSQL extends FreshRSS_EntryDAOSQLite { } protected function autoUpdateDb($errorInfo) { + if (isset($errorInfo[0])) { + if ($errorInfo[0] === '42P01' && stripos($errorInfo[2], 'entrytmp') !== false) { //undefined_table + return $this->createEntryTempTable(); + } + } return false; } @@ -18,6 +23,27 @@ class FreshRSS_EntryDAOPGSQL extends FreshRSS_EntryDAOSQLite { return false; } + public function commitNewEntries() { + $sql = 'DO $$ +DECLARE +maxrank bigint := (SELECT MAX(id) FROM `' . $this->prefix . 'entrytmp`); +rank bigint := (SELECT maxrank - COUNT(*) FROM `' . $this->prefix . 'entrytmp`); +BEGIN + INSERT INTO `' . $this->prefix . 'entry` (id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) + (SELECT rank + row_number() OVER(ORDER BY date) AS id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags FROM `' . $this->prefix . 'entrytmp` ORDER BY date); + DELETE FROM `' . $this->prefix . 'entrytmp` WHERE id <= maxrank; +END $$;'; + $hadTransaction = $this->bd->inTransaction(); + if (!$hadTransaction) { + $this->bd->beginTransaction(); + } + $result = $this->bd->exec($sql) !== false; + if (!$hadTransaction) { + $this->bd->commit(); + } + return $result; + } + public function size($all = true) { $db = FreshRSS_Context::$system_conf->db; $sql = 'SELECT pg_size_pretty(pg_database_size(?))'; diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index 34e854608..ad7bcd865 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -7,21 +7,42 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { } protected function autoUpdateDb($errorInfo) { - if (empty($errorInfo[0]) || $errorInfo[0] == '42S22') { //ER_BAD_FIELD_ERROR - //autoAddColumn - if ($tableInfo = $this->bd->query("SELECT sql FROM sqlite_master where name='entry'")) { - $showCreate = $tableInfo->fetchColumn(); - Minz_Log::debug('FreshRSS_EntryDAOSQLite::autoUpdateDb: ' . $showCreate); - foreach (array('lastSeen', 'hash') as $column) { - if (stripos($showCreate, $column) === false) { - return $this->addColumn($column); - } + Minz_Log::error('FreshRSS_EntryDAO::autoUpdateDb error: ' . print_r($errorInfo, true)); + if ($tableInfo = $this->bd->query("SELECT sql FROM sqlite_master where name='entrytmp'")) { + $showCreate = $tableInfo->fetchColumn(); + if (stripos($showCreate, 'entrytmp') === false) { + return $this->createEntryTempTable(); + } + } + if ($tableInfo = $this->bd->query("SELECT sql FROM sqlite_master where name='entry'")) { + $showCreate = $tableInfo->fetchColumn(); + foreach (array('lastSeen', 'hash') as $column) { + if (stripos($showCreate, $column) === false) { + return $this->addColumn($column); } } } return false; } + public function commitNewEntries() { + $sql = ' +CREATE TEMP TABLE `tmp` AS SELECT id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags FROM `' . $this->prefix . 'entrytmp` ORDER BY date; +INSERT OR IGNORE INTO `' . $this->prefix . 'entry` (id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) + SELECT rowid + (SELECT MAX(id) - COUNT(*) FROM `tmp`) AS id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags FROM `tmp` ORDER BY date; +DELETE FROM `' . $this->prefix . 'entrytmp` WHERE id <= (SELECT MAX(id) FROM `tmp`); +DROP TABLE `tmp`;'; + $hadTransaction = $this->bd->inTransaction(); + if (!$hadTransaction) { + $this->bd->beginTransaction(); + } + $result = $this->bd->exec($sql) !== false; + if (!$hadTransaction) { + $this->bd->commit(); + } + return $result; + } + protected function sqlConcat($s1, $s2) { return $s1 . '||' . $s2; } diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index a60caf395..310c7c096 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -14,21 +14,23 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { $ok = false; $bd_prefix_user = $db['prefix'] . $username . '_'; if (defined('SQL_CREATE_TABLES')) { //E.g. MySQL - $sql = sprintf(SQL_CREATE_TABLES, $bd_prefix_user, _t('gen.short.default_category')); + $sql = sprintf(SQL_CREATE_TABLES . SQL_CREATE_TABLE_ENTRYTMP, $bd_prefix_user, _t('gen.short.default_category')); $stm = $userPDO->bd->prepare($sql); $ok = $stm && $stm->execute(); } else { //E.g. SQLite global $SQL_CREATE_TABLES; + global $SQL_CREATE_TABLE_ENTRYTMP; if (is_array($SQL_CREATE_TABLES)) { - $ok = true; - foreach ($SQL_CREATE_TABLES as $instruction) { + $instructions = array_merge($SQL_CREATE_TABLES, $SQL_CREATE_TABLE_ENTRYTMP); + $ok = !empty($instructions); + foreach ($instructions as $instruction) { $sql = sprintf($instruction, $bd_prefix_user, _t('gen.short.default_category')); $stm = $userPDO->bd->prepare($sql); $ok &= ($stm && $stm->execute()); } } } - if ($insertDefaultFeeds) { + if ($ok && $insertDefaultFeeds) { if (defined('SQL_INSERT_FEEDS')) { //E.g. MySQL $sql = sprintf(SQL_INSERT_FEEDS, $bd_prefix_user); $stm = $userPDO->bd->prepare($sql); -- cgit v1.2.3 From a20fd9db9f0ed0e27c65671bb10402ced10587b1 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 26 Mar 2017 01:41:08 +0100 Subject: Defered insertion MySQL bug The update of cached values remains to be optimized --- app/Controllers/feedController.php | 1 + app/Models/EntryDAO.php | 6 +++--- app/SQL/install.sql.mysql.php | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'app/Models') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index a2d9d5c35..b565d0439 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -436,6 +436,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } if (!$noCommit) { $entryDAO->commitNewEntries(); + $feedDAO->updateCachedValues(); //TODO: Optimize } return array($updated_feeds, reset($feeds)); } diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index decae9307..9d11cec6a 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -121,6 +121,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } protected function autoUpdateDb($errorInfo) { + Minz_Log::warning('FreshRSS_EntryDAO::autoUpdateDb: ' . print_r($errorInfo, true)); if (isset($errorInfo[0])) { if ($errorInfo[0] === '42S22') { //ER_BAD_FIELD_ERROR //autoAddColumn @@ -202,16 +203,15 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } public function commitNewEntries() { - $sql = 'SET @rank=SELECT MAX(id) - COUNT(*) FROM `' . $this->prefix . 'entrytmp`; ' . //MySQL-specific + $sql = 'SET @rank=(SELECT MAX(id) - COUNT(*) FROM `' . $this->prefix . 'entrytmp`); ' . //MySQL-specific 'INSERT IGNORE INTO `' . $this->prefix . 'entry` (id, guid, title, author, content_bin, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) ' . 'SELECT @rank:=@rank+1 AS id, guid, title, author, content_bin, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags FROM `' . $this->prefix . 'entrytmp` ORDER BY date; ' . 'DELETE FROM `' . $this->prefix . 'entrytmp` WHERE id <= @rank;'; - $stm = $this->bd->prepare($sql); $hadTransaction = $this->bd->inTransaction(); if (!$hadTransaction) { $this->bd->beginTransaction(); } - $result = $stm ? $stm->execute() : false; + $result = $this->bd->exec($sql) !== false; if (!$hadTransaction) { $this->bd->commit(); } diff --git a/app/SQL/install.sql.mysql.php b/app/SQL/install.sql.mysql.php index ceca07f93..f42e08ad3 100644 --- a/app/SQL/install.sql.mysql.php +++ b/app/SQL/install.sql.mysql.php @@ -79,7 +79,7 @@ CREATE TABLE IF NOT EXISTS `%1$sentrytmp` ( -- v1.7 PRIMARY KEY (`id`), FOREIGN KEY (`id_feed`) REFERENCES `%1$sfeed`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, UNIQUE KEY (`id_feed`,`guid`), - INDEX (`date`), + INDEX (`date`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = INNODB; '); -- cgit v1.2.3 From e956aee53d561fbdc11a78a50ad7cc041108e5b5 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 26 Mar 2017 14:07:06 +0200 Subject: More defered insertion. New SQL index New index entry_feed_read_index TODO: Auto add this index to existing version --- app/Controllers/feedController.php | 8 +++++- app/Models/EntryDAO.php | 1 - app/Models/Factory.php | 9 +----- app/Models/FeedDAO.php | 56 ++++++++++++++++++-------------------- app/Models/FeedDAOSQLite.php | 19 ------------- app/SQL/install.sql.mysql.php | 1 + app/SQL/install.sql.pgsql.php | 1 + app/SQL/install.sql.sqlite.php | 1 + 8 files changed, 37 insertions(+), 59 deletions(-) delete mode 100644 app/Models/FeedDAOSQLite.php (limited to 'app/Models') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index b565d0439..bfc8b2045 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -393,7 +393,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } - $feedDAO->updateLastUpdate($feed->id(), false, $entryDAO->inTransaction(), $mtime); + $feedDAO->updateLastUpdate($feed->id(), false, $mtime); if ($entryDAO->inTransaction()) { $entryDAO->commit(); } @@ -435,8 +435,14 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } if (!$noCommit) { + if (!$entryDAO->inTransaction()) { + $entryDAO->beginTransaction(); + } $entryDAO->commitNewEntries(); $feedDAO->updateCachedValues(); //TODO: Optimize + if ($entryDAO->inTransaction()) { + $entryDAO->commit(); + } } return array($updated_feeds, reset($feeds)); } diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 9d11cec6a..39c00f01c 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -121,7 +121,6 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } protected function autoUpdateDb($errorInfo) { - Minz_Log::warning('FreshRSS_EntryDAO::autoUpdateDb: ' . print_r($errorInfo, true)); if (isset($errorInfo[0])) { if ($errorInfo[0] === '42S22') { //ER_BAD_FIELD_ERROR //autoAddColumn diff --git a/app/Models/Factory.php b/app/Models/Factory.php index 6502c38b7..dfccc883e 100644 --- a/app/Models/Factory.php +++ b/app/Models/Factory.php @@ -3,14 +3,7 @@ class FreshRSS_Factory { public static function createFeedDao($username = null) { - $conf = Minz_Configuration::get('system'); - switch ($conf->db['type']) { - case 'sqlite': - case 'pgsql': - return new FreshRSS_FeedDAOSQLite($username); - default: - return new FreshRSS_FeedDAO($username); - } + return new FreshRSS_FeedDAO($username); } public static function createEntryDao($username = null) { diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index 0168aebd9..d278122e3 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -92,29 +92,15 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } } - public function updateLastUpdate($id, $inError = false, $updateCache = true, $mtime = 0) { - if ($updateCache) { - $sql = 'UPDATE `' . $this->prefix . 'feed` ' //2 sub-requests with FOREIGN KEY(e.id_feed), INDEX(e.is_read) faster than 1 request with GROUP BY or CASE - . 'SET `cache_nbEntries`=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=`' . $this->prefix . 'feed`.id),' - . '`cache_nbUnreads`=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=`' . $this->prefix . 'feed`.id AND e2.is_read=0),' - . '`lastUpdate`=?, error=? ' - . 'WHERE id=?'; - } else { - $sql = 'UPDATE `' . $this->prefix . 'feed` ' - . 'SET `lastUpdate`=?, error=? ' - . 'WHERE id=?'; - } - - if ($mtime <= 0) { - $mtime = time(); - } - + public function updateLastUpdate($id, $inError = false, $mtime = 0) { //See also updateCachedValue() + $sql = 'UPDATE `' . $this->prefix . 'feed` ' + . 'SET `lastUpdate`=?, error=? ' + . 'WHERE id=?'; $values = array( - $mtime, + $mtime <= 0 ? time() : $mtime, $inError ? 1 : 0, $id, ); - $stm = $this->bd->prepare($sql); if ($stm && $stm->execute($values)) { @@ -294,18 +280,28 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return $res[0]['count']; } - public function updateCachedValues() { //For one single feed, call updateLastUpdate($id) - $sql = 'UPDATE `' . $this->prefix . 'feed` f ' - . 'INNER JOIN (' - . 'SELECT e.id_feed, ' - . 'COUNT(CASE WHEN e.is_read = 0 THEN 1 END) AS nbUnreads, ' - . 'COUNT(e.id) AS nbEntries ' - . 'FROM `' . $this->prefix . 'entry` e ' - . 'GROUP BY e.id_feed' - . ') x ON x.id_feed=f.id ' - . 'SET f.`cache_nbEntries`=x.nbEntries, f.`cache_nbUnreads`=x.nbUnreads'; + public function updateCachedValue($id) { //For multiple feeds, call updateCachedValues() + $sql = 'UPDATE `' . $this->prefix . 'feed` ' //2 sub-requests with FOREIGN KEY(e.id_feed), INDEX(e.is_read) faster than 1 request with GROUP BY or CASE + . 'SET `cache_nbEntries`=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=`' . $this->prefix . 'feed`.id),' + . '`cache_nbUnreads`=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=`' . $this->prefix . 'feed`.id AND e2.is_read=0) ' + . 'WHERE id=?'; + $values = array($id); $stm = $this->bd->prepare($sql); + if ($stm && $stm->execute($values)) { + return $stm->rowCount(); + } else { + $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); + Minz_Log::error('SQL error updateCachedValue: ' . $info[2]); + return false; + } + } + + public function updateCachedValues() { //For one single feed, call updateCachedValue($id) + $sql = 'UPDATE `' . $this->prefix . 'feed` ' + . 'SET `cache_nbEntries`=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=`' . $this->prefix . 'feed`.id),' + . '`cache_nbUnreads`=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=`' . $this->prefix . 'feed`.id AND e2.is_read=0)'; + $stm = $this->bd->prepare($sql); if ($stm && $stm->execute()) { return $stm->rowCount(); } else { @@ -343,7 +339,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return $affected; } - public function cleanOldEntries($id, $date_min, $keep = 15) { //Remember to call updateLastUpdate($id) or updateCachedValues() just after + public function cleanOldEntries($id, $date_min, $keep = 15) { //Remember to call updateCachedValue($id) or updateCachedValues() just after $sql = 'DELETE FROM `' . $this->prefix . 'entry` ' . 'WHERE id_feed=:id_feed AND id<=:id_max ' . 'AND is_favorite=0 ' //Do not remove favourites diff --git a/app/Models/FeedDAOSQLite.php b/app/Models/FeedDAOSQLite.php deleted file mode 100644 index 440ae74da..000000000 --- a/app/Models/FeedDAOSQLite.php +++ /dev/null @@ -1,19 +0,0 @@ -prefix . 'feed` ' - . 'SET `cache_nbEntries`=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=`' . $this->prefix . 'feed`.id),' - . '`cache_nbUnreads`=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=`' . $this->prefix . 'feed`.id AND e2.is_read=0)'; - $stm = $this->bd->prepare($sql); - if ($stm && $stm->execute()) { - return $stm->rowCount(); - } else { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::error('SQL error updateCachedValues: ' . $info[2]); - return false; - } - } - -} diff --git a/app/SQL/install.sql.mysql.php b/app/SQL/install.sql.mysql.php index f42e08ad3..3ad68173f 100644 --- a/app/SQL/install.sql.mysql.php +++ b/app/SQL/install.sql.mysql.php @@ -55,6 +55,7 @@ CREATE TABLE IF NOT EXISTS `%1$sentry` ( INDEX (`is_favorite`), -- v0.7 INDEX (`is_read`), -- v0.7 INDEX `entry_lastSeen_index` (`lastSeen`) -- v1.1.1 + INDEX `entry_feed_read_index` (`id_feed`,`is_read`) -- v1.7 //TODO: Auto add this index to existing version ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = INNODB; diff --git a/app/SQL/install.sql.pgsql.php b/app/SQL/install.sql.pgsql.php index 1a666cb8a..ca4dfa925 100644 --- a/app/SQL/install.sql.pgsql.php +++ b/app/SQL/install.sql.pgsql.php @@ -50,6 +50,7 @@ $SQL_CREATE_TABLES = array( 'CREATE INDEX %1$sis_favorite_index ON "%1$sentry" ("is_favorite");', 'CREATE INDEX %1$sis_read_index ON "%1$sentry" ("is_read");', 'CREATE INDEX %1$sentry_lastSeen_index ON "%1$sentry" ("lastSeen");', +'CREATE INDEX %1$sentry_feed_read_index ON "%1$sentry" ("id_feed","is_read");', //v1.7 //TODO: Auto add this index to existing version 'INSERT INTO "%1$scategory" (name) SELECT \'%2$s\' WHERE NOT EXISTS (SELECT id FROM "%1$scategory" WHERE id = 1);', ); diff --git a/app/SQL/install.sql.sqlite.php b/app/SQL/install.sql.sqlite.php index ad7d525fd..dcb7a351a 100644 --- a/app/SQL/install.sql.sqlite.php +++ b/app/SQL/install.sql.sqlite.php @@ -53,6 +53,7 @@ $SQL_CREATE_TABLES = array( 'CREATE INDEX IF NOT EXISTS entry_is_favorite_index ON `entry`(`is_favorite`);', 'CREATE INDEX IF NOT EXISTS entry_is_read_index ON `entry`(`is_read`);', 'CREATE INDEX IF NOT EXISTS entry_lastSeen_index ON `entry`(`lastSeen`);', //v1.1.1 +'CREATE INDEX IF NOT EXISTS entry_feed_read_index ON `entry`(`id_feed`,`is_read`);', //v1.7 //TODO: Auto add this index to existing version 'INSERT OR IGNORE INTO `category` (id, name) VALUES(1, "%2$s");', ); -- cgit v1.2.3 From cc235c6af36e4cbfcdced1ee323559a32ca65114 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 26 Mar 2017 15:02:35 +0200 Subject: SimplePie light manual update --- app/Models/EntryDAO.php | 2 +- lib/SimplePie/SimplePie.php | 86 ++++++----- lib/SimplePie/SimplePie/Author.php | 5 +- lib/SimplePie/SimplePie/Cache.php | 11 +- lib/SimplePie/SimplePie/Cache/Base.php | 5 +- lib/SimplePie/SimplePie/Cache/DB.php | 5 +- lib/SimplePie/SimplePie/Cache/File.php | 5 +- lib/SimplePie/SimplePie/Cache/Memcache.php | 5 +- lib/SimplePie/SimplePie/Cache/Memcached.php | 166 +++++++++++++++++++++ lib/SimplePie/SimplePie/Cache/MySQL.php | 23 ++- lib/SimplePie/SimplePie/Cache/Redis.php | 166 +++++++++++++++++++++ lib/SimplePie/SimplePie/Caption.php | 5 +- lib/SimplePie/SimplePie/Category.php | 5 +- lib/SimplePie/SimplePie/Content/Type/Sniffer.php | 5 +- lib/SimplePie/SimplePie/Copyright.php | 5 +- lib/SimplePie/SimplePie/Core.php | 5 +- lib/SimplePie/SimplePie/Credit.php | 5 +- lib/SimplePie/SimplePie/Decode/HTML/Entities.php | 5 +- lib/SimplePie/SimplePie/Enclosure.php | 7 +- lib/SimplePie/SimplePie/Exception.php | 5 +- lib/SimplePie/SimplePie/File.php | 16 +- lib/SimplePie/SimplePie/HTTP/Parser.php | 5 +- lib/SimplePie/SimplePie/IRI.php | 67 ++++++--- lib/SimplePie/SimplePie/Item.php | 37 ++--- lib/SimplePie/SimplePie/Locator.php | 60 +++++++- lib/SimplePie/SimplePie/Misc.php | 10 +- lib/SimplePie/SimplePie/Net/IPv6.php | 5 +- lib/SimplePie/SimplePie/Parse/Date.php | 11 +- lib/SimplePie/SimplePie/Parser.php | 5 +- lib/SimplePie/SimplePie/Rating.php | 5 +- lib/SimplePie/SimplePie/Registry.php | 5 +- lib/SimplePie/SimplePie/Restriction.php | 5 +- lib/SimplePie/SimplePie/Sanitize.php | 6 +- lib/SimplePie/SimplePie/Source.php | 5 +- lib/SimplePie/SimplePie/XML/Declaration/Parser.php | 5 +- lib/SimplePie/SimplePie/gzdecode.php | 5 +- 36 files changed, 587 insertions(+), 191 deletions(-) create mode 100644 lib/SimplePie/SimplePie/Cache/Memcached.php create mode 100644 lib/SimplePie/SimplePie/Cache/Redis.php (limited to 'app/Models') diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index afcde3d7f..0167695ca 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -157,7 +157,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $info = $this->addEntryPrepared == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $this->addEntryPrepared->errorInfo(); if ($this->autoUpdateDb($info)) { return $this->addEntry($valuesTmp); - } elseif ((int)($info[0] / 1000) !== 23) { //Filter out "SQLSTATE Class code 23: Constraint Violation" because of expected duplicate entries + } elseif ((int)((int)$info[0] / 1000) !== 23) { //Filter out "SQLSTATE Class code 23: Constraint Violation" because of expected duplicate entries Minz_Log::error('SQL error addEntry: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2] . ' while adding entry in feed ' . $valuesTmp['id_feed'] . ' with title: ' . $valuesTmp['title']); } diff --git a/lib/SimplePie/SimplePie.php b/lib/SimplePie/SimplePie.php index 0f2fdbb87..7240fdf66 100644 --- a/lib/SimplePie/SimplePie.php +++ b/lib/SimplePie/SimplePie.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -34,7 +34,7 @@ * * @package SimplePie * @version 1.4-dev-FreshRSS - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue @@ -614,6 +614,12 @@ class SimplePie */ public $item_limit = 0; + /** + * @var bool Stores if last-modified and/or etag headers were sent with the + * request when checking a feed. + */ + public $check_modified = false; + /** * @var array Stores the default attributes to be stripped by strip_attributes(). * @see SimplePie::strip_attributes() @@ -626,7 +632,7 @@ class SimplePie * @see SimplePie::add_attributes() * @access private */ - public $add_attributes = array('audio' => array('preload' => 'none'), 'iframe' => array('sandbox' => 'allow-scripts allow-same-origin'), 'video' => array('preload' => 'none')); //FreshRSS + public $add_attributes = array('audio' => array('preload' => 'none'), 'iframe' => array('sandbox' => 'allow-scripts allow-same-origin'), 'video' => array('preload' => 'none')); /** * @var array Stores the default tags to be stripped by strip_htmltags(). @@ -657,9 +663,9 @@ class SimplePie */ public function __construct() { - if (version_compare(PHP_VERSION, '5.2', '<')) + if (version_compare(PHP_VERSION, '5.3', '<')) { - trigger_error('PHP 4.x, 5.0 and 5.1 are no longer supported. Please upgrade to PHP 5.2 or newer.'); + trigger_error('Please upgrade to PHP 5.3 or newer.'); die(); } @@ -814,7 +820,7 @@ class SimplePie { $this->timeout = (int) $timeout; } - + /** * Set custom curl options * @@ -1169,7 +1175,7 @@ class SimplePie $this->sanitize->strip_attributes($attribs); } - public function add_attributes($attribs = '') //FreshRSS + public function add_attributes($attribs = '') { if ($attribs === '') { @@ -1191,11 +1197,11 @@ class SimplePie * * Allows you to override SimplePie's output to match that of your webpage. * This is useful for times when your webpages are not being served as - * UTF-8. This setting will be obeyed by {@see handle_content_type()}, and + * UTF-8. This setting will be obeyed by {@see handle_content_type()}, and * is similar to {@see set_input_encoding()}. * * It should be noted, however, that not all character encodings can support - * all characters. If your page is being served as ISO-8859-1 and you try + * all characters. If your page is being served as ISO-8859-1 and you try * to display a Japanese feed, you'll likely see garbled characters. * Because of this, it is highly recommended to ensure that your webpages * are served as UTF-8. @@ -1293,7 +1299,7 @@ class SimplePie /** * Initialize the feed object * - * This is what makes everything happen. Period. This is where all of the + * This is what makes everything happen. Period. This is where all of the * configuration options get processed, feeds are fetched, cached, and * parsed, and all of that other good stuff. * @@ -1361,6 +1367,7 @@ class SimplePie $this->error = null; $this->data = array(); + $this->check_modified = false; $this->multifeed_objects = array(); $cache = false; @@ -1380,6 +1387,7 @@ class SimplePie return $this->data['mtime']; } elseif ($fetched === false) { + $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); return false; } @@ -1486,11 +1494,19 @@ class SimplePie if (isset($parser)) { // We have an error, just set SimplePie_Misc::error to it and quit - $this->error = sprintf('This XML document is invalid, likely due to invalid characters. XML error: %s at line %d, column %d, encoding %s, URL: %s', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column(), $encoding, $this->feed_url); + $this->error = $this->feed_url; + $this->error .= sprintf(' is invalid XML, likely due to invalid characters. XML error: %s at line %d, column %d', $parser->get_error_string(), $parser->get_current_line(), $parser->get_current_column()); } else { - $this->error = 'The data could not be converted to UTF-8. You MUST have either the iconv or mbstring extension installed. Upgrading to PHP 5.x (which includes iconv) is highly recommended.'; + $this->error = 'The data could not be converted to UTF-8.'; + if (!extension_loaded('mbstring') && !extension_loaded('iconv')) { + $this->error .= ' You MUST have either the iconv or mbstring extension installed.'; + } elseif (!extension_loaded('mbstring')) { + $this->error .= ' Try installing the mbstring extension.'; + } elseif (!extension_loaded('iconv')) { + $this->error .= ' Try installing the iconv extension.'; + } } $this->registry->call('Misc', 'error', array($this->error, E_USER_NOTICE, __FILE__, __LINE__)); @@ -1575,6 +1591,7 @@ class SimplePie } else { + $this->check_modified = false; $cache->touch(); $this->error = $file->error; return !empty($this->data); @@ -1669,7 +1686,7 @@ class SimplePie $locate = null; } - $file->body = trim($file->body); + $file->body = trim($file->body); //FreshRSS $this->raw_data = $file->body; $this->permanent_url = $file->permanent_url; $headers = $file->headers; @@ -1870,7 +1887,7 @@ class SimplePie * @todo Support * @todo Also, |atom:link|@rel=self * @param bool $permanent Permanent mode to return only the original URL or the first redirection - * iff it is a 301 redirection + * iff it is a 301 redirection * @return string|null */ public function subscribe_url($permanent = false) @@ -2169,7 +2186,7 @@ class SimplePie * Get a category for the feed * * @since Unknown - * @param int $key The category that you want to return. Remember that arrays begin with 0, not 1 + * @param int $key The category that you want to return. Remember that arrays begin with 0, not 1 * @return SimplePie_Category|null */ public function get_category($key = 0) @@ -2254,7 +2271,7 @@ class SimplePie * Get an author for the feed * * @since 1.1 - * @param int $key The author that you want to return. Remember that arrays begin with 0, not 1 + * @param int $key The author that you want to return. Remember that arrays begin with 0, not 1 * @return SimplePie_Author|null */ public function get_author($key = 0) @@ -2352,7 +2369,7 @@ class SimplePie * Get a contributor for the feed * * @since 1.1 - * @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1 + * @param int $key The contrbutor that you want to return. Remember that arrays begin with 0, not 1 * @return SimplePie_Author|null */ public function get_contributor($key = 0) @@ -2438,7 +2455,7 @@ class SimplePie * Get a single link for the feed * * @since 1.0 (previously called `get_feed_link` since Preview Release, `get_feed_permalink()` since 0.8) - * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1 + * @param int $key The link that you want to return. Remember that arrays begin with 0, not 1 * @param string $rel The relationship of the link to return * @return string|null Link URL */ @@ -2949,7 +2966,7 @@ class SimplePie * * @see get_item_quantity() * @since Beta 2 - * @param int $key The item that you want to return. Remember that arrays begin with 0, not 1 + * @param int $key The item that you want to return. Remember that arrays begin with 0, not 1 * @return SimplePie_Item|null */ public function get_item($key = 0) @@ -2976,7 +2993,7 @@ class SimplePie * @since Beta 2 * @param int $start Index to start at * @param int $end Number of items to return. 0 for all items after `$start` - * @return array|null List of {@see SimplePie_Item} objects + * @return SimplePie_Item[]|null List of {@see SimplePie_Item} objects */ public function get_items($start = 0, $end = 0) { @@ -3147,7 +3164,19 @@ class SimplePie */ public static function sort_items($a, $b) { - return $a->get_date('U') <= $b->get_date('U'); + $a_date = $a->get_date('U'); + $b_date = $b->get_date('U'); + if ($a_date && $b_date) { + return $a_date > $b_date ? -1 : 1; + } + // Sort items without dates to the top. + if ($a_date) { + return 1; + } + if ($b_date) { + return -1; + } + return 0; } /** @@ -3180,20 +3209,7 @@ class SimplePie } } - $do_sort = true; - foreach ($items as $item) - { - if (!$item->get_date('U')) - { - $do_sort = false; - break; - } - } - $item = null; - if ($do_sort) - { - usort($items, array(get_class($urls[0]), 'sort_items')); - } + usort($items, array(get_class($urls[0]), 'sort_items')); if ($end === 0) { diff --git a/lib/SimplePie/SimplePie/Author.php b/lib/SimplePie/SimplePie/Author.php index 19563c5cc..e6768ff29 100644 --- a/lib/SimplePie/SimplePie/Author.php +++ b/lib/SimplePie/SimplePie/Author.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Cache.php b/lib/SimplePie/SimplePie/Cache.php index 86b618693..d98cc6511 100644 --- a/lib/SimplePie/SimplePie/Cache.php +++ b/lib/SimplePie/SimplePie/Cache.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue @@ -62,8 +61,10 @@ class SimplePie_Cache * @var array */ protected static $handlers = array( - 'mysql' => 'SimplePie_Cache_MySQL', - 'memcache' => 'SimplePie_Cache_Memcache', + 'mysql' => 'SimplePie_Cache_MySQL', + 'memcache' => 'SimplePie_Cache_Memcache', + 'memcached' => 'SimplePie_Cache_Memcached', + 'redis' => 'SimplePie_Cache_Redis' ); /** diff --git a/lib/SimplePie/SimplePie/Cache/Base.php b/lib/SimplePie/SimplePie/Cache/Base.php index d3f353961..333fb05cf 100644 --- a/lib/SimplePie/SimplePie/Cache/Base.php +++ b/lib/SimplePie/SimplePie/Cache/Base.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Cache/DB.php b/lib/SimplePie/SimplePie/Cache/DB.php index d728a9a6d..7e8f77532 100644 --- a/lib/SimplePie/SimplePie/Cache/DB.php +++ b/lib/SimplePie/SimplePie/Cache/DB.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Cache/File.php b/lib/SimplePie/SimplePie/Cache/File.php index 72e75a4b6..6ba6c5f6e 100644 --- a/lib/SimplePie/SimplePie/Cache/File.php +++ b/lib/SimplePie/SimplePie/Cache/File.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Cache/Memcache.php b/lib/SimplePie/SimplePie/Cache/Memcache.php index 23b1c9367..5190eef93 100644 --- a/lib/SimplePie/SimplePie/Cache/Memcache.php +++ b/lib/SimplePie/SimplePie/Cache/Memcache.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Cache/Memcached.php b/lib/SimplePie/SimplePie/Cache/Memcached.php new file mode 100644 index 000000000..1f73b3890 --- /dev/null +++ b/lib/SimplePie/SimplePie/Cache/Memcached.php @@ -0,0 +1,166 @@ +options = array( + 'host' => '127.0.0.1', + 'port' => 11211, + 'extras' => array( + 'timeout' => 3600, // one hour + 'prefix' => 'simplepie_', + ), + ); + $this->options = SimplePie_Misc::array_merge_recursive($this->options, SimplePie_Cache::parse_URL($location)); + + $this->name = $this->options['extras']['prefix'] . md5("$name:$type"); + + $this->cache = new Memcached(); + $this->cache->addServer($this->options['host'], (int)$this->options['port']); + } + + /** + * Save data to the cache + * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property + * @return bool Successfulness + */ + public function save($data) { + if ($data instanceof SimplePie) { + $data = $data->data; + } + + return $this->setData(serialize($data)); + } + + /** + * Retrieve the data saved to the cache + * @return array Data for SimplePie::$data + */ + public function load() { + $data = $this->cache->get($this->name); + + if ($data !== false) { + return unserialize($data); + } + return false; + } + + /** + * Retrieve the last modified time for the cache + * @return int Timestamp + */ + public function mtime() { + $data = $this->cache->get($this->name . '_mtime'); + return (int) $data; + } + + /** + * Set the last modified time to the current time + * @return bool Success status + */ + public function touch() { + $data = $this->cache->get($this->name); + return $this->setData($data); + } + + /** + * Remove the cache + * @return bool Success status + */ + public function unlink() { + return $this->cache->delete($this->name, 0); + } + + /** + * Set the last modified time and data to Memcached + * @return bool Success status + */ + private function setData($data) { + + if ($data !== false) { + $this->cache->set($this->name . '_mtime', time(), (int)$this->options['extras']['timeout']); + return $this->cache->set($this->name, $data, (int)$this->options['extras']['timeout']); + } + + return false; + } +} diff --git a/lib/SimplePie/SimplePie/Cache/MySQL.php b/lib/SimplePie/SimplePie/Cache/MySQL.php index 511ef6d3a..8686b6c67 100644 --- a/lib/SimplePie/SimplePie/Cache/MySQL.php +++ b/lib/SimplePie/SimplePie/Cache/MySQL.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue @@ -94,6 +93,7 @@ class SimplePie_Cache_MySQL extends SimplePie_Cache_DB 'path' => '', 'extras' => array( 'prefix' => '', + 'cache_purge_time' => 2592000 ), ); @@ -131,16 +131,20 @@ class SimplePie_Cache_MySQL extends SimplePie_Cache_DB $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'cache_data` (`id` TEXT CHARACTER SET utf8 NOT NULL, `items` SMALLINT NOT NULL DEFAULT 0, `data` BLOB NOT NULL, `mtime` INT UNSIGNED NOT NULL, UNIQUE (`id`(125)))'); if ($query === false) { + trigger_error("Can't create " . $this->options['extras']['prefix'] . "cache_data table, check permissions", E_USER_WARNING); $this->mysql = null; + return; } } if (!in_array($this->options['extras']['prefix'] . 'items', $db)) { - $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'items` (`feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` MEDIUMBLOB CHARACTER SET utf8 NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))'); + $query = $this->mysql->exec('CREATE TABLE `' . $this->options['extras']['prefix'] . 'items` (`feed_id` TEXT CHARACTER SET utf8 NOT NULL, `id` TEXT CHARACTER SET utf8 NOT NULL, `data` MEDIUMBLOB NOT NULL, `posted` INT UNSIGNED NOT NULL, INDEX `feed_id` (`feed_id`(125)))'); if ($query === false) { + trigger_error("Can't create " . $this->options['extras']['prefix'] . "items table, check permissions", E_USER_WARNING); $this->mysql = null; + return; } } } @@ -158,6 +162,17 @@ class SimplePie_Cache_MySQL extends SimplePie_Cache_DB return false; } + $query = $this->mysql->prepare('DELETE i, cd FROM `' . $this->options['extras']['prefix'] . 'cache_data` cd, ' . + '`' . $this->options['extras']['prefix'] . 'items` i ' . + 'WHERE cd.id = i.feed_id ' . + 'AND cd.mtime < (unix_timestamp() - :purge_time)'); + $query->bindValue(':purge_time', $this->options['extras']['cache_purge_time']); + + if (!$query->execute()) + { + return false; + } + if ($data instanceof SimplePie) { $data = clone $data; diff --git a/lib/SimplePie/SimplePie/Cache/Redis.php b/lib/SimplePie/SimplePie/Cache/Redis.php new file mode 100644 index 000000000..04d72c79a --- /dev/null +++ b/lib/SimplePie/SimplePie/Cache/Redis.php @@ -0,0 +1,166 @@ + + * @link http://galvani.cz/ + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version 0.2.9 + */ + + +/** + * Caches data to redis + * + * Registered for URLs with the "redis" protocol + * + * For example, `redis://localhost:6379/?timeout=3600&prefix=sp_&dbIndex=0` will + * connect to redis on `localhost` on port 6379. All tables will be + * prefixed with `simple_primary-` and data will expire after 3600 seconds + * + * @package SimplePie + * @subpackage Caching + * @uses Redis + */ +class SimplePie_Cache_Redis implements SimplePie_Cache_Base { + /** + * Redis instance + * + * @var \Redis + */ + protected $cache; + + /** + * Options + * + * @var array + */ + protected $options; + + /** + * Cache name + * + * @var string + */ + protected $name; + + /** + * Cache Data + * + * @var type + */ + protected $data; + + /** + * Create a new cache object + * + * @param string $location Location string (from SimplePie::$cache_location) + * @param string $name Unique ID for the cache + * @param string $type Either TYPE_FEED for SimplePie data, or TYPE_IMAGE for image data + */ + public function __construct($location, $name, $options = null) { + //$this->cache = \flow\simple\cache\Redis::getRedisClientInstance(); + $parsed = SimplePie_Cache::parse_URL($location); + $redis = new Redis(); + $redis->connect($parsed['host'], $parsed['port']); + $this->cache = $redis; + + if (!is_null($options) && is_array($options)) { + $this->options = $options; + } else { + $this->options = array ( + 'prefix' => 'rss:simple_primary:', + 'expire' => 0, + ); + } + + $this->name = $this->options['prefix'] . $name; + } + + /** + * @param \Redis $cache + */ + public function setRedisClient(\Redis $cache) { + $this->cache = $cache; + } + + /** + * Save data to the cache + * + * @param array|SimplePie $data Data to store in the cache. If passed a SimplePie object, only cache the $data property + * @return bool Successfulness + */ + public function save($data) { + if ($data instanceof SimplePie) { + $data = $data->data; + } + $response = $this->cache->set($this->name, serialize($data)); + if ($this->options['expire']) { + $this->cache->expire($this->name, $this->options['expire']); + } + + return $response; + } + + /** + * Retrieve the data saved to the cache + * + * @return array Data for SimplePie::$data + */ + public function load() { + $data = $this->cache->get($this->name); + + if ($data !== false) { + return unserialize($data); + } + return false; + } + + /** + * Retrieve the last modified time for the cache + * + * @return int Timestamp + */ + public function mtime() { + + $data = $this->cache->get($this->name); + + if ($data !== false) { + return time(); + } + + return false; + } + + /** + * Set the last modified time to the current time + * + * @return bool Success status + */ + public function touch() { + + $data = $this->cache->get($this->name); + + if ($data !== false) { + $return = $this->cache->set($this->name, $data); + if ($this->options['expire']) { + return $this->cache->expire($this->name, $this->ttl); + } + return $return; + } + + return false; + } + + /** + * Remove the cache + * + * @return bool Success status + */ + public function unlink() { + return $this->cache->set($this->name, null); + } + +} diff --git a/lib/SimplePie/SimplePie/Caption.php b/lib/SimplePie/SimplePie/Caption.php index a77b02ef1..abf07de1b 100644 --- a/lib/SimplePie/SimplePie/Caption.php +++ b/lib/SimplePie/SimplePie/Caption.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Category.php b/lib/SimplePie/SimplePie/Category.php index c6a273989..92d511e1a 100644 --- a/lib/SimplePie/SimplePie/Category.php +++ b/lib/SimplePie/SimplePie/Category.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Content/Type/Sniffer.php b/lib/SimplePie/SimplePie/Content/Type/Sniffer.php index ec0bf0952..b68b73134 100644 --- a/lib/SimplePie/SimplePie/Content/Type/Sniffer.php +++ b/lib/SimplePie/SimplePie/Content/Type/Sniffer.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Copyright.php b/lib/SimplePie/SimplePie/Copyright.php index 09f22f8df..3f3d07d3b 100644 --- a/lib/SimplePie/SimplePie/Copyright.php +++ b/lib/SimplePie/SimplePie/Copyright.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Core.php b/lib/SimplePie/SimplePie/Core.php index 7cf34876f..c856ba361 100644 --- a/lib/SimplePie/SimplePie/Core.php +++ b/lib/SimplePie/SimplePie/Core.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2009, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Credit.php b/lib/SimplePie/SimplePie/Credit.php index 50aef1c68..9bad9ef34 100644 --- a/lib/SimplePie/SimplePie/Credit.php +++ b/lib/SimplePie/SimplePie/Credit.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Decode/HTML/Entities.php b/lib/SimplePie/SimplePie/Decode/HTML/Entities.php index 46b3a1dff..de3f2cb53 100644 --- a/lib/SimplePie/SimplePie/Decode/HTML/Entities.php +++ b/lib/SimplePie/SimplePie/Decode/HTML/Entities.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Enclosure.php b/lib/SimplePie/SimplePie/Enclosure.php index fa0217800..15060e193 100644 --- a/lib/SimplePie/SimplePie/Enclosure.php +++ b/lib/SimplePie/SimplePie/Enclosure.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue @@ -451,7 +450,7 @@ class SimplePie_Enclosure /** * Get the duration of the enclosure * - * @param string $convert Convert seconds into hh:mm:ss + * @param bool $convert Convert seconds into hh:mm:ss * @return string|int|null 'hh:mm:ss' string if `$convert` was specified, otherwise integer (or null if none found) */ public function get_duration($convert = false) diff --git a/lib/SimplePie/SimplePie/Exception.php b/lib/SimplePie/SimplePie/Exception.php index 73e104d69..53c015e77 100644 --- a/lib/SimplePie/SimplePie/Exception.php +++ b/lib/SimplePie/SimplePie/Exception.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/File.php b/lib/SimplePie/SimplePie/File.php index 45994d102..c1fab42dc 100644 --- a/lib/SimplePie/SimplePie/File.php +++ b/lib/SimplePie/SimplePie/File.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue @@ -64,7 +63,7 @@ class SimplePie_File var $redirects = 0; var $error; var $method = SIMPLEPIE_FILE_SOURCE_NONE; - var $permanent_url; //FreshRSS + var $permanent_url; public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false, $curl_options = array(), $syslog_enabled = SIMPLEPIE_SYSLOG) { @@ -108,6 +107,7 @@ class SimplePie_File curl_setopt($fp, CURLOPT_URL, $url); curl_setopt($fp, CURLOPT_HEADER, 1); curl_setopt($fp, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($fp, CURLOPT_FAILONERROR, 1); curl_setopt($fp, CURLOPT_TIMEOUT, $timeout); curl_setopt($fp, CURLOPT_CONNECTTIMEOUT, $timeout); curl_setopt($fp, CURLOPT_REFERER, $url); @@ -144,7 +144,7 @@ class SimplePie_File if ($parser->parse()) { $this->headers = $parser->headers; - $this->body = $parser->body; + $this->body = trim($parser->body); $this->status_code = $parser->status_code; if ((in_array($this->status_code, array(300, 301, 302, 303, 307)) || $this->status_code > 307 && $this->status_code < 400) && isset($this->headers['location']) && $this->redirects < $redirects) { @@ -237,7 +237,7 @@ class SimplePie_File $location = SimplePie_Misc::absolutize_url($this->headers['location'], $url); $previousStatusCode = $this->status_code; $this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen); - $this->permanent_url = ($previousStatusCode == 301) ? $location : $url; //FreshRSS + $this->permanent_url = ($previousStatusCode == 301) ? $location : $url; return; } if (isset($this->headers['content-encoding'])) @@ -255,7 +255,7 @@ class SimplePie_File } else { - $this->body = $decoder->data; + $this->body = trim($decoder->data); } break; @@ -298,7 +298,7 @@ class SimplePie_File else { $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS; - if (empty($url) || !($this->body = file_get_contents($url))) + if (empty($url) || !($this->body = trim(file_get_contents($url)))) { $this->error = 'file_get_contents could not read the file'; $this->success = false; diff --git a/lib/SimplePie/SimplePie/HTTP/Parser.php b/lib/SimplePie/SimplePie/HTTP/Parser.php index 2fc3a6779..63ae1e03d 100644 --- a/lib/SimplePie/SimplePie/HTTP/Parser.php +++ b/lib/SimplePie/SimplePie/HTTP/Parser.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/IRI.php b/lib/SimplePie/SimplePie/IRI.php index ed0574701..2b3fbaf07 100644 --- a/lib/SimplePie/SimplePie/IRI.php +++ b/lib/SimplePie/SimplePie/IRI.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue @@ -259,6 +258,15 @@ class SimplePie_IRI $this->set_iri($iri); } + /** + * Clean up + */ + public function __destruct() { + $this->set_iri(null, true); + $this->set_path(null, true); + $this->set_authority(null, true); + } + /** * Create a new IRI object by resolving a relative IRI * @@ -768,24 +776,20 @@ class SimplePie_IRI */ public function is_valid() { - $isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null; - if ($this->ipath !== '' && - ( - $isauthority && ( - $this->ipath[0] !== '/' || - substr($this->ipath, 0, 2) === '//' - ) || - ( - $this->scheme === null && - !$isauthority && - strpos($this->ipath, ':') !== false && - (strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/')) - ) - ) - ) - { - return false; - } + if ($this->ipath === '') return true; + + $isauthority = $this->iuserinfo !== null || $this->ihost !== null || + $this->port !== null; + if ($isauthority && $this->ipath[0] === '/') return true; + + if (!$isauthority && (substr($this->ipath, 0, 2) === '//')) return false; + + // Relative urls cannot have a colon in the first path segment (and the + // slashes themselves are not included so skip the first character). + if (!$this->scheme && !$isauthority && + strpos($this->ipath, ':') !== false && + strpos($this->ipath, '/', 1) !== false && + strpos($this->ipath, ':') < strpos($this->ipath, '/', 1)) return false; return true; } @@ -797,9 +801,14 @@ class SimplePie_IRI * @param string $iri * @return bool */ - public function set_iri($iri) + public function set_iri($iri, $clear_cache = false) { static $cache; + if ($clear_cache) + { + $cache = null; + return; + } if (!$cache) { $cache = array(); @@ -879,9 +888,14 @@ class SimplePie_IRI * @param string $authority * @return bool */ - public function set_authority($authority) + public function set_authority($authority, $clear_cache = false) { static $cache; + if ($clear_cache) + { + $cache = null; + return; + } if (!$cache) $cache = array(); @@ -1049,9 +1063,14 @@ class SimplePie_IRI * @param string $ipath * @return bool */ - public function set_path($ipath) + public function set_path($ipath, $clear_cache = false) { static $cache; + if ($clear_cache) + { + $cache = null; + return; + } if (!$cache) { $cache = array(); diff --git a/lib/SimplePie/SimplePie/Item.php b/lib/SimplePie/SimplePie/Item.php index 19ba7c8f4..daecf0a15 100644 --- a/lib/SimplePie/SimplePie/Item.php +++ b/lib/SimplePie/SimplePie/Item.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue @@ -203,14 +202,13 @@ class SimplePie_Item * * Uses ``, ``, `` or the `about` attribute * for RDF. If none of these are supplied (or `$hash` is true), creates an - * MD5 hash based on the permalink and title. If either of those are not - * supplied, creates a hash based on the full feed data. + * MD5 hash based on the permalink, title and content. * * @since Beta 2 * @param boolean $hash Should we force using a hash instead of the supplied ID? * @return string */ - public function get_id($hash = false) + public function get_id($hash = false, $fn = '') { if (!$hash) { @@ -238,23 +236,10 @@ class SimplePie_Item { return $this->sanitize($this->data['attribs'][SIMPLEPIE_NAMESPACE_RDF]['about'], SIMPLEPIE_CONSTRUCT_TEXT); } - elseif (($return = $this->get_permalink()) !== null) - { - return $return; - } - elseif (($return = $this->get_title()) !== null) - { - return $return; - } - } - if ($this->get_permalink() !== null || $this->get_title() !== null) - { - return md5($this->get_permalink() . $this->get_title()); - } - else - { - return md5(serialize($this->data)); } + if ($fn === '' || !is_callable($fn)) $fn = 'md5'; + return call_user_func($fn, + $this->get_permalink().$this->get_title().$this->get_content()); } /** @@ -457,7 +442,7 @@ class SimplePie_Item * Uses ``, `` or `` * * @since Beta 3 - * @return array|null List of {@see SimplePie_Category} objects + * @return SimplePie_Category[]|null List of {@see SimplePie_Category} objects */ public function get_categories() { @@ -1105,7 +1090,7 @@ class SimplePie_Item * @since Beta 2 * @todo Add support for end-user defined sorting of enclosures by type/handler (so we can prefer the faster-loading FLV over MP4). * @todo If an element exists at a level, but its value is empty, we should fall back to the value from the parent (if it exists). - * @return array|null List of SimplePie_Enclosure items + * @return SimplePie_Enclosure[]|null List of SimplePie_Enclosure items */ public function get_enclosures() { @@ -2682,7 +2667,9 @@ class SimplePie_Item // PLAYER if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'])) { - $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + if (isset($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'])) { + $player = $this->sanitize($content['child'][SIMPLEPIE_NAMESPACE_MEDIARSS]['player'][0]['attribs']['']['url'], SIMPLEPIE_CONSTRUCT_IRI); + } } else { diff --git a/lib/SimplePie/SimplePie/Locator.php b/lib/SimplePie/SimplePie/Locator.php index ba4a843b0..36bc02895 100644 --- a/lib/SimplePie/SimplePie/Locator.php +++ b/lib/SimplePie/SimplePie/Locator.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue @@ -226,7 +225,7 @@ class SimplePie_Locator } if ($link->hasAttribute('href') && $link->hasAttribute('rel')) { - $rel = array_unique($this->registry->call('Misc', 'space_seperated_tokens', array(strtolower($link->getAttribute('rel'))))); + $rel = array_unique($this->registry->call('Misc', 'space_separated_tokens', array(strtolower($link->getAttribute('rel'))))); $line = method_exists($link, 'getLineNo') ? $link->getLineNo() : 1; if ($this->base_location < $line) @@ -275,7 +274,7 @@ class SimplePie_Locator { $href = trim($link->getAttribute('href')); $parsed = $this->registry->call('Misc', 'parse_url', array($href)); - if ($parsed['scheme'] === '' || preg_match('/^(http(s)|feed)?$/i', $parsed['scheme'])) + if ($parsed['scheme'] === '' || preg_match('/^(https?|feed)?$/i', $parsed['scheme'])) { if (method_exists($link, 'getLineNo') && $this->base_location < $link->getLineNo()) { @@ -312,6 +311,57 @@ class SimplePie_Locator return null; } + public function get_rel_link($rel) + { + if ($this->dom === null) + { + throw new SimplePie_Exception('DOMDocument not found, unable to use '. + 'locator'); + } + if (!class_exists('DOMXpath')) + { + throw new SimplePie_Exception('DOMXpath not found, unable to use '. + 'get_rel_link'); + } + + $xpath = new DOMXpath($this->dom); + $query = '//a[@rel and @href] | //link[@rel and @href]'; + foreach ($xpath->query($query) as $link) + { + $href = trim($link->getAttribute('href')); + $parsed = $this->registry->call('Misc', 'parse_url', array($href)); + if ($parsed['scheme'] === '' || + preg_match('/^https?$/i', $parsed['scheme'])) + { + if (method_exists($link, 'getLineNo') && + $this->base_location < $link->getLineNo()) + { + $href = + $this->registry->call('Misc', 'absolutize_url', + array(trim($link->getAttribute('href')), + $this->base)); + } + else + { + $href = + $this->registry->call('Misc', 'absolutize_url', + array(trim($link->getAttribute('href')), + $this->http_base)); + } + if ($href === false) + { + return null; + } + $rel_values = explode(' ', strtolower($link->getAttribute('rel'))); + if (in_array($rel, $rel_values)) + { + return $href; + } + } + } + return null; + } + public function extension(&$array) { foreach ($array as $key => $value) diff --git a/lib/SimplePie/SimplePie/Misc.php b/lib/SimplePie/SimplePie/Misc.php index 2d154cbcb..ca2810611 100644 --- a/lib/SimplePie/SimplePie/Misc.php +++ b/lib/SimplePie/SimplePie/Misc.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue @@ -1947,7 +1946,7 @@ class SimplePie_Misc return (bool) preg_match('/^([A-Za-z0-9\-._~\x{A0}-\x{D7FF}\x{F900}-\x{FDCF}\x{FDF0}-\x{FFEF}\x{10000}-\x{1FFFD}\x{20000}-\x{2FFFD}\x{30000}-\x{3FFFD}\x{40000}-\x{4FFFD}\x{50000}-\x{5FFFD}\x{60000}-\x{6FFFD}\x{70000}-\x{7FFFD}\x{80000}-\x{8FFFD}\x{90000}-\x{9FFFD}\x{A0000}-\x{AFFFD}\x{B0000}-\x{BFFFD}\x{C0000}-\x{CFFFD}\x{D0000}-\x{DFFFD}\x{E1000}-\x{EFFFD}!$&\'()*+,;=@]|(%[0-9ABCDEF]{2}))+$/u', $string); } - public static function space_seperated_tokens($string) + public static function space_separated_tokens($string) { $space_characters = "\x20\x09\x0A\x0B\x0C\x0D"; $string_length = strlen($string); @@ -2178,7 +2177,8 @@ function embed_wmedia(width, height, link) { /** * Get the SimplePie build timestamp * - * Return SimplePie.php modification time. + * Uses the git index if it exists, otherwise uses the modification time + * of the newest file. */ public static function get_build() { diff --git a/lib/SimplePie/SimplePie/Net/IPv6.php b/lib/SimplePie/SimplePie/Net/IPv6.php index 2ff1afc90..47658aff2 100644 --- a/lib/SimplePie/SimplePie/Net/IPv6.php +++ b/lib/SimplePie/SimplePie/Net/IPv6.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Parse/Date.php b/lib/SimplePie/SimplePie/Parse/Date.php index 50bb5cffa..1f2156655 100644 --- a/lib/SimplePie/SimplePie/Parse/Date.php +++ b/lib/SimplePie/SimplePie/Parse/Date.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue @@ -331,8 +330,8 @@ class SimplePie_Parse_Date 'CCT' => 23400, 'CDT' => -18000, 'CEDT' => 7200, - 'CET' => 3600, 'CEST' => 7200, + 'CET' => 3600, 'CGST' => -7200, 'CGT' => -10800, 'CHADT' => 49500, @@ -631,7 +630,7 @@ class SimplePie_Parse_Date /** * Parse a superset of W3C-DTF (allows hyphens and colons to be omitted, as * well as allowing any of upper or lower case "T", horizontal tabs, or - * spaces to be used as the time seperator (including more than one)) + * spaces to be used as the time separator (including more than one)) * * @access protected * @return int Timestamp @@ -691,7 +690,7 @@ class SimplePie_Parse_Date } // Convert the number of seconds to an integer, taking decimals into account - $second = round($match[6] + $match[7] / pow(10, strlen($match[7]))); + $second = round((int)$match[6] + (int)$match[7] / pow(10, strlen($match[7]))); return gmmktime($match[4], $match[5], $second, $match[2], $match[3], $match[1]) - $timezone; } diff --git a/lib/SimplePie/SimplePie/Parser.php b/lib/SimplePie/SimplePie/Parser.php index 7fb7bd9be..e3966218c 100644 --- a/lib/SimplePie/SimplePie/Parser.php +++ b/lib/SimplePie/SimplePie/Parser.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Rating.php b/lib/SimplePie/SimplePie/Rating.php index b5fe80516..eaf57080c 100644 --- a/lib/SimplePie/SimplePie/Rating.php +++ b/lib/SimplePie/SimplePie/Rating.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Registry.php b/lib/SimplePie/SimplePie/Registry.php index dac55e34e..e0909bb74 100755 --- a/lib/SimplePie/SimplePie/Registry.php +++ b/lib/SimplePie/SimplePie/Registry.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Restriction.php b/lib/SimplePie/SimplePie/Restriction.php index a1d59916d..001a5cd28 100644 --- a/lib/SimplePie/SimplePie/Restriction.php +++ b/lib/SimplePie/SimplePie/Restriction.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/Sanitize.php b/lib/SimplePie/SimplePie/Sanitize.php index bdc601100..49fe5dbd5 100644 --- a/lib/SimplePie/SimplePie/Sanitize.php +++ b/lib/SimplePie/SimplePie/Sanitize.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue @@ -348,6 +347,7 @@ class SimplePie_Sanitize } $document = new DOMDocument(); $document->encoding = 'UTF-8'; + $data = $this->preprocess($data, $type); set_error_handler(array('SimplePie_Misc', 'silence_errors')); diff --git a/lib/SimplePie/SimplePie/Source.php b/lib/SimplePie/SimplePie/Source.php index 2613798fd..1a66a392d 100644 --- a/lib/SimplePie/SimplePie/Source.php +++ b/lib/SimplePie/SimplePie/Source.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/XML/Declaration/Parser.php b/lib/SimplePie/SimplePie/XML/Declaration/Parser.php index 589e452a2..99e751672 100644 --- a/lib/SimplePie/SimplePie/XML/Declaration/Parser.php +++ b/lib/SimplePie/SimplePie/XML/Declaration/Parser.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue diff --git a/lib/SimplePie/SimplePie/gzdecode.php b/lib/SimplePie/SimplePie/gzdecode.php index 6e65f0811..0e8bc8fc6 100644 --- a/lib/SimplePie/SimplePie/gzdecode.php +++ b/lib/SimplePie/SimplePie/gzdecode.php @@ -5,7 +5,7 @@ * A PHP-Based RSS and Atom Feed Framework. * Takes the hard work out of managing a complete RSS/Atom solution. * - * Copyright (c) 2004-2012, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors + * Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are @@ -33,8 +33,7 @@ * POSSIBILITY OF SUCH DAMAGE. * * @package SimplePie - * @version 1.4-dev - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue + * @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue * @author Ryan Parman * @author Geoffrey Sneddon * @author Ryan McCue -- cgit v1.2.3 From 7a6751b50d9c809d2127a154bf811f576a24e4a4 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 26 Mar 2017 18:48:34 +0200 Subject: PDO fix PHP 7.1 http://php.net/manual/migration71.changed-functions.php#migration71.changed-functions.pdo --- app/Controllers/importExportController.php | 9 ++++----- app/Models/EntryDAO.php | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'app/Models') diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 6ae89defb..af3c0bf46 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -464,15 +464,14 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } $values = $entry->toArray(); + $ok = false; if (isset($existingHashForGuids[$entry->guid()])) { - $id = $this->entryDAO->updateEntry($values); + $ok = $this->entryDAO->updateEntry($values); } else { - $id = $this->entryDAO->addEntry($values); + $ok = $this->entryDAO->addEntry($values); } + $error |= ($ok === false); - if (!$error && ($id === false)) { - $error = true; - } } $this->entryDAO->commit(); diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 0167695ca..96790c69c 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -152,7 +152,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } if ($this->addEntryPrepared && $this->addEntryPrepared->execute()) { - return $this->bd->lastInsertId(); + return true; } else { $info = $this->addEntryPrepared == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $this->addEntryPrepared->errorInfo(); if ($this->autoUpdateDb($info)) { @@ -212,7 +212,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } if ($this->updateEntryPrepared && $this->updateEntryPrepared->execute()) { - return $this->bd->lastInsertId(); + return true; } else { $info = $this->updateEntryPrepared == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $this->updateEntryPrepared->errorInfo(); if ($this->autoUpdateDb($info)) { -- cgit v1.2.3 From d9c0d25b85ef3df7ea2cdc261e274efcdd5cfce0 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 1 Apr 2017 22:31:12 +0200 Subject: Improve search: intitle, author, inurl Allow multiple values of intitle: , author:, inurl: Note: Tests for UserQueryTest are broken due to https://github.com/sebastianbergmann/phpunit/wiki/Release-Announcement-for-PHPUnit-4.0.0#backwards-compatibility-issues --- app/Models/EntryDAO.php | 21 ++++-- app/Models/Search.php | 60 +++++++++------ tests/README.md | 7 ++ tests/app/Models/CategoryTest.php | 2 +- tests/app/Models/ContextTest.php | 5 -- tests/app/Models/SearchTest.php | 149 +++++++++++++++++++------------------ tests/app/Models/UserQueryTest.php | 2 +- 7 files changed, 134 insertions(+), 112 deletions(-) create mode 100644 tests/README.md delete mode 100644 tests/app/Models/ContextTest.php (limited to 'app/Models') diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 61ec48d08..510755a2f 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -631,16 +631,22 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } if ($filter) { if ($filter->getIntitle()) { - $search .= 'AND ' . $alias . 'title LIKE ? '; - $values[] = "%{$filter->getIntitle()}%"; + foreach ($filter->getIntitle() as $title) { + $search .= 'AND ' . $alias . 'title LIKE ? '; + $values[] = "%{$title}%"; + } } if ($filter->getInurl()) { - $search .= 'AND CONCAT(' . $alias . 'link, ' . $alias . 'guid) LIKE ? '; - $values[] = "%{$filter->getInurl()}%"; + foreach ($filter->getInurl() as $url) { + $search .= 'AND CONCAT(' . $alias . 'link, ' . $alias . 'guid) LIKE ? '; + $values[] = "%{$url}%"; + } } if ($filter->getAuthor()) { - $search .= 'AND ' . $alias . 'author LIKE ? '; - $values[] = "%{$filter->getAuthor()}%"; + foreach ($filter->getAuthor() as $author) { + $search .= 'AND ' . $alias . 'author LIKE ? '; + $values[] = "%{$author}%"; + } } if ($filter->getMinDate()) { $search .= 'AND ' . $alias . 'id >= ? '; @@ -659,8 +665,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $values[] = $filter->getMaxPubdate(); } if ($filter->getTags()) { - $tags = $filter->getTags(); - foreach ($tags as $tag) { + foreach ($filter->getTags() as $tag) { $search .= 'AND ' . $alias . 'tags LIKE ? '; $values[] = "%{$tag}%"; } diff --git a/app/Models/Search.php b/app/Models/Search.php index 575a9a2cb..7b801f40b 100644 --- a/app/Models/Search.php +++ b/app/Models/Search.php @@ -81,6 +81,10 @@ class FreshRSS_Search { return $this->search; } + private static function removeEmptyValues($anArray) { + return is_array($anArray) ? array_filter($anArray, function($value) { return $value !== ''; }) : array(); + } + /** * Parse the search string to find intitle keyword and the search related * to it. @@ -90,14 +94,15 @@ class FreshRSS_Search { * @return string */ private function parseIntitleSearch($input) { - if (preg_match('/intitle:(?P[\'"])(?P.*)(?P=delim)/U', $input, $matches)) { + if (preg_match_all('/intitle:(?P[\'"])(?P.*)(?P=delim)/U', $input, $matches)) { $this->intitle = $matches['search']; - return str_replace($matches[0], '', $input); + $input = str_replace($matches[0], '', $input); } - if (preg_match('/intitle:(?P\w*)/', $input, $matches)) { - $this->intitle = $matches['search']; - return str_replace($matches[0], '', $input); + if (preg_match_all('/intitle:(?P\w*)/', $input, $matches)) { + $this->intitle = array_merge($this->intitle ? $this->intitle : array(), $matches['search']); + $input = str_replace($matches[0], '', $input); } + $this->intitle = self::removeEmptyValues($this->intitle); return $input; } @@ -112,30 +117,32 @@ class FreshRSS_Search { * @return string */ private function parseAuthorSearch($input) { - if (preg_match('/author:(?P[\'"])(?P.*)(?P=delim)/U', $input, $matches)) { + if (preg_match_all('/author:(?P[\'"])(?P.*)(?P=delim)/U', $input, $matches)) { $this->author = $matches['search']; - return str_replace($matches[0], '', $input); + $input = str_replace($matches[0], '', $input); } - if (preg_match('/author:(?P\w*)/', $input, $matches)) { - $this->author = $matches['search']; - return str_replace($matches[0], '', $input); + if (preg_match_all('/author:(?P\w*)/', $input, $matches)) { + $this->author = array_merge($this->author ? $this->author : array(), $matches['search']); + $input = str_replace($matches[0], '', $input); } + $this->author = self::removeEmptyValues($this->author); return $input; } /** * Parse the search string to find inurl keyword and the search related * to it. - * The search is the first word following the keyword except. + * The search is the first word following the keyword. * * @param string $input * @return string */ private function parseInurlSearch($input) { - if (preg_match('/inurl:(?P[^\s]*)/', $input, $matches)) { + if (preg_match_all('/inurl:(?P[^\s]*)/', $input, $matches)) { $this->inurl = $matches['search']; - return str_replace($matches[0], '', $input); + $input = str_replace($matches[0], '', $input); } + $this->inurl = self::removeEmptyValues($this->inurl); return $input; } @@ -148,9 +155,12 @@ class FreshRSS_Search { * @return string */ private function parseDateSearch($input) { - if (preg_match('/date:(?P[^\s]*)/', $input, $matches)) { - list($this->min_date, $this->max_date) = parseDateInterval($matches['search']); - return str_replace($matches[0], '', $input); + if (preg_match_all('/date:(?P[^\s]*)/', $input, $matches)) { + $input = str_replace($matches[0], '', $input); + $dates = self::removeEmptyValues($matches['search']); + if (!empty($dates[0])) { + list($this->min_date, $this->max_date) = parseDateInterval($dates[0]); + } } return $input; } @@ -164,9 +174,12 @@ class FreshRSS_Search { * @return string */ private function parsePubdateSearch($input) { - if (preg_match('/pubdate:(?P[^\s]*)/', $input, $matches)) { - list($this->min_pubdate, $this->max_pubdate) = parseDateInterval($matches['search']); - return str_replace($matches[0], '', $input); + if (preg_match_all('/pubdate:(?P[^\s]*)/', $input, $matches)) { + $input = str_replace($matches[0], '', $input); + $dates = self::removeEmptyValues($matches['search']); + if (!empty($dates[0])) { + list($this->min_pubdate, $this->max_pubdate) = parseDateInterval($dates[0]); + } } return $input; } @@ -182,8 +195,9 @@ class FreshRSS_Search { private function parseTagsSeach($input) { if (preg_match_all('/#(?P[^\s]+)/', $input, $matches)) { $this->tags = $matches['search']; - return str_replace($matches[0], '', $input); + $input = str_replace($matches[0], '', $input); } + $this->tags = self::removeEmptyValues($this->tags); return $input; } @@ -196,7 +210,7 @@ class FreshRSS_Search { * @return string */ private function parseSearch($input) { - $input = $this->cleanSearch($input); + $input = self::cleanSearch($input); if (strcmp($input, '') == 0) { return; } @@ -204,7 +218,7 @@ class FreshRSS_Search { $this->search = $matches['search']; $input = str_replace($matches[0], '', $input); } - $input = $this->cleanSearch($input); + $input = self::cleanSearch($input); if (strcmp($input, '') == 0) { return; } @@ -221,7 +235,7 @@ class FreshRSS_Search { * @param string $input * @return string */ - private function cleanSearch($input) { + private static function cleanSearch($input) { $input = preg_replace('/\s+/', ' ', $input); return trim($input); } diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 000000000..3dd9602be --- /dev/null +++ b/tests/README.md @@ -0,0 +1,7 @@ +# FreshRSS tests + +```sh +cd ./tests/ +wget https://phar.phpunit.de/phpunit.phar +php phpunit.phar --bootstrap bootstrap.php +``` diff --git a/tests/app/Models/CategoryTest.php b/tests/app/Models/CategoryTest.php index da439b785..2fd153aee 100644 --- a/tests/app/Models/CategoryTest.php +++ b/tests/app/Models/CategoryTest.php @@ -1,6 +1,6 @@ 'a'); -- cgit v1.2.3 From aadba9fb707d2d2bc6c05a84bb3a823895531137 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sat, 1 Apr 2017 23:41:19 +0200 Subject: Negative searches https://github.com/FreshRSS/FreshRSS/issues/1381 Possibility to exclude authors, titles, tags, urls, words by prefixing them by ! or - (like Google Search): * !intitle:unwanted * -intitle:unwanted * -author:unwanted * -#unwanted * -unwanted And one can use many of each and combine them with positive searches --- app/Models/EntryDAO.php | 72 +++++++++++++++++++++-------- app/Models/Search.php | 120 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 159 insertions(+), 33 deletions(-) (limited to 'app/Models') diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 510755a2f..7e836097a 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -630,24 +630,6 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $search .= 'AND ' . $alias . 'id >= ' . $date_min . '000000 '; } if ($filter) { - if ($filter->getIntitle()) { - foreach ($filter->getIntitle() as $title) { - $search .= 'AND ' . $alias . 'title LIKE ? '; - $values[] = "%{$title}%"; - } - } - if ($filter->getInurl()) { - foreach ($filter->getInurl() as $url) { - $search .= 'AND CONCAT(' . $alias . 'link, ' . $alias . 'guid) LIKE ? '; - $values[] = "%{$url}%"; - } - } - if ($filter->getAuthor()) { - foreach ($filter->getAuthor() as $author) { - $search .= 'AND ' . $alias . 'author LIKE ? '; - $values[] = "%{$author}%"; - } - } if ($filter->getMinDate()) { $search .= 'AND ' . $alias . 'id >= ? '; $values[] = "{$filter->getMinDate()}000000"; @@ -664,19 +646,69 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $search .= 'AND ' . $alias . 'date <= ? '; $values[] = $filter->getMaxPubdate(); } + + if ($filter->getAuthor()) { + foreach ($filter->getAuthor() as $author) { + $search .= 'AND ' . $alias . 'author LIKE ? '; + $values[] = "%{$author}%"; + } + } + if ($filter->getIntitle()) { + foreach ($filter->getIntitle() as $title) { + $search .= 'AND ' . $alias . 'title LIKE ? '; + $values[] = "%{$title}%"; + } + } if ($filter->getTags()) { foreach ($filter->getTags() as $tag) { $search .= 'AND ' . $alias . 'tags LIKE ? '; $values[] = "%{$tag}%"; } } + if ($filter->getInurl()) { + foreach ($filter->getInurl() as $url) { + $search .= 'AND CONCAT(' . $alias . 'link, ' . $alias . 'guid) LIKE ? '; + $values[] = "%{$url}%"; + } + } + + if ($filter->getNotAuthor()) { + foreach ($filter->getNotAuthor() as $author) { + $search .= 'AND (NOT ' . $alias . 'author LIKE ?) '; + $values[] = "%{$author}%"; + } + } + if ($filter->getNotIntitle()) { + foreach ($filter->getNotIntitle() as $title) { + $search .= 'AND (NOT ' . $alias . 'title LIKE ?) '; + $values[] = "%{$title}%"; + } + } + if ($filter->getNotTags()) { + foreach ($filter->getNotTags() as $tag) { + $search .= 'AND (NOT ' . $alias . 'tags LIKE ?) '; + $values[] = "%{$tag}%"; + } + } + if ($filter->getNotInurl()) { + foreach ($filter->getNotInurl() as $url) { + $search .= 'AND (NOT CONCAT(' . $alias . 'link, ' . $alias . 'guid) LIKE ?) '; + $values[] = "%{$url}%"; + } + } + if ($filter->getSearch()) { - $search_values = $filter->getSearch(); - foreach ($search_values as $search_value) { + foreach ($filter->getSearch() as $search_value) { $search .= 'AND ' . $this->sqlconcat($alias . 'title', $this->isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ? '; $values[] = "%{$search_value}%"; } } + if ($filter->getNotSearch()) { + foreach ($filter->getNotSearch() as $search_value) { + $search .= 'AND (NOT ' . $this->sqlconcat($alias . 'title', $this->isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ?) '; + $values[] = "%{$search_value}%"; + } + } } return array($values, $search); } diff --git a/app/Models/Search.php b/app/Models/Search.php index 7b801f40b..2e89ea05b 100644 --- a/app/Models/Search.php +++ b/app/Models/Search.php @@ -23,18 +23,33 @@ class FreshRSS_Search { private $tags; private $search; + private $not_intitle; + private $not_inurl; + private $not_author; + private $not_tags; + private $not_search; + public function __construct($input) { - if (strcmp($input, '') == 0) { + if ($input == '') { return; } $this->raw_input = $input; + + $input = $this->parseNotIntitleSearch($input); + $input = $this->parseNotAuthorSearch($input); + $input = $this->parseNotInurlSearch($input); + $input = $this->parseNotTagsSeach($input); + + $input = $this->parsePubdateSearch($input); + $input = $this->parseDateSearch($input); + $input = $this->parseIntitleSearch($input); $input = $this->parseAuthorSearch($input); $input = $this->parseInurlSearch($input); - $input = $this->parsePubdateSearch($input); - $input = $this->parseDateSearch($input); $input = $this->parseTagsSeach($input); - $this->parseSearch($input); + + $input = $this->parseNotSearch($input); + $input = $this->parseSearch($input); } public function __toString() { @@ -48,6 +63,9 @@ class FreshRSS_Search { public function getIntitle() { return $this->intitle; } + public function getNotIntitle() { + return $this->not_intitle; + } public function getMinDate() { return $this->min_date; @@ -68,18 +86,30 @@ class FreshRSS_Search { public function getInurl() { return $this->inurl; } + public function getNotInurl() { + return $this->not_inurl; + } public function getAuthor() { return $this->author; } + public function getNotAuthor() { + return $this->not_author; + } public function getTags() { return $this->tags; } + public function getNotTags() { + return $this->not_tags; + } public function getSearch() { return $this->search; } + public function getNotSearch() { + return $this->not_search; + } private static function removeEmptyValues($anArray) { return is_array($anArray) ? array_filter($anArray, function($value) { return $value !== ''; }) : array(); @@ -94,11 +124,11 @@ class FreshRSS_Search { * @return string */ private function parseIntitleSearch($input) { - if (preg_match_all('/intitle:(?P[\'"])(?P.*)(?P=delim)/U', $input, $matches)) { + if (preg_match_all('/\bintitle:(?P[\'"])(?P.*)(?P=delim)/U', $input, $matches)) { $this->intitle = $matches['search']; $input = str_replace($matches[0], '', $input); } - if (preg_match_all('/intitle:(?P\w*)/', $input, $matches)) { + if (preg_match_all('/\bintitle:(?P\w*)/', $input, $matches)) { $this->intitle = array_merge($this->intitle ? $this->intitle : array(), $matches['search']); $input = str_replace($matches[0], '', $input); } @@ -106,6 +136,19 @@ class FreshRSS_Search { return $input; } + private function parseNotIntitleSearch($input) { + if (preg_match_all('/[!-]intitle:(?P[\'"])(?P.*)(?P=delim)/U', $input, $matches)) { + $this->not_intitle = $matches['search']; + $input = str_replace($matches[0], '', $input); + } + if (preg_match_all('/[!-]intitle:(?P\w*)/', $input, $matches)) { + $this->not_intitle = array_merge($this->not_intitle ? $this->not_intitle : array(), $matches['search']); + $input = str_replace($matches[0], '', $input); + } + $this->not_intitle = self::removeEmptyValues($this->not_intitle); + return $input; + } + /** * Parse the search string to find author keyword and the search related * to it. @@ -117,11 +160,11 @@ class FreshRSS_Search { * @return string */ private function parseAuthorSearch($input) { - if (preg_match_all('/author:(?P[\'"])(?P.*)(?P=delim)/U', $input, $matches)) { + if (preg_match_all('/\bauthor:(?P[\'"])(?P.*)(?P=delim)/U', $input, $matches)) { $this->author = $matches['search']; $input = str_replace($matches[0], '', $input); } - if (preg_match_all('/author:(?P\w*)/', $input, $matches)) { + if (preg_match_all('/\bauthor:(?P\w*)/', $input, $matches)) { $this->author = array_merge($this->author ? $this->author : array(), $matches['search']); $input = str_replace($matches[0], '', $input); } @@ -129,6 +172,19 @@ class FreshRSS_Search { return $input; } + private function parseNotAuthorSearch($input) { + if (preg_match_all('/[!-]author:(?P[\'"])(?P.*)(?P=delim)/U', $input, $matches)) { + $this->not_author = $matches['search']; + $input = str_replace($matches[0], '', $input); + } + if (preg_match_all('/[!-]author:(?P\w*)/', $input, $matches)) { + $this->not_author = array_merge($this->not_author ? $this->not_author : array(), $matches['search']); + $input = str_replace($matches[0], '', $input); + } + $this->not_author = self::removeEmptyValues($this->not_author); + return $input; + } + /** * Parse the search string to find inurl keyword and the search related * to it. @@ -138,7 +194,7 @@ class FreshRSS_Search { * @return string */ private function parseInurlSearch($input) { - if (preg_match_all('/inurl:(?P[^\s]*)/', $input, $matches)) { + if (preg_match_all('/\binurl:(?P[^\s]*)/', $input, $matches)) { $this->inurl = $matches['search']; $input = str_replace($matches[0], '', $input); } @@ -146,6 +202,15 @@ class FreshRSS_Search { return $input; } + private function parseNotInurlSearch($input) { + if (preg_match_all('/[!-]inurl:(?P[^\s]*)/', $input, $matches)) { + $this->not_inurl = $matches['search']; + $input = str_replace($matches[0], '', $input); + } + $this->not_inurl = self::removeEmptyValues($this->not_inurl); + return $input; + } + /** * Parse the search string to find date keyword and the search related * to it. @@ -155,7 +220,7 @@ class FreshRSS_Search { * @return string */ private function parseDateSearch($input) { - if (preg_match_all('/date:(?P[^\s]*)/', $input, $matches)) { + if (preg_match_all('/\bdate:(?P[^\s]*)/', $input, $matches)) { $input = str_replace($matches[0], '', $input); $dates = self::removeEmptyValues($matches['search']); if (!empty($dates[0])) { @@ -174,7 +239,7 @@ class FreshRSS_Search { * @return string */ private function parsePubdateSearch($input) { - if (preg_match_all('/pubdate:(?P[^\s]*)/', $input, $matches)) { + if (preg_match_all('/\bpubdate:(?P[^\s]*)/', $input, $matches)) { $input = str_replace($matches[0], '', $input); $dates = self::removeEmptyValues($matches['search']); if (!empty($dates[0])) { @@ -201,6 +266,15 @@ class FreshRSS_Search { return $input; } + private function parseNotTagsSeach($input) { + if (preg_match_all('/[!-]#(?P[^\s]+)/', $input, $matches)) { + $this->not_tags = $matches['search']; + $input = str_replace($matches[0], '', $input); + } + $this->not_tags = self::removeEmptyValues($this->not_tags); + return $input; + } + /** * Parse the search string to find search values. * Every word is a distinct search value, except when using a delimiter. @@ -211,7 +285,7 @@ class FreshRSS_Search { */ private function parseSearch($input) { $input = self::cleanSearch($input); - if (strcmp($input, '') == 0) { + if ($input == '') { return; } if (preg_match_all('/(?P[\'"])(?P.*)(?P=delim)/U', $input, $matches)) { @@ -219,7 +293,7 @@ class FreshRSS_Search { $input = str_replace($matches[0], '', $input); } $input = self::cleanSearch($input); - if (strcmp($input, '') == 0) { + if ($input == '') { return; } if (is_array($this->search)) { @@ -229,6 +303,26 @@ class FreshRSS_Search { } } + private function parseNotSearch($input) { + $input = self::cleanSearch($input); + if ($input == '') { + return; + } + if (preg_match_all('/[!-](?P[\'"])(?P.*)(?P=delim)/U', $input, $matches)) { + $this->not_search = $matches['search']; + $input = str_replace($matches[0], '', $input); + } + if ($input == '') { + return; + } + if (preg_match_all('/[!-](?P[^\s]+)/', $input, $matches)) { + $this->not_search = array_merge(is_array($this->not_search) ? $this->not_search : array(), $matches['search']); + $input = str_replace($matches[0], '', $input); + } + $this->not_search = self::removeEmptyValues($this->not_search); + return $input; + } + /** * Remove all unnecessary spaces in the search * -- cgit v1.2.3 From 3011bbc5e1360a6b01f3ea652eae509a0bb5f164 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 2 Apr 2017 11:55:20 +0200 Subject: Search allow double quotes `author:"some name"` --- app/Models/Search.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'app/Models') diff --git a/app/Models/Search.php b/app/Models/Search.php index 2e89ea05b..5cc7f8e8d 100644 --- a/app/Models/Search.php +++ b/app/Models/Search.php @@ -35,6 +35,8 @@ class FreshRSS_Search { } $this->raw_input = $input; + $input = preg_replace('/:"(.*?)"/', ':"\1"', $input); + $input = $this->parseNotIntitleSearch($input); $input = $this->parseNotAuthorSearch($input); $input = $this->parseNotInurlSearch($input); -- cgit v1.2.3 From d42a20506ba07ec72d2d618db095ec853e51f9c1 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 5 Apr 2017 14:44:18 +0200 Subject: Fallback when GUID is empty https://github.com/FreshRSS/FreshRSS/issues/1482 --- app/Models/Entry.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'app/Models') diff --git a/app/Models/Entry.php b/app/Models/Entry.php index a562a963a..2946e839d 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -22,7 +22,6 @@ class FreshRSS_Entry extends Minz_Model { public function __construct($feed = '', $guid = '', $title = '', $author = '', $content = '', $link = '', $pubdate = 0, $is_read = false, $is_favorite = false, $tags = '') { - $this->_guid($guid); $this->_title($title); $this->_author($author); $this->_content($content); @@ -32,6 +31,7 @@ class FreshRSS_Entry extends Minz_Model { $this->_isFavorite($is_favorite); $this->_feed($feed); $this->_tags(preg_split('/[\s#]/', $tags)); + $this->_guid($guid); } public function id() { @@ -101,6 +101,12 @@ class FreshRSS_Entry extends Minz_Model { $this->id = $value; } public function _guid($value) { + if ($value == '') { + $value = $this->title; + if ($value == '') { + $value = $this->hash(); + } + } $this->guid = $value; } public function _title($value) { -- cgit v1.2.3 From dadd6e7beed41b7a7f3473fda60f7fcb57a9f866 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 5 Apr 2017 14:50:16 +0200 Subject: Use link instead of title fallback --- app/Models/Entry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'app/Models') diff --git a/app/Models/Entry.php b/app/Models/Entry.php index 2946e839d..26cd24797 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -102,7 +102,7 @@ class FreshRSS_Entry extends Minz_Model { } public function _guid($value) { if ($value == '') { - $value = $this->title; + $value = $this->link; if ($value == '') { $value = $this->hash(); } -- cgit v1.2.3 From 0ce43be9de5bf676ceffa2e419941863f98fa970 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 9 Apr 2017 00:25:04 +0200 Subject: Multi-user token https://github.com/FreshRSS/FreshRSS/issues/1390 https://github.com/FreshRSS/FreshRSS/issues/366 --- app/Controllers/authController.php | 9 ++------- app/Controllers/userController.php | 4 ++++ app/Models/Auth.php | 27 +++++++++++++++++++++++---- app/layout/nav_menu.phtml | 1 + app/views/auth/index.phtml | 13 ------------- app/views/user/profile.phtml | 13 +++++++++++++ lib/lib_rss.php | 3 +++ 7 files changed, 46 insertions(+), 24 deletions(-) (limited to 'app/Models') diff --git a/app/Controllers/authController.php b/app/Controllers/authController.php index 1398e4e49..5ad1a51d9 100644 --- a/app/Controllers/authController.php +++ b/app/Controllers/authController.php @@ -27,11 +27,6 @@ class FreshRSS_auth_Controller extends Minz_ActionController { if (Minz_Request::isPost()) { $ok = true; - $current_token = FreshRSS_Context::$user_conf->token; - $token = Minz_Request::param('token', $current_token); - FreshRSS_Context::$user_conf->token = $token; - $ok &= FreshRSS_Context::$user_conf->save(); - $anon = Minz_Request::param('anon_access', false); $anon = ((bool)$anon) && ($anon !== 'no'); $anon_refresh = Minz_Request::param('anon_refresh', false); @@ -123,7 +118,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController { $challenge = Minz_Request::param('challenge', ''); $conf = get_user_configuration($username); - if (is_null($conf)) { + if ($conf == null) { Minz_Error::error(403, array(_t('feedback.auth.login.invalid')), false); return; } @@ -164,7 +159,7 @@ class FreshRSS_auth_Controller extends Minz_ActionController { } $conf = get_user_configuration($username); - if (is_null($conf)) { + if ($conf == null) { return; } diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index f910cecd9..ee575fa09 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -74,6 +74,10 @@ class FreshRSS_user_Controller extends Minz_ActionController { FreshRSS_Context::$user_conf->apiPasswordHash = $passwordHash; } + $current_token = FreshRSS_Context::$user_conf->token; + $token = Minz_Request::param('token', $current_token); + FreshRSS_Context::$user_conf->token = $token; + $ok &= FreshRSS_Context::$user_conf->save(); if ($ok) { diff --git a/app/Models/Auth.php b/app/Models/Auth.php index 476627e10..4de058999 100644 --- a/app/Models/Auth.php +++ b/app/Models/Auth.php @@ -74,6 +74,10 @@ class FreshRSS_Auth { public static function giveAccess() { $current_user = Minz_Session::param('currentUser'); $user_conf = get_user_configuration($current_user); + if ($user_conf == null) { + self::$login_ok = false; + return; + } $system_conf = Minz_Configuration::get('system'); switch ($system_conf->auth_type) { @@ -120,13 +124,28 @@ class FreshRSS_Auth { * Removes all accesses for the current user. */ public static function removeAccess() { - Minz_Session::_param('loginOk'); self::$login_ok = false; - $conf = Minz_Configuration::get('system'); - Minz_Session::_param('currentUser', $conf->default_user); + Minz_Session::_param('loginOk'); Minz_Session::_param('csrf'); + $system_conf = Minz_Configuration::get('system'); - switch ($conf->auth_type) { + $username = ''; + $token_param = Minz_Request::param('token', ''); + if ($token_param != '') { + $username = trim(Minz_Request::param('user', '')); + if ($username != '') { + $conf = get_user_configuration($username); + if ($conf == null) { + $username = ''; + } + } + } + if ($username == '') { + $username = $system_conf->default_user; + } + Minz_Session::_param('currentUser', $username); + + switch ($system_conf->auth_type) { case 'form': Minz_Session::_param('passwordHash'); FreshRSS_FormAuth::deleteCookie(); diff --git a/app/layout/nav_menu.phtml b/app/layout/nav_menu.phtml index f6d824d55..04ee03cd6 100644 --- a/app/layout/nav_menu.phtml +++ b/app/layout/nav_menu.phtml @@ -149,6 +149,7 @@ token) { + $url_output['params']['user'] = Minz_Session::param('currentUser'); $url_output['params']['token'] = FreshRSS_Context::$user_conf->token; } if (FreshRSS_Context::$user_conf->since_hours_posts_per_rss) { diff --git a/app/views/auth/index.phtml b/app/views/auth/index.phtml index 010eae33f..20966f24e 100644 --- a/app/views/auth/index.phtml +++ b/app/views/auth/index.phtml @@ -52,19 +52,6 @@ - -
- - token; ?> -
- data-leave-validation=""/> - - 'rss', 'params' => array('token' => $token, 'hours' => FreshRSS_Context::$user_conf->since_hours_posts_per_rss)), 'html', true); ?> -
-
- -
+ +
+ + token; ?> +
+ data-leave-validation=""/> + + 'rss', 'params' => array('user' => Minz_Session::param('currentUser'), 'token' => $token, 'hours' => FreshRSS_Context::$user_conf->since_hours_posts_per_rss)), 'html', true); ?> +
+
+ +
diff --git a/lib/lib_rss.php b/lib/lib_rss.php index 4298e90bf..247cc707b 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -334,6 +334,9 @@ function max_registrations_reached() { * @return a Minz_Configuration object, null if the configuration cannot be loaded. */ function get_user_configuration($username) { + if (!FreshRSS_user_Controller::checkUsername($username)) { + return null; + } $namespace = 'user_' . $username; try { Minz_Configuration::register($namespace, -- cgit v1.2.3 From 535aa35ba70d8cc584f37388692022272ab883b2 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 10 Apr 2017 19:09:21 +0200 Subject: PSHB better unsubscribe Cases when a user is deleted, or when a feed is deleted. Removed random key do reduce the risk of subscribing several times to the same PSHB feed. --- app/Controllers/userController.php | 1 + app/Models/Feed.php | 15 ++++++++------- p/api/pshb.php | 39 +++++++++++++++++++++++++++++--------- 3 files changed, 39 insertions(+), 16 deletions(-) (limited to 'app/Models') diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index f910cecd9..ddc0c6fe4 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -213,6 +213,7 @@ class FreshRSS_user_Controller extends Minz_ActionController { $userDAO = new FreshRSS_UserDAO(); $ok &= $userDAO->deleteUser($username); $ok &= recursive_unlink($user_data); + array_map('unlink', glob(PSHB_PATH . '/feeds/*/' . $username . '.txt')); } return $ok; } diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 7a9cf8612..1bc6e48e8 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -429,7 +429,7 @@ class FreshRSS_Feed extends Minz_Model { } } else { @mkdir($path, 0777, true); - $key = sha1($path . FreshRSS_Context::$system_conf->salt . uniqid(mt_rand(), true)); + $key = sha1($path . FreshRSS_Context::$system_conf->salt); $hubJson = array( 'hub' => $this->hubUrl, 'key' => $key, @@ -451,15 +451,16 @@ class FreshRSS_Feed extends Minz_Model { //Parameter true to subscribe, false to unsubscribe. function pubSubHubbubSubscribe($state) { - if (FreshRSS_Context::$system_conf->base_url && $this->hubUrl && $this->selfUrl) { - $hubFilename = PSHB_PATH . '/feeds/' . base64url_encode($this->selfUrl) . '/!hub.json'; + $url = $this->selfUrl ? $this->selfUrl : $this->url; + if (FreshRSS_Context::$system_conf->base_url && $url) { + $hubFilename = PSHB_PATH . '/feeds/' . base64url_encode($url) . '/!hub.json'; $hubFile = @file_get_contents($hubFilename); if ($hubFile === false) { Minz_Log::warning('JSON not found for PubSubHubbub: ' . $this->url); return false; } $hubJson = json_decode($hubFile, true); - if (!$hubJson || empty($hubJson['key']) || !ctype_xdigit($hubJson['key'])) { + if (!$hubJson || empty($hubJson['key']) || !ctype_xdigit($hubJson['key']) || empty($hubJson['hub'])) { Minz_Log::warning('Invalid JSON for PubSubHubbub: ' . $this->url); return false; } @@ -474,13 +475,13 @@ class FreshRSS_Feed extends Minz_Model { } $ch = curl_init(); curl_setopt_array($ch, array( - CURLOPT_URL => $this->hubUrl, + CURLOPT_URL => $hubJson['hub'], CURLOPT_FOLLOWLOCATION => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_USERAGENT => 'FreshRSS/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ')', CURLOPT_POSTFIELDS => 'hub.verify=sync' . '&hub.mode=' . ($state ? 'subscribe' : 'unsubscribe') - . '&hub.topic=' . urlencode($this->selfUrl) + . '&hub.topic=' . urlencode($url) . '&hub.callback=' . urlencode($callbackUrl) ) ); @@ -488,7 +489,7 @@ class FreshRSS_Feed extends Minz_Model { $info = curl_getinfo($ch); file_put_contents(USERS_PATH . '/_/log_pshb.txt', date('c') . "\t" . - 'PubSubHubbub ' . ($state ? 'subscribe' : 'unsubscribe') . ' to ' . $this->selfUrl . + 'PubSubHubbub ' . ($state ? 'subscribe' : 'unsubscribe') . ' to ' . $url . ' with callback ' . $callbackUrl . ': ' . $info['http_code'] . ' ' . $response . "\n", FILE_APPEND); if (substr($info['http_code'], 0, 1) == '2') { diff --git a/p/api/pshb.php b/p/api/pshb.php index e9b66b167..378f43516 100644 --- a/p/api/pshb.php +++ b/p/api/pshb.php @@ -23,8 +23,13 @@ if (!ctype_xdigit($key)) { chdir(PSHB_PATH); $canonical64 = @file_get_contents('keys/' . $key . '.txt'); if ($canonical64 === false) { + if (!empty($_REQUEST['hub_mode']) && $_REQUEST['hub_mode'] === 'unsubscribe') { + logMe('Warning: Accept unknown unsubscribe'); + header('Connection: close'); + exit(isset($_REQUEST['hub_challenge']) ? $_REQUEST['hub_challenge'] : ''); + } header('HTTP/1.1 404 Not Found'); - logMe('Error: Feed key not found!: ' . $key); + logMe('Warning: Feed key not found!: ' . $key); die('Feed key not found!'); } $canonical64 = trim($canonical64); @@ -36,7 +41,7 @@ if (!preg_match('/^[A-Za-z0-9_-]+$/D', $canonical64)) { $hubFile = @file_get_contents('feeds/' . $canonical64 . '/!hub.json'); if ($hubFile === false) { header('HTTP/1.1 404 Not Found'); - //@unlink('keys/' . $key . '.txt'); + unlink('keys/' . $key . '.txt'); logMe('Error: Feed info not found!: ' . $canonical64); die('Feed info not found!'); } @@ -50,8 +55,19 @@ chdir('feeds/' . $canonical64); $users = glob('*.txt', GLOB_NOSORT); if (empty($users)) { header('HTTP/1.1 410 Gone'); - logMe('Error: Nobody is subscribed to this feed anymore!: ' . $canonical64); - die('Nobody is subscribed to this feed anymore!'); + $url = base64url_decode($canonical64); + logMe('Warning: Nobody subscribes to this feed anymore!: ' . $url); + unlink('../../keys/' . $key . '.txt'); + Minz_Configuration::register('system', + DATA_PATH . '/config.php', + DATA_PATH . '/config.default.php'); + FreshRSS_Context::$system_conf = Minz_Configuration::get('system'); + $feed = new FreshRSS_Feed($url); + $feed->pubSubHubbubSubscribe(false); + unlink('!hub.json'); + chdir('..'); + recursive_unlink($canonical64); + die('Nobody subscribes to this feed anymore!'); } if (!empty($_REQUEST['hub_mode']) && $_REQUEST['hub_mode'] === 'subscribe') { @@ -108,7 +124,9 @@ $nb = 0; foreach ($users as $userFilename) { $username = basename($userFilename, '.txt'); if (!file_exists(USERS_PATH . '/' . $username . '/config.php')) { - break; + logMe('Warning: Removing broken user link: ' . $username . ' for ' . $self); + unlink($userFilename); + continue; } try { @@ -119,11 +137,14 @@ foreach ($users as $userFilename) { new Minz_ModelPdo($username); //TODO: FIXME: Quick-fix while waiting for a better FreshRSS() constructor/init FreshRSS_Context::init(); list($updated_feeds, $feed) = FreshRSS_feed_Controller::actualizeFeed(0, $self, false, $simplePie); - if ($updated_feeds > 0) { + if ($updated_feeds > 0 || $feed != false) { $nb++; + } else { + logMe('Warning: User ' . $username . ' does not subscribe anymore to ' . $self); + unlink($userFilename); } } catch (Exception $e) { - logMe('Error: ' . $e->getMessage()); + logMe('Error: ' . $e->getMessage() . ' for user ' . $username . ' and feed ' . $self); } } @@ -132,8 +153,8 @@ unset($simplePie); if ($nb === 0) { header('HTTP/1.1 410 Gone'); - logMe('Error: Nobody is subscribed to this feed anymore after all!: ' . $self); - die('Nobody is subscribed to this feed anymore after all!'); + logMe('Warning: Nobody subscribes to this feed anymore after all!: ' . $self); + die('Nobody subscribes to this feed anymore after all!'); } elseif (!empty($hubJson['error'])) { $hubJson['error'] = false; file_put_contents('./!hub.json', json_encode($hubJson)); -- cgit v1.2.3