diff --git a/Provider/AbstractHttpProvider.php b/Provider/AbstractHttpProvider.php index 03db6ce..b0d29c4 100644 --- a/Provider/AbstractHttpProvider.php +++ b/Provider/AbstractHttpProvider.php @@ -16,10 +16,16 @@ use Geocoder\Exception\InvalidServerResponse; use Geocoder\Exception\QuotaExceeded; use Geocoder\Provider\AbstractProvider; -use Http\Discovery\MessageFactoryDiscovery; +use Http\Discovery\Psr17Factory; use Http\Message\MessageFactory; use Psr\Http\Client\ClientInterface; +use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseFactoryInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamFactoryInterface; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UriInterface; /** * @author William Durand @@ -33,14 +39,17 @@ abstract class AbstractHttpProvider extends AbstractProvider private $client; /** - * @var MessageFactory + * @var RequestFactoryInterface&StreamFactoryInterface)|MessageFactory */ private $messageFactory; - public function __construct(ClientInterface $client, MessageFactory $factory = null) + /** + * @param Psr17Factory|MessageFactory|null $factory Passing a MessageFactory is @deprecated + */ + public function __construct(ClientInterface $client, MessageFactory|Psr17Factory $factory = null) { $this->client = $client; - $this->messageFactory = $factory ?: MessageFactoryDiscovery::find(); + $this->messageFactory = $factory ?? ($client instanceof RequestFactoryInterface && $client instanceof StreamFactoryInterface ? $client : new Psr17Factory()); } /** @@ -57,7 +66,35 @@ protected function getUrlContents(string $url): string protected function getRequest(string $url): RequestInterface { - return $this->getMessageFactory()->createRequest('GET', $url); + return $this->createRequest('GET', $url); + } + + /** + * @param array $headers + */ + protected function createRequest(string $method, string $uri, array $headers = [], string $body = null): RequestInterface + { + if ($this->messageFactory instanceof MessageFactory) { + return $this->messageFactory->createRequest($method, $uri, $headers, $body); + } + + $request = $this->messageFactory->createRequest($method, $uri); + + foreach ($headers as $name => $value) { + $request = $request->withAddedHeader($name, $value); + } + + if (null === $body) { + return $request; + } + + $stream = $this->messageFactory->createStream($body); + + if ($stream->isSeekable()) { + $stream->seek(0); + } + + return $request->withBody($stream); } /** @@ -94,8 +131,93 @@ protected function getHttpClient(): ClientInterface return $this->client; } + /** + * @deprecated Use createRequest instead + */ protected function getMessageFactory(): MessageFactory { - return $this->messageFactory; + if ($this->messageFactory instanceof MessageFactory) { + return $this->messageFactory; + } + + $factory = $this->messageFactory instanceof ResponseFactoryInterface ? $this->messageFactory : new Psr17Factory(); + + return new class($factory) implements MessageFactory { + public function __construct( + /** + * @param RequestFactoryInterface&ResponseFactoryInterface&StreamFactoryInterface $factory + */ + private RequestFactoryInterface|ResponseFactoryInterface|StreamFactoryInterface $factory, + ) { + } + + /** + * @param string $method + * @param string|UriInterface $uri + * @param array $headers + * @param resource|string|StreamInterface|null $body + * @param string $protocolVersion + */ + public function createRequest($method, $uri, array $headers = [], $body = null, $protocolVersion = '1.1'): RequestInterface + { + $request = $this->factory->createRequest($method, $uri); + + foreach ($headers as $name => $value) { + $request = $request->withAddedHeader($name, $value); + } + + if (null !== $body) { + $request = $request->withBody($this->createStream($body)); + } + + return $request->withProtocolVersion($protocolVersion); + } + + /** + * @param int $statusCode + * @param string|null $reasonPhrase + * @param array $headers + * @param resource|string|StreamInterface|null $body + * @param string $protocolVersion + */ + public function createResponse($statusCode = 200, $reasonPhrase = null, array $headers = [], $body = null, $protocolVersion = '1.1'): ResponseInterface + { + $response = $this->factory->createResponse($statusCode, $reasonPhrase); + + foreach ($headers as $name => $value) { + $response = $response->withAddedHeader($name, $value); + } + + if (null !== $body) { + $response = $response->withBody($this->createStream($body)); + } + + return $response->withProtocolVersion($protocolVersion); + } + + /** + * @param string|resource|StreamInterface|null $body + */ + private function createStream($body = ''): StreamInterface + { + if ($body instanceof StreamInterface) { + return $body; + } + + if (\is_string($body ?? '')) { + $stream = $this->factory->createStream($body ?? ''); + } elseif (\is_resource($body)) { + $stream = $this->factory->createStreamFromResource($body); + } else { + throw new \InvalidArgumentException(sprintf('"%s()" expects string, resource or StreamInterface, "%s" given.', __METHOD__, get_debug_type($body))); + } + + if ($stream->isSeekable()) { + $stream->seek(0); + } + + return $stream; + } + }; } } diff --git a/composer.json b/composer.json index c8f86a8..b3a7844 100644 --- a/composer.json +++ b/composer.json @@ -15,12 +15,9 @@ ], "require": { "php": "^8.0", - "php-http/client-implementation": "^1.0", - "php-http/discovery": "^1.6", - "php-http/httplug": "^1.0 || ^2.0", - "php-http/message-factory": "^1.0.2", - "psr/http-message": "^1.0 || ^2.0", - "psr/http-message-implementation": "^1.0", + "php-http/discovery": "^1.17", + "psr/http-client-implementation": "^1.0", + "psr/http-factory-implementation": "^1.0", "willdurand/geocoder": "^4.0" }, "require-dev": { @@ -48,5 +45,10 @@ "scripts": { "test": "vendor/bin/phpunit", "test-ci": "vendor/bin/phpunit --coverage-text --coverage-clover=build/coverage.xml" + }, + "config": { + "allow-plugins": { + "php-http/discovery": false + } } -} \ No newline at end of file +}