first commit

This commit is contained in:
/usr/bin/nano
2017-04-15 01:34:36 +03:00
commit c715e2a604
5325 changed files with 329700 additions and 0 deletions

99
lib/Debug/ErrorHook/Catcher.php Executable file
View File

@@ -0,0 +1,99 @@
<?php
/**
* Auxilary class.
* It performs all work with notification catching.
*/
class Debug_ErrorHook_Catcher
{
private $_notifiers = array();
private $_active = true;
private $_prevHdl = null;
private $_types = array(
"E_ERROR", "E_WARNING", "E_PARSE", "E_NOTICE", "E_CORE_ERROR",
"E_CORE_WARNING", "E_COMPILE_ERROR", "E_COMPILE_WARNING",
"E_USER_ERROR", "E_USER_WARNING", "E_USER_NOTICE", "E_STRICT",
"E_RECOVERABLE_ERROR", "E_DEPRECATED", "E_USER_DEPRECATED",
);
public function __construct()
{
$this->_prevHdl = set_error_handler(array($this, "_handleNotice"));
register_shutdown_function(array($this, "_handleFatal"));
}
public function remove()
{
restore_error_handler();
$this->_prevHdl = null;
// There is no unregister_shutdown_function(), so we emulate it via flag.
$this->_active = false;
}
public function addNotifier(Debug_ErrorHook_INotifier $notifier)
{
$this->_notifiers[] = $notifier;
}
public function _handleNotice($errno, $errstr, $errfile, $errline)
{
if (!($errno & error_reporting())) {
return $this->_callPrevHdl($errno, $errstr, $errfile, $errline);
}
$trace = debug_backtrace();
array_shift($trace);
if ($this->_notify($errno, $errstr, $errfile, $errline, $trace) === false) {
return $this->_callPrevHdl($errno, $errstr, $errfile, $errline, $trace);
}
}
public function _handleFatal()
{
$error = error_get_last();
if (!$this->_active || !is_array($error) || !in_array($error['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR))) {
return;
}
$this->_notify($error['type'], $error['message'], $error['file'], $error['line'], null);
}
/**
* Processes a notification.
*
* @param mixed $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
* @param array $trace
* @return bool True if we need to stop the processing.
*/
private function _notify($errno, $errstr, $errfile, $errline, $trace)
{
// Translate error number to error name.
if (is_numeric($errno)) {
foreach ($this->_types as $t) {
if (defined($t)) {
$e = constant($t);
if ($errno == $e) {
$errno = $t;
break;
}
}
}
}
// Send data to all notifiers.
foreach ($this->_notifiers as $notifier) {
if ($notifier->notify($errno, $errstr, $errfile, $errline, $trace) === true) {
return true;
}
}
return false;
}
private function _callPrevHdl()
{
if ($this->_prevHdl) {
$args = func_get_args();
return call_user_func_array($this->_prevHdl, $args);
}
return false;
}
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* Notifier interface.
* Should be implemented by all notifiers in the stack.
*/
interface Debug_ErrorHook_INotifier
{
/**
* Called when an error occurred.
*
* @param string $errno
* @param string $errstr
* @param string $errfile
* @param string $errline
* @param array $trace
* @return void
*/
public function notify($errno, $errstr, $errfile, $errline, $trace);
}

View File

@@ -0,0 +1,47 @@
<?php
/**
* Class to catch notices, warnings and even fatal errors
* and push them to a number of notifiers (e.g. - to email).
*
* A Listener object is kind of a guard object.
*
* @version 1.00
*/
class Debug_ErrorHook_Listener
{
private $_catcher = null;
/**
* Creates a new listener object.
* When this object is destroyed, all hooks are removed.
*
* @return Debug_ErrorHook_Listener
*/
public function __construct()
{
$this->_catcher = new Debug_ErrorHook_Catcher();
}
/**
* Destructor. Cancels all listenings.
*
* @return void
*/
public function __destruct()
{
$this->_catcher->remove();
}
/**
* Adds a new notifier to the list. Notifiers are called in case
* of notices and even fatal errors.
*
* @param Debug_ErrorHook_INotifier $notifier
* @return void
*/
public function addNotifier(Debug_ErrorHook_INotifier $notifier)
{
$this->_catcher->addNotifier($notifier);
}
}

View File

@@ -0,0 +1,57 @@
<?php
/**
* Sends all notifications to a specified email.
*
* Consider using this class together with Debug_ErrorHook_RemoveDupsWrapper
* to avoid mail server flooding when a lot of errors arrives.
*/
class Debug_ErrorHook_MailNotifier extends Debug_ErrorHook_TextNotifier
{
private $_to;
private $_charset;
private $_whatToSend;
private $_subjPrefix;
public function __construct($to, $whatToSend, $subjPrefix = "[ERROR] ", $charset = "UTF-8")
{
parent::__construct($whatToSend);
$this->_to = $to;
$this->_subjPrefix = $subjPrefix;
$this->_charset = $charset;
}
protected function _notifyText($subject, $body)
{
$this->_mail(
$this->_to,
$this->_encodeMailHeader($this->_subjPrefix . $subject),
$body,
join("\r\n", array(
"From: {$this->_to}",
"Content-Type: text/plain; charset={$this->_charset}"
))
);
}
protected function _mail()
{
$args = func_get_args();
@call_user_func_array("mail", $args);
}
private function _encodeMailHeader($header)
{
return preg_replace_callback(
'/((?:^|>)\s*)([^<>]*?[^\w\s.][^<>]*?)(\s*(?:<|$))/s',
array(__CLASS__, '_encodeMailHeaderCallback'),
$header
);
}
private function _encodeMailHeaderCallback($p)
{
$encoding = $this->_charset;
return $p[1] . "=?$encoding?B?" . base64_encode($p[2]) . "?=" . $p[3];
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* Wrapper which denies duplicated notifications to be
* processed again and again. It is needed to lower the
* traffic to mail server in case the site is down.
*
* This class stores meta-informations in filesystem.
* It takes care about garbage collecting.
*/
class Debug_ErrorHook_RemoveDupsWrapper implements Debug_ErrorHook_INotifier
{
const DEFAULT_PERIOD = 300;
const ERROR_FILE_SUFFIX = ".error";
const GC_PROBABILITY = 0.01;
private $_notifier;
private $_tmpPath;
private $_period;
private $_gcExecuted = false;
public function __construct(Debug_ErrorHook_INotifier $notifier, $tmpPath = null, $period = null)
{
$this->_tmpPath = $tmpPath? $tmpPath : $this->_getDefaultTmpPath();
$this->_period = $period? $period : self::DEFAULT_PERIOD;
$this->_notifier = $notifier;
if (!@is_dir($this->_tmpPath)) {
if (!@mkdir($this->_tmpPath, 0777, true)) {
$error = error_get_last();
throw new Exception("Cannot create '{$this->_tmpPath}': {$error['message']}");
}
}
}
public function notify($errno, $errstr, $errfile, $errline, $trace)
{
$hash = md5(join(":", array($errno, $errfile, $errline)));
if ($this->_isExpired($hash)) {
$this->_notifier->notify($errno, $errstr, $errfile, $errline, $trace);
}
// Touch always, even if we did not send anything. Else same errors will
// be mailed again and again after $period (e.g. once per 5 minutes).
$this->_touch($hash, $errfile, $errline);
}
protected function _getDefaultTmpPath()
{
return sys_get_temp_dir() . "/" . get_class($this);
}
protected function _getGcProbability()
{
return self::GC_PROBABILITY;
}
private function _getLockFname($hash)
{
return $this->_tmpPath . '/' . $hash . self::ERROR_FILE_SUFFIX;
}
private function _isExpired($hash)
{
$file = $this->_getLockFname($hash);
return !file_exists($file) || filemtime($file) < time() - $this->_period;
}
private function _touch($hash, $errfile, $errline)
{
$file = $this->_getLockFname($hash);
file_put_contents($file, "$errfile:$errline");
@chmod($file, 0666);
$this->_gc();
}
private function _gc()
{
if ($this->_gcExecuted || mt_rand(0, 10000) >= $this->_getGcProbability() * 10000) {
return;
}
foreach (glob("{$this->_tmpPath}/*" . self::ERROR_FILE_SUFFIX) as $file) {
if (filemtime($file) <= time() - $this->_period * 2) {
@unlink($file);
}
}
$this->_gcExecuted = true;
}
}

View File

@@ -0,0 +1,78 @@
<?php
/**
* Generic notifier wrapper. Converts notification
* to a human-readable text representation.
*/
abstract class Debug_ErrorHook_TextNotifier implements Debug_ErrorHook_INotifier
{
const LOG_SERVER = 1;
const LOG_TRACE = 2;
const LOG_COOKIE = 4;
const LOG_GET = 8;
const LOG_POST = 16;
const LOG_SESSION = 32;
const LOG_ALL = 65535;
private $_whatToLog;
private $_bodySuffix;
public function __construct($whatToLog)
{
$this->_whatToLog = $whatToLog;
}
public function setBodySuffixTest($text)
{
$this->_bodySuffix = $text;
}
public function notify($errno, $errstr, $errfile, $errline, $trace)
{
$body = array();
$body[] = $this->_makeSection(
"",
join("\n", array(
(@$_SERVER['GATEWAY_INTERFACE']? "//{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}" : ""),
"$errno: $errstr",
"at $errfile on line $errline",
))
);
if ($this->_whatToLog & self::LOG_TRACE && $trace) {
$body[] = $this->_makeSection("TRACE", Debug_ErrorHook_Util::backtraceToString($trace));
}
if ($this->_whatToLog & self::LOG_SERVER) {
$body[] = $this->_makeSection("SERVER", Debug_ErrorHook_Util::varExport($_SERVER));
}
if ($this->_whatToLog & self::LOG_COOKIE) {
$body[] = $this->_makeSection("COOKIES", Debug_ErrorHook_Util::varExport($_COOKIE));
}
if ($this->_whatToLog & self::LOG_GET) {
$body[] = $this->_makeSection("GET", Debug_ErrorHook_Util::varExport($_GET));
}
if ($this->_whatToLog & self::LOG_POST) {
$body[] = $this->_makeSection("POST", Debug_ErrorHook_Util::varExport($_POST));
}
if ($this->_whatToLog & self::LOG_SESSION) {
$body[] = $this->_makeSection("SESSION", Debug_ErrorHook_Util::varExport(@$_SESSION));
}
// Append body suffix?
$suffix = $this->_bodySuffix && is_callable($this->_bodySuffix)? call_user_func($this->_bodySuffix) : $this->_bodySuffix;
if ($suffix) {
$body[] = $this->_makeSection("ADDITIONAL INFO", $suffix);
}
// Remain only 1st line for subject.
$errstr = preg_replace("/\r?\n.*/s", '', $errstr);
$this->_notifyText("$errno: $errstr at $errfile on line $errline", join("\n", $body));
}
private function _makeSection($name, $body)
{
$body = rtrim($body);
if ($name) $body = preg_replace('/^/m', ' ', $body);
$body = preg_replace('/^([ \t\r]*\n)+/s', '', $body);
return ($name? $name . ":\n" : "") . $body . "\n";
}
abstract protected function _notifyText($subject, $body);
}

102
lib/Debug/ErrorHook/Util.php Executable file
View File

@@ -0,0 +1,102 @@
<?php
class Debug_ErrorHook_Util
{
/**
* var_export clone, without using output buffering.
* (For calls in ob_handler)
*
* @param mixed $var to be exported
* @param integer $maxLevel (recursion protect)
* @param integer $level of current indent
* @return string
*/
public static function varExport($var, $maxLevel = 10, $level = 0)
{
$escapes = "\"\r\t\x00\$";
$tab = ' ';
if (is_bool($var)) {
return $var ? 'TRUE' : 'FALSE';
} elseif (is_string($var)) {
return '"' . addcslashes($var, $escapes) . '"';
} elseif (is_float($var) || is_int($var)) {
return $var;
} elseif (is_null($var)) {
return 'NULL';
} elseif (is_resource($var)) {
return 'NULL /* ' . $var . ' */';
}
if ($maxLevel < $level) {
return 'NULL /* ' . (string) $var . ' MAX LEVEL ' . $maxLevel . " REACHED*/";
}
if (is_array($var)) {
$return = "array(\n";
} else {
$return = get_class($var) . "::__set_state(array(\n";
}
$offset = str_repeat($tab, $level + 1);
foreach ((array) $var as $key => $value) {
$return .= $offset;
if (is_int($key)) {
$return .= $key;
} else {
$return .= '"' . addcslashes($key, $escapes). '"';
}
$return .= ' => ' . self::varExport($value, $maxLevel, $level + 1) . ",\n";
}
return $return
. str_repeat($tab, $level)
. (is_array($var) ? ')' : '))');
}
/**
* Analog for debug_print_backtrace(), but returns string.
*
* @return string
*/
public static function backtraceToString($backtrace)
{
// Iterate backtrace
$calls = array();
foreach ($backtrace as $i => $call) {
if (!isset($call['file'])) {
$call['file'] = '(null)';
}
if (!isset($call['line'])) {
$call['line'] = '0';
}
$location = $call['file'] . ':' . $call['line'];
$function = (isset($call['class'])) ?
$call['class'] . (isset($call['type']) ? $call['type'] : '.') . $call['function'] :
$call['function'];
$params = '';
if (isset($call['args']) && is_array($call['args'])) {
$args = array();
foreach ($call['args'] as $arg) {
if (is_array($arg)) {
$args[] = "Array(...)";
} elseif (is_object($arg)) {
$args[] = get_class($arg);
} else {
$args[] = $arg;
}
}
$params = implode(', ', $args);
}
$calls[] = sprintf('#%d %s(%s) called at [%s]',
$i,
$function,
$params,
$location);
}
return implode("\n", $calls) . "\n";
}
}

View File

@@ -0,0 +1,37 @@
<?php
/**
* Вывод ошибки на экран и лог или только в лог в зависимоти от DEBUG_MODE
*
* @author Zmi
*/
class MyDebug_ErrorHook_TextNotifier extends Debug_ErrorHook_TextNotifier
{
protected function _notifyText($subject, $body)
{
global $g_config;
// Подготовка сообщения ошибки
$msg = PHP_EOL .
"Text notification:" . PHP_EOL .
"\tsubject: {$subject}" . PHP_EOL .
"\t{$body}" .
PHP_EOL;
// Запись ошибки в лог-файл
$path = $g_config['logErrors']['logFile'];
$fileLogger = FileLogger::Create($path);
$fileLogger->Error($msg);
// Вывод ошибки на экран
if ($g_config['phpIni']['display_errors'])
{
echo "<pre>$msg</pre>";
}
else
{
IncludeCom('500');
}
}
};
?>