Completed
Pull Request — master (#274)
by Markus
05:34
created

base.php ➔ html2xhtml()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 26
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 4
nop 1
dl 0
loc 26
ccs 11
cts 11
cp 1
crap 3
rs 8.8571
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 11 and the first side effect is on line 9.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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 'config.php';
10
11
define ('VERSION', '1.0.2');
12
define ('DB', 'db');
13
date_default_timezone_set($config['default_timezone']);
14
15
16
function useServerSideRendering()
17 3
{
18 3
    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...
19
    return preg_match('/' . $config['cops_server_side_render'] . '/', $_SERVER['HTTP_USER_AGENT']);
20
}
21
22
function serverSideRender($data)
23 2
{
24 2
    // Get the templates
25 2
    $theme = getCurrentTemplate ();
26 2
    $header = file_get_contents('templates/' . $theme . '/header.html');
27 2
    $footer = file_get_contents('templates/' . $theme . '/footer.html');
28 2
    $main = file_get_contents('templates/' . $theme . '/main.html');
29
    $bookdetail = file_get_contents('templates/' . $theme . '/bookdetail.html');
30
    $page = file_get_contents('templates/' . $theme . '/page.html');
31 2
32 2
    // Generate the function for the template
33 2
    $template = new doT ();
34 2
    $dot = $template->template ($page, array ('bookdetail' => $bookdetail,
35 2
                                              'header' => $header,
36
                                              'footer' => $footer,
37
                                              'main' => $main));
38 2
    // If there is a syntax error in the function created
39
    // $dot will be equal to FALSE
40
    if (!$dot) {
41
        return FALSE;
42 2
    }
43
    // Execute the template
44
    if (!empty ($data)) {
45
        return $dot ($data);
46 2
    }
47
48
    return NULL;
49
}
50 18
51 16
function getQueryString()
52
{
53 2
    if (isset($_SERVER['QUERY_STRING'])) {
54
        return $_SERVER['QUERY_STRING'];
55
    }
56
    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...
57
}
58
59
function notFound()
60
{
61
    header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
62
    header('Status: 404 Not Found');
63
64 134
    $_SERVER['REDIRECT_STATUS'] = 404;
65 34
}
66
67 122
function getURLParam($name, $default = NULL)
68
{
69
    if (!empty ($_GET) && isset($_GET[$name]) && $_GET[$name] != '') {
70
        return $_GET[$name];
71 100
    }
72 100
    return $default;
73 2
}
74
75
function getCurrentOption($option)
76 2
{
77
    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...
78
    if (isset($_COOKIE[$option])) {
79 98
        if (isset($config ['cops_' . $option]) && is_array ($config ['cops_' . $option])) {
80 98
            return explode (',', $_COOKIE[$option]);
81
        } else {
82
            return $_COOKIE[$option];
83
        }
84
    }
85
    if (isset($config ['cops_' . $option])) {
86
        return $config ['cops_' . $option];
87 2
    }
88
89
    return '';
90
}
91 4
92
function getCurrentCss()
93
{
94
    return 'templates/' . getCurrentTemplate () . '/styles/style-' . getCurrentOption('style') . '.css';
95 69
}
96
97
function getCurrentTemplate()
98
{
99 37
    return getCurrentOption ('template');
100
}
101
102 37
function getUrlWithVersion($url)
103
{
104
    return $url . '?v=' . VERSION;
105
}
106
107
function xml2xhtml($xml)
108
{
109
    return preg_replace_callback('#<(\w+)([^>]*)\s*/>#s', function($m) {
110
        $xhtml_tags = array('br', 'hr', 'input', 'frame', 'img', 'area', 'link', 'col', 'base', 'basefont', 'param');
111
        if (in_array($m[1], $xhtml_tags)) {
112
            return '<' . $m[1] . $m[2] . ' />';
113
        } else {
114
            return '<' . $m[1] . $m[2] . '></' . $m[1] . '>';
115
        }
116
    }, $xml);
117
}
118
119
function display_xml_error($error)
0 ignored issues
show
Coding Style introduced by
As per coding-style, this function should be in camelCase.

CamelCase (...) is the practice of writing compound words or phrases such that
each word or abbreviation begins with a capital letter.

Learn more about camelCase.

Loading history...
120
{
121
    $return = '';
122
    $return .= str_repeat('-', $error->column) . "^\n";
123
124
    switch ($error->level) {
125
        case LIBXML_ERR_WARNING:
126
            $return .= 'Warning ' . $error->code . ': ';
127
            break;
128
         case LIBXML_ERR_ERROR:
129
            $return .= 'Error ' . $error->code . ': ';
130
            break;
131
        case LIBXML_ERR_FATAL:
132
            $return .= 'Fatal Error ' . $error->code . ': ';
133
            break;
134
    }
135 37
136
    $return .= trim($error->message) .
137 37
               "\n  Line: " . $error->line .
138
               "\n  Column: " . $error->column;
139 37
140 37
    if ($error->file) {
141
        $return .= "\n  File: " . $error->file;
142
    }
143
144 37
    return "$return\n\n--------------------------------------------\n\n";
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $return 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...
145 37
}
146
147 37
function are_libxml_errors_ok()
0 ignored issues
show
Coding Style introduced by
As per coding-style, this function should be in camelCase.

CamelCase (...) is the practice of writing compound words or phrases such that
each word or abbreviation begins with a capital letter.

Learn more about camelCase.

Loading history...
148 37
{
149 37
    $errors = libxml_get_errors();
150 37
151 37
    foreach ($errors as $error) {
152 37
        if ($error->code == 801) return false;
153 37
    }
154
    return true;
155
}
156
157
function html2xhtml($html)
158
{
159
    $doc = new DOMDocument();
160
    libxml_use_internal_errors(true);
161
162
    $doc->loadHTML('<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body>' .
163 37
                        $html  . '</body></html>'); // Load the HTML
164
    $output = $doc->saveXML($doc->documentElement); // Transform to an Ansi xml stream
165 37
    $output = xml2xhtml($output);
166 37
    if (preg_match ('#<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></meta></head><body>(.*)</body></html>#ms', $output, $matches)) {
167
        $output = $matches [1]; // Remove <html><body>
168
    }
169
    /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
36% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
170
    // In case of error with summary, use it to debug
171
    $errors = libxml_get_errors();
172
173
    foreach ($errors as $error) {
174 116
        $output .= display_xml_error($error);
175 116
    }
176
    */
177 116
178 116
    if (!are_libxml_errors_ok ()) $output = 'HTML code not valid.';
179 116
180 116
    libxml_use_internal_errors(false);
181 116
    return $output;
182 116
}
183 116
184
/**
185 116
 * This method is a direct copy-paste from
186
 * http://tmont.com/blargh/2010/1/string-format-in-php
187
 */
188
function str_format($format)
0 ignored issues
show
Unused Code introduced by
The parameter $format is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
As per coding-style, this function should be in camelCase.

CamelCase (...) is the practice of writing compound words or phrases such that
each word or abbreviation begins with a capital letter.

Learn more about camelCase.

Loading history...
189
{
190
    $args = func_get_args();
191
    $format = array_shift($args);
192
193
    preg_match_all('/(?=\{)\{(\d+)\}(?!\})/', $format, $matches, PREG_OFFSET_CAPTURE);
194 16
    $offset = 0;
195
    foreach ($matches[1] as $data) {
196 16
        $i = $data[0];
197
        $format = substr_replace($format, @$args[$i], $offset + $data[1] - 1, 2 + strlen($i));
198 16
        $offset += strlen(@$args[$i]) - 2 - strlen($i);
199 16
    }
200
201 3
    return $format;
202 3
}
203 16
204
/**
205 16
 * Get all accepted languages from the browser and put them in a sorted array
206 16
 * languages id are normalized : fr-fr -> fr_FR
207 16
 * @return array of languages
208
 */
209 16
function getAcceptLanguages()
210 11
{
211 11
    $langs = array();
212 11
213 11
    if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
214 16
        // break up string into pieces (languages and q factors)
215 16
        $accept = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
216 View Code Duplication
        if (preg_match('/^(\w{2})-\w{2}$/', $accept, $matches)) {
0 ignored issues
show
Duplication introduced by
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...
217 16
            // Special fix for IE11 which send fr-FR and nothing else
218
            $accept = $accept . ',' . $matches[1] . ';q=0.8';
219
        }
220 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);
221 16
222 16
        if (count($lang_parse[1])) {
223
            $langs = array();
224
            foreach ($lang_parse[1] as $lang) {
225 16
                // Format the language code (not standard among browsers)
226 16
                if (strlen($lang) == 5) {
227 16
                    $lang = str_replace('-', '_', $lang);
228
                    $splitted = preg_split('/_/', $lang);
229 16
                    $lang = $splitted[0] . '_' . strtoupper($splitted[1]);
230
                }
231
                array_push($langs, $lang);
232
            }
233
            // create a list like "en" => 0.8
234
            $langs = array_combine($langs, $lang_parse[4]);
235
236
            // set default to 1 for any without q factor
237 17
            foreach ($langs as $lang => $val) {
238 17
                if ($val === '') $langs[$lang] = 1;
239 17
            }
240 17
241
            // sort list based on value
242
            arsort($langs, SORT_NUMERIC);
243 17
        }
244 16
    }
