Files
server/core/lib/Server.php
2025-12-21 15:15:15 -05:00

199 lines
6.4 KiB
PHP

<?php
namespace KTXC;
use ErrorException;
use KTXC\Http\Request\Request;
use KTXC\Http\Response\Response;
use KTXC\Injection\Container;
use Psr\Log\LoggerInterface;
use Throwable;
/**
* Provides static access to the server container
*/
class Server
{
public const ENVIRONMENT_DEV = 'dev';
public const ENVIRONMENT_PROD = 'prod';
protected static $kernel;
protected static $composerLoader;
public static function run() {
// Set up global error handler before anything else
self::setupErrorHandlers();
try {
self::$kernel = new Kernel(self::ENVIRONMENT_DEV, true);
$request = Request::createFromGlobals();
$response = self::$kernel->handle($request);
if ($response instanceof Response) {
$response->send();
}
} catch (\Throwable $e) {
self::logException($e);
$content = self::debug()
? '<pre>' . htmlspecialchars((string) $e) . '</pre>'
: 'An error occurred. Please try again later.';
$response = new Response($content, Response::HTTP_INTERNAL_SERVER_ERROR, [
'Content-Type' => 'text/html; charset=UTF-8',
]);
$response->send();
exit(1);
}
}
public static function environment(): string {
return self::$kernel->environment();
}
public static function debug(): bool {
return self::$kernel->debug();
}
public static function runtimeKernel(): Kernel {
return self::$kernel;
}
public static function runtimeContainer(): Container {
return self::$kernel->container();
}
public static function runtimeRootLocation(): string {
return self::$kernel->folderRoot();
}
public static function runtimeModuleLocation(): string {
return self::$kernel->folderRoot() . '/modules';
}
/**
* Set the Composer ClassLoader instance
*/
public static function setComposerLoader($loader): void {
self::$composerLoader = $loader;
}
/**
* Get the Composer ClassLoader instance
*/
public static function getComposerLoader() {
return self::$composerLoader;
}
/**
* Set up global error and exception handlers
*/
protected static function setupErrorHandlers(): void {
// Convert PHP errors to exceptions
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
// Don't throw exception if error reporting is turned off
if (!(error_reporting() & $errno)) {
return false;
}
$message = sprintf(
"PHP Error [%d]: %s in %s:%d",
$errno,
$errstr,
$errfile,
$errline
);
self::logError($message, [
'errno' => $errno,
'file' => $errfile,
'line' => $errline,
]);
// Throw exception for fatal errors
if ($errno === E_ERROR || $errno === E_CORE_ERROR || $errno === E_COMPILE_ERROR || $errno === E_USER_ERROR) {
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
return true;
});
// Handle uncaught exceptions
set_exception_handler(function (Throwable $exception) {
self::logException($exception);
if (self::debug()) {
echo '<pre>Uncaught Exception: ' . $exception . '</pre>';
} else {
echo 'An unexpected error occurred. Please try again later.';
}
exit(1);
});
// Handle fatal errors
register_shutdown_function(function () {
$error = error_get_last();
if ($error !== null && in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE])) {
$message = sprintf(
"Fatal Error [%d]: %s in %s:%d",
$error['type'],
$error['message'],
$error['file'],
$error['line']
);
self::logError($message, $error);
if (self::debug()) {
echo '<pre>' . $message . '</pre>';
} else {
echo 'A fatal error occurred. Please try again later.';
}
}
});
}
/**
* Log an error message
*/
protected static function logError(string $message, array $context = []): void {
try {
if (self::$kernel && self::$kernel->container()->has(LoggerInterface::class)) {
$logger = self::$kernel->container()->get(LoggerInterface::class);
$logger->error($message, $context);
} else {
// Fallback to error_log if logger not available
error_log($message . ' ' . json_encode($context));
}
} catch (Throwable $e) {
// Last resort fallback
error_log('Error logging failed: ' . $e->getMessage());
error_log($message . ' ' . json_encode($context));
}
}
/**
* Log an exception
*/
protected static function logException(Throwable $exception): void {
try {
if (self::$kernel && self::$kernel->container()->has(LoggerInterface::class)) {
$logger = self::$kernel->container()->get(LoggerInterface::class);
$logger->error('Exception caught: ' . $exception->getMessage(), [
'exception' => $exception,
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'trace' => $exception->getTraceAsString(),
]);
} else {
// Fallback to error_log if logger not available
error_log('Exception: ' . $exception->getMessage() . ' in ' . $exception->getFile() . ':' . $exception->getLine());
error_log($exception->getTraceAsString());
}
} catch (Throwable $e) {
// Last resort fallback
error_log('Exception logging failed: ' . $e->getMessage());
error_log('Original exception: ' . $exception->getMessage());
}
}
}