167 lines
3.9 KiB
PHP
167 lines
3.9 KiB
PHP
<?php
|
|
|
|
namespace Web;
|
|
|
|
/**
|
|
* App is the application controller itself
|
|
*/
|
|
class App
|
|
{
|
|
private Router $router;
|
|
private array $middleware = [];
|
|
private Context $context;
|
|
private ErrorHandler $errorHandler;
|
|
private Cookies $cookie;
|
|
|
|
public function __construct(bool $debug = false, array $cookieDefaults = [])
|
|
{
|
|
$this->router = new Router();
|
|
$this->errorHandler = new ErrorHandler($debug);
|
|
$this->cookie = new Cookies($cookieDefaults);
|
|
}
|
|
|
|
public function use(callable $middleware): self
|
|
{
|
|
$this->middleware[] = $middleware;
|
|
return $this;
|
|
}
|
|
|
|
public function get(string $route, callable $handler): self
|
|
{
|
|
$this->router->get($route, $handler);
|
|
return $this;
|
|
}
|
|
|
|
public function post(string $route, callable $handler): self
|
|
{
|
|
$this->router->post($route, $handler);
|
|
return $this;
|
|
}
|
|
|
|
public function put(string $route, callable $handler): self
|
|
{
|
|
$this->router->put($route, $handler);
|
|
return $this;
|
|
}
|
|
|
|
public function patch(string $route, callable $handler): self
|
|
{
|
|
$this->router->patch($route, $handler);
|
|
return $this;
|
|
}
|
|
|
|
public function delete(string $route, callable $handler): self
|
|
{
|
|
$this->router->delete($route, $handler);
|
|
return $this;
|
|
}
|
|
|
|
public function head(string $route, callable $handler): self
|
|
{
|
|
$this->router->head($route, $handler);
|
|
return $this;
|
|
}
|
|
|
|
public function route(string $method, string $route, callable $handler): self
|
|
{
|
|
$this->router->add($method, $route, $handler);
|
|
return $this;
|
|
}
|
|
|
|
public function group(string $prefix, callable $callback): self
|
|
{
|
|
$originalRouter = $this->router;
|
|
$groupRouter = new Router();
|
|
|
|
$this->router = $groupRouter;
|
|
$callback($this);
|
|
|
|
foreach ($groupRouter->dump() as $path => $methods) {
|
|
$this->addGroupRoutes($originalRouter, $prefix, $path, $methods);
|
|
}
|
|
|
|
$this->router = $originalRouter;
|
|
return $this;
|
|
}
|
|
|
|
public function setErrorHandler(int $status, callable $handler): self
|
|
{
|
|
$this->errorHandler->register($status, $handler);
|
|
return $this;
|
|
}
|
|
|
|
public function setDefaultErrorHandler(callable $handler): self
|
|
{
|
|
$this->errorHandler->setDefaultHandler($handler);
|
|
return $this;
|
|
}
|
|
|
|
private function addGroupRoutes(Router $router, string $prefix, string $path, mixed $node, string $currentPath = ''): void
|
|
{
|
|
if ($path !== '') $currentPath = $currentPath ? "$currentPath/$path" : $path;
|
|
|
|
if (!is_array($node)) return;
|
|
|
|
foreach ($node as $key => $value) {
|
|
if (in_array($key, ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'])) {
|
|
$fullPath = rtrim($prefix, '/') . '/' . ltrim($currentPath, '/');
|
|
$fullPath = str_replace(':x', ':param', $fullPath);
|
|
$router->add($key, $fullPath, $value);
|
|
} elseif (is_array($value)) {
|
|
$this->addGroupRoutes($router, $prefix, $key, $value, $currentPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function run(): void
|
|
{
|
|
$this->context = new Context($this->cookie);
|
|
|
|
try {
|
|
$next = function() {
|
|
$result = $this->router->lookup(
|
|
$this->context->request->method,
|
|
$this->context->request->path
|
|
);
|
|
|
|
if ($result['code'] === 404) {
|
|
$this->errorHandler->handle($this->context, 404);
|
|
return;
|
|
}
|
|
|
|
if ($result['code'] === 405) {
|
|
$this->errorHandler->handle($this->context, 405);
|
|
return;
|
|
}
|
|
|
|
$this->context->request->params = $result['params'];
|
|
|
|
$handler = $result['handler'];
|
|
$response = $handler($this->context, ...$result['params']);
|
|
|
|
if ($response instanceof Response) {
|
|
$response->send();
|
|
} elseif (is_array($response) || is_object($response)) {
|
|
$this->context->json($response);
|
|
} elseif (is_string($response)) {
|
|
$this->context->text($response);
|
|
}
|
|
};
|
|
|
|
$chain = array_reduce(
|
|
array_reverse($this->middleware),
|
|
function($next, $middleware) {
|
|
return function() use ($middleware, $next) {
|
|
$middleware($this->context, $next);
|
|
};
|
|
},
|
|
$next
|
|
);
|
|
|
|
$chain();
|
|
} catch (\Exception $e) {
|
|
$this->errorHandler->handleException($this->context, $e);
|
|
}
|
|
}
|
|
}
|