77use Http\Client\Exception;
88use Http\Client\HttpAsyncClient;
99use Http\Client\HttpClient;
10+ use Http\Discovery\Exception\NotFoundException;
1011use Http\Discovery\Psr17FactoryDiscovery;
12+ use Http\Promise\Promise;
1113use Psr\Http\Message\RequestInterface;
1214use Psr\Http\Message\ResponseFactoryInterface;
1315use Psr\Http\Message\ResponseInterface;
@@ -63,11 +65,16 @@ class Client implements HttpClient, HttpAsyncClient
6365 private $multiRunner;
6466
6567 /**
68+ * Create HTTP client.
69+ *
6670 * @param ResponseFactoryInterface|null $responseFactory PSR-17 HTTP response factory.
6771 * @param StreamFactoryInterface|null $streamFactory PSR-17 HTTP stream factory.
68- * @param array $options cURL options {@link http://php.net/curl_setopt}
72+ * @param array $options cURL options
73+ * {@link http://php.net/curl_setopt}.
74+ *
75+ * @throws NotFoundException If factory discovery failed.
6976 *
70- * @throws \Http\Discovery\Exception\NotFoundException If factory discovery failed
77+ * @since 2.0 Accepts PSR-17 factories instead of HTTPlug ones.
7178 */
7279 public function __construct(
7380 ResponseFactoryInterface $responseFactory = null,
@@ -81,11 +88,21 @@ public function __construct(
8188 [
8289 CURLOPT_HEADER => false,
8390 CURLOPT_RETURNTRANSFER => false,
84- CURLOPT_FOLLOWLOCATION => false,
91+ CURLOPT_FOLLOWLOCATION => false
8592 ]
8693 );
87- $resolver->setAllowedValues(CURLOPT_HEADER, [false]); // our parsing will fail if this is set to true
88- $resolver->setAllowedValues(CURLOPT_RETURNTRANSFER, [false]); // our parsing will fail if this is set to true
94+
95+ // Our parsing will fail if this is set to true.
96+ $resolver->setAllowedValues(
97+ CURLOPT_HEADER,
98+ [false]
99+ );
100+
101+ // Our parsing will fail if this is set to true.
102+ $resolver->setAllowedValues(
103+ CURLOPT_RETURNTRANSFER,
104+ [false]
105+ );
89106
90107 // We do not know what everything curl supports and might support in the future.
91108 // Make sure that we accept everything that is in the options.
@@ -105,12 +122,16 @@ public function __destruct()
105122 }
106123
107124 /**
108- * {@inheritdoc}
125+ * Sends a PSR-7 request and returns a PSR-7 response.
126+ *
127+ * @param RequestInterface $request
109128 *
110- * @throws \Http\Client\Exception\NetworkException In case of network problems
111- * @throws \Http\Client\Exception\RequestException On invalid request
112- * @throws \InvalidArgumentException For invalid header names or values
113- * @throws \RuntimeException If creating the body stream fails
129+ * @return ResponseInterface
130+ *
131+ * @throws \InvalidArgumentException For invalid header names or values.
132+ * @throws \RuntimeException If creating the body stream fails.
133+ * @throws Exception\NetworkException In case of network problems.
134+ * @throws Exception\RequestException On invalid request.
114135 *
115136 * @since 1.6 \UnexpectedValueException replaced with RequestException
116137 * @since 1.6 Throw NetworkException on network errors
@@ -152,42 +173,37 @@ public function sendRequest(RequestInterface $request): ResponseInterface
152173 }
153174
154175 /**
155- * {@inheritdoc}
156- *
157- * @throws \Http\Client\Exception\RequestException On invalid request
158- * @throws \InvalidArgumentException For invalid header names or values
159- * @throws \RuntimeException If creating the body stream fails
176+ * Create builder to use for building response object.
160177 *
161- * @since 1.6 \UnexpectedValueException replaced with RequestException
162- * @since 1.0
178+ * @return ResponseBuilder
163179 */
164- public function sendAsyncRequest(RequestInterface $request)
180+ private function createResponseBuilder(): ResponseBuilder
165181 {
166- if (!$this->multiRunner instanceof MultiRunner) {
167- $this->multiRunner = new MultiRunner();
168- }
169-
170- $handle = curl_init();
171- $responseBuilder = $this->createResponseBuilder();
172- $requestOptions = $this->prepareRequestOptions($request, $responseBuilder);
173- curl_setopt_array($handle, $requestOptions);
182+ $body = $this->streamFactory->createStreamFromFile('php://temp', 'w+b');
174183
175- $core = new PromiseCore($request, $handle, $responseBuilder);
176- $promise = new CurlPromise($core, $this->multiRunner);
177- $this->multiRunner->add($core );
184+ $response = $this->responseFactory
185+ ->createResponse(200)
186+ ->withBody($body );
178187
179- return $promise ;
188+ return new ResponseBuilder($response) ;
180189 }
181190
182191 /**
183- * Update cURL options for this request and hook in the response builder.
192+ * Update cURL options for given request and hook in the response builder.
193+ *
194+ * @param RequestInterface $request Request on which to create options.
195+ * @param ResponseBuilder $responseBuilder Builder to use for building response.
184196 *
185- * @throws \Http\Client\Exception\RequestException On invalid request
186- * @throws \InvalidArgumentException For invalid header names or values
187- * @throws \RuntimeException If can not read body
197+ * @return array cURL options based on request.
198+ *
199+ * @throws \InvalidArgumentException For invalid header names or values.
200+ * @throws \RuntimeException If can not read body.
201+ * @throws Exception\RequestException On invalid request.
188202 */
189- private function prepareRequestOptions(RequestInterface $request, ResponseBuilder $responseBuilder): array
190- {
203+ private function prepareRequestOptions(
204+ RequestInterface $request,
205+ ResponseBuilder $responseBuilder
206+ ): array {
191207 $curlOptions = $this->curlOptions;
192208
193209 try {
@@ -196,7 +212,7 @@ private function prepareRequestOptions(RequestInterface $request, ResponseBuilde
196212 } catch (\UnexpectedValueException $e) {
197213 throw new Exception\RequestException($e->getMessage(), $request);
198214 }
199- $curlOptions[CURLOPT_URL] = (string) $request->getUri();
215+ $curlOptions[CURLOPT_URL] = (string)$request->getUri();
200216
201217 $curlOptions = $this->addRequestBodyOptions($request, $curlOptions);
202218
@@ -209,7 +225,7 @@ private function prepareRequestOptions(RequestInterface $request, ResponseBuilde
209225 $curlOptions[CURLOPT_HEADERFUNCTION] = function ($ch, $data) use ($responseBuilder) {
210226 $str = trim($data);
211227 if ('' !== $str) {
212- if (strpos(strtolower( $str) , 'http/') === 0) {
228+ if (stripos( $str, 'http/') === 0) {
213229 $responseBuilder->setStatus($str)->getResponse();
214230 } else {
215231 $responseBuilder->addHeader($str);
@@ -229,7 +245,11 @@ private function prepareRequestOptions(RequestInterface $request, ResponseBuilde
229245 /**
230246 * Return cURL constant for specified HTTP version.
231247 *
232- * @throws \UnexpectedValueException If unsupported version requested
248+ * @param string $requestVersion HTTP version ("1.0", "1.1" or "2.0").
249+ *
250+ * @return int Respective CURL_HTTP_VERSION_x_x constant.
251+ *
252+ * @throws \UnexpectedValueException If unsupported version requested.
233253 */
234254 private function getProtocolVersion(string $requestVersion): int
235255 {
@@ -250,6 +270,11 @@ private function getProtocolVersion(string $requestVersion): int
250270
251271 /**
252272 * Add request body related cURL options.
273+ *
274+ * @param RequestInterface $request Request on which to create options.
275+ * @param array $curlOptions Options created by prepareRequestOptions().
276+ *
277+ * @return array cURL options based on request.
253278 */
254279 private function addRequestBodyOptions(RequestInterface $request, array $curlOptions): array
255280 {
@@ -281,7 +306,7 @@ private function addRequestBodyOptions(RequestInterface $request, array $curlOpt
281306 };
282307 } else {
283308 // Small body can be loaded into memory
284- $curlOptions[CURLOPT_POSTFIELDS] = (string) $body;
309+ $curlOptions[CURLOPT_POSTFIELDS] = (string)$body;
285310 }
286311 }
287312 }
@@ -300,6 +325,9 @@ private function addRequestBodyOptions(RequestInterface $request, array $curlOpt
300325 /**
301326 * Create headers array for CURLOPT_HTTPHEADER.
302327 *
328+ * @param RequestInterface $request Request on which to create headers.
329+ * @param array $curlOptions Options created by prepareRequestOptions().
330+ *
303331 * @return string[]
304332 */
305333 private function createHeaders(RequestInterface $request, array $curlOptions): array
@@ -322,7 +350,7 @@ private function createHeaders(RequestInterface $request, array $curlOptions): a
322350 }
323351 }
324352 foreach ($values as $value) {
325- $curlHeaders[] = $name. ': '. $value;
353+ $curlHeaders[] = $name . ': ' . $value;
326354 }
327355 }
328356 /*
@@ -334,14 +362,37 @@ private function createHeaders(RequestInterface $request, array $curlOptions): a
334362 return $curlHeaders;
335363 }
336364
337- private function createResponseBuilder(): ResponseBuilder
365+ /**
366+ * Sends a PSR-7 request in an asynchronous way.
367+ *
368+ * Exceptions related to processing the request are available from the returned Promise.
369+ *
370+ * @param RequestInterface $request
371+ *
372+ * @return Promise Resolves a PSR-7 Response or fails with an Http\Client\Exception.
373+ *
374+ * @throws \InvalidArgumentException For invalid header names or values.
375+ * @throws \RuntimeException If creating the body stream fails.
376+ * @throws Exception\RequestException On invalid request.
377+ *
378+ * @since 1.6 \UnexpectedValueException replaced with RequestException
379+ * @since 1.0
380+ */
381+ public function sendAsyncRequest(RequestInterface $request)
338382 {
339- $body = $this->streamFactory->createStreamFromFile('php://temp', 'w+b');
383+ if (!$this->multiRunner instanceof MultiRunner) {
384+ $this->multiRunner = new MultiRunner();
385+ }
340386
341- $response = $this->responseFactory
342- ->createResponse(200)
343- ->withBody($body);
387+ $handle = curl_init();
388+ $responseBuilder = $this->createResponseBuilder();
389+ $requestOptions = $this->prepareRequestOptions($request, $responseBuilder);
390+ curl_setopt_array($handle, $requestOptions);
344391
345- return new ResponseBuilder($response);
392+ $core = new PromiseCore($request, $handle, $responseBuilder);
393+ $promise = new CurlPromise($core, $this->multiRunner);
394+ $this->multiRunner->add($core);
395+
396+ return $promise;
346397 }
347398}
0 commit comments