Initial Version
This commit is contained in:
216
core/lib/Application.php
Normal file
216
core/lib/Application.php
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
namespace KTXC;
|
||||
|
||||
use KTXC\Http\Request\Request;
|
||||
use KTXC\Http\Response\Response;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Application class - entry point for the framework
|
||||
* Handles configuration loading and kernel lifecycle
|
||||
*/
|
||||
class Application
|
||||
{
|
||||
private static $composerLoader = null;
|
||||
|
||||
private Kernel $kernel;
|
||||
private array $config;
|
||||
private string $rootDir;
|
||||
|
||||
public function __construct(string $rootDir, ?string $environment = null, ?bool $debug = null)
|
||||
{
|
||||
$this->rootDir = $this->resolveProjectRoot($rootDir);
|
||||
|
||||
// Load configuration
|
||||
$this->config = $this->loadConfig();
|
||||
|
||||
// Determine environment and debug mode
|
||||
$environment = $environment ?? $this->config['environment'] ?? 'prod';
|
||||
$debug = $debug ?? $this->config['debug'] ?? false;
|
||||
|
||||
// Create kernel with configuration
|
||||
$this->kernel = new Kernel($environment, $debug, $this->config, $rootDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the application - handle incoming request and send response
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
try {
|
||||
$request = Request::createFromGlobals();
|
||||
$response = $this->handle($request);
|
||||
$response->send();
|
||||
$this->terminate();
|
||||
} catch (\Throwable $e) {
|
||||
// Last resort error handling for kernel initialization failures
|
||||
error_log('Application error: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
|
||||
$content = $this->kernel->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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a request
|
||||
*/
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
return $this->kernel->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate the application - process deferred events
|
||||
*/
|
||||
public function terminate(): void
|
||||
{
|
||||
$this->kernel->processEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the kernel instance
|
||||
*/
|
||||
public function kernel(): Kernel
|
||||
{
|
||||
return $this->kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the container instance
|
||||
*/
|
||||
public function container(): ContainerInterface
|
||||
{
|
||||
return $this->kernel->container();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the application root directory
|
||||
*/
|
||||
public function rootDir(): string
|
||||
{
|
||||
return $this->rootDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the modules directory
|
||||
*/
|
||||
public function moduleDir(): string
|
||||
{
|
||||
return $this->rootDir . '/modules';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration value
|
||||
*/
|
||||
public function config(?string $key = null, mixed $default = null): mixed
|
||||
{
|
||||
if ($key === null) {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
// Support dot notation: 'database.uri'
|
||||
$keys = explode('.', $key);
|
||||
$value = $this->config;
|
||||
|
||||
foreach ($keys as $k) {
|
||||
if (!is_array($value) || !array_key_exists($k, $value)) {
|
||||
return $default;
|
||||
}
|
||||
$value = $value[$k];
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get environment
|
||||
*/
|
||||
public function environment(): string
|
||||
{
|
||||
return $this->kernel->environment();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if debug mode is enabled
|
||||
*/
|
||||
public function debug(): bool
|
||||
{
|
||||
return $this->kernel->debug();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load configuration from config directory
|
||||
*/
|
||||
protected function loadConfig(): array
|
||||
{
|
||||
$configFile = $this->rootDir . '/config/system.php';
|
||||
|
||||
if (!file_exists($configFile)) {
|
||||
error_log('Configuration file not found: ' . $configFile);
|
||||
return [];
|
||||
}
|
||||
|
||||
$config = include $configFile;
|
||||
|
||||
if (!is_array($config)) {
|
||||
throw new \RuntimeException('Configuration file must return an array');
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the project root directory.
|
||||
*
|
||||
* Some entrypoints may pass the public/ directory or another subdirectory.
|
||||
* We walk up the directory tree until we find composer.json.
|
||||
*/
|
||||
private function resolveProjectRoot(string $startDir): string
|
||||
{
|
||||
$dir = rtrim($startDir, '/');
|
||||
if ($dir === '') {
|
||||
return $startDir;
|
||||
}
|
||||
|
||||
// If startDir is a file path, use its directory.
|
||||
if (is_file($dir)) {
|
||||
$dir = dirname($dir);
|
||||
}
|
||||
|
||||
$current = $dir;
|
||||
while (true) {
|
||||
if (is_file($current . '/composer.json')) {
|
||||
return $current;
|
||||
}
|
||||
|
||||
$parent = dirname($current);
|
||||
if ($parent === $current) {
|
||||
// Reached filesystem root
|
||||
return $dir;
|
||||
}
|
||||
$current = $parent;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user