184 lines
6.0 KiB
PHP
184 lines
6.0 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;
|
|
|
|
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 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());
|
|
}
|
|
}
|
|
|
|
}
|