Initial
This commit is contained in:
commit
17a426a0d2
230
MDErrorReporter.php
Normal file
230
MDErrorReporter.php
Normal file
@ -0,0 +1,230 @@
|
||||
<?PHP
|
||||
/**
|
||||
* Debugging and error handling helper for MD.
|
||||
*/
|
||||
declare(strict_types = 1);
|
||||
|
||||
/**
|
||||
* Debugging and error handling class.
|
||||
*/
|
||||
class MDErrorReporter {
|
||||
|
||||
const MD_ERROR_UNKNOWN = 20;
|
||||
const MD_ERROR_CRITICAL = 10;
|
||||
const MD_ERROR_KNOWN = 0;
|
||||
|
||||
private $_context;
|
||||
private $_contact_mail_addr;
|
||||
|
||||
/**
|
||||
* Categorizes an error to better decide how to act on it.
|
||||
*
|
||||
* @param Throwable $exception Exception to be categorized.
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
final public static function categorizeError(Throwable $exception):int {
|
||||
|
||||
if ($exception instanceof MDpageParameterMissingException
|
||||
|| $exception instanceof MDpageParameterNotNumericException
|
||||
|| $exception instanceof MDpageParameterNotFromListException
|
||||
|| $exception instanceof MDmainEntityNotExistentException
|
||||
|| $exception instanceof MDmainEntityNotPublicException
|
||||
|| $exception instanceof MDDBConnectionImpossible
|
||||
|| $exception instanceof MDMysqliExpectedError
|
||||
|| $exception instanceof MDMysqliTimeout
|
||||
|| $exception instanceof MDMysqliInvalidInputEncoding
|
||||
|| $exception instanceof MDPageNotInAggregatedException
|
||||
) {
|
||||
return self::MD_ERROR_KNOWN;
|
||||
}
|
||||
|
||||
return self::MD_ERROR_UNKNOWN;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Function human_filesize translates byte-level filesizes to human readable ones.
|
||||
* Thanks to Jeffrey Sambells http://jeffreysambells.com/2012/10/25/human-readable-filesize-php
|
||||
*
|
||||
* @param string $bytes A file size, e.g. returned from filesize().
|
||||
* @param integer $decimals Number of decimal digits to allow.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final public function human_filesize(string $bytes, int $decimals = 2):string {
|
||||
|
||||
$size = ['B','kB','MB','GB','TB','PB','EB','ZB','YB'];
|
||||
$factor = floor((strlen($bytes) - 1) / 3);
|
||||
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . $size[$factor];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Function for formatting a section in an error mail.
|
||||
*
|
||||
* @param string $headline Headline of the section.
|
||||
* @param array<mixed> $toFormat Associative array containing information to
|
||||
* be formatted.
|
||||
* @param integer $level Indentation level. Defaults to 0.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
final private function _formulateDebugMailSection(string $headline, array $toFormat, int $level = 0):string {
|
||||
|
||||
$msg = "";
|
||||
if (!empty($headline)) {
|
||||
if ($level === 0) {
|
||||
$msg .= PHP_EOL . "## " . $headline;
|
||||
$msg .= PHP_EOL . "----------------------" . PHP_EOL . PHP_EOL;
|
||||
}
|
||||
else $msg .= "$headline" . PHP_EOL;
|
||||
}
|
||||
|
||||
$longestKey = 0;
|
||||
foreach ($toFormat as $key => $value) {
|
||||
if (strlen((string)$key) + (4 * $level) > $longestKey) $longestKey = strlen((string)$key) + (4 * $level);
|
||||
}
|
||||
foreach ($toFormat as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$msg .= $this->_formulateDebugMailSection((string)$key, $value, $level + 1);
|
||||
}
|
||||
else if (is_object($value)) {
|
||||
$msg .= sprintf("%-{$longestKey}s :: %s", $key, var_export($value, true)) . PHP_EOL;
|
||||
}
|
||||
else {
|
||||
$msg .= sprintf("%-{$longestKey}s :: %s", $key, (string)$value) . PHP_EOL;
|
||||
}
|
||||
}
|
||||
$msg .= PHP_EOL;
|
||||
|
||||
return $msg;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets additional debugging information, e.g. RAM usage.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
final public function getAdditionalDebuggingInfo():array {
|
||||
|
||||
$output = [
|
||||
"Current memory usage" => $this->human_filesize((string)memory_get_usage()),
|
||||
"Peak memory usage" => $this->human_filesize((string)memory_get_peak_usage()),
|
||||
"Included / required files" => get_included_files(),
|
||||
# "Allowed memory usage" => $this->human_filesize((string)ini_get("memory_limit")),
|
||||
];
|
||||
|
||||
return $output;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function sends a mail to the specified address, containing relevant debug information.
|
||||
* .
|
||||
*
|
||||
* @param string $to Recipient mail address.
|
||||
* @param string $subject Optional subject of the notification. Defaults to "".
|
||||
* @param string $description Optional text of the notification. Defaults to : "".
|
||||
* @param array<mixed> $contextInfo Context information that will be displayed in a
|
||||
* key-value format at the start of the mail.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final public function sendDebugMail(string $to, string $subject = "", string $description = "", array $contextInfo = []):void {
|
||||
|
||||
$subject = "[{$this->_context}]{$subject}[Controlled error notification]";
|
||||
|
||||
$msg = "
|
||||
# Automated error message
|
||||
=========================
|
||||
";
|
||||
|
||||
if (!empty($contextInfo)) {
|
||||
$msg .= $this->_formulateDebugMailSection("Context information", $contextInfo);
|
||||
}
|
||||
|
||||
if ($description != "") {
|
||||
$msg .= "## Description" . PHP_EOL;
|
||||
$msg .= "--------------" . PHP_EOL . PHP_EOL;
|
||||
$msg .= "$description" . PHP_EOL;
|
||||
}
|
||||
|
||||
$msg .= $this->_formulateDebugMailSection("Debug backtrace", debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT));
|
||||
|
||||
// Append the contents of $_SERVER
|
||||
if (!empty($_SERVER)) {
|
||||
$msg .= $this->_formulateDebugMailSection("The current content of \$_SERVER", $_SERVER);
|
||||
}
|
||||
|
||||
// Append the contents of $_SESSION
|
||||
if (session_status() == PHP_SESSION_ACTIVE) {
|
||||
$msg .= $this->_formulateDebugMailSection("The current content of \$_SESSION", $_SESSION);
|
||||
}
|
||||
else $msg .= "No active session";
|
||||
|
||||
$msg .= $this->_formulateDebugMailSection("Additional debugging information",
|
||||
$this->getAdditionalDebuggingInfo());
|
||||
|
||||
$msg .= PHP_EOL . PHP_EOL;
|
||||
|
||||
$msg = shell_exec("echo " . escapeshellarg($msg) . " | gpg2 --always-trust --recipient " . escapeshellarg($to) . " --encrypt --armor --local-user 35CA0E31F6F44FB5 --sign");
|
||||
|
||||
// Set header
|
||||
$header = [
|
||||
'From' => $this->_contact_mail_addr,
|
||||
'X-Mailer' => 'PHP/' . phpversion()
|
||||
];
|
||||
|
||||
// Send email
|
||||
mail("$to", "$subject", "$msg", $header);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around MDErrorReporter::sendDebugMail that can handle a throwable.
|
||||
*
|
||||
* @param Throwable $exception Exception to report on.
|
||||
* @param string $recipient Recipient mail address.
|
||||
* @param string $addDesc Additional descriptive information.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
final public function sendErrorReport(Throwable $exception, string $recipient, string $addDesc = ""):void {
|
||||
|
||||
$subject = get_class($exception) . ": " . $exception->getCode();
|
||||
|
||||
$description = $addDesc;
|
||||
$description .= $this->_formulateDebugMailSection("Detailed error information", $exception);
|
||||
|
||||
$additionalContextInfo = [
|
||||
"Message" => $exception->getMessage(),
|
||||
"File" => $exception->getFile(),
|
||||
"Line" => $exception->getLine()
|
||||
];
|
||||
|
||||
$this->sendDebugMail($recipient,
|
||||
$subject,
|
||||
$description,
|
||||
$additionalContextInfo
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $toolName Name of the current tool.
|
||||
* @param string $contact_mail_addr Mail address for mail's from header.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(string $toolName = "museum-digital (unspecified)", string $contact_mail_addr = "bugs-md") {
|
||||
|
||||
$this->_context = $toolName;
|
||||
$this->_contact_mail_addr = $contact_mail_addr;
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user