kernal clean-up
This commit is contained in:
@@ -11,16 +11,17 @@ namespace KTXC;
|
||||
|
||||
use KTXC\Http\Request\Request;
|
||||
use KTXC\Http\Response\Response;
|
||||
use KTXC\Http\Middleware\MiddlewarePipeline;
|
||||
use KTXC\Http\Middleware\TenantMiddleware;
|
||||
use KTXC\Http\Middleware\FirewallMiddleware;
|
||||
use KTXC\Http\Middleware\AuthenticationMiddleware;
|
||||
use KTXC\Http\Middleware\RouterMiddleware;
|
||||
use KTXC\Injection\Builder;
|
||||
use KTXC\Injection\Container;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use KTXC\Module\ModuleManager;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use KTXC\Logger\FileLogger;
|
||||
use KTXC\Routing\Router;
|
||||
use KTXC\Routing\Route;
|
||||
use KTXC\Service\SecurityService;
|
||||
use KTXC\Service\FirewallService;
|
||||
use KTXF\Event\EventBus;
|
||||
use KTXF\Cache\EphemeralCacheInterface;
|
||||
use KTXF\Cache\PersistentCacheInterface;
|
||||
@@ -42,16 +43,27 @@ class Kernel
|
||||
protected bool $booted = false;
|
||||
protected ?float $startTime = null;
|
||||
protected ?ContainerInterface $container = null;
|
||||
protected ?LoggerInterface $logger = null;
|
||||
protected ?MiddlewarePipeline $pipeline = null;
|
||||
|
||||
private string $projectDir;
|
||||
private array $config;
|
||||
|
||||
public function __construct(
|
||||
protected string $environment = 'prod',
|
||||
protected bool $debug = false,
|
||||
array $config = [],
|
||||
?string $projectDir = null,
|
||||
) {
|
||||
if (!$environment) {
|
||||
throw new \InvalidArgumentException(\sprintf('Invalid environment provided to "%s": the environment cannot be empty.', get_debug_type($this)));
|
||||
}
|
||||
|
||||
$this->config = $config;
|
||||
|
||||
if ($projectDir !== null) {
|
||||
$this->projectDir = $projectDir;
|
||||
}
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
@@ -63,6 +75,7 @@ class Kernel
|
||||
|
||||
private function initialize(): void
|
||||
{
|
||||
|
||||
if ($this->debug) {
|
||||
$this->startTime = microtime(true);
|
||||
}
|
||||
@@ -74,12 +87,90 @@ class Kernel
|
||||
$_SERVER['SHELL_VERBOSITY'] = 3;
|
||||
}
|
||||
|
||||
// Create logger with config support
|
||||
$logDir = $this->config['log.directory'] ?? $this->getLogDir();
|
||||
$logChannel = $this->config['log.channel'] ?? 'app';
|
||||
$this->logger = new FileLogger($logDir, $logChannel);
|
||||
|
||||
$this->initializeErrorHandlers();
|
||||
|
||||
$container = $this->initializeContainer();
|
||||
|
||||
$this->container = $container;
|
||||
$this->initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up global error and exception handlers
|
||||
*/
|
||||
protected function initializeErrorHandlers(): 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
|
||||
);
|
||||
|
||||
$this->logger->error($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) {
|
||||
$this->logger->error('Exception caught: ' . $exception->getMessage(), [
|
||||
'exception' => $exception,
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
'trace' => $exception->getTraceAsString(),
|
||||
]);
|
||||
|
||||
if ($this->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']
|
||||
);
|
||||
|
||||
$this->logger->error($message, $error);
|
||||
|
||||
if ($this->debug) {
|
||||
echo '<pre>' . $message . '</pre>';
|
||||
} else {
|
||||
echo 'A fatal error occurred. Please try again later.';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function boot(): void
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
@@ -90,6 +181,10 @@ class Kernel
|
||||
/** @var ModuleManager $moduleManager */
|
||||
$moduleManager = $this->container->get(ModuleManager::class);
|
||||
$moduleManager->modulesBoot();
|
||||
|
||||
// Build middleware pipeline
|
||||
$this->pipeline = $this->buildMiddlewarePipeline();
|
||||
|
||||
$this->booted = true;
|
||||
}
|
||||
}
|
||||
@@ -117,46 +212,24 @@ class Kernel
|
||||
$this->boot();
|
||||
}
|
||||
|
||||
/** @var SessionTenant $sessionTenant */
|
||||
$sessionTenant = $this->container->get(SessionTenant::class);
|
||||
$sessionTenant->configure($request->getHost());
|
||||
if (!$sessionTenant->configured() && !$sessionTenant->enabled()) {
|
||||
return new Response(Response::$statusTexts[Response::HTTP_UNAUTHORIZED], Response::HTTP_UNAUTHORIZED);
|
||||
}
|
||||
// Use middleware pipeline to handle the request
|
||||
return $this->pipeline->handle($request);
|
||||
}
|
||||
|
||||
/** @var FirewallService $firewall */
|
||||
$firewall = $this->container->get(FirewallService::class);
|
||||
if (!$firewall->authorized($request)) {
|
||||
return new Response(Response::$statusTexts[Response::HTTP_FORBIDDEN], Response::HTTP_FORBIDDEN);
|
||||
}
|
||||
|
||||
/** @var Router $router */
|
||||
$router = $this->container->get(Router::class);
|
||||
if ($router) {
|
||||
$match = $router->match($request);
|
||||
if ($match instanceof Route) {
|
||||
/** @var SecurityService $securityService */
|
||||
$securityService = $this->container->get(SecurityService::class);
|
||||
$identity = $securityService->authenticate($request);
|
||||
|
||||
if ($match->authenticated && $identity === null) {
|
||||
return new Response(Response::$statusTexts[Response::HTTP_UNAUTHORIZED], Response::HTTP_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if ($identity) {
|
||||
/** @var SessionIdentity $sessionIdentity */
|
||||
$sessionIdentity = $this->container->get(SessionIdentity::class);
|
||||
$sessionIdentity->initialize($identity, true);
|
||||
}
|
||||
|
||||
$response = $router->dispatch($match, $request);
|
||||
if ($response instanceof Response) {
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(Response::$statusTexts[Response::HTTP_NOT_FOUND], Response::HTTP_NOT_FOUND);
|
||||
/**
|
||||
* Build the middleware pipeline
|
||||
*/
|
||||
protected function buildMiddlewarePipeline(): MiddlewarePipeline
|
||||
{
|
||||
$pipeline = new MiddlewarePipeline($this->container);
|
||||
|
||||
// Register middleware in execution order
|
||||
$pipeline->pipe(TenantMiddleware::class);
|
||||
$pipeline->pipe(FirewallMiddleware::class);
|
||||
$pipeline->pipe(AuthenticationMiddleware::class);
|
||||
$pipeline->pipe(RouterMiddleware::class);
|
||||
|
||||
return $pipeline;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,7 +336,6 @@ class Kernel
|
||||
|
||||
public function getBuildDir(): string
|
||||
{
|
||||
// Returns $this->getCacheDir() for backward compatibility
|
||||
return $this->getCacheDir();
|
||||
}
|
||||
|
||||
@@ -277,14 +349,6 @@ class Kernel
|
||||
return 'UTF-8';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a new container builder instance used to build the service container.
|
||||
*/
|
||||
protected function containerBuilder(): Builder
|
||||
{
|
||||
return new Builder(Container::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the service container
|
||||
*/
|
||||
@@ -303,10 +367,12 @@ class Kernel
|
||||
*/
|
||||
protected function buildContainer(): Container
|
||||
{
|
||||
$builder = $this->containerBuilder();
|
||||
$builder = new Builder(Container::class);
|
||||
$builder->useAutowiring(true);
|
||||
$builder->useAttributes(true);
|
||||
$builder->addDefinitions($this->parameters());
|
||||
$builder->addDefinitions($this->config);
|
||||
|
||||
$this->configureContainer($builder);
|
||||
|
||||
return $builder->build();
|
||||
@@ -314,14 +380,25 @@ class Kernel
|
||||
|
||||
protected function configureContainer(Builder $builder): void
|
||||
{
|
||||
$builder->addDefinitions($this->getConfigDir() . '/system.php');
|
||||
// Service definitions
|
||||
$logDir = $this->getLogDir();
|
||||
$projectDir = $this->folderRoot();
|
||||
$moduleDir = $projectDir . '/modules';
|
||||
$environment = $this->environment;
|
||||
|
||||
$builder->addDefinitions([
|
||||
LoggerInterface::class => function() use ($logDir) {
|
||||
return new FileLogger($logDir);
|
||||
},
|
||||
|
||||
// Provide primitives for injection
|
||||
'rootDir' => \DI\value($projectDir),
|
||||
'moduleDir' => \DI\value($moduleDir),
|
||||
'environment' => \DI\value($environment),
|
||||
|
||||
// IMPORTANT: ensure Container::class resolves to the *current* container instance.
|
||||
// Without this alias, PHP-DI will happily autowire a new empty Container when asked
|
||||
Container::class => \DI\get(ContainerInterface::class),
|
||||
|
||||
// Use the kernel's logger instance
|
||||
LoggerInterface::class => \DI\value($this->logger),
|
||||
|
||||
// EventBus as singleton for consistent event handling
|
||||
EventBus::class => \DI\create(EventBus::class),
|
||||
// Ephemeral Cache - for short-lived data (sessions, rate limits, challenges)
|
||||
|
||||
Reference in New Issue
Block a user