81 lines
2.4 KiB
PHP
81 lines
2.4 KiB
PHP
<?php
|
|
// force-debug.php
|
|
// Automatically prepended; guarantees fatals reach container logs and optionally surfaces verbose output.
|
|
|
|
$forceDebugRaw = getenv('FORCE_DEBUG_ERRORS');
|
|
$forceDebugEnabled = false;
|
|
if ($forceDebugRaw !== false) {
|
|
$normalized = strtolower(trim($forceDebugRaw));
|
|
$forceDebugEnabled = in_array($normalized, ['1', 'true', 'yes', 'on'], true);
|
|
}
|
|
|
|
// Ensure PHP always logs to stderr even if application code tampers with settings.
|
|
ini_set('log_errors', '1');
|
|
ini_set('error_log', '/proc/self/fd/2');
|
|
|
|
// Helper to safely write to container stderr without relying on php.ini.
|
|
$forceDebugLog = static function (string $message): void {
|
|
static $stderr = null;
|
|
if ($stderr === null) {
|
|
$stderr = @fopen('php://stderr', 'ab');
|
|
}
|
|
if ($stderr) {
|
|
@fwrite($stderr, $message . PHP_EOL);
|
|
} else {
|
|
// Fallback to PHP's error_log if stderr is unavailable for any reason.
|
|
error_log($message);
|
|
}
|
|
};
|
|
|
|
// Capture uncaught exceptions so they can never disappear silently.
|
|
set_exception_handler(static function (Throwable $throwable) use ($forceDebugLog, $forceDebugEnabled): void {
|
|
$message = sprintf(
|
|
'[force-debug] Uncaught %s: %s in %s:%d',
|
|
get_class($throwable),
|
|
$throwable->getMessage(),
|
|
$throwable->getFile(),
|
|
$throwable->getLine()
|
|
);
|
|
$forceDebugLog($message);
|
|
$forceDebugLog($throwable->getTraceAsString());
|
|
|
|
if ($forceDebugEnabled) {
|
|
http_response_code(500);
|
|
echo '<pre>' . htmlspecialchars($message . "\n" . $throwable->getTraceAsString(), ENT_QUOTES, 'UTF-8') . '</pre>';
|
|
}
|
|
});
|
|
|
|
// Capture fatal shutdown errors (E_ERROR, E_PARSE, etc.) and surface them to stderr.
|
|
register_shutdown_function(static function () use ($forceDebugLog, $forceDebugEnabled): void {
|
|
$error = error_get_last();
|
|
if ($error === null) {
|
|
return;
|
|
}
|
|
|
|
$fatalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR];
|
|
if (!in_array($error['type'], $fatalTypes, true)) {
|
|
return;
|
|
}
|
|
|
|
$message = sprintf(
|
|
'[force-debug] Fatal error (%s): %s in %s:%d',
|
|
$error['type'],
|
|
$error['message'],
|
|
$error['file'],
|
|
$error['line']
|
|
);
|
|
$forceDebugLog($message);
|
|
|
|
if ($forceDebugEnabled) {
|
|
http_response_code(500);
|
|
echo '<pre>' . htmlspecialchars($message, ENT_QUOTES, 'UTF-8') . '</pre>';
|
|
}
|
|
});
|
|
|
|
if ($forceDebugEnabled) {
|
|
error_reporting(E_ALL);
|
|
ini_set('display_errors', '1');
|
|
ini_set('display_startup_errors', '1');
|
|
$forceDebugLog('[force-debug] Verbose error reporting enabled via FORCE_DEBUG_ERRORS');
|
|
}
|