Completed
Push — master ( 865827...08e2eb )
by Markus
04:14
created

base.php (73 issues)

Upgrade to new PHP Analysis Engine

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     Sébastien Lucas <[email protected]>
7
 */
8
9
require_once 'config.php';
10
11
define ("VERSION", "1.0.0RC4");
12
define ("DB", "db");
13
date_default_timezone_set($config['default_timezone']);
14
15
16
function useServerSideRendering () {
17 3
    global $config;
18 3
    return preg_match("/" . $config['cops_server_side_render'] . "/", $_SERVER['HTTP_USER_AGENT']);
19
}
20
21
function serverSideRender ($data) {
22
    // Get the templates
23 2
    $theme = getCurrentTemplate ();
24 2
    $header = file_get_contents('templates/' . $theme . '/header.html');
25 2
    $footer = file_get_contents('templates/' . $theme . '/footer.html');
26 2
    $main = file_get_contents('templates/' . $theme . '/main.html');
27 2
    $bookdetail = file_get_contents('templates/' . $theme . '/bookdetail.html');
28 2
    $page = file_get_contents('templates/' . $theme . '/page.html');
29
30
    // Generate the function for the template
31 2
    $template = new doT ();
32 2
    $dot = $template->template ($page, array ("bookdetail" => $bookdetail,
33 2
                                              "header" => $header,
34 2
                                              "footer" => $footer,
35 2
                                              "main" => $main));
36
    // If there is a syntax error in the function created
37
    // $dot will be equal to FALSE
38 2
    if (!$dot) {
39
        return FALSE;
40
    }
41
    // Execute the template
42 2
    if (!empty ($data)) {
43
        return $dot ($data);
44
    }
45
46 2
    return NULL;
47
}
48
49
function getQueryString () {
50 18
    if ( isset($_SERVER['QUERY_STRING']) ) {
51 16
        return $_SERVER['QUERY_STRING'];
52
    }
53 2
    return "";
54
}
55
56
function notFound () {
57
    header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
58
    header("Status: 404 Not Found");
59
60
    $_SERVER['REDIRECT_STATUS'] = 404;
61
}
62
63
function getURLParam ($name, $default = NULL) {
64 101
    if (!empty ($_GET) && isset($_GET[$name]) && $_GET[$name] != "") {
65 24
        return $_GET[$name];
66
    }
67 101
    return $default;
68
}
69
70
function getCurrentOption ($option) {
71 87
    global $config;
72 87
    if (isset($_COOKIE[$option])) {
73 2
        if (isset($config ["cops_" . $option]) && is_array ($config ["cops_" . $option])) {
74
            return explode (",", $_COOKIE[$option]);
75
        } else {
76 2
            return $_COOKIE[$option];
77
        }
78
    }
79 85
    if (isset($config ["cops_" . $option])) {
80 85
        return $config ["cops_" . $option];
81
    }
82
83
    return "";
84
}
85
86
function getCurrentCss () {
87 2
    return "templates/" . getCurrentTemplate () . "/styles/style-" . getCurrentOption ("style") . ".css";
88
}
89
90
function getCurrentTemplate () {
91 4
    return getCurrentOption ("template");
92
}
93
94
function getUrlWithVersion ($url) {
95 51
    return $url . "?v=" . VERSION;
96
}
97
98
function xml2xhtml($xml) {
99 35
    return preg_replace_callback('#<(\w+)([^>]*)\s*/>#s', create_function('$m', '
100
        $xhtml_tags = array("br", "hr", "input", "frame", "img", "area", "link", "col", "base", "basefont", "param");
101
        return in_array($m[1], $xhtml_tags) ? "<$m[1]$m[2] />" : "<$m[1]$m[2]></$m[1]>";
102 35
    '), $xml);
103
}
104
105
function display_xml_error($error)
106
{
107
    $return = "";
108
    $return .= str_repeat('-', $error->column) . "^\n";
109
110
    switch ($error->level) {
111
        case LIBXML_ERR_WARNING:
112
            $return .= "Warning $error->code: ";
113
            break;
114
         case LIBXML_ERR_ERROR:
115
            $return .= "Error $error->code: ";
116
            break;
117
        case LIBXML_ERR_FATAL:
118
            $return .= "Fatal Error $error->code: ";
119
            break;
120
    }
121
122
    $return .= trim($error->message) .
123
               "\n  Line: $error->line" .
124
               "\n  Column: $error->column";
125
126
    if ($error->file) {
127
        $return .= "\n  File: $error->file";
128
    }
129
130
    return "$return\n\n--------------------------------------------\n\n";
131
}
132
133
function are_libxml_errors_ok ()
134
{
135 35
    $errors = libxml_get_errors();
136
137 35
    foreach ($errors as $error) {
138
        if ($error->code == 801) return false;
139 35
    }
140 35
    return true;
141
}
142
143
function html2xhtml ($html) {
144 35
    $doc = new DOMDocument();
145 35
    libxml_use_internal_errors(true);
146
147 35
    $doc->loadHTML('<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>' .
148 35
                        $html  . '</body></html>'); // Load the HTML
149 35
    $output = $doc->saveXML($doc->documentElement); // Transform to an Ansi xml stream
150 35
    $output = xml2xhtml($output);
151 35
    if (preg_match ('#<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></meta></head><body>(.*)</body></html>#ms', $output, $matches)) {
152 35
        $output = $matches [1]; // Remove <html><body>
153 35
    }
154
    /*
155
    // In case of error with summary, use it to debug
156
    $errors = libxml_get_errors();
157
158
    foreach ($errors as $error) {
159
        $output .= display_xml_error($error);
160
    }
161
    */
162
163 35
    if (!are_libxml_errors_ok ()) $output = "HTML code not valid.";
164
165 35
    libxml_use_internal_errors(false);
166 35
    return $output;
167
}
168
169
/**
170
 * This method is a direct copy-paste from
171
 * http://tmont.com/blargh/2010/1/string-format-in-php
172
 */
173
function str_format($format) {
174 94
    $args = func_get_args();
175 94
    $format = array_shift($args);
176
177 94
    preg_match_all('/(?=\{)\{(\d+)\}(?!\})/', $format, $matches, PREG_OFFSET_CAPTURE);
178 94
    $offset = 0;
179 94
    foreach ($matches[1] as $data) {
180 94
        $i = $data[0];
181 94
        $format = substr_replace($format, @$args[$i], $offset + $data[1] - 1, 2 + strlen($i));
182 94
        $offset += strlen(@$args[$i]) - 2 - strlen($i);
183 94
    }
184
185 94
    return $format;
186
}
187
188
/**
189
 * Get all accepted languages from the browser and put them in a sorted array
190
 * languages id are normalized : fr-fr -> fr_FR
191
 * @return array of languages
192
 */
193
function getAcceptLanguages() {
194 16
    $langs = array();
195
196 16
    if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
197
        // break up string into pieces (languages and q factors)
198 16
        $accept = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
199 16 View Code Duplication
        if (preg_match('/^(\w{2})-\w{2}$/', $accept, $matches)) {
200
            // Special fix for IE11 which send fr-FR and nothing else
201 3
            $accept = $accept . "," . $matches[1] . ";q=0.8";
202 3
        }
203 16
        preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $accept, $lang_parse);
204
205 16
        if (count($lang_parse[1])) {
206 16
            $langs = array();
207 16
            foreach ($lang_parse[1] as $lang) {
208
                // Format the language code (not standard among browsers)
209 16
                if (strlen($lang) == 5) {
210 11
                    $lang = str_replace("-", "_", $lang);
211 11
                    $splitted = preg_split("/_/", $lang);
212 11
                    $lang = $splitted[0] . "_" . strtoupper($splitted[1]);
213 11
                }
214 16
                array_push($langs, $lang);
215 16
            }
216
            // create a list like "en" => 0.8
217 16
            $langs = array_combine($langs, $lang_parse[4]);
218
219
            // set default to 1 for any without q factor
220 16
            foreach ($langs as $lang => $val) {
221 16
                if ($val === '') $langs[$lang] = 1;
222 16
            }
223
224
            // sort list based on value
225 16
            arsort($langs, SORT_NUMERIC);
226 16
        }
227 16
    }
228
229 16
    return $langs;
230
}
231
232
/**
233
 * Find the best translation file possible based on the accepted languages
234
 * @return array of language and language file
235
 */
