'json',
'application/xml' => 'xml',
'text/html' => 'html',
'text/plain' => 'text',
];
public static function HandleException(Throwable $oException)
{
$aStatus = ob_get_status();
if (count($aStatus) !== 0) {
ob_end_clean();
}
// Log the exception
IssueLog::Exception('Fatal error', $oException);
$mime = self::NegotiateMimeType();
if ($mime === null) {
http_response_code(406);
header('Content-Type: application/json; charset=utf-8');
header('Vary: Accept');
echo json_encode(['error' => 'Not Acceptable'], JSON_UNESCAPED_UNICODE);
return;
}
http_response_code(500);
header("Content-Type: {$mime}; charset=utf-8");
header('Vary: Accept');
$aData = [
'error' => 'Fatal error',
'message' => 'We are sorry, an unexpected error has occurred. Please try again later.',
];
switch (self::$aSupportedMimeTypes[$mime]) {
case 'json':
echo json_encode($aData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
break;
case 'xml':
$oXml = new SimpleXMLElement('');
array_walk_recursive($aData, function ($sValue, $sKey) use ($oXml) {
$oXml->addChild((string)$sKey, htmlspecialchars((string)$sValue, ENT_QUOTES | ENT_XML1, 'UTF-8'));
});
echo $oXml->asXML();
break;
case 'html':
// Create error page
$oErrorPage = new ErrorPage('Fatal error');
$oErrorPage->error('We are sorry, an unexpected error has occurred. Please try again later.
', $oException);
$oErrorPage->output();
break;
case 'text':
echo "Fatal error\n";
echo "We are sorry, an unexpected error has occurred. Please try again later.\n";
break;
}
}
public static function NegotiateMimeType(): ?string
{
$supportedMimes = array_keys(self::$aSupportedMimeTypes);
$acceptHeader = $_SERVER['HTTP_ACCEPT'] ?? '*/*';
if (trim($acceptHeader) === '' || $acceptHeader === '*/*') {
return in_array('application/json', $supportedMimes, true) ? 'application/json' : $supportedMimes[0];
}
$accepted = [];
foreach (explode(',', $acceptHeader) as $part) {
$part = trim($part);
$q = 1.0;
if (str_contains($part, ';')) {
[$type, $params] = array_map('trim', explode(';', $part, 2));
if (preg_match('/q=([0-9.]+)/', $params, $m)) {
$q = (float)$m[1];
}
} else {
$type = $part;
}
$accepted[] = ['type' => $type, 'q' => $q];
}
usort($accepted, fn ($a, $b) => $b['q'] <=> $a['q']);
foreach ($accepted as $a) {
foreach ($supportedMimes as $mime) {
if ($a['type'] === $mime || $a['type'] === '*/*') {
return $mime;
}
// Ex: application/* match application/json
if (str_ends_with($a['type'], '/*')) {
$prefix = explode('/', $a['type'])[0].'/';
if (str_starts_with($mime, $prefix)) {
return $mime;
}
}
}
}
return null;
}
}