From 8ac999cbd4fb7d0049a31b83f54a1147aab41fad Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 4 Nov 2021 16:55:04 +0000 Subject: [PATCH] Use object models for crashdump generation --- src/CrashDump.php | 120 ++++++++++++------------- src/Server.php | 6 +- src/crash/CrashDumpData.php | 84 +++++++++++++++++ src/crash/CrashDumpDataGeneral.php | 46 ++++++++++ src/crash/CrashDumpDataPluginEntry.php | 45 ++++++++++ 5 files changed, 233 insertions(+), 68 deletions(-) create mode 100644 src/crash/CrashDumpData.php create mode 100644 src/crash/CrashDumpDataGeneral.php create mode 100644 src/crash/CrashDumpDataPluginEntry.php diff --git a/src/CrashDump.php b/src/CrashDump.php index d6cf5caf0..b18d09021 100644 --- a/src/CrashDump.php +++ b/src/CrashDump.php @@ -24,6 +24,9 @@ declare(strict_types=1); namespace pocketmine; use Composer\InstalledVersions; +use pocketmine\crash\CrashDumpData; +use pocketmine\crash\CrashDumpDataGeneral; +use pocketmine\crash\CrashDumpDataPluginEntry; use pocketmine\errorhandler\ErrorTypeToStringMap; use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\plugin\PluginBase; @@ -69,6 +72,7 @@ use const FILE_IGNORE_NEW_LINES; use const JSON_UNESCAPED_SLASHES; use const PHP_EOL; use const PHP_OS; +use const PHP_VERSION; use const SORT_STRING; class CrashDump{ @@ -91,13 +95,9 @@ class CrashDump{ private $fp; /** @var float */ private $time; - /** - * @var mixed[] - * @phpstan-var array - */ - private $data = []; + private CrashDumpData $data; /** @var string */ - private $encodedData = ""; + private $encodedData; /** @var string */ private $path; @@ -118,9 +118,10 @@ class CrashDump{ throw new \RuntimeException("Could not create Crash Dump"); } $this->fp = $fp; - $this->data["format_version"] = self::FORMAT_VERSION; - $this->data["time"] = $this->time; - $this->data["uptime"] = $this->time - $this->server->getStartTime(); + $this->data = new CrashDumpData(); + $this->data->format_version = self::FORMAT_VERSION; + $this->data->time = $this->time; + $this->data->uptime = $this->time - $this->server->getStartTime(); $this->addLine($this->server->getName() . " Crash Dump " . date("D M j H:i:s T Y", (int) $this->time)); $this->addLine(); $this->baseCrash(); @@ -142,11 +143,7 @@ class CrashDump{ return $this->encodedData; } - /** - * @return mixed[] - * @phpstan-return array - */ - public function getData() : array{ + public function getData() : CrashDumpData{ return $this->data; } @@ -173,22 +170,21 @@ class CrashDump{ $plugins = $this->pluginManager->getPlugins(); $this->addLine(); $this->addLine("Loaded plugins:"); - $this->data["plugins"] = []; ksort($plugins, SORT_STRING); foreach($plugins as $p){ $d = $p->getDescription(); - $this->data["plugins"][$d->getName()] = [ - "name" => $d->getName(), - "version" => $d->getVersion(), - "authors" => $d->getAuthors(), - "api" => $d->getCompatibleApis(), - "enabled" => $p->isEnabled(), - "depends" => $d->getDepend(), - "softDepends" => $d->getSoftDepend(), - "main" => $d->getMain(), - "load" => mb_strtoupper($d->getOrder()->name()), - "website" => $d->getWebsite() - ]; + $this->data->plugins[$d->getName()] = new CrashDumpDataPluginEntry( + name: $d->getName(), + version: $d->getVersion(), + authors: $d->getAuthors(), + api: $d->getCompatibleApis(), + enabled: $p->isEnabled(), + depends: $d->getDepend(), + softDepends: $d->getSoftDepend(), + main: $d->getMain(), + load: mb_strtoupper($d->getOrder()->name()), + website: $d->getWebsite() + ); $this->addLine($d->getName() . " " . $d->getVersion() . " by " . implode(", ", $d->getAuthors()) . " for API(s) " . implode(", ", $d->getCompatibleApis())); } } @@ -198,32 +194,26 @@ class CrashDump{ global $argv; if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-settings", true)){ - $this->data["parameters"] = (array) $argv; + $this->data->parameters = (array) $argv; if(($serverDotProperties = @file_get_contents(Path::join($this->server->getDataPath(), "server.properties"))) !== false){ - $this->data["server.properties"] = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $serverDotProperties); - }else{ - $this->data["server.properties"] = $serverDotProperties; + $this->data->serverDotProperties = preg_replace("#^rcon\\.password=(.*)$#m", "rcon.password=******", $serverDotProperties) ?? throw new AssumptionFailedError("Pattern is valid"); } if(($pocketmineDotYml = @file_get_contents(Path::join($this->server->getDataPath(), "pocketmine.yml"))) !== false){ - $this->data["pocketmine.yml"] = $pocketmineDotYml; - }else{ - $this->data["pocketmine.yml"] = ""; + $this->data->pocketmineDotYml = $pocketmineDotYml; } - }else{ - $this->data["pocketmine.yml"] = ""; - $this->data["server.properties"] = ""; - $this->data["parameters"] = []; } $extensions = []; foreach(get_loaded_extensions() as $ext){ - $extensions[$ext] = phpversion($ext); + $version = phpversion($ext); + if($version === false) throw new AssumptionFailedError(); + $extensions[$ext] = $version; } - $this->data["extensions"] = $extensions; + $this->data->extensions = $extensions; if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-phpinfo", true)){ ob_start(); phpinfo(); - $this->data["phpinfo"] = ob_get_contents(); + $this->data->phpinfo = ob_get_contents(); // @phpstan-ignore-line ob_end_clean(); } } @@ -255,18 +245,18 @@ class CrashDump{ if(isset($lastError["trace"])){ $lastError["trace"] = Utils::printableTrace($lastError["trace"]); } - $this->data["lastError"] = $lastError; + $this->data->lastError = $lastError; } - $this->data["error"] = $error; - unset($this->data["error"]["fullFile"]); - unset($this->data["error"]["trace"]); + $this->data->error = $error; + unset($this->data->error["fullFile"]); + unset($this->data->error["trace"]); $this->addLine("Error: " . $error["message"]); $this->addLine("File: " . $error["file"]); $this->addLine("Line: " . $error["line"]); $this->addLine("Type: " . $error["type"]); - $this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_NONE; + $this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_NONE; if(!$this->determinePluginFromFile($error["fullFile"], true)){ //fatal errors won't leave any stack trace foreach($error["trace"] as $frame){ if(!isset($frame["file"])){ @@ -280,21 +270,20 @@ class CrashDump{ $this->addLine(); $this->addLine("Code:"); - $this->data["code"] = []; if($this->server->getConfigGroup()->getPropertyBool("auto-report.send-code", true) and file_exists($error["fullFile"])){ $file = @file($error["fullFile"], FILE_IGNORE_NEW_LINES); if($file !== false){ for($l = max(0, $error["line"] - 10); $l < $error["line"] + 10 and isset($file[$l]); ++$l){ $this->addLine("[" . ($l + 1) . "] " . $file[$l]); - $this->data["code"][$l + 1] = $file[$l]; + $this->data->code[$l + 1] = $file[$l]; } } } $this->addLine(); $this->addLine("Backtrace:"); - foreach(($this->data["trace"] = Utils::printableTrace($error["trace"])) as $line){ + foreach(($this->data->trace = Utils::printableTrace($error["trace"])) as $line){ $this->addLine($line); } $this->addLine(); @@ -306,10 +295,10 @@ class CrashDump{ $this->addLine(); if($crashFrame){ $this->addLine("THIS CRASH WAS CAUSED BY A PLUGIN"); - $this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_DIRECT; + $this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_DIRECT; }else{ $this->addLine("A PLUGIN WAS INVOLVED IN THIS CRASH"); - $this->data["plugin_involvement"] = self::PLUGIN_INVOLVEMENT_INDIRECT; + $this->data->plugin_involvement = self::PLUGIN_INVOLVEMENT_INDIRECT; } if(file_exists($filePath)){ @@ -319,7 +308,7 @@ class CrashDump{ foreach($this->server->getPluginManager()->getPlugins() as $plugin){ $filePath = Filesystem::cleanPath($file->getValue($plugin)); if(strpos($frameCleanPath, $filePath) === 0){ - $this->data["plugin"] = $plugin->getName(); + $this->data->plugin = $plugin->getName(); $this->addLine("BAD PLUGIN: " . $plugin->getDescription()->getFullName()); break; } @@ -341,19 +330,20 @@ class CrashDump{ ); } - $this->data["general"] = []; - $this->data["general"]["name"] = $this->server->getName(); - $this->data["general"]["base_version"] = VersionInfo::BASE_VERSION; - $this->data["general"]["build"] = VersionInfo::BUILD_NUMBER; - $this->data["general"]["is_dev"] = VersionInfo::IS_DEVELOPMENT_BUILD; - $this->data["general"]["protocol"] = ProtocolInfo::CURRENT_PROTOCOL; - $this->data["general"]["git"] = VersionInfo::GIT_HASH(); - $this->data["general"]["uname"] = php_uname("a"); - $this->data["general"]["php"] = phpversion(); - $this->data["general"]["zend"] = zend_version(); - $this->data["general"]["php_os"] = PHP_OS; - $this->data["general"]["os"] = Utils::getOS(); - $this->data["general"]["composer_libraries"] = $composerLibraries; + $this->data->general = new CrashDumpDataGeneral( + name: $this->server->getName(), + base_version: VersionInfo::BASE_VERSION, + build: VersionInfo::BUILD_NUMBER, + is_dev: VersionInfo::IS_DEVELOPMENT_BUILD, + protocol: ProtocolInfo::CURRENT_PROTOCOL, + git: VersionInfo::GIT_HASH(), + uname: php_uname("a"), + php: PHP_VERSION, + zend: zend_version(), + php_os: PHP_OS, + os: Utils::getOS(), + composer_libraries: $composerLibraries, + ); $this->addLine($this->server->getName() . " version: " . $version->getFullVersion(true) . " [Protocol " . ProtocolInfo::CURRENT_PROTOCOL . "]"); $this->addLine("Git commit: " . VersionInfo::GIT_HASH()); $this->addLine("uname -a: " . php_uname("a")); diff --git a/src/Server.php b/src/Server.php index 753556ff8..30fba8f25 100644 --- a/src/Server.php +++ b/src/Server.php @@ -1504,8 +1504,8 @@ class Server{ } @touch($stamp); //update file timestamp - $plugin = $dump->getData()["plugin"]; - if(is_string($plugin)){ + $plugin = $dump->getData()->plugin; + if($plugin !== ""){ $p = $this->pluginManager->getPlugin($plugin); if($p instanceof Plugin and !($p->getPluginLoader() instanceof PharPluginLoader)){ $this->logger->debug("Not sending crashdump due to caused by non-phar plugin"); @@ -1513,7 +1513,7 @@ class Server{ } } - if($dump->getData()["error"]["type"] === \ParseError::class){ + if($dump->getData()->error["type"] === \ParseError::class){ $report = false; } diff --git a/src/crash/CrashDumpData.php b/src/crash/CrashDumpData.php new file mode 100644 index 000000000..a3f13f2ca --- /dev/null +++ b/src/crash/CrashDumpData.php @@ -0,0 +1,84 @@ + + */ + public array $plugins = []; + + /** @var string[] */ + public array $parameters = []; + + public string $serverDotProperties = ""; + + public string $pocketmineDotYml = ""; + + /** + * @var string[] + * @phpstan-var array + */ + public array $extensions = []; + + public string $phpinfo = ""; + + public CrashDumpDataGeneral $general; + + /** + * @return mixed[] + */ + public function jsonSerialize() : array{ + $result = (array) $this; + unset($result["serverDotProperties"]); + unset($result["pocketmineDotYml"]); + $result["pocketmine.yml"] = $this->pocketmineDotYml; + $result["server.properties"] = $this->serverDotProperties; + return $result; + } +} \ No newline at end of file diff --git a/src/crash/CrashDumpDataGeneral.php b/src/crash/CrashDumpDataGeneral.php new file mode 100644 index 000000000..33e41ce01 --- /dev/null +++ b/src/crash/CrashDumpDataGeneral.php @@ -0,0 +1,46 @@ + $composer_libraries + */ + public function __construct( + public string $name, + public string $base_version, + public int $build, + public bool $is_dev, + public int $protocol, + public string $git, + public string $uname, + public string $php, + public string $zend, + public string $php_os, + public string $os, + public array $composer_libraries, + ){} +} \ No newline at end of file diff --git a/src/crash/CrashDumpDataPluginEntry.php b/src/crash/CrashDumpDataPluginEntry.php new file mode 100644 index 000000000..21139f431 --- /dev/null +++ b/src/crash/CrashDumpDataPluginEntry.php @@ -0,0 +1,45 @@ +