245 16
246
    return $langs;
247 17
}
248 17
249 16
/**
250 16
 * Find the best translation file possible based on the accepted languages
251 16
 * @return array of language and language file
252 16
 */
253 16
function getLangAndTranslationFile()
254
{
255 17
    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...
256 17
    $langs = array();
257 3
    $lang = 'en';
258 3
    if (!empty($config['cops_language'])) {
259 17
        $lang = $config['cops_language'];
260
    }
261
    elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
262
        $langs = getAcceptLanguages();
263
    }
264
    //echo var_dump($langs);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
265
    $lang_file = NULL;
266
    foreach ($langs as $language => $val) {
267 136
        $temp_file = dirname(__FILE__). '/lang/Localization_' . $language . '.json';
268 136
        if (file_exists($temp_file)) {
269 136
            $lang = $language;
270 136
            $lang_file = $temp_file;
271 136
            break;
272 136
        }
273 136
    }
274
    if (empty ($lang_file)) {
275
        $lang_file = dirname(__FILE__). '/lang/Localization_' . $lang . '.json';
276 136
    }
277 136
    return array($lang, $lang_file);
278 16
}
279 16
280
/**
281 136
 * This method is based on this page
282 17
 * http://www.mind-it.info/2010/02/22/a-simple-approach-to-localization-in-php/
283 17
 */
