_csrf_protection) { return; } $request = Request::$current; $method = $request?->method() ?? HTTPRequest::GET; $unsafe = [HTTPRequest::POST, HTTPRequest::PUT, HTTPRequest::PATCH, HTTPRequest::DELETE]; if (in_array($method, $unsafe, true) && !CSRF::validate($request->post(CSRF::$key))) { throw HTTPException::factory(403); } } /** * Рендерит шаблон контента внутри layout. * Если $dir пуст — определяется автоматически из имени класса * (App\Controller\FooController → view/Foo). * * @param string $template Имя шаблона контента без расширения * @param array $data Данные, передаваемые в шаблон * @param string $dir Каталог шаблона относительно view/ (опционально) * @return string Готовый HTML * @throws MyException */ protected function render(string $template, array $data = [], string $dir = ''): string { if ($dir === '') { $class = substr(get_class($this), strlen('App\\Controller\\')); // [Admin\]FooController $dir = 'view/' . str_replace('\\', '/', substr($class, 0, -10)); // view/[Admin/]Foo } return (new View($this->_layout, 'view', [ 'content' => (new View($template, $dir, $data))->render(), ] + $this->layoutData()))->render(); } /** * Дополнительные данные, передаваемые в layout помимо 'content'. * По умолчанию — пункты главного меню; наследники могут переопределить * полностью (как AdminController — своим боковым меню). * * @return array */ protected function layoutData(): array { return ['menu' => $this->menu()]; } /** * Пункты главного меню сайта. Активный пункт — по текущему URI. * * @return array */ protected function menu(): array { $uri = Request::$current?->uri() ?? ''; $items = [ ['title' => 'Главная', 'url' => '/'], ['title' => 'Обратная связь', 'url' => '/feedback'], ['title' => 'Админка', 'url' => '/admin/logs'], ]; foreach ($items as &$item) { $item['active'] = trim($item['url'], '/') === $uri; } return $items; } /** * Формирует JSON-ответ: ставит статус и Content-Type, кодирует данные. * * @param mixed $data Данные ответа * @param int $status HTTP-статус * @return string JSON */ protected function json(mixed $data, int $status = 200): string { http_response_code($status); if (!headers_sent()) { header('Content-Type: application/json; charset=utf-8'); } return json_encode($data, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); } }