135 lines
3.6 KiB
PHP
Executable File
135 lines
3.6 KiB
PHP
Executable File
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace ServerManager\Core;
|
|
|
|
class View
|
|
{
|
|
private string $viewsPath;
|
|
private string $layout = 'main';
|
|
private array $data = [];
|
|
private Security $security;
|
|
|
|
public function __construct(Security $security)
|
|
{
|
|
$this->viewsPath = dirname(__DIR__, 2) . '/views/';
|
|
$this->security = $security;
|
|
}
|
|
|
|
public function setLayout(string $layout): self
|
|
{
|
|
$this->layout = $layout;
|
|
return $this;
|
|
}
|
|
|
|
public function assign(string $key, mixed $value): self
|
|
{
|
|
$this->data[$key] = $value;
|
|
return $this;
|
|
}
|
|
|
|
public function assignMultiple(array $data): self
|
|
{
|
|
$this->data = array_merge($this->data, $data);
|
|
return $this;
|
|
}
|
|
|
|
public function render(string $view, array $data = []): string
|
|
{
|
|
$data = array_merge($this->data, $data);
|
|
$content = $this->renderView($view, $data);
|
|
|
|
if ($this->layout !== null) {
|
|
$data['content'] = $content;
|
|
$content = $this->renderView('layouts/' . $this->layout, $data);
|
|
}
|
|
|
|
return $content;
|
|
}
|
|
|
|
public function renderView(string $view, array $data = []): string
|
|
{
|
|
$viewFile = $this->viewsPath . str_replace('.', '/', $view) . '.php';
|
|
|
|
if (!file_exists($viewFile)) {
|
|
throw new \RuntimeException("View not found: {$view}");
|
|
}
|
|
|
|
extract($data, EXTR_SKIP);
|
|
|
|
ob_start();
|
|
include $viewFile;
|
|
return ob_get_clean();
|
|
}
|
|
|
|
public function renderPartial(string $partial, array $data = []): string
|
|
{
|
|
return $this->renderView('partials/' . $partial, $data);
|
|
}
|
|
|
|
public function display(string $view, array $data = []): void
|
|
{
|
|
echo $this->render($view, $data);
|
|
}
|
|
|
|
public function escape(string $value): string
|
|
{
|
|
return $this->security->purify($value);
|
|
}
|
|
|
|
public function csrfToken(): string
|
|
{
|
|
return $this->security->generateCSRFToken();
|
|
}
|
|
|
|
public function csrfField(): string
|
|
{
|
|
$token = $this->csrfToken();
|
|
return '<input type="hidden" name="_csrf_token" value="' . $this->escape($token) . '">';
|
|
}
|
|
|
|
public function redirect(string $url, int $statusCode = 302): never
|
|
{
|
|
http_response_code($statusCode);
|
|
header("Location: {$url}");
|
|
exit;
|
|
}
|
|
|
|
public function json(mixed $data, int $statusCode = 200): never
|
|
{
|
|
http_response_code($statusCode);
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
echo json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
public function error(int $statusCode, string $message = ''): never
|
|
{
|
|
http_response_code($statusCode);
|
|
if (str_starts_with($_SERVER['HTTP_ACCEPT'] ?? '', 'application/json')) {
|
|
$this->json(['error' => true, 'message' => $message ?: $this->getStatusMessage($statusCode)], $statusCode);
|
|
}
|
|
$this->display("errors.{$statusCode}", [
|
|
'code' => $statusCode,
|
|
'message' => $message ?: $this->getStatusMessage($statusCode),
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
private function getStatusMessage(int $code): string
|
|
{
|
|
return match($code) {
|
|
400 => 'Bad Request',
|
|
401 => 'Unauthorized',
|
|
403 => 'Forbidden',
|
|
404 => 'Not Found',
|
|
405 => 'Method Not Allowed',
|
|
429 => 'Too Many Requests',
|
|
500 => 'Internal Server Error',
|
|
503 => 'Service Unavailable',
|
|
default => 'Error',
|
|
};
|
|
}
|
|
}
|