236
function getLangAndTranslationFile() {
237 17
    global $config;
238 17
    $langs = array();
239 17
    $lang = "en";
240 17
    if (!empty($config['cops_language'])) {
241
        $lang = $config['cops_language'];
242
    }
243 17
    elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
244 16
        $langs = getAcceptLanguages();
245 16
    }
246
    //echo var_dump($langs);
247 17
    $lang_file = NULL;
248 17
    foreach ($langs as $language => $val) {
249 16
        $temp_file = dirname(__FILE__). '/lang/Localization_' . $language . '.json';
250 16
        if (file_exists($temp_file)) {
251 16
            $lang = $language;
252 16
            $lang_file = $temp_file;
253 16
            break;
254
        }
255 17
    }
256 17
    if (empty ($lang_file)) {
257 3
        $lang_file = dirname(__FILE__). '/lang/Localization_' . $lang . '.json';
258 3
    }
259 17
    return array($lang, $lang_file);
260
}
261
262
/**
263
 * This method is based on this page
264
 * http://www.mind-it.info/2010/02/22/a-simple-approach-to-localization-in-php/
265
 */
266
function localize($phrase, $count=-1, $reset=false) {
267 114
    global $config;
268 114
    if ($count == 0)
269 114
        $phrase .= ".none";
270 114
    if ($count == 1)
271 114
        $phrase .= ".one";
272 114
    if ($count > 1)
273 114
        $phrase .= ".many";
274
275
    /* Static keyword is used to ensure the file is loaded only once */
276 114
    static $translations = NULL;
277 114
    if ($reset) {
278 16
        $translations = NULL;
279 16
    }
280
    /* If no instance of $translations has occured load the language file */
281 114
    if (is_null($translations)) {
282 17
        $lang_file_en = NULL;
283 17
        list ($lang, $lang_file) = getLangAndTranslationFile();
284 17
        if ($lang != "en") {
285 1
            $lang_file_en = dirname(__FILE__). '/lang/' . 'Localization_en.json';
286 1
        }
287
288 17
        $lang_file_content = file_get_contents($lang_file);
289
        /* Load the language file as a JSON object and transform it into an associative array */
290 17
        $translations = json_decode($lang_file_content, true);
291
292
        /* Clean the array of all unfinished translations */
293 17
        foreach (array_keys ($translations) as $key) {
294 17
            if (preg_match ("/^##TODO##/", $key)) {
295
                unset ($translations [$key]);
296
            }
297 17
        }
298
        if ($lang_file_en)
299 17
        {
300 1
            $lang_file_content = file_get_contents($lang_file_en);
301 1
            $translations_en = json_decode($lang_file_content, true);
302 1
            $translations = array_merge ($translations_en, $translations);
303 1
        }
304 17
    }
305 114
    if (array_key_exists ($phrase, $translations)) {
306 114
        return $translations[$phrase];
307
    }
308 1
    return $phrase;
309
}
310
311
function addURLParameter($urlParams, $paramName, $paramValue) {
312 58
    if (empty ($urlParams)) {
313 48
        $urlParams = "";
314 48
    }
315 58
    $start = "";
316 58
    if (preg_match ("#^\?(.*)#", $urlParams, $matches)) {
317 15
        $start = "?";
318 15
        $urlParams = $matches[1];
319 15
    }
320 58
    $params = array();
321 58
    parse_str($urlParams, $params);
322 58
    if (empty ($paramValue) && $paramValue != 0) {
323
        unset ($params[$paramName]);
324
    } else {
325 58
        $params[$paramName] = $paramValue;
326
    }
327 58
    return $start . http_build_query($params);
328
}
329
330
function useNormAndUp () {
331 113
    global $config;
332 113
    return $config ['cops_normalized_search'] == "1";
333
}
334
335
function normalizeUtf8String( $s) {
336 8
    include_once 'transliteration.php';
337 8
    return _transliteration_process($s);
338
}
339
340
function normAndUp ($s) {
341 7
    return mb_strtoupper (normalizeUtf8String($s), 'UTF-8');
342
}
343
344
class Link
345
{
346
    const OPDS_THUMBNAIL_TYPE = "http://opds-spec.org/image/thumbnail";
347
    const OPDS_IMAGE_TYPE = "http://opds-spec.org/image";
348
    const OPDS_ACQUISITION_TYPE = "http://opds-spec.org/acquisition";
349
    const OPDS_NAVIGATION_TYPE = "application/atom+xml;profile=opds-catalog;kind=navigation";
350
    const OPDS_PAGING_TYPE = "application/atom+xml;profile=opds-catalog;kind=acquisition";
351
352
    public $href;
353
    public $type;
354
    public $rel;
355
    public $title;
356
    public $facetGroup;
357
    public $activeFacet;
358
359 96
    public function __construct($phref, $ptype, $prel = NULL, $ptitle = NULL, $pfacetGroup = NULL, $pactiveFacet = FALSE) {
360 96
        $this->href = $phref;
361 96
        $this->type = $ptype;
362 96
        $this->rel = $prel;
363 96
        $this->title = $ptitle;
364 96
        $this->facetGroup = $pfacetGroup;
365 96
        $this->activeFacet = $pactiveFacet;
366 96
    }
367
368 10
    public function hrefXhtml () {
369 10
        return $this->href;
370
    }
371
372 95
    public function getScriptName() {
373 95
        $parts = explode('/', $_SERVER["SCRIPT_NAME"]);
374 95
        return $parts[count($parts) - 1];
375
    }
376
}
377
378
class LinkNavigation extends Link
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
379
{
380 95
    public function __construct($phref, $prel = NULL, $ptitle = NULL) {
381 95
        parent::__construct ($phref, Link::OPDS_NAVIGATION_TYPE, $prel, $ptitle);
382 95 View Code Duplication
        if (!is_null (GetUrlParam (DB))) $this->href = addURLParameter ($this->href, DB, GetUrlParam (DB));
383 95
        if (!preg_match ("#^\?(.*)#", $this->href) && !empty ($this->href)) $this->href = "?" . $this->href;
384 95
        if (preg_match ("/(bookdetail|getJSON).php/", parent::getScriptName())) {
385
            $this->href = "index.php" . $this->href;
386
        } else {
387 95
            $this->href = parent::getScriptName() . $this->href;
388
        }
389 95
    }
390
}
391
392
class LinkFacet extends Link
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
393
{
394 1
    public function __construct($phref, $ptitle = NULL, $pfacetGroup = NULL, $pactiveFacet = FALSE) {
395 1
        parent::__construct ($phref, Link::OPDS_PAGING_TYPE, "http://opds-spec.org/facet", $ptitle, $pfacetGroup, $pactiveFacet);
396 1 View Code Duplication
        if (!is_null (GetUrlParam (DB))) $this->href = addURLParameter ($this->href, DB, GetUrlParam (DB));
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
397 1
        $this->href = parent::getScriptName() . $this->href;
398 1
    }
399
}
400
401
class Entry
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
402
{
403
    public $title;
404
    public $id;
405
    public $content;
406
    public $numberOfElement;
407
    public $contentType;
408
    public $linkArray;
409
    public $localUpdated;
410
    public $className;
411
    private static $updated = NULL;
412
413
    public static $icons = array(
414
        Author::ALL_AUTHORS_ID       => 'images/author.png',
415
        Serie::ALL_SERIES_ID         => 'images/serie.png',
416
        Book::ALL_RECENT_BOOKS_ID    => 'images/recent.png',
417
        Tag::ALL_TAGS_ID             => 'images/tag.png',
418
        Language::ALL_LANGUAGES_ID   => 'images/language.png',
419
        CustomColumn::ALL_CUSTOMS_ID => 'images/custom.png',
420
        Rating::ALL_RATING_ID        => 'images/rating.png',
421
        "cops:books$"             => 'images/allbook.png',
422
        "cops:books:letter"       => 'images/allbook.png',
423
        Publisher::ALL_PUBLISHERS_ID => 'images/publisher.png'
424
    );
425
426
    public function getUpdatedTime () {
427
        if (!is_null ($this->localUpdated)) {
428
            return date (DATE_ATOM, $this->localUpdated);
429
        }
430
        if (is_null (self::$updated)) {
431
            self::$updated = time();
432
        }
433
        return date (DATE_ATOM, self::$updated);
434
    }
435
436 7 View Code Duplication
    public function getNavLink () {
437 7
        foreach ($this->linkArray as $link) {
438 7
            if ($link->type != Link::OPDS_NAVIGATION_TYPE) { continue; }
439
440 7
            return $link->hrefXhtml ();
441
        }
442
        return "#";
443
    }
444
445 89
    public function __construct($ptitle, $pid, $pcontent, $pcontentType, $plinkArray, $pclass = "", $pcount = 0) {
446 89
        global $config;
447 89
        $this->title = $ptitle;
448 89
        $this->id = $pid;
449 89
        $this->content = $pcontent;
450 89
        $this->contentType = $pcontentType;
451 89
        $this->linkArray = $plinkArray;
452 89
        $this->className = $pclass;
453 89
        $this->numberOfElement = $pcount;
454
455 89
        if ($config['cops_show_icons'] == 1)
456 89
        {
457 89
            foreach (self::$icons as $reg => $image)
458
            {
459 89
                if (preg_match ("/" . $reg . "/", $pid)) {
460 51
                    array_push ($this->linkArray, new Link (getUrlWithVersion ($image), "image/png", Link::OPDS_THUMBNAIL_TYPE));
461 51
                    break;
462
                }
463 89
            }
464 89
        }
465
466 89
        if (!is_null (GetUrlParam (DB))) $this->id = str_replace ("cops:", "cops:" . GetUrlParam (DB) . ":", $this->id);
467 89
    }
468
}
469
470
class EntryBook extends Entry
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
471
{
472
    public $book;
473
474 39
    public function __construct($ptitle, $pid, $pcontent, $pcontentType, $plinkArray, $pbook) {
475 39
        parent::__construct ($ptitle, $pid, $pcontent, $pcontentType, $plinkArray);
476 39
        $this->book = $pbook;
477 39
        $this->localUpdated = $pbook->timestamp;
478 39
    }
479
480 View Code Duplication
    public function getCoverThumbnail () {
481
        foreach ($this->linkArray as $link) {
482
            if ($link->rel == Link::OPDS_THUMBNAIL_TYPE)
483
                return $link->hrefXhtml ();
484
        }
485
        return null;
486
    }
487
488 View Code Duplication
    public function getCover () {
489
        foreach ($this->linkArray as $link) {
490
            if ($link->rel == Link::OPDS_IMAGE_TYPE)
491
                return $link->hrefXhtml ();
492
        }
493
        return null;
494
    }
495
}
496
497
class Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
498
{
499
    public $title;
500
    public $subtitle = "";
501
    public $authorName = "";
502
    public $authorUri = "";
503
    public $authorEmail = "";
504
    public $idPage;
505
    public $idGet;
506
    public $query;
507
    public $favicon;
508
    public $n;
509
    public $book;
510
    public $totalNumber = -1;
511
    public $entryArray = array();
512
513 81
    public static function getPage ($pageId, $id, $query, $n)
514
    {
515
        switch ($pageId) {
516 81
            case Base::PAGE_ALL_AUTHORS :
517 3
                return new PageAllAuthors ($id, $query, $n);
518 78
            case Base::PAGE_AUTHORS_FIRST_LETTER :
519 1
                return new PageAllAuthorsLetter ($id, $query, $n);
520 77
            case Base::PAGE_AUTHOR_DETAIL :
521 7
                return new PageAuthorDetail ($id, $query, $n);
522 70
            case Base::PAGE_ALL_TAGS :
523 2
                return new PageAllTags ($id, $query, $n);
524 68
            case Base::PAGE_TAG_DETAIL :
525 1
                return new PageTagDetail ($id, $query, $n);
526 67
            case Base::PAGE_ALL_LANGUAGES :
527 2
                return new PageAllLanguages ($id, $query, $n);
528 65
            case Base::PAGE_LANGUAGE_DETAIL :
529 1
                return new PageLanguageDetail ($id, $query, $n);
530 64
            case Base::PAGE_ALL_CUSTOMS :
531 3
                return new PageAllCustoms ($id, $query, $n);
532 61
            case Base::PAGE_CUSTOM_DETAIL :
533 3
                return new PageCustomDetail ($id, $query, $n);
534 58
            case Base::PAGE_ALL_RATINGS :
535 1
                return new PageAllRating ($id, $query, $n);
536 57
            case Base::PAGE_RATING_DETAIL :
537 1
                return new PageRatingDetail ($id, $query, $n);
538 56
            case Base::PAGE_ALL_SERIES :
539 2
                return new PageAllSeries ($id, $query, $n);
540 54
            case Base::PAGE_ALL_BOOKS :
541 3
                return new PageAllBooks ($id, $query, $n);
542 51
            case Base::PAGE_ALL_BOOKS_LETTER:
543 1
                return new PageAllBooksLetter ($id, $query, $n);
544 50
            case Base::PAGE_ALL_RECENT_BOOKS :
545 4
                return new PageRecentBooks ($id, $query, $n);
546 46
            case Base::PAGE_SERIE_DETAIL :
547 1
                return new PageSerieDetail ($id, $query, $n);
548 45
            case Base::PAGE_OPENSEARCH_QUERY :
549 31
                return new PageQueryResult ($id, $query, $n);
550 14
            case Base::PAGE_BOOK_DETAIL :
551 1
                return new PageBookDetail ($id, $query, $n);
552 13
            case Base::PAGE_ALL_PUBLISHERS:
553 2
                return new PageAllPublishers ($id, $query, $n);
554 11
            case Base::PAGE_PUBLISHER_DETAIL :
555 1
                return new PagePublisherDetail ($id, $query, $n);
556 10
            case Base::PAGE_ABOUT :
557
                return new PageAbout ($id, $query, $n);
558 10
            case Base::PAGE_CUSTOMIZE :
559
                return new PageCustomize ($id, $query, $n);
560 10
            default:
561 10
                $page = new Page ($id, $query, $n);
562 10
                $page->idPage = "cops:catalog";
563 10
                return $page;
564 10
        }
565
    }
566
567 81
    public function __construct($pid, $pquery, $pn) {
568 81
        global $config;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
569
570 81
        $this->idGet = $pid;
571 81
        $this->query = $pquery;
572 81
        $this->n = $pn;
573 81
        $this->favicon = $config['cops_icon'];
574 81
        $this->authorName = empty($config['cops_author_name']) ? utf8_encode('Sébastien Lucas') : $config['cops_author_name'];
575 81
        $this->authorUri = empty($config['cops_author_uri']) ? 'http://blog.slucas.fr' : $config['cops_author_uri'];
576 81
        $this->authorEmail = empty($config['cops_author_email']) ? '[email protected]' : $config['cops_author_email'];
577 81
    }
578
579 10
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
580
    {
581 10
        global $config;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
582 10
        $this->title = $config['cops_title_default'];
583 10
        $this->subtitle = $config['cops_subtitle_default'];
584 10
        if (Base::noDatabaseSelected ()) {
585 2
            $i = 0;
586 2
            foreach (Base::getDbNameList () as $key) {
587 2
                $nBooks = Book::getBookCount ($i);
588 2
                array_push ($this->entryArray, new Entry ($key, "cops:{$i}:catalog",
589 2
                                        str_format (localize ("bookword", $nBooks), $nBooks), "text",
590 2
                                        array ( new LinkNavigation ("?" . DB . "={$i}")), "", $nBooks));
591 2
                $i++;
592 2
                Base::clearDb ();
593 2
            }
594 2
        } else {
595 8
            if (!in_array (PageQueryResult::SCOPE_AUTHOR, getCurrentOption ('ignored_categories'))) {
596 7
                array_push ($this->entryArray, Author::getCount());
597 7
            }
598 8 View Code Duplication
            if (!in_array (PageQueryResult::SCOPE_SERIES, getCurrentOption ('ignored_categories'))) {
599 7
                $series = Serie::getCount();
600 7
                if (!is_null ($series)) array_push ($this->entryArray, $series);
601 7
            }
602 8 View Code Duplication
            if (!in_array (PageQueryResult::SCOPE_PUBLISHER, getCurrentOption ('ignored_categories'))) {
603 7
                $publisher = Publisher::getCount();
604 7
                if (!is_null ($publisher)) array_push ($this->entryArray, $publisher);
605 7
            }
606 8 View Code Duplication
            if (!in_array (PageQueryResult::SCOPE_TAG, getCurrentOption ('ignored_categories'))) {
607 7
                $tags = Tag::getCount();
608 7
                if (!is_null ($tags)) array_push ($this->entryArray, $tags);
609 7
            }
610 8 View Code Duplication
            if (!in_array (PageQueryResult::SCOPE_RATING, getCurrentOption ('ignored_categories'))) {
611 8
                $rating = Rating::getCount();
612 8
                if (!is_null ($rating)) array_push ($this->entryArray, $rating);
613 8
            }
614 8 View Code Duplication
            if (!in_array ("language", getCurrentOption ('ignored_categories'))) {
615 7
                $languages = Language::getCount();
616 7
                if (!is_null ($languages)) array_push ($this->entryArray, $languages);
617 7
            }
618 8
            foreach ($config['cops_calibre_custom_column'] as $lookup) {
619 4
                $customId = CustomColumn::getCustomId ($lookup);
620 4
                if (!is_null ($customId)) {
621 4
                    array_push ($this->entryArray, CustomColumn::getCount($customId));
622 4
                }
623 8
            }
624 8
            $this->entryArray = array_merge ($this->entryArray, Book::getCount());
625
626 8
            if (Base::isMultipleDatabaseEnabled ()) $this->title =  Base::getDbName ();
627
        }
628 10
    }
629
630 17
    public function isPaginated ()
631
    {
632 17
        return (getCurrentOption ("max_item_per_page") != -1 &&
633 17
                $this->totalNumber != -1 &&
634 17
                $this->totalNumber > getCurrentOption ("max_item_per_page"));
635
    }
636
637 2 View Code Duplication
    public function getNextLink ()
638
    {
639 2
        $currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . getQueryString ());
640 2
        if (($this->n) * getCurrentOption ("max_item_per_page") < $this->totalNumber) {
641 1
            return new LinkNavigation ($currentUrl . "&n=" . ($this->n + 1), "next", localize ("paging.next.alternate"));
642
        }
643 1
        return NULL;
644
    }
