_uri = $uri; $route = Route::resolve($uri); $this->_controller = $route['controller']; $this->_action = $route['action']; $this->_params = $route['params']; } /** * Создаёт запрос из суперглобалов: URI, метод, $_GET, $_POST, тело, X-Requested-With. * * @return static */ public static function factory(): static { $uri = self::detectUri(); $instance = new static($uri); if (self::$initial === null) { self::$initial = $instance; } self::$current = $instance; $instance->method($_SERVER['REQUEST_METHOD'] ?? HTTPRequest::GET); $instance->query($_GET); $instance->post($_POST); $instance->_requested_with = strtolower($_SERVER['HTTP_X_REQUESTED_WITH'] ?? ''); if ($instance->method() !== HTTPRequest::GET) { $body = file_get_contents('php://input'); if ($body !== false && $body !== '') { $instance->body($body); } } return $instance; } /** * Определяет URI через PATH_INFO → REQUEST_URI → PHP_SELF. * * @return string URI без ведущего/замыкающего слэша */ public static function detectUri(): string { if (!empty($_SERVER['PATH_INFO'])) { $uri = $_SERVER['PATH_INFO']; } elseif (isset($_SERVER['REQUEST_URI'])) { $uri = $_SERVER['REQUEST_URI']; if ($path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)) { $uri = $path; } $uri = rawurldecode($uri); } elseif (isset($_SERVER['PHP_SELF'])) { $uri = $_SERVER['PHP_SELF']; } else { $uri = '/'; } return trim($uri, '/'); } /** * Подключает контроллер и вызывает {action}Action(). При отсутствии — 404. * * @return Response */ public function execute(): Response { $method = lcfirst($this->_action) . 'Action'; $parts = explode('/', $this->_controller); $file = DOCROOT . '/App/Controller/' . implode('/', array_map('ucfirst', $parts)) . 'Controller.php'; if (!file_exists($file)) { return HTTPException::factory(404)->getResponse(); } require_once $file; $class = 'App\\Controller\\' . implode('\\', array_map('ucfirst', $parts)) . 'Controller'; if (!method_exists($class, $method)) { return HTTPException::factory(404)->getResponse(); } return (new Response())->body((new $class())->$method()); } /** * Геттер/сеттер HTTP-метода. * * @param string|null $method Новый метод или null для чтения * @return string|static */ public function method(?string $method = null): string|static { if ($method === null) { return $this->_method; } $this->_method = strtoupper($method); return $this; } /** * Геттер/сеттер POST-данных. * * @param string|array|null $key Массив (замена всех), ключ (чтение/запись) или null (все) * @param mixed $value Значение при записи по ключу * @return array|string|static|null */ public function post(null|string|array $key = null, mixed $value = null): null|array|string|static { if (is_array($key)) { $this->_post = $key; return $this; } if ($key === null) { return $this->_post; } if ($value === null) { return $this->_post[$key] ?? null; } $this->_post[$key] = $value; return $this; } /** * Геттер/сеттер GET-параметров. * * @param string|array|null $key Массив (замена всех), ключ (чтение/запись) или null (все) * @param mixed $value Значение при записи по ключу * @return array|string|static|null */ public function query(null|string|array $key = null, mixed $value = null): null|array|string|static { if (is_array($key)) { $this->_get = $key; return $this; } if ($key === null) { return $this->_get; } if ($value === null) { return $this->_get[$key] ?? null; } $this->_get[$key] = $value; return $this; } /** * Геттер/сеттер тела запроса. * * @param string|null $content Новое тело или null для чтения * @return string|static */ public function body(?string $content = null): string|static { if ($content === null) { return $this->_body ?? ''; } $this->_body = $content; return $this; } /** * Геттер/сеттер заголовка X-Requested-With. * * @param string|null $value Новое значение или null для чтения * @return string|static */ public function requestedWith(?string $value = null): string|static { if ($value === null) { return $this->_requested_with; } $this->_requested_with = strtolower($value); return $this; } /** * Проверяет, является ли запрос AJAX (X-Requested-With: XMLHttpRequest). * * @return bool */ public function isAjax(): bool { return $this->_requested_with === 'xmlhttprequest'; } /** @return string Имя контроллера */ public function controller(): string { return $this->_controller; } /** @return string Имя действия */ public function action(): string { return $this->_action; } /** @return array Позиционные параметры */ public function params(): array { return $this->_params; } /** @return string Исходный URI */ public function uri(): string { return $this->_uri; } /** * Возвращает позиционный параметр по индексу. * * @param int $index Индекс параметра * @param mixed $default Значение по умолчанию * @return mixed */ public function param(int $index, mixed $default = null): mixed { return $this->_params[$index] ?? $default; } }