Completed
Push — master ( de3b4c...4691b8 )
by Julián
03:00
created

Client::populateResponse()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 21
rs 9.0534
cc 4
eloc 12
nc 8
nop 3
1
<?php
2
/**
3
 * Spiral: PSR7 aware cURL client (https://github.com/juliangut/spiral)
4
 *
5
 * @link https://github.com/juliangut/spiral for the canonical source repository
6
 *
7
 * @license https://raw.githubusercontent.com/juliangut/spiral/master/LICENSE
8
 */
9
10
namespace Jgut\Spiral;
11
12
use Jgut\Spiral\Exception\TransportException;
13
use Jgut\Spiral\Transport\Curl;
14
use Psr\Http\Message\RequestInterface;
15
use Psr\Http\Message\ResponseInterface;
16
use Zend\Diactoros\Stream;
17
18
/**
19
 * PSR7 aware client.
20
 */
21
class Client
22
{
23
    /**
24
     * cURL transport handler.
25
     *
26
     * @var \Jgut\Spiral\Transport
27
     */
28
    private $transport;
29
30
    /**
31
     * @param \Jgut\Spiral\Transport|null $transport
32
     */
33
    public function __construct(Transport $transport = null)
34
    {
35
        $this->transport = $transport;
36
    }
37
38
    /**
39
     * Set transport handler.
40
     *
41
     * @param \Jgut\Spiral\Transport $transport
42
     */
43
    public function setTransport(Transport $transport)
44
    {
45
        $this->transport = $transport;
46
    }
47
48
    /**
49
     * Retrieve transport handler.
50
     *
51
     * @return \Jgut\Spiral\Transport
52
     */
53
    public function getTransport()
54
    {
55
        if (!$this->transport instanceof Transport) {
56
            $this->transport = Curl::createFromDefaults();
57
        }
58
59
        return $this->transport;
60
    }
61
62
    /**
63
     * Run PSR7 request.
64
     *
65
     * @param \Psr\Http\Message\RequestInterface  $request
66
     * @param \Psr\Http\Message\ResponseInterface $response
67
     * @param array                               $vars
68
     * @param array                               $flags
69
     *
70
     * @throws \Jgut\Spiral\Exception\TransportException
71
     *
72
     * @return \Psr\Http\Message\ResponseInterface
73
     */
74
    public function request(
75
        RequestInterface $request,
76
        ResponseInterface $response,
77
        array $vars = [],
78
        array $flags = []
79
    ) {
80
        $transport = $this->getTransport();
81
82
        try {
83
            $transferResponse = $transport->request(
84
                $request->getMethod(),
85
                (string) $request->getUri(),
86
                $request->getHeaders(),
87
                $vars,
88
                $flags
89
            );
90
        } catch (TransportException $exception) {
91
            $transport->close();
92
93
            // Bubble exception
94
            throw $exception;
95
        }
96
97
        $transferInfo = $transport->responseInfo();
98
        $transport->close();
99
100
        $responseHeaders = '';
101
        $responseContent = $transferResponse;
102
103
        if (isset($transferInfo['header_size']) && $transferInfo['header_size']) {
104
            $headersSize = $transferInfo['header_size'];
105
106
            $responseHeaders = rtrim(substr($transferResponse, 0, $headersSize));
107
            $responseContent = (strlen($transferResponse) === $headersSize)
108
                ? ''
109
                : substr($transferResponse, $headersSize);
110
        }
111
112
        // Split headers blocks
113
        $responseHeaders = preg_split('/(\\r?\\n){2}/', $responseHeaders);
114
115
        $responseHeaders = $this->getTransferHeaders(
116
            preg_split('/\\r?\\n/', array_pop($responseHeaders)),
117
            $responseContent,
118
            $transferInfo
119
        );
120
121
        return $this->populateResponse($response, $responseHeaders, $responseContent);
122
    }
123
124
    /**
125
     * Get response headers based on transfer information.
126
     *
127
     * @param array  $transferHeaders
128
     * @param string $transferContent
129
     * @param array  $transferInfo
130
     *
131
     * @return array
132
     */
133
    protected function getTransferHeaders(array $transferHeaders, $transferContent, array $transferInfo)
134
    {
135
        $responseHeaders = [
136
            'Status'         => $transferInfo['http_code'],
137
            'Content-Type'   => $transferInfo['content_type'],
138
            'Content-Length' => strlen($transferContent),
139
        ];
140
141
        foreach ($transferHeaders as $header) {
142
            if (preg_match('/^HTTP\/(1\.\d) +([1-5][0-9]{2}) +.+$/', $header, $matches)) {
143
                $responseHeaders['Protocol-Version'] = $matches[1];
144
                $responseHeaders['Status'] = $matches[2];
145
            } elseif (strpos($header, ':') !== false) {
146
                list($name, $value) = explode(':', $header, 2);
147
                $responseHeaders[$name] = trim($value);
148
            }
149
        }
150
151
        return $responseHeaders;
152
    }
153
154
    /**
155
     * Set response headers and content.
156
     *
157
     * @param \Psr\Http\Message\ResponseInterface $response
158
     * @param array                               $headers
159
     * @param string                              $content
160
     *
161
     * @return \Psr\Http\Message\ResponseInterface
162
     */
163
    protected function populateResponse(ResponseInterface $response, array $headers, $content)
164
    {
165
        if (array_key_exists('Protocol-Version', $headers)) {
166
            $response = $response->withProtocolVersion($headers['Protocol-Version']);
167
            unset($headers['Protocol-Version']);
168
        }
169
170
        if (array_key_exists('Status', $headers)) {
171
            $response = $response->withStatus($headers['Status']);
172
            unset($headers['Status']);
173
        }
174
175
        foreach ($headers as $name => $value) {
176
            $response = $response->withHeader($name, (string) $value);
177
        }
178
179
        $body = new Stream('php://temp', 'r+');
180
        $body->write($content);
181
182
        return $response->withBody($body);
183
    }
184
}
185