645
646 2 View Code Duplication
    public function getPrevLink ()
647
    {
648 2
        $currentUrl = preg_replace ("/\&n=.*?$/", "", "?" . getQueryString ());
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal /\&n=.*?$/ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style Comprehensibility introduced by
The string literal does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style Comprehensibility introduced by
The string literal ? does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
649 2
        if ($this->n > 1) {
650 1
            return new LinkNavigation ($currentUrl . "&n=" . ($this->n - 1), "previous", localize ("paging.previous.alternate"));
651
        }
652 2
        return NULL;
653
    }
654
655 2
    public function getMaxPage ()
656
    {
657 2
        return ceil ($this->totalNumber / getCurrentOption ("max_item_per_page"));
658
    }
659
660 70
    public function containsBook ()
661
    {
662 70
        if (count ($this->entryArray) == 0) return false;
663 68
        if (get_class ($this->entryArray [0]) == "EntryBook") return true;
664 46
        return false;
665
    }
666
}
667
668 View Code Duplication
class PageAllAuthors extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
669
{
670 3
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
671
    {
672 3
        $this->title = localize("authors.title");
673 3
        if (getCurrentOption ("author_split_first_letter") == 1) {
674 2
            $this->entryArray = Author::getAllAuthorsByFirstLetter();
675 2
        }
676
        else {
677 1
            $this->entryArray = Author::getAllAuthors();
678
        }
679 3
        $this->idPage = Author::ALL_AUTHORS_ID;
680 3
    }
681
}
682
683
class PageAllAuthorsLetter extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
684
{
685 1
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
686
    {
687 1
        $this->idPage = Author::getEntryIdByLetter ($this->idGet);
688 1
        $this->entryArray = Author::getAuthorsByStartingLetter ($this->idGet);
689 1
        $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("authorword", count ($this->entryArray)), count ($this->entryArray)), $this->idGet);
