date('c'),
'type' => $type,
'message' => $message,
'file' => $file,
'line' => $line,
'trace' => $trace,
'user_id' => $userId,
'user_name' => $userName,
'url' => $url,
'ip' => $ip,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$logFile = ERROR_LOG_FILE;
// Append then rotate if over limit.
file_put_contents($logFile, $entry . "\n", FILE_APPEND | LOCK_EX);
// Trim to last ERROR_LOG_MAX lines when the file grows large.
if (filesize($logFile) > 256 * 1024) {
$lines = file($logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if (count($lines) > ERROR_LOG_MAX) {
$lines = array_slice($lines, -ERROR_LOG_MAX);
file_put_contents($logFile, implode("\n", $lines) . "\n", LOCK_EX);
}
}
}
function _error_page(string $title, string $detail, string $trace = ''): void {
$isProd = defined('APP_ENV') && APP_ENV === 'prod';
$cssBase = (defined('APP_BASE_URL') ? rtrim(APP_BASE_URL, '/') : '');
http_response_code(500);
if (ob_get_level()) ob_end_clean();
?>
Erro — = htmlspecialchars(defined('APP_NAME') ? APP_NAME : 'Plataforma') ?>
Algo correu mal
Ocorreu um erro inesperado. Por favor tente novamente ou contacte o suporte.
= htmlspecialchars($title) ?>
= htmlspecialchars($detail) ?>
= htmlspecialchars($trace) ?>
getMessage(),
$e->getFile(),
$e->getLine(),
$e->getTraceAsString()
);
_error_page(
get_class($e) . ': ' . $e->getMessage(),
$e->getMessage() . "\n\nFicheiro: " . $e->getFile() . ':' . $e->getLine(),
$e->getTraceAsString()
);
});
// ── PHP errors ────────────────────────────────────────────────────────────────
set_error_handler(function (int $errno, string $errstr, string $errfile, int $errline): bool {
if (!(error_reporting() & $errno)) return false;
$label = match(true) {
(bool)($errno & E_NOTICE) => 'Notice',
(bool)($errno & E_WARNING) => 'Warning',
(bool)($errno & E_DEPRECATED) => 'Deprecated',
(bool)($errno & E_USER_NOTICE) => 'User Notice',
(bool)($errno & E_USER_WARNING) => 'User Warning',
(bool)($errno & E_USER_DEPRECATED)=> 'User Deprecated',
default => "Error ($errno)",
};
_error_log_write($label, $errstr, $errfile, $errline);
// In prod, only show error page for fatal-level user errors.
if (defined('APP_ENV') && APP_ENV === 'prod') {
$fatal = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR;
if (!($errno & $fatal)) return false;
}
_error_page("PHP $label: $errstr", "$errstr\n\nFicheiro: $errfile:$errline");
return true;
});
// ── Fatal errors (parse errors, out-of-memory, etc.) ─────────────────────────
register_shutdown_function(function (): void {
$e = error_get_last();
if (!$e) return;
$fatal = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR;
if (!($e['type'] & $fatal)) return;
_error_log_write('Fatal', $e['message'], $e['file'], $e['line']);
_error_page("Fatal Error: {$e['message']}", "{$e['message']}\n\nFicheiro: {$e['file']}:{$e['line']}");
});