Completed
Pull Request — globalErrorHandling (#3230)
by Andreas
10:02 queued 06:44
created

infoutils.php ➔ dbglog()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 2
nop 2
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
1
<?php
2
/**
3
 * Information and debugging functions
4
 *
5
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6
 * @author     Andreas Gohr <[email protected]>
7
 */
8
9
use dokuwiki\HTTP\DokuHTTPClient;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, DokuHTTPClient.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
10
use dokuwiki\Logger;
11
12
if(!defined('DOKU_MESSAGEURL')){
13
    if(in_array('ssl', stream_get_transports())) {
14
        define('DOKU_MESSAGEURL','https://update.dokuwiki.org/check/');
15
    }else{
16
        define('DOKU_MESSAGEURL','http://update.dokuwiki.org/check/');
17
    }
18
}
19
20
/**
21
 * Check for new messages from upstream
22
 *
23
 * @author Andreas Gohr <[email protected]>
24
 */
25
function checkUpdateMessages(){
26
    global $conf;
27
    global $INFO;
28
    global $updateVersion;
29
    if(!$conf['updatecheck']) return;
30
    if($conf['useacl'] && !$INFO['ismanager']) return;
31
32
    $cf = getCacheName($updateVersion, '.updmsg');
33
    $lm = @filemtime($cf);
34
    $is_http = substr(DOKU_MESSAGEURL, 0, 5) != 'https';
35
36
    // check if new messages needs to be fetched
37
    if($lm < time()-(60*60*24) || $lm < @filemtime(DOKU_INC.DOKU_SCRIPT)){
38
        @touch($cf);
39
        Logger::debug("checkUpdateMessages(): downloading messages to ".$cf.($is_http?' (without SSL)':' (with SSL)'));
40
        $http = new DokuHTTPClient();
41
        $http->timeout = 12;
42
        $resp = $http->get(DOKU_MESSAGEURL.$updateVersion);
43
        if(is_string($resp) && ($resp == "" || substr(trim($resp), -1) == '%')) {
44
            // basic sanity check that this is either an empty string response (ie "no messages")
45
            // or it looks like one of our messages, not WiFi login or other interposed response
46
            io_saveFile($cf,$resp);
47
        } else {
48
            Logger::debug("checkUpdateMessages(): unexpected HTTP response received", $http->error);
49
        }
50
    }else{
51
        Logger::debug("checkUpdateMessages(): messages up to date");
52
    }
53
54
    $data = io_readFile($cf);
55
    // show messages through the usual message mechanism
56
    $msgs = explode("\n%\n",$data);
57
    foreach($msgs as $msg){
58
        if($msg) msg($msg,2);
59
    }
60
}
61
62
63
/**
64
 * Return DokuWiki's version (split up in date and type)
65
 *
66
 * @author Andreas Gohr <[email protected]>
67
 */
68
function getVersionData(){
69
    $version = array();
70
    //import version string
71
    if(file_exists(DOKU_INC.'VERSION')){
72
        //official release
73
        $version['date'] = trim(io_readFile(DOKU_INC.'VERSION'));
74
        $version['type'] = 'Release';
75
    }elseif(is_dir(DOKU_INC.'.git')){
76
        $version['type'] = 'Git';
77
        $version['date'] = 'unknown';
78
79
        $inventory = DOKU_INC.'.git/logs/HEAD';
80
        if(is_file($inventory)){
81
            $sz   = filesize($inventory);
82
            $seek = max(0,$sz-2000); // read from back of the file
83
            $fh   = fopen($inventory,'rb');
84
            fseek($fh,$seek);
85
            $chunk = fread($fh,2000);
86
            fclose($fh);
87
            $chunk = trim($chunk);
88
            $chunk = @array_pop(explode("\n",$chunk));   //last log line
0 ignored issues
show
Bug introduced by
explode(' ', $chunk) cannot be passed to array_pop() as the parameter $array expects a reference.
Loading history...
89
            $chunk = @array_shift(explode("\t",$chunk)); //strip commit msg
0 ignored issues
show
Bug introduced by
explode(' ', $chunk) cannot be passed to array_shift() as the parameter $array expects a reference.
Loading history...
90
            $chunk = explode(" ",$chunk);
91
            array_pop($chunk); //strip timezone
92
            $date = date('Y-m-d',array_pop($chunk));
93
            if($date) $version['date'] = $date;
94
        }
95
    }else{
96
        global $updateVersion;
97
        $version['date'] = 'update version '.$updateVersion;
98
        $version['type'] = 'snapshot?';
99
    }
100
    return $version;
101
}
102
103
/**
104
 * Return DokuWiki's version (as a string)
105
 *
106
 * @author Anika Henke <[email protected]>
107
 */
108
function getVersion(){
109
    $version = getVersionData();
110
    return $version['type'].' '.$version['date'];
111
}
112
113
/**
114
 * Run a few sanity checks
115
 *
116
 * @author Andreas Gohr <[email protected]>
117
 */
118
function check(){
119
    global $conf;
120
    global $INFO;
121
    /* @var Input $INPUT */
122
    global $INPUT;
123
124
    if ($INFO['isadmin'] || $INFO['ismanager']){
125
        msg('DokuWiki version: '.getVersion(),1);
126
127
        if(version_compare(phpversion(),'7.2.0','<')){
128
            msg('Your PHP version is too old ('.phpversion().' vs. 7.2+ needed)',-1);
129
        }else{
130
            msg('PHP version '.phpversion(),1);
131
        }
132
    } else {
133
        if(version_compare(phpversion(),'7.2.0','<')){
134
            msg('Your PHP version is too old',-1);
135
        }
136
    }
137
138
    $mem = (int) php_to_byte(ini_get('memory_limit'));
139
    if($mem){
140
        if ($mem === -1) {
141
            msg('PHP memory is unlimited', 1);
142
        } else if ($mem < 16777216) {
143
            msg('PHP is limited to less than 16MB RAM (' . filesize_h($mem) . ').
144
            Increase memory_limit in php.ini', -1);
145
        } else if ($mem < 20971520) {
146
            msg('PHP is limited to less than 20MB RAM (' . filesize_h($mem) . '),
147
                you might encounter problems with bigger pages. Increase memory_limit in php.ini', -1);
148
        } else if ($mem < 33554432) {
149
            msg('PHP is limited to less than 32MB RAM (' . filesize_h($mem) . '),
150
                but that should be enough in most cases. If not, increase memory_limit in php.ini', 0);
151
        } else {
152
            msg('More than 32MB RAM (' . filesize_h($mem) . ') available.', 1);
153
        }
154
    }
155
156
    if(is_writable($conf['changelog'])){
157
        msg('Changelog is writable',1);
158
    }else{
159
        if (file_exists($conf['changelog'])) {
160
            msg('Changelog is not writable',-1);
161
        }
162
    }
163
164
    if (isset($conf['changelog_old']) && file_exists($conf['changelog_old'])) {
165
        msg('Old changelog exists', 0);
166
    }
167
168
    if (file_exists($conf['changelog'].'_failed')) {
169
        msg('Importing old changelog failed', -1);
170
    } else if (file_exists($conf['changelog'].'_importing')) {
171
        msg('Importing old changelog now.', 0);
172
    } else if (file_exists($conf['changelog'].'_import_ok')) {
173
        msg('Old changelog imported', 1);
174
        if (!plugin_isdisabled('importoldchangelog')) {
175
            msg('Importoldchangelog plugin not disabled after import', -1);
176
        }
177
    }
178
179
    if(is_writable(DOKU_CONF)){
180
        msg('conf directory is writable',1);
181
    }else{
182
        msg('conf directory is not writable',-1);
183
    }
184
185
    if($conf['authtype'] == 'plain'){
186
        global $config_cascade;
187
        if(is_writable($config_cascade['plainauth.users']['default'])){
188
            msg('conf/users.auth.php is writable',1);
189
        }else{
190
            msg('conf/users.auth.php is not writable',0);
191
        }
192
    }
193
194
    if(function_exists('mb_strpos')){
195
        if(defined('UTF8_NOMBSTRING')){
196
            msg('mb_string extension is available but will not be used',0);
197
        }else{
198
            msg('mb_string extension is available and will be used',1);
199
            if(ini_get('mbstring.func_overload') != 0){
200
                msg('mb_string function overloading is enabled, this will cause problems and should be disabled',-1);
201
            }
202
        }
203
    }else{
204
        msg('mb_string extension not available - PHP only replacements will be used',0);
205
    }
206
207
    if (!UTF8_PREGSUPPORT) {
208
        msg('PHP is missing UTF-8 support in Perl-Compatible Regular Expressions (PCRE)', -1);
209
    }
210
    if (!UTF8_PROPERTYSUPPORT) {
211
        msg('PHP is missing Unicode properties support in Perl-Compatible Regular Expressions (PCRE)', -1);
212
    }
213
214
    $loc = setlocale(LC_ALL, 0);
215
    if(!$loc){
216
        msg('No valid locale is set for your PHP setup. You should fix this',-1);
217
    }elseif(stripos($loc,'utf') === false){
218
        msg('Your locale <code>'.hsc($loc).'</code> seems not to be a UTF-8 locale,
219
             you should fix this if you encounter problems.',0);
220
    }else{
221
        msg('Valid locale '.hsc($loc).' found.', 1);
222
    }
223
224
    if($conf['allowdebug']){
225
        msg('Debugging support is enabled. If you don\'t need it you should set $conf[\'allowdebug\'] = 0',-1);
226
    }else{
227
        msg('Debugging support is disabled',1);
228
    }
229
230
    if($INFO['userinfo']['name']){
231
        msg('You are currently logged in as '.$INPUT->server->str('REMOTE_USER').' ('.$INFO['userinfo']['name'].')',0);
232
        msg('You are part of the groups '.join($INFO['userinfo']['grps'],', '),0);
233
    }else{
234
        msg('You are currently not logged in',0);
235
    }
236
237
    msg('Your current permission for this page is '.$INFO['perm'],0);
238
239
    if (file_exists($INFO['filepath']) && is_writable($INFO['filepath'])) {
240
        msg('The current page is writable by the webserver', 1);
241
    } elseif (!file_exists($INFO['filepath']) && is_writable(dirname($INFO['filepath']))) {
242
        msg('The current page can be created by the webserver', 1);
243
    } else {
244
        msg('The current page is not writable by the webserver', -1);
245
    }
246
247
    if ($INFO['writable']) {
248
        msg('The current page is writable by you', 1);
249
    } else {
250
        msg('The current page is not writable by you', -1);
251
    }
252
253
    // Check for corrupted search index
254
    $lengths = idx_listIndexLengths();
255
    $index_corrupted = false;
256
    foreach ($lengths as $length) {
257
        if (count(idx_getIndex('w', $length)) != count(idx_getIndex('i', $length))) {
258
            $index_corrupted = true;
259
            break;
260
        }
261
    }
262
263
    foreach (idx_getIndex('metadata', '') as $index) {
264
        if (count(idx_getIndex($index.'_w', '')) != count(idx_getIndex($index.'_i', ''))) {
265
            $index_corrupted = true;
266
            break;
267
        }
268
    }
269
270
    if($index_corrupted) {
271
        msg(
272
            'The search index is corrupted. It might produce wrong results and most
273
                probably needs to be rebuilt. See
274
                <a href="http://www.dokuwiki.org/faq:searchindex">faq:searchindex</a>
275
                for ways to rebuild the search index.', -1
276
        );
277
    } elseif(!empty($lengths)) {
278
        msg('The search index seems to be working', 1);
279
    } else {
280
        msg(
281
            'The search index is empty. See
282
                <a href="http://www.dokuwiki.org/faq:searchindex">faq:searchindex</a>
283
                for help on how to fix the search index. If the default indexer
284
                isn\'t used or the wiki is actually empty this is normal.'
285
        );
286
    }
287
288
    // rough time check
289
    $http = new DokuHTTPClient();
290
    $http->max_redirect = 0;
291
    $http->timeout = 3;
292
    $http->sendRequest('http://www.dokuwiki.org', '', 'HEAD');
293
    $now = time();
294
    if(isset($http->resp_headers['date'])) {
295
        $time = strtotime($http->resp_headers['date']);
296
        $diff = $time - $now;
297
298
        if(abs($diff) < 4) {
299
            msg("Server time seems to be okay. Diff: {$diff}s", 1);
300
        } else {
301
            msg("Your server's clock seems to be out of sync!
302
                 Consider configuring a sync with a NTP server.  Diff: {$diff}s");
303
        }
304
    }
305
306
}
307
308
/**
309
 * Display a message to the user
310
 *
311
 * If HTTP headers were not sent yet the message is added
312
 * to the global message array else it's printed directly
313
 * using html_msgarea()
314
 *
315
 * Triggers INFOUTIL_MSG_SHOW
316
 *
317
 * @see    html_msgarea()
318
 * @param string $message
319
 * @param int    $lvl   -1 = error, 0 = info, 1 = success, 2 = notify
320
 * @param string $line  line number
321
 * @param string $file  file number
322
 * @param int    $allow who's allowed to see the message, see MSG_* constants
323
 */
324
function msg($message,$lvl=0,$line='',$file='',$allow=MSG_PUBLIC){
325
    global $MSG, $MSG_shown;
326
    static $errors = [
327
        -1 => 'error',
328
        0 => 'info',
329
        1 => 'success',
330
        2 => 'notify',
331
    ];
332
333
    $msgdata = [
334
        'msg' => $message,
335
        'lvl' => $errors[$lvl],
336
        'allow' => $allow,
337
        'line' => $line,
338
        'file' => $file,
339
    ];
340
341
    $evt = new \dokuwiki\Extension\Event('INFOUTIL_MSG_SHOW', $msgdata);
342
    if ($evt->advise_before()) {
343
        /* Show msg normally - event could suppress message show */
344
        if($msgdata['line'] || $msgdata['file']) {
345
            $basename = \dokuwiki\Utf8\PhpString::basename($msgdata['file']);
346
            $msgdata['msg'] .=' ['.$basename.':'.$msgdata['line'].']';
347
        }
348
349
        if(!isset($MSG)) $MSG = array();
350
        $MSG[] = $msgdata;
351
        if(isset($MSG_shown) || headers_sent()){
352
            if(function_exists('html_msgarea')){
353
                html_msgarea();
354
            }else{
355
                print "ERROR(".$msgdata['lvl'].") ".$msgdata['msg']."\n";
356
            }
357
            unset($GLOBALS['MSG']);
358
        }
359
    }
360
    $evt->advise_after();
361
    unset($evt);
362
}
363
/**
364
 * Determine whether the current user is allowed to view the message
365
 * in the $msg data structure
366
 *
367
 * @param  $msg   array    dokuwiki msg structure
368
 *                         msg   => string, the message
369
 *                         lvl   => int, level of the message (see msg() function)
370
 *                         allow => int, flag used to determine who is allowed to see the message
371
 *                                       see MSG_* constants
372
 * @return bool
373
 */
374
function info_msg_allowed($msg){
375
    global $INFO, $auth;
376
377
    // is the message public? - everyone and anyone can see it
378
    if (empty($msg['allow']) || ($msg['allow'] == MSG_PUBLIC)) return true;
379
380
    // restricted msg, but no authentication
381
    if (empty($auth)) return false;
382
383
    switch ($msg['allow']){
384
        case MSG_USERS_ONLY:
385
            return !empty($INFO['userinfo']);
386
387
        case MSG_MANAGERS_ONLY:
388
            return $INFO['ismanager'];
389
390
        case MSG_ADMINS_ONLY:
391
            return $INFO['isadmin'];
392
393
        default:
394
            trigger_error('invalid msg allow restriction.  msg="'.$msg['msg'].'" allow='.$msg['allow'].'"',
395
                          E_USER_WARNING);
396
            return $INFO['isadmin'];
397
    }
398
399
    return false;
0 ignored issues
show
Unused Code introduced by
return false; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
400
}
401
402
/**
403
 * print debug messages
404
 *
405
 * little function to print the content of a var
406
 *
407
 * @author Andreas Gohr <[email protected]>
408
 *
409
 * @param string $msg
410
 * @param bool $hidden
411
 */
412
function dbg($msg,$hidden=false){
413
    if($hidden){
414
        echo "<!--\n";
415
        print_r($msg);
416
        echo "\n-->";
417
    }else{
418
        echo '<pre class="dbg">';
419
        echo hsc(print_r($msg,true));
420
        echo '</pre>';
421
    }
422
}
423
424
/**
425
 * Print info to debug log file
426
 *
427
 * @author Andreas Gohr <[email protected]>
428
 * @deprecated 2020-08-13
429
 * @param string $msg
430
 * @param string $header
431
 */
432
function dbglog($msg,$header=''){
433
    dbg_deprecated('\\dokuwiki\\Logger');
434
435
    // was the msg as single line string? use it as header
436
    if($header === '' && is_string($msg) && strpos($msg, "\n") === false) {
437
        $header = $msg;
438
        $msg = '';
439
    }
440
441
    Logger::getInstance(Logger::LOG_DEBUG)->log(
442
        $header, $msg
443
    );
444
}
445
446
/**
447
 * Log accesses to deprecated fucntions to the debug log
448
 *
449
 * @param string $alternative The function or method that should be used instead
450
 * @triggers INFO_DEPRECATION_LOG
451
 */
452
function dbg_deprecated($alternative = '') {
453
    \dokuwiki\Debug\DebugHelper::dbgDeprecatedFunction($alternative, 2);
454
}
455
456
/**
457
 * Print a reversed, prettyprinted backtrace
458
 *
459
 * @author Gary Owen <[email protected]>
460
 */
461
function dbg_backtrace(){
462
    // Get backtrace
463
    $backtrace = debug_backtrace();
464
465
    // Unset call to debug_print_backtrace
466
    array_shift($backtrace);
467
468
    // Iterate backtrace
469
    $calls = array();
470
    $depth = count($backtrace) - 1;
471
    foreach ($backtrace as $i => $call) {
472
        $location = $call['file'] . ':' . $call['line'];
473
        $function = (isset($call['class'])) ?
474
            $call['class'] . $call['type'] . $call['function'] : $call['function'];
475
476
        $params = array();
477
        if (isset($call['args'])){
478
            foreach($call['args'] as $arg){
479
                if(is_object($arg)){
480
                    $params[] = '[Object '.get_class($arg).']';
481
                }elseif(is_array($arg)){
482
                    $params[] = '[Array]';
483
                }elseif(is_null($arg)){
484
                    $params[] = '[NULL]';
485
                }else{
486
                    $params[] = (string) '"'.$arg.'"';
487
                }
488
            }
489
        }
490
        $params = implode(', ',$params);
491
492
        $calls[$depth - $i] = sprintf('%s(%s) called at %s',
493
                $function,
494
                str_replace("\n", '\n', $params),
495
                $location);
496
    }
497
    ksort($calls);
498
499
    return implode("\n", $calls);
500
}
501
502
/**
503
 * Remove all data from an array where the key seems to point to sensitive data
504
 *
505
 * This is used to remove passwords, mail addresses and similar data from the
506
 * debug output
507
 *
508
 * @author Andreas Gohr <[email protected]>
509
 *
510
 * @param array $data
511
 */
512
function debug_guard(&$data){
513
    foreach($data as $key => $value){
514
        if(preg_match('/(notify|pass|auth|secret|ftp|userinfo|token|buid|mail|proxy)/i',$key)){
515
            $data[$key] = '***';
516
            continue;
517
        }
518
        if(is_array($value)) debug_guard($data[$key]);
519
    }
520
}
521