690 1
    }
691
}
692
693 View Code Duplication
class PageAuthorDetail extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
694
{
695 7
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
696
    {
697 7
        $author = Author::getAuthorById ($this->idGet);
698 7
        $this->idPage = $author->getEntryId ();
699 7
        $this->title = $author->name;
700 7
        list ($this->entryArray, $this->totalNumber) = Book::getBooksByAuthor ($this->idGet, $this->n);
701 7
    }
702
}
703
704
class PageAllPublishers extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
705
{
706 2
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
707
    {
708 2
        $this->title = localize("publishers.title");
709 2
        $this->entryArray = Publisher::getAllPublishers();
710 2
        $this->idPage = Publisher::ALL_PUBLISHERS_ID;
711 2
    }
712
}
713
714 View Code Duplication
class PagePublisherDetail extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
715
{
716 1
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
717
    {
718 1
        $publisher = Publisher::getPublisherById ($this->idGet);
719 1
        $this->title = $publisher->name;
720 1
        list ($this->entryArray, $this->totalNumber) = Book::getBooksByPublisher ($this->idGet, $this->n);
721 1
        $this->idPage = $publisher->getEntryId ();
722 1
    }
723
}
724
725
class PageAllTags extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
726
{
727 2
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
728
    {
729 2
        $this->title = localize("tags.title");
730 2
        $this->entryArray = Tag::getAllTags();
731 2
        $this->idPage = Tag::ALL_TAGS_ID;
732 2
    }
733
}
734
735
class PageAllLanguages extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
736
{
737 2
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
738
    {
739 2
        $this->title = localize("languages.title");
740 2
        $this->entryArray = Language::getAllLanguages();
741 2
        $this->idPage = Language::ALL_LANGUAGES_ID;
742 2
    }
743
}
744
745
class PageCustomDetail extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
746
{
747 3
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
748
    {
749 3
        $customId = getURLParam ("custom", NULL);
750 3
        $custom = CustomColumn::getCustomById ($customId, $this->idGet);
751 3
        $this->idPage = $custom->getEntryId ();
752 3
        $this->title = $custom->name;
753 3
        list ($this->entryArray, $this->totalNumber) = Book::getBooksByCustom ($customId, $this->idGet, $this->n);
754 3
    }
755
}
756
757
class PageAllCustoms extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
758
{
759 3
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
760
    {
761 3
        $customId = getURLParam ("custom", NULL);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal custom does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
762 3
        $this->title = CustomColumn::getAllTitle ($customId);
763 3
        $this->entryArray = CustomColumn::getAllCustoms($customId);
764 3
        $this->idPage = CustomColumn::getAllCustomsId ($customId);
765 3
    }
766
}
767
768 View Code Duplication
class PageTagDetail extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
769
{
770 1
    public function InitializeContent ()
771
    {
772 1
        $tag = Tag::getTagById ($this->idGet);
773 1
        $this->idPage = $tag->getEntryId ();
774 1
        $this->title = $tag->name;
775 1
        list ($this->entryArray, $this->totalNumber) = Book::getBooksByTag ($this->idGet, $this->n);
776 1
    }
777
}
778
779 View Code Duplication
class PageLanguageDetail extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
780
{
781 1
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
782
    {
783 1
        $language = Language::getLanguageById ($this->idGet);
784 1
        $this->idPage = $language->getEntryId ();
785 1
        $this->title = $language->lang_code;
786 1
        list ($this->entryArray, $this->totalNumber) = Book::getBooksByLanguage ($this->idGet, $this->n);
787 1
    }
788
}
789
790
class PageAllSeries extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
791
{
792 2
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
793
    {
794 2
        $this->title = localize("series.title");
795 2
        $this->entryArray = Serie::getAllSeries();
796 2
        $this->idPage = Serie::ALL_SERIES_ID;
797 2
    }
798
}
799
800 View Code Duplication
class PageSerieDetail extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
801
{
802 1
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
803
    {
804 1
        $serie = Serie::getSerieById ($this->idGet);
805 1
        $this->title = $serie->name;
806 1
        list ($this->entryArray, $this->totalNumber) = Book::getBooksBySeries ($this->idGet, $this->n);
807 1
        $this->idPage = $serie->getEntryId ();
808 1
    }
809
}
810
811
class PageAllRating extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
812
{
813 1
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
814
    {
815 1
        $this->title = localize("ratings.title");
816 1
        $this->entryArray = Rating::getAllRatings();
817 1
        $this->idPage = Rating::ALL_RATING_ID;
818 1
    }
819
}
820
821
class PageRatingDetail extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
822
{
823 1
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
824
    {
825 1
        $rating = Rating::getRatingById ($this->idGet);
826 1
        $this->idPage = $rating->getEntryId ();
827 1
        $this->title =str_format (localize ("ratingword", $rating->name/2), $rating->name/2);
828 1
        list ($this->entryArray, $this->totalNumber) = Book::getBooksByRating ($this->idGet, $this->n);
829 1
    }
830
}
831
832 View Code Duplication
class PageAllBooks extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
833
{
834 3
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
835
    {
836 3
        $this->title = localize ("allbooks.title");
837 3
        if (getCurrentOption ("titles_split_first_letter") == 1) {
838 2
            $this->entryArray = Book::getAllBooks();
839 2
        }
840
        else {
841 1
            list ($this->entryArray, $this->totalNumber) = Book::getBooks ($this->n);
842
        }
843 3
        $this->idPage = Book::ALL_BOOKS_ID;
844 3
    }
845
}
846
847
class PageAllBooksLetter extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
848
{
849 1
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
850
    {
851 1
        list ($this->entryArray, $this->totalNumber) = Book::getBooksByStartingLetter ($this->idGet, $this->n);
852 1
        $this->idPage = Book::getEntryIdByLetter ($this->idGet);
853
854 1
        $count = $this->totalNumber;
855 1
        if ($count == -1)
856 1
            $count = count ($this->entryArray);
857
858 1
        $this->title = str_format (localize ("splitByLetter.letter"), str_format (localize ("bookword", $count), $count), $this->idGet);
859 1
    }
860
}
861
862
class PageRecentBooks extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
863
{
864 4
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
865
    {
866 4
        $this->title = localize ("recent.title");
867 4
        $this->entryArray = Book::getAllRecentBooks ();
868 4
        $this->idPage = Book::ALL_RECENT_BOOKS_ID;
869 4
    }
870
}
871
872
class PageQueryResult extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
873
{
874
    const SCOPE_TAG = "tag";
875
    const SCOPE_RATING = "rating";
876
    const SCOPE_SERIES = "series";
877
    const SCOPE_AUTHOR = "author";
878
    const SCOPE_BOOK = "book";
879
    const SCOPE_PUBLISHER = "publisher";
880
881 24
    private function useTypeahead () {
882 24
        return !is_null (getURLParam ("search"));
883
    }
884
885 29
    private function searchByScope ($scope, $limit = FALSE) {
886 29
        $n = $this->n;
887 29
        $numberPerPage = NULL;
888 29
        $queryNormedAndUp = $this->query;
889 29
        if (useNormAndUp ()) {
890 7
            $queryNormedAndUp = normAndUp ($this->query);
891 7
        }
892 29
        if ($limit) {
893 22
            $n = 1;
894 22
            $numberPerPage = 5;
895 22
        }
896
        switch ($scope) {
897 29
            case self::SCOPE_BOOK :
898 23
                $array = Book::getBooksByStartingLetter ('%' . $queryNormedAndUp, $n, NULL, $numberPerPage);
899 23
                break;
900 28
            case self::SCOPE_AUTHOR :
901 23
                $array = Author::getAuthorsForSearch ('%' . $queryNormedAndUp);
902 23
                break;
903 25
            case self::SCOPE_SERIES :
904 22
                $array = Serie::getAllSeriesByQuery ($queryNormedAndUp);
905 22
                break;
906 24
            case self::SCOPE_TAG :
907 23
                $array = Tag::getAllTagsByQuery ($queryNormedAndUp, $n, NULL, $numberPerPage);
908 23
                break;
909 23
            case self::SCOPE_PUBLISHER :
910 23
                $array = Publisher::getAllPublishersByQuery ($queryNormedAndUp);
911 23
                break;
912
            default:
913
                $array = Book::getBooksByQuery (
914
                    array ("all" => "%" . $queryNormedAndUp . "%"), $n);
915
        }
916
917 29
        return $array;
918
    }
919
920 22
    public function doSearchByCategory () {
921 22
        $database = GetUrlParam (DB);
922 22
        $out = array ();
923 22
        $pagequery = Base::PAGE_OPENSEARCH_QUERY;
924 22
        $dbArray = array ("");
925 22
        $d = $database;
926 22
        $query = $this->query;
927
        // Special case when no databases were chosen, we search on all databases
928 22
        if (Base::noDatabaseSelected ()) {
929 1
            $dbArray = Base::getDbNameList ();
930 1
            $d = 0;
931 1
        }
932 22
        foreach ($dbArray as $key) {
933 22
            if (Base::noDatabaseSelected ()) {
934 1
                array_push ($this->entryArray, new Entry ($key, DB . ":query:{$d}",
935 1
                                        " ", "text",
936 1
                                        array ( new LinkNavigation ("?" . DB . "={$d}")), "tt-header"));
937 1
                Base::getDb ($d);
938 1
            }
939 22
            foreach (array (PageQueryResult::SCOPE_BOOK,
940 22
                            PageQueryResult::SCOPE_AUTHOR,
941 22
                            PageQueryResult::SCOPE_SERIES,
942 22
                            PageQueryResult::SCOPE_TAG,
943 22
                            PageQueryResult::SCOPE_PUBLISHER) as $key) {
944 22
                if (in_array($key, getCurrentOption ('ignored_categories'))) {
945 3
                    continue;
946
                }
947 22
                $array = $this->searchByScope ($key, TRUE);
948
949 22
                $i = 0;
950 22
                if (count ($array) == 2 && is_array ($array [0])) {
951 22
                    $total = $array [1];
952 22
                    $array = $array [0];
953 22
                } else {
954 22
                    $total = count($array);
955
                }
956 22
                if ($total > 0) {
957
                    // Comment to help the perl i18n script
958
                    // str_format (localize("bookword", count($array))
959
                    // str_format (localize("authorword", count($array))
960
                    // str_format (localize("seriesword", count($array))
961
                    // str_format (localize("tagword", count($array))
962
                    // str_format (localize("publisherword", count($array))
963 21
                    array_push ($this->entryArray, new Entry (str_format (localize ("search.result.{$key}"), $this->query), DB . ":query:{$d}:{$key}",
964 21
                                        str_format (localize("{$key}word", $total), $total), "text",
965 21
                                        array ( new LinkNavigation ("?page={$pagequery}&query={$query}&db={$d}&scope={$key}")),
966 21
                                        Base::noDatabaseSelected () ? "" : "tt-header", $total));
967 21
                }
968 22
                if (!Base::noDatabaseSelected () && $this->useTypeahead ()) {
969 6
                    foreach ($array as $entry) {
970 6
                        array_push ($this->entryArray, $entry);
971 6
                        $i++;
972 6
                        if ($i > 4) { break; };
973 6
                    }
974 6
                }
975 22
            }
976 22
            $d++;
977 22
            if (Base::noDatabaseSelected ()) {
978 1
                Base::clearDb ();
979 1
            }
980 22
        }
981 22
        return $out;
982
    }
983
984 31
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
985
    {
986 31
        $scope = getURLParam ("scope");
987 31
        if (empty ($scope)) {
988 24
            $this->title = str_format (localize ("search.result"), $this->query);
989 24
        } else {
990
            // Comment to help the perl i18n script
991
            // str_format (localize ("search.result.author"), $this->query)
992
            // str_format (localize ("search.result.tag"), $this->query)
993
            // str_format (localize ("search.result.series"), $this->query)
994
            // str_format (localize ("search.result.book"), $this->query)
995
            // str_format (localize ("search.result.publisher"), $this->query)
996 7
            $this->title = str_format (localize ("search.result.{$scope}"), $this->query);
997
        }
998
999 31
        $crit = "%" . $this->query . "%";
1000
1001
        // Special case when we are doing a search and no database is selected
1002 31
        if (Base::noDatabaseSelected () && !$this->useTypeahead ()) {
1003 2
            $i = 0;
1004 2
            foreach (Base::getDbNameList () as $key) {
1005 2
                Base::clearDb ();
1006 2
                list ($array, $totalNumber) = Book::getBooksByQuery (array ("all" => $crit), 1, $i, 1);
1007 2
                array_push ($this->entryArray, new Entry ($key, DB . ":query:{$i}",
1008 2
                                        str_format (localize ("bookword", $totalNumber), $totalNumber), "text",
1009 2
                                        array ( new LinkNavigation ("?" . DB . "={$i}&page=9&query=" . $this->query)), "", $totalNumber));
1010 2
                $i++;
1011 2
            }
1012 2
            return;
1013
        }
1014 29
        if (empty ($scope)) {
1015 22
            $this->doSearchByCategory ();
1016 22
            return;
1017
        }
1018
1019 7
        $array = $this->searchByScope ($scope);
1020 7
        if (count ($array) == 2 && is_array ($array [0])) {
1021 2
            list ($this->entryArray, $this->totalNumber) = $array;
1022 2
        } else {
1023 5
            $this->entryArray = $array;
1024
        }
1025 7
    }
1026
}
1027
1028
class PageBookDetail extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
1029
{
1030 1
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
1031
    {
1032 1
        $this->book = Book::getBookById ($this->idGet);
1033 1
        $this->title = $this->book->title;
1034 1
    }
1035
}
1036
1037
class PageAbout extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
1038
{
1039
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
1040
    {
1041
        $this->title = localize ("about.title");
1042
    }
1043
}
1044
1045
class PageCustomize extends Page
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
1046
{
1047
    private function isChecked ($key, $testedValue = 1) {
1048
        $value = getCurrentOption ($key);
1049
        if (is_array ($value)) {
1050
            if (in_array ($testedValue, $value)) {
1051
                return "checked='checked'";
1052
            }
1053
        } else {
1054
            if ($value == $testedValue) {
1055
                return "checked='checked'";
1056
            }
1057
        }
1058
        return "";
1059
    }
1060
1061
    private function isSelected ($key, $value) {
1062
        if (getCurrentOption ($key) == $value) {
1063
            return "selected='selected'";
1064
        }
1065
        return "";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1066
    }
1067
1068
    private function getStyleList () {
1069
        $result = array ();
1070
        foreach (glob ("templates/" . getCurrentTemplate () . "/styles/style-*.css") as $filename) {
1071
            if (preg_match ('/styles\/style-(.*?)\.css/', $filename, $m)) {
1072
                array_push ($result, $m [1]);
1073
            }
1074
        }
1075
        return $result;
1076
    }
1077
1078
    public function InitializeContent ()
0 ignored issues
show
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
1079
    {
1080
        $this->title = localize ("customize.title");
1081
        $this->entryArray = array ();
1082
1083
        $ignoredBaseArray = array (PageQueryResult::SCOPE_AUTHOR,
1084
                                   PageQueryResult::SCOPE_TAG,
1085
                                   PageQueryResult::SCOPE_SERIES,
1086
                                   PageQueryResult::SCOPE_PUBLISHER,
1087
                                   PageQueryResult::SCOPE_RATING,
1088
                                   "language");
1089
1090
        $content = "";
1091
        array_push ($this->entryArray, new Entry ("Template", "",
1092
                                        "<span style='cursor: pointer;' onclick='$.cookie(\"template\", \"bootstrap\", { expires: 365 });window.location=$(\".headleft\").attr(\"href\");'>Click to switch to Bootstrap</span>", "text",
1093
                                        array ()));
1094
        if (!preg_match("/(Kobo|Kindle\/3.0|EBRD1101)/", $_SERVER['HTTP_USER_AGENT'])) {
1095
            $content .= '<select id="style" onchange="updateCookie (this);">';
1096 View Code Duplication
            foreach ($this-> getStyleList () as $filename) {
1097
                $content .= "<option value='{$filename}' " . $this->isSelected ("style", $filename) . ">{$filename}</option>";
1098
            }
1099
            $content .= '</select>';
1100
        } else {
1101 View Code Duplication
            foreach ($this-> getStyleList () as $filename) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1102
                $content .= "<input type='radio' onchange='updateCookieFromCheckbox (this);' id='style-{$filename}' name='style' value='{$filename}' " . $this->isChecked ("style", $filename) . " /><label for='style-{$filename}'> {$filename} </label>";
1103
            }
1104
        }
1105
        array_push ($this->entryArray, new Entry (localize ("customize.style"), "",
1106
                                        $content, "text",
1107
                                        array ()));
1108
        if (!useServerSideRendering ()) {
1109
            $content = '<input type="checkbox" onchange="updateCookieFromCheckbox (this);" id="use_fancyapps" ' . $this->isChecked ("use_fancyapps") . ' />';
1110
            array_push ($this->entryArray, new Entry (localize ("customize.fancybox"), "",
1111
                                            $content, "text",
1112
                                            array ()));
1113
        }
1114
        $content = '<input type="number" onchange="updateCookie (this);" id="max_item_per_page" value="' . getCurrentOption ("max_item_per_page") . '" min="-1" max="1200" pattern="^[-+]?[0-9]+$" />';
1115
        array_push ($this->entryArray, new Entry (localize ("customize.paging"), "",
1116
                                        $content, "text",
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal text does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1117
                                        array ()));
1118
        $content = '<input type="text" onchange="updateCookie (this);" id="email" value="' . getCurrentOption ("email") . '" />';
1119
        array_push ($this->entryArray, new Entry (localize ("customize.email"), "",
1120
                                        $content, "text",
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal text does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1121
                                        array ()));
1122
        $content = '<input type="checkbox" onchange="updateCookieFromCheckbox (this);" id="html_tag_filter" ' . $this->isChecked ("html_tag_filter") . ' />';
1123
        array_push ($this->entryArray, new Entry (localize ("customize.filter"), "",
1124
                                        $content, "text",
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal text does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1125
                                        array ()));
1126
        $content = "";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1127
        foreach ($ignoredBaseArray as $key) {
1128
            $keyPlural = preg_replace ('/(ss)$/', 's', $key . "s");
1129
            $content .=  '<input type="checkbox" name="ignored_categories[]" onchange="updateCookieFromCheckboxGroup (this);" id="ignored_categories_' . $key . '" ' . $this->isChecked ("ignored_categories", $key) . ' > ' . localize ("{$keyPlural}.title") . '</input> ';
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal ignored_categories does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $keyPlural instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
1130
        }
1131
1132
        array_push ($this->entryArray, new Entry (localize ("customize.ignored"), "",
1133
                                        $content, "text",
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal text does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1134
                                        array ()));
1135
    }
