Issues (1369)

classes/debug.php (1 issue)

1
<?php /** @noinspection PhpUnnecessaryCurlyVarSyntaxInspection */
2
3
/*
4
 * debug.php ::  Clase Debug, maneja reporte de eventos
5
 *
6
 * V4.0 copyright 2010-2011 by Gorlum for http://supernova.ws
7
 *  [!] Merged `errors` to `logs`
8
 *  [+] Now debugger can work with database detached. All messages would be dumped to page
9
 *  [+] Now `logs` has both human-readable and machine-readable fields
10
 *
11
 * V3.0 copyright 2010 by Gorlum for http://supernova.ws
12
 *  [+] Full rewrtie & optimize
13
 *  [*] Now there is fallback procedure if no link to db detected
14
 *
15
 * V2.0 copyright 2010 by Gorlum for http://supernova.ws
16
 *  [*] Now error also contains backtrace - to see exact way problem comes
17
 *  [*] New method 'warning' sends message to dedicated SQL-table for non-errors
18
 *
19
 * V1.0 Created by Perberos. All rights reversed (C) 2006
20
 *
21
 *  Experiment code!!!
22
 *
23
 * vamos a experimentar >:)
24
 * le veo futuro a las classes, ayudaria mucho a tener un codigo mas ordenado...
25
 * que esperabas!!! soy newbie!!! D':<
26
*/
27
28
use DBAL\db_mysql;
29
30
if (!defined('INSIDE')) {
31
  die("attemp hacking");
32
}
33
34
class debug {
35
  protected $log;
36
  protected $numqueries;
37
  protected $log_array;
38
39
  public function log_file($message, $ident_change = 0) {
40
    if (!defined('SN_DEBUG_LOG') || !SN_DEBUG_LOG) {
41
      return;
42
    }
43
44
    static $ident = 0;
45
    static $logFileName;
46
47
    if (!$logFileName) {
48
      $logFileName = SN_ROOT_PHYSICAL . '/.logs/supernova.log';
49
      file_put_contents($logFileName, "\r\n\r\n", FILE_APPEND);
50
    }
51
    if ($ident_change < 0) {
52
      $ident += $ident_change * 2;
53
    }
54
    file_put_contents($logFileName, date(FMT_DATE_TIME_SQL, time()) . str_repeat(' ', $ident + 1) . $message . "\r\n", FILE_APPEND);
55
    if ($ident_change > 0) {
56
      $ident += $ident_change * 2;
57
    }
58
  }
59
60
  public function log_sql($message, $ident_change = 0) {
61
    static $ident = 0;
62
    static $logFileName;
63
    static $mt_rand;
64
65
    if (!$mt_rand) {
66
      $mt_rand = mt_rand();
67
    }
68
69
    if (!$logFileName) {
70
      $dbName      = SN::$db->dbName;
71
      $logFileName = SN_ROOT_PHYSICAL . "/.logs/{$dbName}.mysql." . date('Y-m-d-H-i-s.') . sprintf("%06d", gettimeofday()["usec"]) . ".log";
72
    }
73
    if ($ident_change < 0) {
74
      $ident += $ident_change * 2;
75
    }
76
    file_put_contents($logFileName, str_repeat(' ', $ident) . $message . "\n\n", FILE_APPEND);
77
    if ($ident_change > 0) {
78
      $ident += $ident_change * 2;
79
    }
80
  }
81
82
  public function __construct() {
83
    $this->log        = '';
84
    $this->numqueries = 0;
85
  }
86
87
  function add($mes) {
88
    $this->log .= $mes;
89
    $this->numqueries++;
90
  }
91
92
  function add_to_array($mes) {
93
    $this->log_array[] = $mes;
94
  }
95
96
  function echo_log() {
97
    echo '<br><table><tr><td class=k colspan=4><a href="' . SN_ROOT_PHYSICAL . "admin/settings.php\">Debug Log</a>:</td></tr>{$this->log}</table>";
98
    die();
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
99
  }
100
101
  public function compact_backtrace($backtrace, $long_comment = false, $onlyLastX = null) {
102
    static $exclude_functions = array(
103
      // Caller not needed
104
      'comment_query',
105
      // Excluding includes/requires
106
      'include', 'include_once', 'require_once', 'require',
107
      // Excluding query calls/DB wrap functions
108
      'doquery',
109
      'db_get_record_list', // 'db_user_by_id', 'db_get_user_by_id',
110
      'doSelect', 'doSelectFetch',
111
      'db_query_update',
112
      'selectValue',
113
      // classPersistent
114
      '__get', 'db_loadItem', '__set', 'db_saveItem',
115
      // Constructors ?! Why not...
116
      '__construct',
117
      // Hook handlers
118
      'sn_function_call',
119
      // Chat
120
      'db_chat_player_list_online',
121
      // Transaction-related functions
122
      'db_transaction_commit', 'transactionCommit', 'transactionStart', 'db_transaction_start', 'db_transaction_rollback', 'transactionRollback',
123
    );
124
125
    $raw      = [];
126
    $filtered = [];
127
    foreach ($backtrace as $a_trace) {
128
      $function =
129
        (!empty($a_trace['type'])
130
          ? ($a_trace['type'] == '->'
131
            ? "({$a_trace['class']})" . get_class($a_trace['object'])
132
            : $a_trace['class']
133
          ) . $a_trace['type']
134
          : ''
135
        ) . $a_trace['function'] . '()';
136
137
      $file = str_replace(SN_ROOT_PHYSICAL, '', str_replace('\\', '/', !empty($a_trace['file']) ? $a_trace['file'] : ''));
138
139
      $line  = !empty($a_trace['line']) ? '@' . $a_trace['line'] : '';
140
      $raw[] = "{$function} - '{$file}'{$line}";
141
142
      if (!in_array($a_trace['function'], $exclude_functions)) {
143
        $filtered[] = &$raw[count($raw) - 1];
144
      }
145
146
      if (!$long_comment) {
147
        break;
148
      }
149
    }
150
151
    $raw      = array_reverse($raw);
152
    $filtered = array_reverse($filtered);
153
154
    if ($onlyLastX) {
155
      $raw      = array_slice($raw, -$onlyLastX);
156
      $filtered = array_slice($filtered, -$onlyLastX);
157
    }
158
159
    return [$raw, $filtered,];
160
  }
161
162
  /**
163
   * @param array  $backtrace
164
   * @param string $sql
165
   *
166
   * @return string
167
   */
168
  public function comment_query($backtrace, $sql) {
169
    list($raw, $filtered,) = $this->compact_backtrace($backtrace, defined('DEBUG_SQL_COMMENT_LONG'));
170
171
    $sql_commented = [
172
      "/* ",
173
      'date' => date("s.") . sprintf("%06d", gettimeofday()["usec"]),
174
      !empty($filtered) ? "\n" . implode("\n", $filtered) . " \n" : '',
175
      "*/\n",
176
      // SQL itself with removed double spaces
177
      preg_replace("/\s+/", ' ', $sql),
178
    ];
179
180
    if (defined('DEBUG_SQL_FILE_LOG') && !empty(DEBUG_SQL_FILE_LOG)) {
181
      $this->log_sql(implode('', $sql_commented));
182
    }
183
184
//      $sql_commented = '/* ' . implode("<br />", $sql_comment) . '<br /> */ ' . preg_replace("/\s+/", ' ', $sql);
185
//      $isSelect = strpos(strtoupper($query), 'SELECT') !== false ? 'true' : 'false';
186
    $sql_commented['date'] = date('Y-m-d H:i:') . $sql_commented['date'];
187
188
//      if(strpos($sql_comment, 'compact_backtrace') === false) {
189
//        $transaction_id = SN::db_transaction_check(false) ? SN::$transaction_id : SN::$transaction_id++;
190
//        $result[] = "tID {$transaction_id}";
191
//      }
192
193
    if (defined('DEBUG_SQL_ERROR')) {
194
      array_unshift($raw, preg_replace("/\s+/", ' ', $sql));
195
      array_unshift($raw, $sql_commented['date']);
196
      $this->add_to_array($raw);
197
    }
198
199
    $sql = implode('', $sql_commented);
200
201
    return $sql;
202
  }
203
204
205
  function dump($dump = false, $force_base = false, $deadlock = false) {
206
    if ($dump === false) {
207
      return [];
208
    }
209
210
    $error_backtrace = array();
211
    $base_dump       = false;
212
213
    if ($force_base === true) {
214
      $base_dump = true;
215
    }
216
217
    if ($dump === true) {
218
      $base_dump = true;
219
    } else {
220
      if (!is_array($dump)) {
221
        $dump = array('var' => $dump);
222
      }
223
224
      foreach ($dump as $dump_var_name => $dump_var) {
225
        if ($dump_var_name == 'base_dump') {
226
          $base_dump = $dump_var;
227
        } else {
228
          $error_backtrace[$dump_var_name] = $dump_var;
229
        }
230
      }
231
    }
232
233
    if ($deadlock && ($q = SN::$db->mysql_get_innodb_status())) {
234
      $error_backtrace['deadlock'] = explode("\n", $q['Status']);
235
      foreach ($error_backtrace['cSN_data'] as &$location) {
236
        foreach ($location as $location_id => &$location_data) //          $location_data = $location_id;
237
        {
238
          $location_data = isset($location_data['username']) ? $location_data['username'] :
239
            (isset($location_data['name']) ? $location_data['name'] : $location_id);
240
        }
241
      }
242
    }
243
244
    if ($base_dump) {
245
      if (!is_array($this->log_array) || empty($this->log_array)) {
246
        $this->log_array = [];
247
      } else {
248
        foreach ($this->log_array as $log) {
249
          $error_backtrace['queries'][] = $log;
250
        }
251
      }
252
253
      $error_backtrace['backtrace'] = debug_backtrace();
254
      unset($error_backtrace['backtrace'][1]);
255
      unset($error_backtrace['backtrace'][0]);
256
257
      // Converting object instances to object names
258
259
      foreach ($error_backtrace['backtrace'] as &$backtrace) {
260
        if (!empty($backtrace['object']) && is_object($backtrace['object'])) {
261
          $backtrace['object'] = get_class($backtrace['object']);
262
        }
263
264
        if (empty($backtrace['args'])) {
265
          continue;
266
        }
267
268
        // Doing same conversion for backtrace params
269
        foreach ($backtrace['args'] as &$arg) {
270
          if (is_object($arg)) {
271
            $arg = 'object::' . get_class($arg);
272
          }
273
        }
274
      }
275
276
      // $error_backtrace['query_log'] = "\r\n\r\nQuery log\r\n<table><tr><th>Number</th><th>Query</th><th>Page</th><th>Table</th><th>Rows</th></tr>{$this->log}</table>\r\n";
277
      $error_backtrace['$_GET']     = $_GET;
278
      $error_backtrace['$_POST']    = $_POST;
279
      $error_backtrace['$_REQUEST'] = $_REQUEST;
280
      $error_backtrace['$_COOKIE']  = $_COOKIE;
281
      $error_backtrace['$_SESSION'] = empty($_SESSION) ? [] : $_SESSION;
282
      $error_backtrace['$_SERVER']  = $_SERVER;
283
      global $user, $planetrow;
284
      $error_backtrace['user']      = $user;
285
      $error_backtrace['planetrow'] = $planetrow;
286
    }
287
288
    return $error_backtrace;
289
  }
290
291
  function error_fatal($die_message, $details = 'There is a fatal error on page') {
292
    // TODO - Записывать детали ошибки в лог-файл
293
    die($die_message);
294
  }
295
296
  public function error($message = 'There is a error on page', $title = 'Internal Error', $httpCode = 500, $dump = true) {
297
    global $config, $sys_stop_log_hit, $lang, $sys_log_disabled, $user;
298
299
    if (empty(SN::$db->connected)) {
300
      // TODO - писать ошибку в файл
301
      die('SQL server currently unavailable. Please contact Administration...');
302
    }
303
304
    db_mysql::db_transaction_rollback();
305
306
    if (SN::$config->debug == 1) {
307
      /** @noinspection HtmlDeprecatedTag */
308
      /** @noinspection XmlDeprecatedElement */
309
      /** @noinspection HtmlDeprecatedAttribute */
310
      echo "<h2>{$title}</h2><br><font color=red>" . htmlspecialchars($message) . "</font><br><hr>";
311
      echo "<table>{$this->log}</table>";
312
    }
313
314
    $fatal_error = 'Fatal error: cannot write to `logs` table. Please contact Administration...';
315
316
    $error_text      = SN::$db->db_escape($message);
317
    $error_backtrace = $this->dump($dump, true, strpos($message, 'Deadlock') !== false);
318
319
    if (!$sys_log_disabled) {
320
      $this->_writeLogMessage($httpCode, $user, $title, $message, $error_backtrace, $fatal_error);
321
322
      $message = "Пожалуйста, свяжитесь с админом, если ошибка повторится. Ошибка №: <b>" . SN::$db->db_insert_id() . "</b>";
323
324
      $sys_stop_log_hit = true;
325
      $sys_log_disabled = true;
326
      !function_exists('messageBox') ? die($message) : SnTemplate::messageBox($message, 'Ошибка', '', 0, false);
327
    } else {
328
//        // TODO Здесь надо писать в файло
329
      ob_start();
330
      print("<hr>User ID {$user['id']} raised error code {$httpCode} titled '{$title}' with text '{$error_text}' on page {$_SERVER['SCRIPT_NAME']}");
331
332
      foreach ($error_backtrace as $name => $value) {
333
        print('<hr>');
334
        pdump($value, $name);
335
      }
336
      ob_end_flush();
337
      die();
338
    }
339
  }
340
341
  function warning($message, $title = 'System Message', $httpCode = 300, $dump = false) {
342
    global $user, $lang, $sys_log_disabled;
343
344
    if (empty(SN::$db->connected)) {
345
      // TODO - писать ошибку в файл
346
      die('SQL server currently unavailable. Please contact Administration...');
347
    }
348
349
    $fatal_error = 'Fatal error: cannot write to `logs` table. Please contact Administration...';
350
351
    $error_backtrace = $this->dump($dump, false);
352
353
    if (empty($sys_log_disabled)) {
354
      $this->_writeLogMessage($httpCode, $user, $title, $message, $error_backtrace, $fatal_error);
355
    } else {
356
//        // TODO Здесь надо писать в файло
357
      $id = !empty($user['id']) ? $user['id'] : 0;
358
      print("<hr>User ID {$id} made log entry with code {$httpCode} titled '{$title}' with text '{$message}' on page {$_SERVER['SCRIPT_NAME']}");
359
    }
360
  }
361
362
  /**
363
   * @param       $httpCode
364
   * @param       $user
365
   * @param       $title
366
   * @param       $message
367
   * @param array $error_backtrace
368
   * @param       $fatal_error
369
   *
370
   * @return void
371
   */
372
  function _writeLogMessage($httpCode, $user, $title, $message, array $error_backtrace, $fatal_error) {
373
    /** @noinspection SqlResolve */
374
    $query = "INSERT INTO `{{logs}}` SET
375
        `log_time` = '" . time() . "', `log_code` = '" . SN::$db->db_escape($httpCode) . "', " .
376
      "`log_sender` = '" . (!empty($user['id']) ? SN::$db->db_escape($user['id']) : 0) . "', " .
377
      "`log_username` = '" . SN::$db->db_escape(!empty($user['user_name']) ? $user['user_name'] : '') . "', " .
378
      "`log_title` = '" . SN::$db->db_escape($title) . "',  `log_text` = '" . SN::$db->db_escape($message) . "', " .
379
      "`log_page` = '" . SN::$db->db_escape(strpos($_SERVER['SCRIPT_NAME'], SN_ROOT_RELATIVE) === false ? $_SERVER['SCRIPT_NAME'] : substr($_SERVER['SCRIPT_NAME'], strlen(SN_ROOT_RELATIVE))) . "'" . ", " .
380
      "`log_dump` = '" . ($error_backtrace ? SN::$db->db_escape(json_encode($error_backtrace)) : '') . "'" . ";";
381
382
    doquery($query, '', false, true) or die($fatal_error . SN::$db->db_error());
383
  }
384
}
385
386
// Copyright (c) 2009-2010 Gorlum for http://supernova.ws
387
// Dump variables nicer then var_dump()
388
389
function dump($value, $varname = null, $level = 0, $dumper = '') {
390
  if (isset($varname)) {
391
    $varname .= " = ";
392
  }
393
394
  if ($level == -1) {
395
    $trans[' ']  = '&there4;';
396
    $trans["\t"] = '&rArr;';
397
    $trans["\n"] = '&para;;';
398
    $trans["\r"] = '&lArr;';
399
    $trans["\0"] = '&oplus;';
400
401
    return strtr(htmlspecialchars($value), $trans);
402
  }
403
  if ($level == 0) {
404
//    $dumper = '<pre>' . mt_rand(10, 99) . '|' . $varname;
405
    $dumper = mt_rand(10, 99) . '|' . $varname;
406
  }
407
408
  $type   = gettype($value);
409
  $dumper .= $type;
410
411
  if ($type == 'string') {
412
    $dumper .= '(' . strlen($value) . ')';
413
    $value  = dump($value, '', -1);
414
  } elseif ($type == 'boolean') {
415
    $value = ($value ? 'true' : 'false');
416
  } elseif ($type == 'object') {
417
    $props  = get_class_vars(get_class($value));
418
    $dumper .= '(' . count($props) . ') <u>' . get_class($value) . '</u>';
419
    foreach ($props as $key => $val) {
420
      $dumper .= "\n" . str_repeat("\t", $level + 1) . $key . ' => ';
421
      $dumper .= dump($value->$key, '', $level + 1);
422
    }
423
    $value = '';
424
  } elseif ($type == 'array') {
425
    $dumper .= '(' . count($value) . ')';
426
    foreach ($value as $key => $val) {
427
      $dumper .= "\n" . str_repeat("\t", $level + 1) . dump($key, '', -1) . ' => ';
428
      $dumper .= dump($val, '', $level + 1);
429
    }
430
    $value = '';
431
  }
432
  $dumper .= " <b>$value</b>";
433
//  if($level == 0) {
434
//    $dumper .= '</pre>';
435
//  }
436
437
  return $dumper;
438
}
439
440
function pdump($value, $varname = null) {
441
  $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
442
//  print_rr($backtrace);
443
//  $backtrace = $backtrace[1];
444
445
  $caller = '';
446
  if (defined('SN_DEBUG_PDUMP_CALLER') && SN_DEBUG_PDUMP_CALLER) {
447
    $caller = (!empty($backtrace[1]['class']) ? $backtrace[1]['class'] : '') .
448
      (!empty($backtrace[1]['type']) ? $backtrace[1]['type'] : '') .
449
      $backtrace[1]['function'] .
450
      (!empty($backtrace[0]['file'])
451
        ? (
452
          ' (' . substr($backtrace[0]['file'], SN_ROOT_PHYSICAL_STR_LEN) .
453
          (!empty($backtrace[0]['line']) ? ':' . $backtrace[0]['line'] : '') .
454
          ')'
455
        )
456
        : ''
457
      );
458
    $caller = "\r\n" . $caller;
459
  }
460
461
  if (php_sapi_name() == "cli") {
462
    print("\n" .
463
      dump($value, $varname) .
464
      $caller .
465
      "\n\n"
466
    );
467
  } else {
468
    print('<pre style="text-align: left; background-color: #111111; color: #0A0; font-family: Courier, monospace !important; padding: 1em 0; font-weight: 800; font-size: 14px;">' .
469
      dump($value, $varname) .
470
      $caller .
471
      '</pre>'
472
    );
473
  }
474
}
475
476
function debug($value, $varname = null) {
477
  pdump($value, $varname);
478
}
479
480
function pr($prePrint = false) {
481
  if ($prePrint) {
482
    print("<br>");
483
  }
484
  print(mt_rand() . "<br>");
485
}
486
487
function pc($prePrint = false) {
488
  global $_PRINT_COUNT_VALUE;
489
  $_PRINT_COUNT_VALUE++;
490
491
  if ($prePrint) {
492
    print("<br>");
493
  }
494
  print($_PRINT_COUNT_VALUE . "<br>");
495
}
496
497
function prep($message) {
498
  print('<pre>' . $message . '</pre>');
499
}
500
501
function backtrace_no_arg() {
502
  $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
503
  array_shift($trace);
504
505
  return $trace;
506
}
507
508
if (!function_exists('pre')) {
509
  define('START', microtime(true)); // SHOULD NEVER BE REMOVED!
510
511
  /**
512
   * @param mixed $value     <p>
513
   *                         The variable you want to export.
514
   *                         </p>
515
   * @param mixed ...$values [optional]
516
   *
517
   * @return void
518
   */
519
  function pre() {
520
    if (func_num_args() <= 0) {
521
      return;
522
    }
523
524
    foreach (func_get_args() ?: [] as $var) {
525
      print "<pre>";
526
      print_r(
527
        $var === null ? 'null' :
528
          (($type = gettype($var)) == 'object' || $type == 'array'
529
            ? $var :
530
            ($type === 'string'
531
              ? $type . '(' . strlen($var) . ') `' . $var . '`' :
532
              ($type == 'boolean'
533
                ? ($var ? 'true' : 'false')
534
                : $type . ' ' . print_r($var, true)
535
              )
536
            )
537
          )
538
      );
539
      print "</pre>";
540
    }
541
542
    $trace = debug_backtrace();
543
544
    $p     = $trace[1]['function'] == 'pred' ? $trace[1] : $trace[0];
545
//            print("\n{$p['file']}@{$p['line']}<br />\n" . (is_string($die) ? 'Die message: ' . $die . "<br />\n" : ''));
546
    print("\n{$p['file']}@{$p['line']}<br />\n");
547
  }
548
549
  function pred() {
550
    call_user_func_array('pre', func_get_args());
551
    die();
552
  }
553
}
554