From 4b72a4b26c28e83eb81626513ad2c3be12b48cf6 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sat, 24 Feb 2024 10:40:29 +0100 Subject: [PATCH] Adds a simple PSR17/PSR18 Http client (#555) Adds a PSR17/PSR18 Http client --- phpstan-baseline.neon | 15 +++ src/metadata-service/src/Psr18HttpClient.php | 128 +++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 src/metadata-service/src/Psr18HttpClient.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ad2259a6..79468f75 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -45,6 +45,21 @@ parameters: count: 1 path: src/metadata-service/src/Denormalizer/ExtensionDescriptorDenormalizer.php + - + message: "#^Method Webauthn\\\\MetadataService\\\\Psr18HttpClient\\:\\:request\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Psr18HttpClient.php + + - + message: "#^Method Webauthn\\\\MetadataService\\\\Psr18HttpClient\\:\\:withOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Psr18HttpClient.php + + - + message: "#^Property Webauthn\\\\MetadataService\\\\Psr18HttpClient\\:\\:\\$options type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/metadata-service/src/Psr18HttpClient.php + - message: "#^Call to an undefined method Psr\\\\Http\\\\Client\\\\ClientInterface\\|Symfony\\\\Contracts\\\\HttpClient\\\\HttpClientInterface\\:\\:request\\(\\)\\.$#" count: 1 diff --git a/src/metadata-service/src/Psr18HttpClient.php b/src/metadata-service/src/Psr18HttpClient.php new file mode 100644 index 00000000..1f0b6f71 --- /dev/null +++ b/src/metadata-service/src/Psr18HttpClient.php @@ -0,0 +1,128 @@ +requestFactory->createRequest($method, $baseUri . $url); + $body = $options['body'] ?? null; + if ($body !== null) { + $request = $request->withBody($this->streamFactory->createStream($body)); + } + foreach ($this->options as $name => $value) { + $request = $request->withHeader($name, $value); + } + foreach ($options['headers'] ?? [] as $name => $value) { + $request = $request->withHeader($name, $value); + } + $response = $this->client->sendRequest($request); + + return static::fromPsr17($response); + } + + /** + * @param ResponseInterface|iterable $responses + */ + public function stream(iterable|ResponseInterface $responses, float $timeout = null): ResponseStreamInterface + { + throw new LogicException('Not implemented'); + } + + public function withOptions(array $options): static + { + $this->options = $options; + return $this; + } + + protected static function fromPsr17(Psr17ResponseInterface $response): ResponseInterface + { + $headers = $response->getHeaders(); + $content = $response->getBody() + ->getContents(); + $status = $response->getStatusCode(); + + return new class($status, $headers, $content) implements ResponseInterface { + /** + * @param array $headers + */ + public function __construct( + private readonly int $status, + private readonly array $headers, + private readonly string $content, + ) { + } + + public function getStatusCode(): int + { + return $this->status; + } + + /** + * @return array + */ + public function getHeaders(bool $throw = true): array + { + return $this->headers; + } + + public function getContent(bool $throw = true): string + { + return $this->content; + } + + /** + * @return array + */ + public function toArray(bool $throw = true): array + { + $result = json_decode($this->content, true); + if (! is_array($result) || json_last_error() !== JSON_ERROR_NONE) { + throw new JsonException('Failed to decode JSON response: ' . json_last_error_msg()); + } + + return $result; + } + + public function cancel(): void + { + // noop + } + + public function getInfo(string $type = null): mixed + { + return null; + } + }; + } +}