Files

89 lines
3.2 KiB
Markdown
Raw Permalink Normal View History

2026-04-06 15:43:45 +03:00
# HTTP слой
## RouteDefinition (`src/Http/RouteDefinition.php`)
```php
new RouteDefinition(
method: 'GET',
path: '/users/{id}',
controller: GetUserController::class,
middleware: [AuthMiddleware::class], // опционально
)
```
## Router (`src/Http/Router.php`)
- Компилирует `{param}` → именованные regex-группы `(?P<param>[^/]+)`
- `match(method, path): RouteMatch`
- Сначала проверяет все маршруты на совпадение пути
- Если путь совпал, но метод нет → `RouteMatch::methodNotAllowed($allowedMethods)`
- Если ничего → `RouteMatch::notFound()`
## MiddlewarePipeline (`src/Http/MiddlewarePipeline.php`)
Резолвит каждый middleware через DI и вызывает `process(Request): Request` последовательно.
Middleware может изменять request через `withContext()` или бросать исключение.
**Интерфейс middleware:**
```php
interface MiddlewareInterface
{
public function process(Request $request): Request;
}
```
## Request (`src/Http/Request.php`)
Иммутабельный объект. Конструктор принимает именованные параметры.
```php
$request->method // 'GET', 'POST', ...
$request->path // '/users/123'
$request->query // $_GET
$request->pathParams // ['id' => '123'] — устанавливается роутером
$request->headers // lowercase-hyphen: 'content-type', 'authorization'
$request->cookies
$request->files
$request->body() // decoded JSON array (только если Content-Type: application/json)
$request->get('auth') // значение из context, установленного middleware
$request->withContext('key', $value): self // иммутабельное добавление в context
```
**Разбор заголовков:** `HTTP_*` из `$_SERVER` → lowercase-hyphen. `CONTENT_TYPE` тоже нормализуется.
## Response (`src/Http/Response.php`)
```php
Response::json($data, $status = 200): self // Content-Type: application/json
Response::error($message, $status): self // json(['error' => $message], $status)
$response->withHeader($name, $value): self
$response->withHeaders($headers): self
$response->emit(): void // http_response_code + headers + echo body
```
## HttpException (`src/Http/HttpException.php`)
```php
throw new HttpException('Forbidden', 403);
// → перехватывается в HttpApplication::dispatch() → Response::error(message, code)
```
## HttpApplication — конфигурационные зависимости
`HttpApplication` требует наличия ключа `cors` в `config/app.php`. Конфиг читается при каждом
запросе (в т.ч. OPTIONS-preflight). Если ключ отсутствует — бросается
`\RuntimeException('app.cors config is missing')`.
```php
// config/app.php
return [
'cors' => [
'origins' => '*',
'methods' => 'GET, POST, PUT, DELETE, OPTIONS',
'headers' => 'Content-Type, Authorization',
'max_age' => '86400',
],
];
```