Browse Source

Make Http\Client implement PSR-18's ClientInterface.

ADmad 7 years ago
parent
commit
9497a97b63

+ 2 - 1
composer.json

@@ -31,9 +31,10 @@
         "php": ">=7.1.0",
         "ext-intl": "*",
         "ext-mbstring": "*",
-        "cakephp/chronos": "^1.0.1",
         "aura/intl": "^3.0.0",
+        "cakephp/chronos": "^1.0.1",
         "composer/ca-bundle": "^1.0",
+        "psr/http-client": "^1.0",
         "psr/log": "^1.0.0",
         "psr/simple-cache": "^1.0.0",
         "zendframework/zend-diactoros": "^2.0",

+ 20 - 6
src/Http/Client.php

@@ -26,6 +26,9 @@ use Cake\Http\Cookie\CookieCollection;
 use Cake\Http\Cookie\CookieInterface;
 use Cake\Utility\Hash;
 use InvalidArgumentException;
+use Psr\Http\Client\ClientInterface;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
 use Zend\Diactoros\Uri;
 
 /**
@@ -98,7 +101,7 @@ use Zend\Diactoros\Uri;
  *
  * @mixin \Cake\Core\InstanceConfigTrait
  */
-class Client
+class Client implements ClientInterface
 {
     use InstanceConfigTrait;
 
@@ -397,16 +400,28 @@ class Client
     }
 
     /**
+     * Sends a PSR-7 request and returns a PSR-7 response.
+     *
+     * @param \Psr\Http\Message\RequestInterface $request Request instance.
+     * @return \Psr\Http\Message\ResponseInterface Response instance.
+     * @throws \Psr\Http\Client\ClientExceptionInterface If an error happens while processing the request.
+     */
+    public function sendRequest(RequestInterface $request): ResponseInterface
+    {
+        return $response = $this->send($request, $this->_config);
+    }
+
+    /**
      * Send a request.
      *
      * Used internally by other methods, but can also be used to send
      * handcrafted Request objects.
      *
-     * @param \Cake\Http\Client\Request $request The request to send.
+     * @param \Psr\Http\Message\RequestInterface $request The request to send.
      * @param array $options Additional options to use.
      * @return \Cake\Http\Client\Response
      */
