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
$myVar
assignment in line 1 and the$higher
assignment in line 2 are dead. The first because$myVar
is never used and the second because$higher
is always overwritten for every possible time line.