1136
}
1137
1138
1139
abstract class Base
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
1140
{
1141
    const PAGE_INDEX = "index";
1142
    const PAGE_ALL_AUTHORS = "1";
1143
    const PAGE_AUTHORS_FIRST_LETTER = "2";
1144
    const PAGE_AUTHOR_DETAIL = "3";
1145
    const PAGE_ALL_BOOKS = "4";
1146
    const PAGE_ALL_BOOKS_LETTER = "5";
1147
    const PAGE_ALL_SERIES = "6";
1148
    const PAGE_SERIE_DETAIL = "7";
1149
    const PAGE_OPENSEARCH = "8";
1150
    const PAGE_OPENSEARCH_QUERY = "9";
1151
    const PAGE_ALL_RECENT_BOOKS = "10";
1152
    const PAGE_ALL_TAGS = "11";
1153
    const PAGE_TAG_DETAIL = "12";
1154
    const PAGE_BOOK_DETAIL = "13";
1155
    const PAGE_ALL_CUSTOMS = "14";
1156
    const PAGE_CUSTOM_DETAIL = "15";
1157
    const PAGE_ABOUT = "16";
1158
    const PAGE_ALL_LANGUAGES = "17";
1159
    const PAGE_LANGUAGE_DETAIL = "18";
1160
    const PAGE_CUSTOMIZE = "19";
1161
    const PAGE_ALL_PUBLISHERS = "20";
1162
    const PAGE_PUBLISHER_DETAIL = "21";
1163
    const PAGE_ALL_RATINGS = "22";
1164
    const PAGE_RATING_DETAIL = "23";
1165
1166
    const COMPATIBILITY_XML_ALDIKO = "aldiko";
1167
1168
    private static $db = NULL;
1169
1170 114
    public static function isMultipleDatabaseEnabled () {
1171 114
        global $config;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1172 114
        return is_array ($config['calibre_directory']);
1173
    }
1174
1175 46
    public static function useAbsolutePath () {
1176 46
        global $config;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1177 46
        $path = self::getDbDirectory();
1178 46
        return preg_match ('/^\//', $path) || // Linux /
1179 46
               preg_match ('/^\w\:/', $path); // Windows X:
1180
    }
1181
1182 45
    public static function noDatabaseSelected () {
1183 45
        return self::isMultipleDatabaseEnabled () && is_null (GetUrlParam (DB));
1184
    }
1185
1186 4 View Code Duplication
    public static function getDbList () {
1187 4
        global $config;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1188 4
        if (self::isMultipleDatabaseEnabled ()) {
1189 4
            return $config['calibre_directory'];
1190
        } else {
1191 1
            return array ("" => $config['calibre_directory']);
1192
        }
1193
    }
1194
1195 5 View Code Duplication
    public static function getDbNameList () {
1196 5
        global $config;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1197 5
        if (self::isMultipleDatabaseEnabled ()) {
1198 5
            return array_keys ($config['calibre_directory']);
1199
        } else {
1200
            return array ("");
1201
        }
1202
    }
1203
1204 1 View Code Duplication
    public static function getDbName ($database = NULL) {
1205 1
        global $config;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1206 1
        if (self::isMultipleDatabaseEnabled ()) {
1207 1
            if (is_null ($database)) $database = GetUrlParam (DB, 0);
1208 1
            if (!is_null($database) && !preg_match('/^\d+$/', $database)) {
1209
                return self::error ($database);
1210
            }
1211 1
            $array = array_keys ($config['calibre_directory']);
1212 1
            return  $array[$database];
1213
        }
1214
        return "";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1215
    }
1216
1217 96 View Code Duplication
    public static function getDbDirectory ($database = NULL) {
1218 96
        global $config;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1219 96
        if (self::isMultipleDatabaseEnabled ()) {
1220 9
            if (is_null ($database)) $database = GetUrlParam (DB, 0);
1221 9
            if (!is_null($database) && !preg_match('/^\d+$/', $database)) {
1222
                return self::error ($database);
1223
            }
1224 9
            $array = array_values ($config['calibre_directory']);
1225 9
            return  $array[$database];
1226
        }
1227 87
        return $config['calibre_directory'];
1228
    }
1229
1230
1231 67
    public static function getDbFileName ($database = NULL) {
1232 67
        return self::getDbDirectory ($database) .'metadata.db';
1233
    }
1234
1235 2
    private static function error ($database) {
1236 2
        if (php_sapi_name() != "cli") {
1237
            header("location: checkconfig.php?err=1");
1238
        }
1239 2
        throw new Exception("Database <{$database}> not found.");
1240
    }
1241
1242 132
    public static function getDb ($database = NULL) {
1243 132
        if (is_null (self::$db)) {
1244
            try {
1245 67
                if (is_readable (self::getDbFileName ($database))) {
1246 66
                    self::$db = new PDO('sqlite:'. self::getDbFileName ($database));
1247 66
                    if (useNormAndUp ()) {
1248 7
                        self::$db->sqliteCreateFunction ('normAndUp', 'normAndUp', 1);
1249 7
                    }
1250 66
                } else {
1251 2
                    self::error ($database);
1252
                }
1253 67
            } catch (Exception $e) {
1254 2
                self::error ($database);
1255
            }
1256 66
        }
1257 131
        return self::$db;
1258
    }
1259
1260 4
    public static function checkDatabaseAvailability () {
1261 4
        if (self::noDatabaseSelected ()) {
1262 3
            for ($i = 0; $i < count (self::getDbList ()); $i++) {
1263 3
                self::getDb ($i);
1264 2
                self::clearDb ();
1265 2
            }
1266 1
        } else {
1267 1
            self::getDb ();
1268
        }
1269 2
        return true;
1270
    }
1271
1272 64
    public static function clearDb () {
1273 64
        self::$db = NULL;
1274 64
    }
1275
1276 13
    public static function executeQuerySingle ($query, $database = NULL) {
1277 13
        return self::getDb ($database)->query($query)->fetchColumn();
1278
    }
1279
1280 8
    public static function getCountGeneric($table, $id, $pageId, $numberOfString = NULL) {
1281 8
        if (!$numberOfString) {
1282 7
            $numberOfString = $table . ".alphabetical";
1283 7
        }
1284 8
        $count = self::executeQuerySingle ('select count(*) from ' . $table);
1285 8
        if ($count == 0) return NULL;
1286 8
        $entry = new Entry (localize($table . ".title"), $id,
1287 8
            str_format (localize($numberOfString, $count), $count), "text",
1288 8
            array ( new LinkNavigation ("?page=".$pageId)), "", $count);
1289 8
        return $entry;
1290
    }
1291
1292 35
    public static function getEntryArrayWithBookNumber ($query, $columns, $params, $category) {
1293 35
        list (, $result) = self::executeQuery ($query, $columns, "", $params, -1);
1294 35
        $entryArray = array();
1295 35
        while ($post = $result->fetchObject ())
1296
        {
1297 25
            $instance = new $category ($post);
1298 25
            if (property_exists($post, "sort")) {
1299 17
                $title = $post->sort;
1300 17
            } else {
1301 8
                $title = $post->name;
1302
            }
1303 25
            array_push ($entryArray, new Entry ($title, $instance->getEntryId (),
1304 25
                str_format (localize("bookword", $post->count), $post->count), "text",
1305 25
                array ( new LinkNavigation ($instance->getUri ())), "", $post->count));
1306 25
        }
1307 35
        return $entryArray;
1308
    }
1309
1310 73
    public static function executeQuery($query, $columns, $filter, $params, $n, $database = NULL, $numberPerPage = NULL) {
1311 73
        $totalResult = -1;
1312
1313 73
        if (useNormAndUp ()) {
1314 7
            $query = preg_replace("/upper/", "normAndUp", $query);
1315 7
            $columns = preg_replace("/upper/", "normAndUp", $columns);
1316 7
        }
1317
1318 73
        if (is_null ($numberPerPage)) {
1319 71
            $numberPerPage = getCurrentOption ("max_item_per_page");
1320 71
        }
1321
1322 73
        if ($numberPerPage != -1 && $n != -1)
1323 73
        {
1324
            // First check total number of results
1325 28
            $result = self::getDb ($database)->prepare (str_format ($query, "count(*)", $filter));
1326 28
            $result->execute ($params);
1327 28
            $totalResult = $result->fetchColumn ();
1328
1329
            // Next modify the query and params
1330 28
            $query .= " limit ?, ?";
1331 28
            array_push ($params, ($n - 1) * $numberPerPage, $numberPerPage);
1332 28
        }
1333
1334 73
        $result = self::getDb ($database)->prepare(str_format ($query, $columns, $filter));
1335 73
        $result->execute ($params);
1336 73
        return array ($totalResult, $result);
1337
    }
1338
1339
}
1340