*/ private array $defaultOptions = [ 'timeout' => 30, 'connect_timeout' => 10, 'follow_redirects' => true, 'max_redirects' => 5, 'user_agent' => 'AsyncHttpClient/1.0', ]; public function __construct( private readonly FiberManager $fiberManager = new FiberManager(), /** @var array */ array $defaultOptions = [] ) { $this->defaultOptions = array_merge($this->defaultOptions, $defaultOptions); } /** * Sendet einen GET-Request */ public function get(string $url, /** @var array */ array $headers = [], /** @var array */ array $options = []): HttpResponse { return $this->request('GET', $url, null, $headers, $options); } /** * Sendet einen POST-Request */ public function post(string $url, mixed $data = null, /** @var array */ array $headers = [], /** @var array */ array $options = []): HttpResponse { return $this->request('POST', $url, $data, $headers, $options); } /** * Sendet einen PUT-Request */ public function put(string $url, mixed $data = null, /** @var array */ array $headers = [], /** @var array */ array $options = []): HttpResponse { return $this->request('PUT', $url, $data, $headers, $options); } /** * Sendet einen DELETE-Request */ public function delete(string $url, /** @var array */ array $headers = [], /** @var array */ array $options = []): HttpResponse { return $this->request('DELETE', $url, null, $headers, $options); } /** * Sendet mehrere Requests parallel * * @param array> $requests ['key' => ['method' => 'GET', 'url' => '...', ...]] * @return array */ public function requestMultiple(array $requests): array { /** @var array */ $operations = []; foreach ($requests as $key => $request) { $operations[$key] = fn () => $this->request( $request['method'] ?? 'GET', $request['url'], $request['data'] ?? null, $request['headers'] ?? [], $request['options'] ?? [] ); } return $this->fiberManager->batch($operations); } /** * Sendet Requests mit begrenzter Parallelität * * @param array> $requests * @return array */ public function requestBatch(array $requests, int $maxConcurrency = 10): array { $pool = new AsyncPool($maxConcurrency, $this->fiberManager); foreach ($requests as $key => $request) { $pool->add( fn () => $this->request( $request['method'] ?? 'GET', $request['url'], $request['data'] ?? null, $request['headers'] ?? [], $request['options'] ?? [] ), $key ); } return $pool->execute(); } /** * Hauptmethode für HTTP-Requests */ private function request(string $method, string $url, mixed $data = null, /** @var array */ array $headers = [], /** @var array */ array $options = []): HttpResponse { $options = array_merge($this->defaultOptions, $options); $context = $this->createContext($method, $data, $headers, $options); $startTime = microtime(true); try { $content = @file_get_contents($url, false, $context); if ($content === false) { $error = error_get_last(); throw new HttpException("HTTP request failed: " . ($error['message'] ?? 'Unknown error')); } $responseHeaders = $this->parseHeaders($http_response_header ?? []); $statusCode = $this->extractStatusCode($http_response_header ?? []); return new HttpResponse( statusCode: $statusCode, headers: $responseHeaders, body: $content, requestTime: microtime(true) - $startTime ); } catch (\Throwable $e) { throw new HttpException("HTTP request failed: " . $e->getMessage(), 0, $e); } } /** * @return resource */ private function createContext(string $method, mixed $data, /** @var array */ array $headers, /** @var array */ array $options) { /** @var array> */ $contextOptions = [ 'http' => [ 'method' => $method, 'timeout' => $options['timeout'], 'user_agent' => $options['user_agent'], 'follow_location' => $options['follow_redirects'], 'max_redirects' => $options['max_redirects'], 'ignore_errors' => true, ], ]; if ($data !== null) { if (is_array($data) || is_object($data)) { $contextOptions['http']['content'] = json_encode($data); $headers['Content-Type'] = 'application/json'; } else { $contextOptions['http']['content'] = (string)$data; } } if (! empty($headers)) { /** @var array */ $headerStrings = []; foreach ($headers as $key => $value) { $headerStrings[] = "$key: $value"; } $contextOptions['http']['header'] = implode("\r\n", $headerStrings); } return stream_context_create($contextOptions); } /** * @param array $httpResponseHeader * @return array */ private function parseHeaders(array $httpResponseHeader): array { /** @var array */ $headers = []; foreach ($httpResponseHeader as $header) { if (strpos($header, ':') !== false) { [$key, $value] = explode(':', $header, 2); $headers[trim($key)] = trim($value); } } return $headers; } /** * @param array $httpResponseHeader */ private function extractStatusCode(array $httpResponseHeader): int { if (empty($httpResponseHeader)) { return 0; } $statusLine = $httpResponseHeader[0]; if (preg_match('/HTTP\/\d+\.\d+\s+(\d+)/', $statusLine, $matches)) { return (int)$matches[1]; } return 0; } }