2014-01-27 20:28:37 +01:00
|
|
|
<?php
|
2014-02-10 22:04:09 +01:00
|
|
|
|
|
|
|
namespace ManiaControl;
|
|
|
|
|
2017-05-17 13:05:05 +02:00
|
|
|
use ManiaControl\Files\AsyncHttpRequest;
|
2014-05-15 14:20:44 +02:00
|
|
|
use ManiaControl\Plugins\PluginManager;
|
2014-02-10 22:54:49 +01:00
|
|
|
use ManiaControl\Update\UpdateManager;
|
2014-06-17 21:18:17 +02:00
|
|
|
use ManiaControl\Utils\Formatter;
|
2014-06-27 01:18:55 +02:00
|
|
|
use ManiaControl\Utils\WebReader;
|
2014-06-12 15:46:24 +02:00
|
|
|
use Maniaplanet\DedicatedServer\Xmlrpc\TransportException;
|
2014-01-27 20:28:37 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Error and Exception Manager Class
|
2014-05-02 17:40:47 +02:00
|
|
|
*
|
|
|
|
* @author ManiaControl Team <mail@maniacontrol.com>
|
2017-02-04 11:49:23 +01:00
|
|
|
* @copyright 2014-2017 ManiaControl Team
|
2014-05-02 17:40:47 +02:00
|
|
|
* @license http://www.gnu.org/licenses/ GNU General Public License, Version 3
|
2014-01-27 20:28:37 +01:00
|
|
|
*/
|
|
|
|
class ErrorHandler {
|
2014-04-12 12:14:37 +02:00
|
|
|
/*
|
2014-02-17 12:54:31 +01:00
|
|
|
* Constants
|
|
|
|
*/
|
2014-05-04 00:16:50 +02:00
|
|
|
const MC_DEBUG_NOTICE = 'ManiaControl.DebugNotice';
|
2014-04-28 14:14:04 +02:00
|
|
|
const SETTING_RESTART_ON_EXCEPTION = 'Automatically restart on Exceptions';
|
2014-05-18 21:17:30 +02:00
|
|
|
const LOG_SUPPRESSED_ERRORS = false;
|
2017-05-10 12:40:00 +02:00
|
|
|
const LONG_LOOP_REPORT_TIME = 5;
|
2014-05-02 17:40:47 +02:00
|
|
|
|
2014-04-12 12:14:37 +02:00
|
|
|
/*
|
2014-08-02 22:31:46 +02:00
|
|
|
* Private properties
|
2014-01-27 21:56:49 +01:00
|
|
|
*/
|
2014-08-02 22:31:46 +02:00
|
|
|
/** @var ManiaControl $maniaControl */
|
2017-03-26 13:58:23 +02:00
|
|
|
private $maniaControl = null;
|
2014-06-12 16:59:24 +02:00
|
|
|
private $handlingError = null;
|
2014-01-27 21:56:49 +01:00
|
|
|
|
2014-01-27 20:28:37 +01:00
|
|
|
/**
|
2014-07-25 16:28:47 +02:00
|
|
|
* Construct a new error handler instance
|
2014-05-29 22:46:38 +02:00
|
|
|
*
|
2014-08-02 22:31:46 +02:00
|
|
|
* @param ManiaControl $maniaControl
|
2014-01-27 20:28:37 +01:00
|
|
|
*/
|
2014-01-27 21:56:49 +01:00
|
|
|
public function __construct(ManiaControl $maniaControl) {
|
|
|
|
$this->maniaControl = $maniaControl;
|
2017-04-01 12:23:10 +02:00
|
|
|
set_error_handler(array(&$this, 'handleError')); //TODO before was -1 verify why
|
2014-05-03 22:21:57 +02:00
|
|
|
set_exception_handler(array(&$this, 'handleException'));
|
2014-05-29 22:46:38 +02:00
|
|
|
register_shutdown_function(array(&$this, 'handleShutdown'));
|
2014-01-27 20:28:37 +01:00
|
|
|
}
|
|
|
|
|
2014-04-28 14:14:04 +02:00
|
|
|
/**
|
2014-07-25 16:28:47 +02:00
|
|
|
* Initialize error handler features
|
2014-04-28 14:14:04 +02:00
|
|
|
*/
|
|
|
|
public function init() {
|
2014-08-13 11:05:52 +02:00
|
|
|
$this->maniaControl->getSettingManager()->initSetting($this, self::SETTING_RESTART_ON_EXCEPTION, true);
|
2014-04-28 14:14:04 +02:00
|
|
|
}
|
|
|
|
|
2014-01-27 20:28:37 +01:00
|
|
|
/**
|
2014-05-29 22:46:38 +02:00
|
|
|
* Trigger a Debug Notice to the ManiaControl Website
|
2014-05-02 17:40:47 +02:00
|
|
|
*
|
2014-05-29 22:46:38 +02:00
|
|
|
* @param string $message
|
2014-01-27 20:28:37 +01:00
|
|
|
*/
|
2014-05-29 22:46:38 +02:00
|
|
|
public function triggerDebugNotice($message) {
|
|
|
|
$this->handleError(self::MC_DEBUG_NOTICE, $message);
|
|
|
|
}
|
2014-05-04 00:16:50 +02:00
|
|
|
|
2014-05-29 22:46:38 +02:00
|
|
|
/**
|
|
|
|
* ManiaControl Error Handler
|
|
|
|
*
|
|
|
|
* @param int $errorNumber
|
|
|
|
* @param string $errorString
|
|
|
|
* @param string $errorFile
|
|
|
|
* @param int $errorLine
|
|
|
|
* @param array $errorContext
|
|
|
|
* @param bool $onShutdown
|
|
|
|
* @return bool
|
|
|
|
*/
|
2014-08-25 15:23:08 +02:00
|
|
|
public function handleError($errorNumber, $errorString, $errorFile = null, $errorLine = -1, array $errorContext = array(), $onShutdown = false) {
|
2014-05-29 22:46:38 +02:00
|
|
|
$suppressed = (error_reporting() === 0);
|
|
|
|
if ($suppressed && !self::LOG_SUPPRESSED_ERRORS) {
|
|
|
|
return false;
|
|
|
|
}
|
2014-05-04 00:16:50 +02:00
|
|
|
|
2014-06-12 16:59:24 +02:00
|
|
|
if (!$this->handlingError) {
|
|
|
|
// Reset error handler for safety
|
|
|
|
$this->handlingError = true;
|
2017-04-01 12:23:10 +02:00
|
|
|
set_error_handler(array(&$this, 'handleError'));
|
2014-06-12 16:59:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Build log message
|
2014-07-29 23:43:20 +02:00
|
|
|
$errorTag = $this->getErrorTag($errorNumber);
|
|
|
|
$isUserError = self::isUserErrorNumber($errorNumber);
|
|
|
|
$isFatalError = self::isFatalError($errorNumber);
|
2014-06-15 03:37:07 +02:00
|
|
|
|
2017-04-01 12:23:10 +02:00
|
|
|
$isPluginError = false;
|
|
|
|
|
2014-06-15 03:37:07 +02:00
|
|
|
$traceString = null;
|
|
|
|
$sourceClass = null;
|
2014-05-29 22:46:38 +02:00
|
|
|
$traceSourceClass = null;
|
2014-06-15 03:37:07 +02:00
|
|
|
$fileLine = null;
|
2014-05-29 22:46:38 +02:00
|
|
|
|
2014-06-15 03:37:07 +02:00
|
|
|
$message = $errorTag . ': ' . $errorString;
|
|
|
|
if (!$onShutdown) {
|
|
|
|
$traceString = $this->parseBackTrace(array_slice(debug_backtrace(), 1), $traceSourceClass);
|
|
|
|
}
|
|
|
|
if ($errorFile) {
|
|
|
|
$fileLine = $errorFile . ': ' . $errorLine;
|
|
|
|
$sourceClass = $this->getSourceClass($errorFile);
|
|
|
|
}
|
|
|
|
if (!$sourceClass && $traceSourceClass) {
|
2014-05-29 22:46:38 +02:00
|
|
|
$sourceClass = $traceSourceClass;
|
|
|
|
}
|
|
|
|
|
|
|
|
$logMessage = $message . PHP_EOL . 'File&Line: ' . $fileLine;
|
2014-07-29 23:43:20 +02:00
|
|
|
if (!$isUserError && $traceString) {
|
2014-05-29 22:46:38 +02:00
|
|
|
$logMessage .= PHP_EOL . 'Trace: ' . PHP_EOL . $traceString;
|
|
|
|
}
|
2014-08-05 01:49:13 +02:00
|
|
|
Logger::log($logMessage);
|
2014-05-02 17:40:47 +02:00
|
|
|
|
2014-07-29 23:43:20 +02:00
|
|
|
if (!DEV_MODE && !$isUserError && !$suppressed) {
|
2014-06-12 16:59:24 +02:00
|
|
|
// Report error
|
2014-06-15 03:37:07 +02:00
|
|
|
$report = array();
|
|
|
|
$report['Type'] = 'Error';
|
2014-06-26 11:13:59 +02:00
|
|
|
$report['Message'] = $message;
|
2014-06-15 03:37:07 +02:00
|
|
|
if ($fileLine) {
|
2014-07-29 23:43:20 +02:00
|
|
|
$report['FileLine'] = self::stripBaseDir($fileLine);
|
2014-06-15 03:37:07 +02:00
|
|
|
}
|
2017-04-01 12:23:10 +02:00
|
|
|
|
2017-04-02 16:20:16 +02:00
|
|
|
|
2014-06-15 03:37:07 +02:00
|
|
|
if ($sourceClass) {
|
|
|
|
$report['SourceClass'] = $sourceClass;
|
2014-07-29 23:43:20 +02:00
|
|
|
$pluginId = PluginManager::getPluginId($sourceClass);
|
|
|
|
if ($pluginId > 0) {
|
|
|
|
$report['PluginId'] = $pluginId;
|
2017-04-01 12:23:10 +02:00
|
|
|
|
2017-04-02 16:20:16 +02:00
|
|
|
|
2014-07-29 23:43:20 +02:00
|
|
|
if ($isFatalError) {
|
2014-08-13 11:05:52 +02:00
|
|
|
$this->maniaControl->getPluginManager()->deactivatePlugin($sourceClass);
|
2017-04-01 12:23:10 +02:00
|
|
|
$this->maniaControl->getChat()->sendError("Plugin " . $sourceClass . " has an Error -> The Plugin will be deactivated and ManiaControl restarted");
|
|
|
|
Logger::logError("Plugin " . $sourceClass . " has an Error -> The Plugin will be deactivated and ManiaControl restarted");
|
|
|
|
$isPluginError = true;
|
2014-07-29 23:43:20 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-28 14:38:21 +02:00
|
|
|
|
|
|
|
$report['PluginId'] = $pluginId;
|
|
|
|
$report['PluginVersion'] = PluginManager::getPluginVersion($sourceClass);
|
2014-06-15 03:37:07 +02:00
|
|
|
}
|
2017-04-01 12:23:10 +02:00
|
|
|
|
2014-06-15 03:37:07 +02:00
|
|
|
if ($traceString) {
|
2014-05-29 22:46:38 +02:00
|
|
|
$report['Backtrace'] = $traceString;
|
|
|
|
}
|
2017-06-28 14:38:21 +02:00
|
|
|
|
2014-05-24 21:27:41 +02:00
|
|
|
$report['OperatingSystem'] = php_uname();
|
|
|
|
$report['PHPVersion'] = phpversion();
|
2014-05-02 17:40:47 +02:00
|
|
|
|
2014-08-03 01:34:18 +02:00
|
|
|
if ($this->maniaControl->getServer()) {
|
|
|
|
$report['ServerLogin'] = $this->maniaControl->getServer()->login;
|
2014-02-19 15:44:00 +01:00
|
|
|
}
|
2014-05-02 17:40:47 +02:00
|
|
|
|
2014-08-03 01:34:18 +02:00
|
|
|
if ($this->maniaControl->getSettingManager() && $this->maniaControl->getUpdateManager()) {
|
2014-08-13 11:05:52 +02:00
|
|
|
$report['UpdateChannel'] = $this->maniaControl->getSettingManager()->getSettingValue($this->maniaControl->getUpdateManager(), UpdateManager::SETTING_UPDATECHECK_CHANNEL);
|
2014-09-01 03:42:13 +02:00
|
|
|
$report['ManiaControlVersion'] = ManiaControl::VERSION . ' ' . $this->maniaControl->getUpdateManager()->getBuildDate();
|
2014-05-02 17:40:47 +02:00
|
|
|
} else {
|
2014-05-24 21:27:41 +02:00
|
|
|
$report['ManiaControlVersion'] = ManiaControl::VERSION;
|
2014-02-19 15:44:00 +01:00
|
|
|
}
|
2014-05-02 17:40:47 +02:00
|
|
|
|
2017-03-26 13:58:23 +02:00
|
|
|
$report['DedicatedBuildVersion'] = $this->maniaControl->getDedicatedServerBuildVersion();
|
|
|
|
|
2014-06-26 11:13:59 +02:00
|
|
|
$json = json_encode(Formatter::utf8($report));
|
2014-02-19 15:44:00 +01:00
|
|
|
$info = base64_encode($json);
|
2014-05-02 17:40:47 +02:00
|
|
|
|
2017-05-17 13:05:05 +02:00
|
|
|
$url = ManiaControl::URL_WEBSERVICE . 'errorreport?error=' . urlencode($info);
|
|
|
|
|
|
|
|
if ($isFatalError) {
|
|
|
|
$response = WebReader::getUrl($url);
|
|
|
|
$content = $response->getContent();
|
|
|
|
$success = json_decode($content);
|
|
|
|
if ($success) {
|
|
|
|
Logger::log('Error-Report successful!');
|
|
|
|
} else {
|
|
|
|
Logger::log('Error-Report failed! ' . print_r($content, true));
|
|
|
|
}
|
2014-05-24 21:49:17 +02:00
|
|
|
} else {
|
2017-05-17 13:05:05 +02:00
|
|
|
//Async Report
|
|
|
|
$asyncHttpRequest = new AsyncHttpRequest($this->maniaControl, $url);
|
|
|
|
$asyncHttpRequest->setContentType(AsyncHttpRequest::CONTENT_TYPE_JSON);
|
|
|
|
$asyncHttpRequest->setCallable(function ($json, $error) {
|
|
|
|
if ($error) {
|
|
|
|
Logger::logError("Error while Sending Error Report");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
$success = json_decode($json);
|
|
|
|
if ($success) {
|
|
|
|
Logger::log('Error-Report successful!');
|
|
|
|
} else {
|
|
|
|
Logger::log('Error-Report failed! ' . print_r($json, true));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
$asyncHttpRequest->getData();
|
2014-02-19 15:44:00 +01:00
|
|
|
}
|
2014-02-08 19:33:26 +01:00
|
|
|
}
|
2014-06-12 13:38:08 +02:00
|
|
|
|
2014-07-29 23:43:20 +02:00
|
|
|
if ($isFatalError) {
|
2017-04-01 12:23:10 +02:00
|
|
|
if ($isPluginError) {
|
|
|
|
$this->maniaControl->restart();
|
|
|
|
} else {
|
|
|
|
$this->maniaControl->quit('Quitting ManiaControl after Fatal Error.');
|
|
|
|
}
|
2014-05-29 22:46:38 +02:00
|
|
|
}
|
2014-06-12 13:38:08 +02:00
|
|
|
|
2014-06-12 16:59:24 +02:00
|
|
|
// Disable safety state
|
|
|
|
$this->handlingError = false;
|
|
|
|
|
2017-04-01 12:23:10 +02:00
|
|
|
|
2014-05-29 22:46:38 +02:00
|
|
|
return false;
|
|
|
|
}
|
2014-05-02 17:40:47 +02:00
|
|
|
|
2014-05-29 22:46:38 +02:00
|
|
|
/**
|
|
|
|
* Get the Prefix for the given Error Level
|
|
|
|
*
|
|
|
|
* @param int $errorLevel
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getErrorTag($errorLevel) {
|
|
|
|
switch ($errorLevel) {
|
|
|
|
case E_ERROR:
|
|
|
|
return '[PHP ERROR]';
|
2014-06-20 00:05:50 +02:00
|
|
|
case E_WARNING:
|
|
|
|
return '[PHP WARNING]';
|
|
|
|
case E_PARSE:
|
|
|
|
return '[PHP PARSE ERROR]';
|
|
|
|
case E_NOTICE:
|
|
|
|
return '[PHP NOTICE]';
|
2014-05-29 22:46:38 +02:00
|
|
|
case E_CORE_ERROR:
|
|
|
|
return '[PHP CORE ERROR]';
|
|
|
|
case E_COMPILE_ERROR:
|
|
|
|
return '[PHP COMPILE ERROR]';
|
|
|
|
case E_USER_ERROR:
|
|
|
|
return '[ManiaControl ERROR]';
|
2014-06-20 00:05:50 +02:00
|
|
|
case E_USER_WARNING:
|
|
|
|
return '[ManiaControl WARNING]';
|
|
|
|
case E_USER_NOTICE:
|
|
|
|
return '[ManiaControl NOTICE]';
|
|
|
|
case E_RECOVERABLE_ERROR:
|
|
|
|
return '[PHP RECOVERABLE ERROR]';
|
2014-05-29 22:46:38 +02:00
|
|
|
case self::MC_DEBUG_NOTICE:
|
|
|
|
return '[ManiaControl DEBUG]';
|
2014-04-28 14:14:04 +02:00
|
|
|
}
|
2014-05-29 22:46:38 +02:00
|
|
|
return "[PHP ERROR '{$errorLevel}']";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the given Error Number is a User Error
|
|
|
|
*
|
|
|
|
* @param int $errorNumber
|
|
|
|
* @return bool
|
|
|
|
*/
|
2014-07-29 23:43:20 +02:00
|
|
|
private static function isUserErrorNumber($errorNumber) {
|
2014-06-20 00:05:50 +02:00
|
|
|
return ($errorNumber & E_USER_ERROR || $errorNumber & E_USER_WARNING || $errorNumber & E_USER_NOTICE
|
|
|
|
|| $errorNumber & E_USER_DEPRECATED);
|
2014-01-27 20:28:37 +01:00
|
|
|
}
|
|
|
|
|
2014-07-29 23:43:20 +02:00
|
|
|
/**
|
|
|
|
* Test whether the given Error Number represents a Fatal Error
|
|
|
|
*
|
|
|
|
* @param int $errorNumber
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public static function isFatalError($errorNumber) {
|
|
|
|
$fatalError = (E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR);
|
|
|
|
return ($errorNumber & $fatalError);
|
|
|
|
}
|
|
|
|
|
2014-05-15 14:20:44 +02:00
|
|
|
/**
|
|
|
|
* Parse the Debug Backtrace into a String for the Error Report
|
|
|
|
*
|
|
|
|
* @param array $backtrace
|
|
|
|
* @param string $sourceClass
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private function parseBackTrace(array $backtrace, &$sourceClass = null) {
|
|
|
|
$traceString = '';
|
|
|
|
$stepCount = 0;
|
|
|
|
foreach ($backtrace as $traceStep) {
|
2017-03-26 13:58:23 +02:00
|
|
|
$skipStep = $this->shouldSkipTraceStep($traceStep);
|
2014-05-29 22:46:38 +02:00
|
|
|
$traceString .= '#' . $stepCount . ': ';
|
2014-05-15 14:20:44 +02:00
|
|
|
if (isset($traceStep['class'])) {
|
2014-07-30 10:38:02 +02:00
|
|
|
if (!$sourceClass && !$skipStep && !$this->isIgnoredSourceClass($traceStep['class'])) {
|
2014-05-15 14:20:44 +02:00
|
|
|
$sourceClass = $traceStep['class'];
|
|
|
|
}
|
|
|
|
$traceString .= $traceStep['class'];
|
|
|
|
}
|
|
|
|
if (isset($traceStep['type'])) {
|
|
|
|
$traceString .= $traceStep['type'];
|
|
|
|
}
|
|
|
|
if (isset($traceStep['function'])) {
|
|
|
|
$traceString .= $traceStep['function'] . '(';
|
2014-06-19 17:54:37 +02:00
|
|
|
if (isset($traceStep['args']) && !$skipStep) {
|
2014-05-15 14:20:44 +02:00
|
|
|
$traceString .= $this->parseArgumentsArray($traceStep['args']);
|
|
|
|
}
|
|
|
|
$traceString .= ')';
|
|
|
|
}
|
2014-06-19 17:54:37 +02:00
|
|
|
if (isset($traceStep['file']) && !$skipStep) {
|
2014-05-15 14:20:44 +02:00
|
|
|
$traceString .= ' in File ';
|
2014-07-29 23:43:20 +02:00
|
|
|
$traceString .= self::stripBaseDir($traceStep['file']);
|
2014-05-15 14:20:44 +02:00
|
|
|
}
|
2014-06-19 17:54:37 +02:00
|
|
|
if (isset($traceStep['line']) && !$skipStep) {
|
2014-05-15 14:20:44 +02:00
|
|
|
$traceString .= ' on Line ';
|
|
|
|
$traceString .= $traceStep['line'];
|
|
|
|
}
|
2014-05-29 22:46:38 +02:00
|
|
|
$traceString .= PHP_EOL;
|
2014-08-25 15:31:16 +02:00
|
|
|
if (strlen($traceString) > 2500) {
|
2014-05-24 22:34:57 +02:00
|
|
|
// Too long...
|
|
|
|
$traceString .= '...';
|
|
|
|
break;
|
|
|
|
}
|
2014-05-15 14:20:44 +02:00
|
|
|
$stepCount++;
|
|
|
|
}
|
|
|
|
return $traceString;
|
|
|
|
}
|
|
|
|
|
2014-06-19 17:54:37 +02:00
|
|
|
/**
|
|
|
|
* Check if the given Trace Step should be skipped
|
|
|
|
*
|
|
|
|
* @param array $traceStep
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
private function shouldSkipTraceStep(array $traceStep) {
|
|
|
|
if (isset($traceStep['class'])) {
|
2014-06-19 18:05:58 +02:00
|
|
|
$skippedClasses = array('Symfony\\Component\\EventDispatcher\\EventDispatcher', 'cURL\\Request');
|
2014-06-19 17:54:37 +02:00
|
|
|
foreach ($skippedClasses as $skippedClass) {
|
2014-06-19 18:05:58 +02:00
|
|
|
if ($traceStep['class'] === $skippedClass) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (isset($traceStep['file'])) {
|
|
|
|
$skippedFiles = array('Symfony', 'curl-easy');
|
|
|
|
foreach ($skippedFiles as $skippedFile) {
|
|
|
|
if (strpos($traceStep['file'], $skippedFile)) {
|
2014-06-19 17:54:37 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the given Class Name should be ignored as possible Error Source Class
|
|
|
|
*
|
|
|
|
* @param string $class
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
private function isIgnoredSourceClass($class) {
|
2014-07-30 10:38:02 +02:00
|
|
|
$ignoredClasses = array('Maniaplanet\\', '\\ErrorHandler');
|
2014-06-20 00:05:50 +02:00
|
|
|
foreach ($ignoredClasses as $ignoredClass) {
|
|
|
|
if (strpos($class, $ignoredClass) !== false) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2014-06-19 17:54:37 +02:00
|
|
|
}
|
|
|
|
|
2014-05-15 14:20:44 +02:00
|
|
|
/**
|
2014-08-08 14:41:58 +02:00
|
|
|
* Build a string from an arguments array
|
2014-05-15 14:20:44 +02:00
|
|
|
*
|
|
|
|
* @param array $args
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private function parseArgumentsArray(array $args) {
|
|
|
|
$string = '';
|
|
|
|
$argsCount = count($args);
|
|
|
|
foreach ($args as $index => $arg) {
|
|
|
|
if (is_object($arg)) {
|
|
|
|
$string .= 'object(' . get_class($arg) . ')';
|
|
|
|
} else if (is_array($arg)) {
|
|
|
|
$string .= 'array(' . $this->parseArgumentsArray($arg) . ')';
|
|
|
|
} else {
|
2017-03-26 13:58:23 +02:00
|
|
|
$type = gettype($arg);
|
2014-06-12 13:38:08 +02:00
|
|
|
$string .= $type . '(';
|
|
|
|
if (is_string($arg)) {
|
2014-06-26 11:13:59 +02:00
|
|
|
$param = $arg;
|
2014-08-25 15:31:16 +02:00
|
|
|
if (strlen($param) > 40) {
|
|
|
|
$param = substr($param, 0, 40) . '..';
|
2014-06-14 19:36:54 +02:00
|
|
|
}
|
2014-06-14 19:31:17 +02:00
|
|
|
$string .= print_r($param, true);
|
2014-06-12 13:38:08 +02:00
|
|
|
} else {
|
2014-06-14 18:56:59 +02:00
|
|
|
$string .= print_r($arg, true);
|
2014-06-12 13:38:08 +02:00
|
|
|
}
|
|
|
|
$string .= ')';
|
2014-05-15 14:20:44 +02:00
|
|
|
}
|
|
|
|
if ($index < $argsCount - 1) {
|
|
|
|
$string .= ', ';
|
|
|
|
}
|
2014-08-25 15:31:16 +02:00
|
|
|
if (strlen($string) > 150) {
|
2014-05-24 22:34:57 +02:00
|
|
|
// Too long...
|
|
|
|
$string .= '...';
|
|
|
|
break;
|
|
|
|
}
|
2014-05-15 14:20:44 +02:00
|
|
|
}
|
|
|
|
return $string;
|
|
|
|
}
|
|
|
|
|
2014-06-21 13:31:24 +02:00
|
|
|
/**
|
2014-08-08 14:41:58 +02:00
|
|
|
* Strip the ManiaControl path from the given path to ensure privacy
|
2014-06-21 13:31:24 +02:00
|
|
|
*
|
|
|
|
* @param string $path
|
|
|
|
* @return string
|
|
|
|
*/
|
2014-07-29 23:43:20 +02:00
|
|
|
private static function stripBaseDir($path) {
|
2014-08-08 14:41:58 +02:00
|
|
|
return str_replace(MANIACONTROL_PATH, '', $path);
|
2014-06-21 13:31:24 +02:00
|
|
|
}
|
|
|
|
|
2014-05-02 17:40:47 +02:00
|
|
|
/**
|
2014-08-08 14:41:58 +02:00
|
|
|
* Get the source class via the error file
|
2014-05-02 17:40:47 +02:00
|
|
|
*
|
2014-05-29 22:46:38 +02:00
|
|
|
* @param string $errorFile
|
|
|
|
* @return string
|
2014-05-02 17:40:47 +02:00
|
|
|
*/
|
2014-05-29 22:46:38 +02:00
|
|
|
private function getSourceClass($errorFile) {
|
|
|
|
if (!$errorFile) {
|
|
|
|
return null;
|
2014-05-02 17:40:47 +02:00
|
|
|
}
|
2014-08-08 14:41:58 +02:00
|
|
|
$filePath = substr($errorFile, strlen(MANIACONTROL_PATH));
|
2014-05-29 22:46:38 +02:00
|
|
|
$filePath = str_replace('plugins' . DIRECTORY_SEPARATOR, '', $filePath);
|
|
|
|
$filePath = str_replace('core' . DIRECTORY_SEPARATOR, 'ManiaControl\\', $filePath);
|
|
|
|
$className = str_replace('.php', '', $filePath);
|
|
|
|
$className = str_replace(DIRECTORY_SEPARATOR, '\\', $className);
|
2017-04-01 12:23:10 +02:00
|
|
|
|
|
|
|
|
2014-05-29 22:46:38 +02:00
|
|
|
if (!class_exists($className, false)) {
|
2017-04-01 12:23:10 +02:00
|
|
|
//For Classes With different Folder Namespaces
|
|
|
|
$splitNameSpace = explode('\\', $className);
|
|
|
|
if (is_array($splitNameSpace)) {
|
|
|
|
$className = end($splitNameSpace);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (get_declared_classes() as $declared_class) {
|
|
|
|
if (strpos($declared_class, $className) !== false) {
|
|
|
|
return $declared_class;
|
|
|
|
}
|
|
|
|
}
|
2014-05-29 22:46:38 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return $className;
|
2014-05-02 17:40:47 +02:00
|
|
|
}
|
|
|
|
|
2014-01-27 20:28:37 +01:00
|
|
|
/**
|
2014-05-29 22:46:38 +02:00
|
|
|
* Handle PHP Process Shutdown
|
2014-01-27 20:28:37 +01:00
|
|
|
*/
|
2014-05-29 22:46:38 +02:00
|
|
|
public function handleShutdown() {
|
|
|
|
// Check if the Shutdown was caused by a Fatal Error and report it
|
|
|
|
$error = error_get_last();
|
2014-06-20 00:05:50 +02:00
|
|
|
if ($error && self::isFatalError($error['type'])) {
|
2014-05-29 22:46:38 +02:00
|
|
|
$this->handleError($error['type'], $error['message'], $error['file'], $error['line'], array(), true);
|
2014-05-24 18:31:38 +02:00
|
|
|
}
|
2014-05-29 22:46:38 +02:00
|
|
|
|
|
|
|
$this->maniaControl->quit('Quitting ManiaControl!');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ManiaControl Exception Handler
|
|
|
|
*
|
2017-03-11 22:12:47 +01:00
|
|
|
* @param \Throwable $exception
|
2014-05-29 22:46:38 +02:00
|
|
|
* @param bool $shutdown
|
|
|
|
*/
|
2017-03-10 20:09:39 +01:00
|
|
|
public function handleException($exception, $shutdown = true) {
|
|
|
|
//Removed error type, as php throwed the exception in a case and it was not from class Exception weiredly
|
2014-05-29 22:46:38 +02:00
|
|
|
$message = "[ManiaControl EXCEPTION]: {$exception->getMessage()}";
|
|
|
|
|
|
|
|
$exceptionClass = get_class($exception);
|
|
|
|
$sourceClass = null;
|
|
|
|
$traceString = $this->parseBackTrace($exception->getTrace(), $sourceClass);
|
|
|
|
|
|
|
|
$logMessage = $message . PHP_EOL . 'Class: ' . $exceptionClass . PHP_EOL . 'Trace:' . PHP_EOL . $traceString;
|
2014-08-05 01:49:13 +02:00
|
|
|
Logger::log($logMessage);
|
2014-05-02 17:40:47 +02:00
|
|
|
|
2014-05-29 22:46:38 +02:00
|
|
|
if (!DEV_MODE) {
|
2014-05-24 21:27:41 +02:00
|
|
|
$report = array();
|
2014-05-29 22:46:38 +02:00
|
|
|
$report['Type'] = 'Exception';
|
2014-06-26 11:13:59 +02:00
|
|
|
$report['Message'] = $message;
|
2014-05-29 22:46:38 +02:00
|
|
|
$report['Class'] = $exceptionClass;
|
2014-07-29 23:43:20 +02:00
|
|
|
$report['FileLine'] = self::stripBaseDir($exception->getFile()) . ': ' . $exception->getLine();
|
2014-05-24 21:27:41 +02:00
|
|
|
$report['SourceClass'] = $sourceClass;
|
|
|
|
$report['PluginId'] = PluginManager::getPluginId($sourceClass);
|
2017-06-28 14:38:21 +02:00
|
|
|
$report['PluginVersion'] = PluginManager::getPluginVersion($sourceClass);
|
2014-05-26 12:57:57 +02:00
|
|
|
$report['Backtrace'] = $traceString;
|
2014-05-24 21:27:41 +02:00
|
|
|
$report['OperatingSystem'] = php_uname();
|
|
|
|
$report['PHPVersion'] = phpversion();
|
2014-05-02 17:40:47 +02:00
|
|
|
|
2014-09-01 03:42:13 +02:00
|
|
|
if ($server = $this->maniaControl->getServer()) {
|
|
|
|
$report['ServerLogin'] = $server->login;
|
2014-02-10 15:26:08 +01:00
|
|
|
}
|
2014-05-02 17:40:47 +02:00
|
|
|
|
2014-09-01 15:42:59 +02:00
|
|
|
if (($settingManager = $this->maniaControl->getSettingManager()) && ($updateManager = $this->maniaControl->getUpdateManager())) {
|
|
|
|
$report['UpdateChannel'] = $settingManager->getSettingValue($updateManager, $updateManager::SETTING_UPDATECHECK_CHANNEL);
|
2014-09-01 03:42:13 +02:00
|
|
|
$report['ManiaControlVersion'] = ManiaControl::VERSION . ' #' . $updateManager->getBuildDate();
|
2014-05-02 17:40:47 +02:00
|
|
|
} else {
|
2014-05-24 21:27:41 +02:00
|
|
|
$report['ManiaControlVersion'] = ManiaControl::VERSION;
|
2014-02-15 18:26:31 +01:00
|
|
|
}
|
2014-05-02 17:40:47 +02:00
|
|
|
|
2017-03-26 13:58:23 +02:00
|
|
|
$report['DedicatedBuildVersion'] = $this->maniaControl->getDedicatedServerBuildVersion();
|
|
|
|
|
2014-08-25 15:23:08 +02:00
|
|
|
$errorReport = json_encode(Formatter::utf8($report));
|
2014-05-02 17:40:47 +02:00
|
|
|
|
2014-08-25 15:23:08 +02:00
|
|
|
$url = ManiaControl::URL_WEBSERVICE . 'errorreport';
|
|
|
|
$response = WebReader::postUrl($url, $errorReport);
|
2014-06-27 01:18:55 +02:00
|
|
|
$content = $response->getContent();
|
|
|
|
$success = json_decode($content);
|
2014-05-07 21:37:37 +02:00
|
|
|
if ($success) {
|
2014-06-17 22:59:49 +02:00
|
|
|
Logger::log('Exception successfully reported!');
|
2014-05-07 21:37:37 +02:00
|
|
|
} else {
|
2014-06-27 01:18:55 +02:00
|
|
|
Logger::log('Exception-Report failed! ' . print_r($content, true));
|
2014-02-08 19:33:26 +01:00
|
|
|
}
|
2014-02-10 23:03:25 +01:00
|
|
|
}
|
2014-03-19 14:00:24 +01:00
|
|
|
|
2014-05-29 22:46:38 +02:00
|
|
|
if ($shutdown) {
|
|
|
|
if ($this->shouldRestart()) {
|
|
|
|
$this->maniaControl->restart();
|
|
|
|
}
|
2014-09-01 03:42:13 +02:00
|
|
|
try {
|
|
|
|
$this->maniaControl->quit('Quitting ManiaControl after Exception.');
|
|
|
|
} catch (TransportException $e) {
|
|
|
|
}
|
2014-05-24 13:57:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-02 17:40:47 +02:00
|
|
|
/**
|
2014-05-29 22:46:38 +02:00
|
|
|
* Test if ManiaControl should restart automatically
|
2014-05-02 17:40:47 +02:00
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
2014-05-29 22:46:38 +02:00
|
|
|
private function shouldRestart() {
|
2014-08-03 01:34:18 +02:00
|
|
|
if (!$this->maniaControl || !$this->maniaControl->getSettingManager() || DEV_MODE) {
|
2014-05-29 22:46:38 +02:00
|
|
|
return false;
|
2014-05-03 22:26:49 +02:00
|
|
|
}
|
2014-08-13 11:05:52 +02:00
|
|
|
$setting = $this->maniaControl->getSettingManager()->getSettingValue($this, self::SETTING_RESTART_ON_EXCEPTION, true);
|
2014-05-29 22:46:38 +02:00
|
|
|
return $setting;
|
2014-05-03 22:26:49 +02:00
|
|
|
}
|
2014-06-14 19:45:32 +02:00
|
|
|
}
|