Client   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 187
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
wmc 20
lcom 1
cbo 6
dl 0
loc 187
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A setTransport() 0 4 1
A getTransport() 0 8 2
A setCloseOnError() 0 4 1
A isCloseOnError() 0 4 1
B request() 0 47 6
A getTransferHeaders() 0 20 4
A populateResponse() 0 21 4
1
<?php
2
3
/*
4
 * A PSR7 aware cURL client (https://github.com/juliangut/spiral).
5
 *
6
 * @license BSD-3-Clause
7
 * @link https://github.com/juliangut/spiral
8
 * @author Julián Gutiérrez <[email protected]>
9
 */
10
11
namespace Jgut\Spiral;
12
13
use Jgut\Spiral\Exception\TransportException;
14
use Jgut\Spiral\Transport\Curl;
15
use Jgut\Spiral\Transport\TransportInterface;
16
use Psr\Http\Message\RequestInterface;
17
use Psr\Http\Message\ResponseInterface;
18
use Zend\Diactoros\Stream;
19
20
/**
21
 * PSR7 aware client.
22
 */
23
class Client
24
{
25
    /**
26
     * cURL transport handler.
27
     *
28
     * @var TransportInterface
29
     */
30
    protected $transport;
31
32
    /**
33
     * Auto close on error.
34
     *
35
     * @var bool
36
     */
37
    protected $closeOnError = true;
38
39
    /**
40
     * @param TransportInterface|null $transport
41
     */
42
    public function __construct(TransportInterface $transport = null)
43
    {
44
        $this->transport = $transport;
45
    }
46
47
    /**
48
     * Set transport handler.
49
     *
50
     * @param TransportInterface $transport
51
     */
52
    public function setTransport(TransportInterface $transport)
53
    {
54
        $this->transport = $transport;
55
    }
56
57
    /**
58
     * Retrieve transport handler.
59
     *
60
     * @return TransportInterface
61
     */
62
    public function getTransport()
63
    {
64
        if (!$this->transport instanceof TransportInterface) {
65
            $this->transport = Curl::createFromDefaults();
66
        }
67
68
        return $this->transport;
69
    }
70
71
    /**
72
     * Set automatic connection close on error.
73
     *
74
     * @param bool $closeOnError
75
     */
76
    public function setCloseOnError($closeOnError)
77
    {
78
        $this->closeOnError = $closeOnError === true;
79
    }
80
81
    /**
82
     * Check if automatic connection close on error is active.
83
     *
84
     * @return bool
85
     */
86
    public function isCloseOnError()
87
    {
88
        return $this->closeOnError;
89
    }
90
91
    /**
92
     * Run PSR7 request.
93
     *
94
     * @param RequestInterface  $request
95
     * @param ResponseInterface $response
96
     *
97
     * @throws TransportException
98
     *
99
     * @return ResponseInterface
100
     */
101
    public function request(
102
        RequestInterface $request,
103
        ResponseInterface $response
104
    ) {
105
        $transport = $this->getTransport();
106
107
        try {
108
            $transferResponse = $transport->request(
109
                $request->getMethod(),
110
                (string) $request->getUri(),
111
                $request->getHeaders(),
112
                (string) $request->getBody()
113
            );
114
        } catch (TransportException $exception) {
115
            if ($this->closeOnError) {
116
                $transport->close();
117
            }
118
119
            // Bubble exception
120
            throw $exception;
121
        }
122
123
        $transferInfo = $transport->responseInfo();
124
125
        $responseHeaders = '';
126
        $responseContent = $transferResponse;
127
128
        if (isset($transferInfo['header_size']) && $transferInfo['header_size']) {
129
            $headersSize = $transferInfo['header_size'];
130
131
            $responseHeaders = rtrim(substr($transferResponse, 0, $headersSize));
132
            $responseContent = (strlen($transferResponse) === $headersSize)
133
                ? ''
134
                : substr($transferResponse, $headersSize);
135
        }
136
137
        // Split headers blocks
138
        $responseHeaders = preg_split('/(\\r?\\n){2}/', $responseHeaders);
139
140
        $responseHeaders = $this->getTransferHeaders(
141
            preg_split('/\\r?\\n/', array_pop($responseHeaders)),
142
            $responseContent,
143
            $transferInfo
144
        );
145
146
        return $this->populateResponse($response, $responseHeaders, $responseContent);
147
    }
148
149
    /**
150
     * Get response headers based on transfer information.
151
     *
152
     * @param array  $transferHeaders
153
     * @param string $transferContent
154
     * @param array  $transferInfo
155
     *
156
     * @return array
157
     */
158
    protected function getTransferHeaders(array $transferHeaders, $transferContent, array $transferInfo)
159
    {
160
        $responseHeaders = [
161
            'Status'         => $transferInfo['http_code'],
162
            'Content-Type'   => $transferInfo['content_type'],
163
            'Content-Length' => strlen($transferContent),
164
        ];
165
166
        foreach ($transferHeaders as $header) {
167
            if (preg_match('/^HTTP\/(1\.\d) +([1-5]\d{2}) +.+$/', $header, $matches)) {
168
                $responseHeaders['Protocol-Version'] = $matches[1];
169
                $responseHeaders['Status'] = $matches[2];
170
            } elseif (strpos($header, ':') !== false) {
171
                list($name, $value) = explode(':', $header, 2);
172
                $responseHeaders[$name] = trim($value);
173
            }
174
        }
175
176
        return $responseHeaders;
177
    }
178
179
    /**
180
     * Set response headers and content.
181
     *
182
     * @param ResponseInterface $response
183
     * @param array             $headers
184
     * @param string            $content
185
     *
186
     * @return ResponseInterface
187
     */
188
    protected function populateResponse(ResponseInterface $response, array $headers, $content)
189
    {
190
        if (array_key_exists('Protocol-Version', $headers)) {
191
            $response = $response->withProtocolVersion($headers['Protocol-Version']);
192
            unset($headers['Protocol-Version']);
193
        }
194
195
        if (array_key_exists('Status', $headers)) {
196
            $response = $response->withStatus((int) $headers['Status']);
197
            unset($headers['Status']);
198
        }
199
200
        foreach ($headers as $name => $value) {
201
            $response = $response->withHeader($name, (string) $value);
202
        }
203
204
        $body = new Stream('php://temp', 'wb+');
205
        $body->write($content);
206
207
        return $response->withBody($body);
208
    }
209
}
210