284 17
function localize($phrase, $count=-1, $reset=false)
285 1
{
286 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...
287
    if ($count == 0)
288 17
        $phrase .= '.none';
289
    if ($count == 1)
290 17
        $phrase .= '.one';
291
    if ($count > 1)
292
        $phrase .= '.many';
293 17
294 17
    /* Static keyword is used to ensure the file is loaded only once */
295
    static $translations = NULL;
296
    if ($reset) {
297 17
        $translations = NULL;
298
    }
299 17
    /* If no instance of $translations has occured load the language file */
300 1
    if (is_null($translations)) {
301 1
        $lang_file_en = NULL;
302 1
        list ($lang, $lang_file) = getLangAndTranslationFile();
303 1
        if ($lang != 'en') {
304 17
            $lang_file_en = dirname(__FILE__). '/lang/' . 'Localization_en.json';
305 136
        }
306 136
307
        $lang_file_content = file_get_contents($lang_file);
308 3
        /* Load the language file as a JSON object and transform it into an associative array */
309
        $translations = json_decode($lang_file_content, true);
310
311
        /* Clean the array of all unfinished translations */
312 61
        foreach (array_keys ($translations) as $key) {
313 51
            if (preg_match ('/^##TODO##/', $key)) {
314 51
                unset ($translations [$key]);
315 61
            }
316 61
        }
317 15
        if (!is_null($lang_file_en)) {
318 15
            $lang_file_content = file_get_contents($lang_file_en);
319 15
            $translations_en = json_decode($lang_file_content, true);
320 61
            $translations = array_merge ($translations_en, $translations);
321 61
        }
322 61
    }
323
    if (array_key_exists ($phrase, $translations)) {
324
        return $translations[$phrase];
325 61
    }
326
    return $phrase;
327 61
}
328
329
function addURLParameter($urlParams, $paramName, $paramValue)
330
{
331 144
    if (empty ($urlParams)) {
332 144
        $urlParams = '';
333
    }
334
    $start = '';
335
    if (preg_match ('#^\?(.*)#', $urlParams, $matches)) {
336 8
        $start = '?';
337 8
        $urlParams = $matches[1];
338
    }
339
    $params = array();
340
    parse_str($urlParams, $params);
341 7
    if (empty ($paramValue) && $paramValue != 0) {
342
        unset ($params[$paramName]);
343
    } else {
344
        $params[$paramName] = $paramValue;
345
    }
346
    return $start . http_build_query($params);
347
}
348
349
function useNormAndUp()
350
{
351
    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...
352
    return $config ['cops_normalized_search'] == '1';
353
}
354
355
function normalizeUtf8String($s)
356
{
357
    include_once 'transliteration.php';
358
    return _transliteration_process($s);
359 117
}
360 117
361 117
function normAndUp($s)
362 117
{
363 117
    return mb_strtoupper(normalizeUtf8String($s), 'UTF-8');
364
}
365