seblucas /
cops
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * COPS (Calibre OPDS PHP Server) class file |
||
| 4 | * |
||
| 5 | * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) |
||
| 6 | * @author Klaus Broelemann <[email protected]> |
||
| 7 | */ |
||
| 8 | |||
| 9 | require_once('base.php'); |
||
| 10 | |||
| 11 | /** |
||
| 12 | * Read and filter virtual libraries |
||
| 13 | */ |
||
| 14 | class VirtualLib { |
||
| 15 | const SQL_VL_KEY = "virtual_libraries"; // Key for the virtual library entries. |
||
| 16 | |||
| 17 | private $db_id = null; // Database Index for the virtual lib |
||
| 18 | private $vl_id = null; // Library Index |
||
| 19 | private $filter = null; // structured representation of the current filter |
||
| 20 | |||
| 21 | private static $currentVL = null; // Singleton: current virtual lib |
||
| 22 | |||
| 23 | /** |
||
| 24 | * The constructor parses the calibre search string and creates a array-based representation out of it. |
||
| 25 | * |
||
| 26 | * @param int $database The database id of the new object. |
||
| 27 | * @param int $virtualLib The virtual library id of the new object. |
||
| 28 | */ |
||
| 29 | private function __construct($database, $virtualLib) { |
||
| 30 | $this->db_id = $database; |
||
| 31 | $this->vl_id = $virtualLib; |
||
| 32 | |||
| 33 | // Get the current search string |
||
| 34 | $vlList = self::getVLList($database); |
||
| 35 | $vlList = array_values($vlList); |
||
| 36 | $searchStr = $vlList[$virtualLib]; |
||
| 37 | |||
| 38 | $this->filter = Filter::parseFilter($searchStr); |
||
| 39 | } |
||
| 40 | |||
| 41 | /** |
||
| 42 | * Returns a SQL query that finds the IDs of all books accepted by the filter. |
||
| 43 | * |
||
| 44 | * The sql statement return only one column with the name 'id'. |
||
| 45 | * This statement can be included into other sql statements in order to apply the filter, e.g. by using inner joins |
||
| 46 | * like "select books.* from books inner join ({0}) as filter on books.id = filter.id" |
||
| 47 | * @see Filter |
||
| 48 | * |
||
| 49 | * @return string an sql query |
||
| 50 | */ |
||
| 51 | public function getFilterQuery() { |
||
| 52 | return $this->filter->toSQLQuery(); |
||
| 53 | } |
||
| 54 | |||
| 55 | /** |
||
| 56 | * Get the name of the virtual library. |
||
| 57 | * |
||
| 58 | * @return string Name of the virtual library. |
||
| 59 | */ |
||
| 60 | public function getName() { |
||
| 61 | $names = self::getVLNameList($this->db_id); |
||
| 62 | return $names[$this->vl_id]; |
||
| 63 | } |
||
| 64 | |||
| 65 | /** |
||
| 66 | * Get the current VirtualLib object. |
||
| 67 | * |
||
| 68 | * @param int $database The current database id. |
||
| 69 | * @param int $virtualLib The current virtual library id. |
||
| 70 | * @return VirtualLib The corresponding VirtualLib object. |
||
| 71 | */ |
||
| 72 | public static function getVL($database = null, $virtualLib = null) { |
||
| 73 | if (is_null($database)) |
||
| 74 | $database = getURLParam(DB, 0); |
||
| 75 | if ( is_null(self::$currentVL) || self::$currentVL->db_id != $database || (self::$currentVL->vl_id != $virtualLib && !is_null($virtualLib))) { |
||
| 76 | if (is_null($virtualLib)) |
||
| 77 | $virtualLib = GetUrlParam (VL, 0); |
||
| 78 | self::$currentVL = new VirtualLib($database, $virtualLib); |
||
| 79 | } |
||
| 80 | return self::$currentVL; |
||
| 81 | } |
||
| 82 | |||
| 83 | /** |
||
| 84 | * Checks if the support for virtual libraries is enabled in the settings. |
||
| 85 | * |
||
| 86 | * @return boolean true, if virtual libraries are enabled. |
||
| 87 | */ |
||
| 88 | public static function isVLEnabled() { |
||
| 89 | global $config; |
||
| 90 | return ($config['enable_virtual_libraries'] == 1); |
||
| 91 | } |
||
| 92 | |||
| 93 | /** |
||
| 94 | * Gets a list of all virtual libraries in a database. |
||
| 95 | * If virtual libraries are disabled, only an empty entry is returned. |
||
| 96 | * |
||
| 97 | * @param int $database id of the database |
||
| 98 | * @return array An array of virtual libraries with the names as keys and the filter strings as values. |
||
| 99 | */ |
||
| 100 | public static function getVLList($database = NULL) { |
||
| 101 | // Standard return if virtual libraries are not enabled |
||
| 102 | if (!self::isVLEnabled()) |
||
| 103 | return array("" => ""); |
||
| 104 | // Load list from Database |
||
| 105 | $vLibs = json_decode(Base::getCalibreSetting(self::SQL_VL_KEY, $database), true); |
||
| 106 | // Add "All Books" at the beginning |
||
| 107 | if (is_null($vLibs)) |
||
| 108 | return array(localize ("allbooks.title") => ""); |
||
| 109 | else |
||
| 110 | return array_merge(array(localize ("allbooks.title") => ""), $vLibs); |
||
| 111 | } |
||
| 112 | 41 | ||
| 113 | 41 | /** |
|
| 114 | 41 | * Gets a list of all virtual libraries in a database. |
|
| 115 | * |
||
| 116 | * @param int $database id of the database |
||
| 117 | * @return array An array of virtual libraries with the names as keys and the filter strings as values. |
||
| 118 | */ |
||
| 119 | public static function getVLNameList($database = NULL) { |
||
| 120 | return array_keys(self::getVLList($database)); |
||
| 121 | } |
||
| 122 | |||
| 123 | 22 | /** |
|
| 124 | * Combines the database and virtual lib names into a merged name. |
||
| 125 | 22 | * |
|
| 126 | * The resulting name has the form "{$dbName} - {$vlName}". If one of these parameters is empty, the dash will also be removed. |
||
| 127 | 22 | * If the support for virtual libraries is not enabled, this function simply returns the database name. |
|
| 128 | 22 | * |
|
| 129 | * @param string $dbName The database name. If NULL, the name of the current db is taken. |
||
| 130 | * @param string $vlName The name of the virtual library. If NULL, the name of the current vl is taken. |
||
| 131 | */ |
||
| 132 | public static function getDisplayName($dbName = NULL, $vlName = NULL) { |
||
| 133 | if (is_null($dbName)) |
||
| 134 | $dbName = Base::getDbName(); |
||
| 135 | if (is_null($vlName)) |
||
| 136 | $vlName = self::getVL()->getName(); |
||
| 137 | if (self::isVLEnabled()) |
||
| 138 | return trim(str_format('{0} - {1}', $dbName, $vlName), ' -'); |
||
| 139 | 22 | else |
|
| 140 | 22 | return $dbName; |
|
| 141 | } |
||
| 142 | } |
||
| 143 | |||
| 144 | /** |
||
| 145 | * Abstract classe to store filters internally. It's derived classes represent the different filter types. |
||
| 146 | * |
||
| 147 | */ |
||
| 148 | abstract class Filter { |
||
| 149 | public static $KNOWN_ATTRIBUTES = array( |
||
| 150 | "tags" => array( |
||
| 151 | "table" => "tags", |
||
| 152 | 1 | "filterColumn" => "name", |
|
| 153 | 1 | "link_table" => "books_tags_link", |
|
| 154 | 1 | "link_join_on" => "tag", |
|
| 155 | "bookID" => "book" |
||
| 156 | 1 | ) |
|
| 157 | ); |
||
| 158 | |||
| 159 | |||
| 160 | private $isNegated = false; |
||
| 161 | |||
| 162 | /** |
||
| 163 | * Converts the calibre search string into afilter object |
||
| 164 | * |
||
| 165 | * @param string $searchStr The calibre string |
||
| 166 | * @return Filter The internal, array-based representation |
||
| 167 | */ |
||
| 168 | public static function parseFilter($searchStr) { |
||
| 169 | // deal with empty input strings |
||
| 170 | if (strlen($searchStr) == 0) |
||
| 171 | return new EmptyFilter(); |
||
| 172 | |||
| 173 | // Simple search string pattern. It recognizes search string of the form |
||
| 174 | // [attr]:[value] |
||
| 175 | // and their negation |
||
| 176 | // not [attr]:[value] |
||
| 177 | // where value is either a number, a boolean or a string in double quote. |
||
| 178 | // In the latter case, the string starts with an operator (= or ~), followed by the search text. |
||
| 179 | // TODO: deal with more complex search terms that can contain "and", "or" and brackets |
||
| 180 | $pattern = '#(?P<neg>not)?\s*(?P<attr>\w+):(?P<value>"(?P<op>=|~)(?P<text>.*)"|true|false|\d+)#i'; |
||
| 181 | if (!preg_match($pattern, $searchStr, $match)) { |
||
| 182 | trigger_error("Virtual Library Filter is not supported.", E_USER_WARNING); |
||
| 183 | return new EmptyFilter(); |
||
| 184 | } |
||
| 185 | |||
| 186 | // Create the actual filter object |
||
| 187 | $value = $match["value"]; |
||
| 188 | $filter = null; |
||
|
0 ignored issues
–
show
|
|||
| 189 | if (substr($value, 0, 1) == '"') { |
||
| 190 | $filter = new ComparingFilter($match["attr"], $match["text"], $match["op"]); |
||
| 191 | } elseif (preg_match("#\d+", $value)) { |
||
| 192 | $filter = new ComparingFilter($match["attr"], $value, $match["op"]); |
||
| 193 | } else { |
||
| 194 | $value = (strcasecmp($value, "true") == 0); |
||
| 195 | $filter = new ExistenceFilter($match["attr"], $value); |
||
| 196 | } |
||
| 197 | |||
| 198 | // Negate if a leading "not" is given |
||
| 199 | if (strlen($match["neg"]) > 0) |
||
| 200 | $filter->negate(); |
||
| 201 | |||
| 202 | return $filter; |
||
| 203 | } |
||
| 204 | |||
| 205 | /** |
||
| 206 | * Returns a SQL query that finds the IDs of all books accepted by the filter. The single columns name is id. |
||
| 207 | */ |
||
| 208 | public abstract function toSQLQuery(); |
||
| 209 | |||
| 210 | /** |
||
| 211 | * Negates the current filter. A second call will undo it. |
||
| 212 | */ |
||
| 213 | public function negate() { |
||
| 214 | $this->isNegated = !$this->isNegated; |
||
| 215 | } |
||
| 216 | |||
| 217 | public function isNegated() { |
||
| 218 | return $this->isNegated; |
||
| 219 | } |
||
| 220 | } |
||
| 221 | |||
| 222 | /** |
||
| 223 | * Class that represents an empty filter |
||
| 224 | * |
||
| 225 | */ |
||
| 226 | class EmptyFilter extends Filter { |
||
| 227 | public function __construct() { |
||
| 228 | // Do Nothing |
||
| 229 | } |
||
| 230 | |||
| 231 | // Return all books (or no book if the filter is negated) |
||
| 232 | public function toSQLQuery() { |
||
| 233 | if ($this->isNegated()) |
||
| 234 | return "select id from books where 1 = 0"; |
||
| 235 | return "select id from books"; |
||
| 236 | } |
||
| 237 | } |
||
| 238 | |||
| 239 | /** |
||
| 240 | * Class that represents a filter, that compares an attribute with a given value, e.g. tags with "Fiction" |
||
| 241 | * |
||
| 242 | * This class allows for other comparation operators beside "=" |
||
| 243 | */ |
||
| 244 | class ComparingFilter extends Filter { |
||
| 245 | private $attr = null; // The attribute that is filtered |
||
| 246 | private $value = null; // The value with which to compare |
||
| 247 | private $op = null; // The operator that is used for comparing |
||
| 248 | |||
| 249 | /** |
||
| 250 | * Creates a comparing filter |
||
| 251 | * |
||
| 252 | * @param string $attr The attribute that is filtered. |
||
| 253 | * @param mixed $value The value with which to compare. |
||
| 254 | * @param string $op The operator that is used for comparing, optional. |
||
| 255 | */ |
||
| 256 | public function __construct($attr, $value, $op = "=") { |
||
| 257 | $this->attr = strtolower($attr); |
||
| 258 | $this->value = $value; |
||
| 259 | $this->op = $op; |
||
| 260 | } |
||
| 261 | |||
| 262 | public function toSQLQuery() { |
||
| 263 | // Do not filter if attribute is not valid |
||
| 264 | if (!array_key_exists($this->attr, self::$KNOWN_ATTRIBUTES)) |
||
| 265 | return "select id from books"; |
||
| 266 | |||
| 267 | // Include parameters into the sql query |
||
| 268 | $queryParams = self::$KNOWN_ATTRIBUTES[$this->attr]; |
||
| 269 | $queryParams["value"] = $this->value; |
||
| 270 | $sql = str_format_n( |
||
| 271 | "select distinct {link_table}.{bookID} as id ". |
||
| 272 | "from {table} inner join {link_table} on {table}.id = {link_table}.{link_join_on} ". |
||
| 273 | "where {table}.{filterColumn} = '{value}'", |
||
| 274 | $queryParams); |
||
| 275 | // TODO: support different operators |
||
| 276 | return $sql; |
||
| 277 | } |
||
| 278 | } |
||
| 279 | |||
| 280 | /** |
||
| 281 | * Class that represents a filter, that checks if a given attribute exists for a book. |
||
| 282 | */ |
||
| 283 | class ExistenceFilter extends Filter { |
||
| 284 | private $attr = null; // The attribute that is filtered |
||
| 285 | |||
| 286 | /** |
||
| 287 | * Creates an existence filter |
||
| 288 | * |
||
| 289 | * @param string $attr The attribute that is filtered. |
||
| 290 | * @param boolean $value True, if objects with that attribute are accepted by the filter, false if not. |
||
| 291 | */ |
||
| 292 | public function __construct($attr, $value = true) { |
||
| 293 | $this->attr = $attr; |
||
| 294 | |||
| 295 | // $value == false is the negation of $value == true |
||
| 296 | if (!$value) |
||
| 297 | $this->negate(); |
||
| 298 | } |
||
| 299 | |||
| 300 | public function toSQLQuery() { |
||
| 301 | // Do not filter if attribute is not valid |
||
| 302 | if (!array_key_exists($this->attr, self::KNOWN_ATTRIBUTES)) |
||
| 303 | return "select id from books"; |
||
| 304 | |||
| 305 | // Include parameters into the sql query |
||
| 306 | $queryParams = self::$KNOWN_ATTRIBUTES[$this->attr]; |
||
| 307 | $sql = str_format_n( |
||
| 308 | "select distinct {link_table}.{bookID} as id". |
||
| 309 | "from {table} inner join {link_table} on {table}.id = {link_table}.{link_join_on} ", |
||
| 310 | $queryParams); |
||
| 311 | return $sql; |
||
| 312 | } |
||
| 313 | } |
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.
Both the
$myVarassignment in line 1 and the$higherassignment in line 2 are dead. The first because$myVaris never used and the second because$higheris always overwritten for every possible time line.