-    public function send(Request $request, array $options = []): Response
+    public function send(RequestInterface $request, array $options = [])
     {
         $redirects = 0;
         if (isset($options['redirect'])) {
@@ -440,14 +455,13 @@ class Client
     /**
      * Send a request without redirection.
      *
-     * @param \Cake\Http\Client\Request $request The request to send.
+     * @param \Psr\Http\Message\RequestInterface $request The request to send.
      * @param array $options Additional options to use.
      * @return \Cake\Http\Client\Response
      */
-    protected function _sendRequest(Request $request, array $options): Response
+    protected function _sendRequest(RequestInterface $request, array $options)
     {
         $responses = $this->_adapter->send($request, $options);
-        $url = $request->getUri();
         foreach ($responses as $response) {
             $this->_cookies = $this->_cookies->addFromResponse($response, $request);
         }

+ 4 - 3
src/Http/Client/Adapter/Curl.php

@@ -19,6 +19,7 @@ use Cake\Http\Client\Request;
 use Cake\Http\Client\Response;
 use Cake\Http\Exception\HttpException;
 use Composer\CaBundle\CaBundle;
+use Psr\Http\Message\RequestInterface;
 
 /**
  * Implements sending Cake\Http\Client\Request via ext/curl.
@@ -33,7 +34,7 @@ class Curl implements AdapterInterface
     /**
      * @inheritDoc
      */
-    public function send(Request $request, array $options): array
+    public function send(RequestInterface $request, array $options): array
     {
         $ch = curl_init();
         $options = $this->buildOptions($request, $options);
@@ -61,11 +62,11 @@ class Curl implements AdapterInterface
     /**
      * Convert client options into curl options.
      *
-     * @param \Cake\Http\Client\Request $request The request.
+     * @param \Psr\Http\Message\RequestInterface $request The request.
      * @param array $options The client options
      * @return array
      */
-    public function buildOptions(Request $request, array $options): array
+    public function buildOptions(RequestInterface $request, array $options): array
     {
         $headers = [];
         foreach ($request->getHeaders() as $key => $values) {

+ 14 - 14
src/Http/Client/Adapter/Stream.php

@@ -16,10 +16,10 @@ namespace Cake\Http\Client\Adapter;
 
 use Cake\Core\Exception\Exception;
 use Cake\Http\Client\AdapterInterface;
-use Cake\Http\Client\Request;
 use Cake\Http\Client\Response;
 use Cake\Http\Exception\HttpException;
 use Composer\CaBundle\CaBundle;
+use Psr\Http\Message\RequestInterface;
 
 /**
  * Implements sending Cake\Http\Client\Request
@@ -67,7 +67,7 @@ class Stream implements AdapterInterface
     /**
      * @inheritDoc
      */
-    public function send(Request $request, array $options): array
+    public function send(RequestInterface $request, array $options): array
     {
         $this->_stream = null;
         $this->_context = null;
@@ -112,11 +112,11 @@ class Stream implements AdapterInterface
     /**
      * Build the stream context out of the request object.
      *
-     * @param \Cake\Http\Client\Request $request The request to build context from.
+     * @param \Psr\Http\Message\RequestInterface $request The request to build context from.
      * @param array $options Additional request options.
      * @return void
      */
-    protected function _buildContext(Request $request, array $options): void
+    protected function _buildContext(RequestInterface $request, array $options): void
     {
         $this->_buildContent($request, $options);
         $this->_buildHeaders($request, $options);
@@ -138,11 +138,11 @@ class Stream implements AdapterInterface
      *
      * Creates cookies & headers.
      *
-     * @param \Cake\Http\Client\Request $request The request being sent.
+     * @param \Psr\Http\Message\RequestInterface $request The request being sent.
      * @param array $options Array of options to use.
      * @return void
      */
-    protected function _buildHeaders(Request $request, array $options): void
+    protected function _buildHeaders(RequestInterface $request, array $options): void
     {
         $headers = [];
         foreach ($request->getHeaders() as $name => $values) {
@@ -157,11 +157,11 @@ class Stream implements AdapterInterface
      * If the $request->body() is a string, it will be used as is.
      * Array data will be processed with Cake\Http\Client\FormData
      *
-     * @param \Cake\Http\Client\Request $request The request being sent.
+     * @param \Psr\Http\Message\RequestInterface $request The request being sent.
      * @param array $options Array of options to use.
      * @return void
      */
-    protected function _buildContent(Request $request, array $options): void
+    protected function _buildContent(RequestInterface $request, array $options): void
     {
         $body = $request->getBody();
         if (empty($body)) {
@@ -176,11 +176,11 @@ class Stream implements AdapterInterface
     /**
      * Build miscellaneous options for the request.
      *
-     * @param \Cake\Http\Client\Request $request The request being sent.
+     * @param \Psr\Http\Message\RequestInterface $request The request being sent.
      * @param array $options Array of options to use.
      * @return void
      */
-    protected function _buildOptions(Request $request, array $options): void
+    protected function _buildOptions(RequestInterface $request, array $options): void
     {
         $this->_contextOptions['method'] = $request->getMethod();
         $this->_contextOptions['protocol_version'] = $request->getProtocolVersion();
@@ -201,11 +201,11 @@ class Stream implements AdapterInterface
     /**
      * Build SSL options for the request.
      *
-     * @param \Cake\Http\Client\Request $request The request being sent.
+     * @param \Psr\Http\Message\RequestInterface $request The request being sent.
      * @param array $options Array of options to use.
      * @return void
      */
-    protected function _buildSslContext(Request $request, array $options): void
+    protected function _buildSslContext(RequestInterface $request, array $options): void
     {
         $sslOptions = [
             'ssl_verify_peer',
@@ -235,11 +235,11 @@ class Stream implements AdapterInterface
     /**
      * Open the stream and send the request.
      *
-     * @param \Cake\Http\Client\Request $request The request object.
+     * @param \Psr\Http\Message\RequestInterface $request The request object.
      * @return array Array of populated Response objects
      * @throws \Cake\Http\Exception\HttpException
      */
-    protected function _send(Request $request): array
+    protected function _send(RequestInterface $request): array
     {
         $deadline = false;
         if (isset($this->_contextOptions['timeout']) && $this->_contextOptions['timeout'] > 0) {

+ 4 - 2
src/Http/Client/AdapterInterface.php

@@ -14,14 +14,16 @@ declare(strict_types=1);
  */
 namespace Cake\Http\Client;
 
+use Psr\Http\Message\RequestInterface;
+
 interface AdapterInterface
 {
     /**
      * Send a request and get a response back.
      *
-     * @param \Cake\Http\Client\Request $request The request object to send.
+     * @param \Psr\Http\Message\RequestInterface $request The request object to send.
      * @param array $options Array of options for the stream.
      * @return \Cake\Http\Client\Response[] Array of populated Response objects
      */
-    public function send(Request $request, array $options): array;
+    public function send(RequestInterface $request, array $options): array;
 }

+ 45 - 0
tests/TestCase/Http/ClientTest.php

@@ -15,6 +15,7 @@ declare(strict_types=1);
 namespace Cake\Test\TestCase\Http;
 
 use Cake\Http\Client;
+use Cake\Http\Client\Adapter\Stream;
 use Cake\Http\Client\Request;
 use Cake\Http\Client\Response;
 use Cake\Http\Cookie\Cookie;
@@ -802,4 +803,48 @@ class ClientTest extends TestCase
         $this->assertTrue($cookies->has('redirect1'));
         $this->assertTrue($cookies->has('redirect2'));
     }
+
+    /**
+     * testSendRequest
+     *
+     * @return void
+     */
+    public function testSendRequest()
+    {
+        $response = new Response();
+
+        $headers = [
+            'User-Agent' => 'Cake',
+            'Connection' => 'close',
+            'Content-Type' => 'application/x-www-form-urlencoded',
+        ];
+
+        $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
+            ->setMethods(['send'])
+            ->getMock();
+        $mock->expects($this->once())
+            ->method('send')
+            ->with($this->callback(function ($request) use ($headers) {
+                $this->assertInstanceOf('Zend\Diactoros\Request', $request);
+                $this->assertEquals(Request::METHOD_GET, $request->getMethod());
+                $this->assertEquals('http://cakephp.org/test.html', $request->getUri() . '');
+                $this->assertEquals($headers['Content-Type'], $request->getHeaderLine('content-type'));
+                $this->assertEquals($headers['Connection'], $request->getHeaderLine('connection'));
+
+                return true;
+            }))
+            ->will($this->returnValue([$response]));
+
+        $http = new Client(['adapter' => $mock]);
+        $request = new \Zend\Diactoros\Request(
+            'http://cakephp.org/test.html',
+            Request::METHOD_GET,
+            'php://temp',
+            $headers
+        );
+        $result = $http->sendRequest($request);
+
+        $this->assertSame($result, $response);
+    }
+
 }