Files
scoutiq/app/Core/App.php

124 lines
3.7 KiB
PHP

<?php
namespace App\Core;
use Throwable;
use App\Middleware\MiddlewareInterface;
class App
{
public static App $app;
public Container $container;
public Request $request;
public Response $response;
public Session $session;
public Router $router;
public function __construct(Container $container)
{
self::$app = $this;
$this->container = $container;
$this->request = new Request();
$this->response = new Response();
$this->session = new Session();
$this->router = new Router();
}
/**
* Boot and run the application.
*/
public function run(): void
{
try {
// Match request to route
$routeInfo = $this->router->resolve($this->request);
$callback = $routeInfo['callback'];
$middlewares = $routeInfo['middleware'];
$params = $routeInfo['params'];
// Inject matched route parameters into Request
$this->request->setRouteParams($params);
// Run Middleware Chain
$this->executeMiddlewareChain($middlewares, function() use ($callback) {
// Execute Route action
if (is_callable($callback)) {
$response = $callback($this->request, $this->response);
} else {
[$controllerClass, $method] = $callback;
$controller = $this->container->get($controllerClass);
$response = $controller->$method($this->request, $this->response);
}
// Auto-output string responses as HTML
if (is_string($response)) {
$this->response->html($response);
}
});
} catch (Throwable $e) {
$this->handleException($e);
}
}
/**
* Executes the array of middlewares sequentially before firing destination action.
*/
private function executeMiddlewareChain(array $middlewares, callable $destination): void
{
$runner = function (int $index) use ($middlewares, $destination, &$runner) {
if ($index >= count($middlewares)) {
$destination();
return;
}
$middlewareClass = $middlewares[$index];
/** @var MiddlewareInterface $middleware */
$middleware = $this->container->get($middlewareClass);
$middleware->handle($this->request, $this->response, function() use ($runner, $index) {
$runner($index + 1);
});
};
$runner(0);
}
/**
* Global exception handler.
*/
private function handleException(Throwable $e): void
{
$code = $e->getCode();
if ($code < 100 || $code > 599) {
$code = 500;
}
// Return API error JSON for API routes or JSON requests
if ($this->request->isJson() || str_starts_with($this->request->getPath(), '/api')) {
$this->response->json([
'success' => false,
'error' => $e->getMessage(),
'code' => $code
], $code);
return;
}
// Render error templates for Web requests
$viewPath = __DIR__ . "/../../resources/views/errors/{$code}.php";
if (!file_exists($viewPath)) {
$viewPath = __DIR__ . '/../../resources/views/errors/500.php';
}
if (file_exists($viewPath)) {
ob_start();
$message = $e->getMessage();
include $viewPath;
$content = ob_get_clean();
$this->response->html($content, $code);
} else {
$this->response->html("<h1>Error {$code}</h1><p>{$e->getMessage()}</p>", $code);
}
}
}