'E_ERROR',
2 => 'E_WARNING',
4 => 'E_PARSE',
8 => 'E_NOTICE',
16 => 'E_CORE_ERROR',
32 => 'E_CORE_WARNING',
64 => 'E_COMPILE_ERROR',
128 => 'E_COMPILE_WARNING',
256 => 'E_USER_ERROR',
512 => 'E_USER_WARNING',
1024 => 'E_USER_NOTICE',
2048 => 'E_STRICT',
4096 => 'E_RECOVERABLE_ERROR',
8192 => 'E_DEPRECATED',
16384 => 'E_USER_DEPRECATED',
32767 => 'E_ALL'
];
const STORE_NONE = -1;
const STORE_MYSQL = 0;
const STORE_FILE = 1;
const STORE_BOTH = 2;
private static $instance = null;
protected $enabled = false;
protected $errCounter = 0;
protected $logCounter = 0;
protected $messagesStoreType = self::STORE_NONE;
protected $errorsStoreType = self::STORE_NONE;
protected $filter;
protected $reportRecursionLevel = 0;
protected $overridenDebugFile = null;
protected $silent = false;
protected $prefix;
private function __construct($filter) {
$this->filter = $filter;
}
public static function getInstance($filter = null) {
if (is_null(self::$instance)) {
self::$instance = new self($filter);
}
return self::$instance;
}
public function enable() {
$self = $this;
set_error_handler(function($no, $str, $file, $line) use ($self) {
if ($self->silent || !$self->enabled) {
return;
}
if ((is_callable($this->filter) && !($this->filter)($no, $file, $line, $str)) || !$self->canReport()) {
return;
}
$self->report(true, $str, $no, $file, $line);
});
append_shutdown_function(function() use ($self) {
if (!$self->enabled || !($error = error_get_last())) {
return;
}
if (is_callable($this->filter)
&& !($this->filter)($error['type'], $error['file'], $error['line'], $error['message'])) {
return;
}
if (!$self->canReport()) {
return;
}
$self->report(true, $error['message'], $error['type'], $error['file'], $error['line']);
});
$this->enabled = true;
}
public function disable() {
restore_error_handler();
$this->enabled = false;
}
public function report($is_error, $text, $errno = 0, $errfile = '', $errline = '') {
global $config;
$this->reportRecursionLevel++;
$logstarted = $this->errCounter > 0 || $this->logCounter > 0;
$num = $is_error ? $this->errCounter++ : $this->logCounter++;
$custom = $is_error && !$errno;
$ts = time();
$exectime = exectime();
$bt = backtrace(2);
$store_file = (!$is_error && $this->checkMessagesStoreType(self::STORE_FILE))
|| ($is_error && $this->checkErrorsStoreType(self::STORE_FILE));
$store_mysql = (!$is_error && $this->checkMessagesStoreType(self::STORE_MYSQL))
|| ($is_error && $this->checkErrorsStoreType(self::STORE_MYSQL));
if ($this->prefix)
$text = $this->prefix.$text;
// if ($store_mysql) {
// $db = getMySQL('local_logs', true);
// $data = [
// 'ts' => $ts,
// 'num' => $num,
// 'time' => $exectime,
// 'custom' => intval($custom),
// 'errno' => $errno,
// 'file' => $errfile,
// 'line' => $errline,
// 'text' => $text,
// 'stacktrace' => $bt,
// 'is_cli' => PHP_SAPI == 'cli' ? 1 : 0,
// ];
// if (PHP_SAPI == 'cli') {
// $data += [
// 'ip' => '',
// 'ua' => '',
// 'url' => '',
// ];
// } else {
// $data += [
// 'ip' => ip2ulong($_SERVER['REMOTE_ADDR']),
// 'ua' => $_SERVER['HTTP_USER_AGENT'] ?? '',
// 'url' => $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']
// ];
// }
// $db->insert('backend_errors', $data);
// }
if ($store_file) {
$title = PHP_SAPI == 'cli' ? 'cli' : $_SERVER['REQUEST_URI'];
$date = date('d/m/y H:i:s', $ts);
$exectime = (string)$exectime;
if (strlen($exectime) < 6)
$exectime .= str_repeat('0', 6 - strlen($exectime));
$buf = "";
if (!$logstarted) {
$buf .= "\n {$title} {$date} \n";
}
$buf .= "".($is_error ? 'E' : 'I')."=${num} {$exectime} ";
if ($is_error && !$custom) {
$buf .= "{$errfile}:{$errline} (".self::errname($errno).") ";
}
$buf = stransi($buf);
$buf .= $text;
$buf .= "\n";
if ($is_error && $config['debug_backtrace']) {
$buf .= $bt."\n";
}
$debug_file = $this->getDebugFile();
$logdir = dirname($debug_file);
if (!file_exists($logdir)) {
mkdir($logdir);
setperm($logdir);
}
$f = fopen($debug_file, 'a');
if ($f) {
fwrite($f, $buf);
fclose($f);
}
}
$this->reportRecursionLevel--;
}
public function canReport() {
return $this->reportRecursionLevel < 2;
}
public function setErrorsStoreType($errorsStoreType) {
$this->errorsStoreType = $errorsStoreType;
}
public function setMessagesStoreType($messagesStoreType) {
$this->messagesStoreType = $messagesStoreType;
}
public function checkMessagesStoreType($store_type) {
return $this->messagesStoreType == $store_type || $this->messagesStoreType == self::STORE_BOTH;
}
public function checkErrorsStoreType($store_type) {
return $this->errorsStoreType == $store_type || $this->errorsStoreType == self::STORE_BOTH;
}
public function overrideDebugFile($file) {
$this->overridenDebugFile = $file;
}
protected function getDebugFile() {
global $config;
return is_null($this->overridenDebugFile) ? ROOT.'/'.$config['debug_file'] : $this->overridenDebugFile;
}
public function setSilence($silent) {
$this->silent = $silent;
}
public function setPrefix($prefix) {
$this->prefix = $prefix;
}
public static function errname($errno) {
static $errors = null;
if (is_null($errors)) {
$errors = array_flip(array_slice(get_defined_constants(true)['Core'], 0, 15, true));
}
return $errors[$errno];
}
public static function getTypes() {
return self::$Types;
}
}
class debug_measure {
private $name;
private $time;
private $started = false;
/**
* @param string $name
* @return $this
*/
public function start($name = null) {
if (is_null($name)) {
$name = strgen(3);
}
$this->name = $name;
$this->time = microtime(true);
$this->started = true;
return $this;
}
/**
* @return float|string|null
*/
public function finish() {
if (!$this->started) {
debugLog("debug_measure::finish(): not started, name=".$this->name);
return null;
}
$time = (microtime(true) - $this->time);
debugLog("MEASURE".($this->name != '' ? ' '.$this->name : '').": ".$time);
$this->started = false;
return $time;
}
}
/**
* @param $var
* @return string
*/
function str_print_r($var) {
ob_start();
print_r($var);
return trim(ob_get_clean());
}
/**
* @param $var
* @return string
*/
function str_var_dump($var) {
ob_start();
var_dump($var);
return trim(ob_get_clean());
}
/**
* @param $args
* @param bool $all_dump
* @return string
*/
function str_vars($args, $all_dump = false) {
return implode(' ', array_map(function($a) use ($all_dump) {
if ($all_dump) {
return str_var_dump($a);
}
$type = gettype($a);
if ($type == 'string' || $type == 'integer' || $type == 'double') {
return $a;
} else if ($type == 'array' || $type == 'object') {
return str_print_r($a);
} else {
return str_var_dump($a);
}
}, $args));
}
/**
* @param int $shift
* @return string
*/
function backtrace($shift = 0) {
$bt = debug_backtrace();
$lines = [];
foreach ($bt as $i => $t) {
if ($i < $shift) {
continue;
}
if (!isset($t['file'])) {
$lines[] = 'from ?';
} else {
$lines[] = 'from '.$t['file'].':'.$t['line'];
}
}
return implode("\n", $lines);
}
/**
* @param mixed ...$args
*/
function debugLog(...$args) {
global $config;
if (!$config['is_dev'])
return;
debug::getInstance()->report(false, str_vars($args));
}
function debugLogOnProd(...$args) {
debug::getInstance()->report(false, str_vars($args));
}
/**
* @param mixed ...$args
*/
function debugError(...$args) {
$debug = debug::getInstance();
if ($debug->canReport()) {
$debug->report(true, str_vars($args));
}
}