method === 'OPTIONS') { $this->emitCorsHeaders(); http_response_code(204); return; } $response = $this->dispatch($request); $response = $response->withHeaders($this->corsHeaders()); $response->emit(); } private function dispatch(Request $request): Response { try { $match = $this->router->match($request->method, $request->path); if ($match->methodNotAllowed) { return Response::error('Method Not Allowed', 405) ->withHeader('Allow', implode(', ', $match->allowedMethods)); } if (!$match->found) { return Response::error('Not Found', 404); } $request = $request->withPathParams($match->pathParams); $request = $this->pipeline->run($request, $match->definition->middleware); $controller = $this->container->get($match->definition->controller); return ($controller)($request); } catch (AuthException $e) { return Response::error($e->getMessage(), 401); } catch (HttpException $e) { return Response::error($e->getMessage(), $e->getCode()); } catch (\JsonException) { return Response::error('Invalid JSON body', 400); } catch (\Throwable $e) { $this->logger->error('Unhandled exception during dispatch', [ 'exception' => $e, 'method' => $request->method, 'path' => $request->path, ]); return Response::error('Internal Server Error', 500); } } public function terminate(): void { // Завершение работы приложения. Закрытие соединений, ресурсов и.т.п. } /** @return array */ private function corsHeaders(): array { $cors = $this->config->get('app.cors'); return [ 'Access-Control-Allow-Origin' => $cors['origins'], 'Access-Control-Allow-Methods' => $cors['methods'], 'Access-Control-Allow-Headers' => $cors['headers'], ]; } private function emitCorsHeaders(): void { $cors = $this->config->get('app.cors'); header('Access-Control-Allow-Origin: ' . $cors['origins']); header('Access-Control-Allow-Methods: ' . $cors['methods']); header('Access-Control-Allow-Headers: ' . $cors['headers']); header('Access-Control-Max-Age: ' . $cors['max_age']); } }