Complete Phase 1: MVC, DB migrations, Auth, RBAC, Security, and Views
This commit is contained in:
123
app/Core/App.php
Normal file
123
app/Core/App.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user