Completed
Pull Request — master (#72)
by Nate
03:35
created

RequestBuilder::build()   C

Complexity

Conditions 7
Paths 21

Size

Total Lines 28
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 16
nc 21
nop 0
1
<?php
2
/*
3
 * Copyright (c) Nate Brunette.
4
 * Distributed under the MIT License (http://opensource.org/licenses/MIT)
5
 */
6
7
declare(strict_types=1);
8
9
namespace Tebru\Retrofit\Internal;
10
11
use GuzzleHttp\Psr7;
12
use GuzzleHttp\Psr7\MultipartStream;
13
use GuzzleHttp\Psr7\Request;
14
use GuzzleHttp\Psr7\Uri;
15
use LogicException;
16
use Psr\Http\Message\StreamInterface;
17
18
/**
19
 * Class RequestBuilder
20
 *
21
 * @author Nate Brunette <[email protected]>
22
 */
23
final class RequestBuilder
24
{
25
    /**
26
     * The request method
27
     *
28
     * @var string
29
     */
30
    private $method;
31
32
    /**
33
     * The request [@see Uri] object
34
     *
35
     * @var Uri
36
     */
37
    private $uri;
38
39
    /**
40
     * An array of query strings that can be appended together
41
     *
42
     * @var array
43
     */
44
    private $queries = [];
45
46
    /**
47
     * An array of headers in PSR-7 format
48
     *
49
     * @var array
50
     */
51
    private $headers;
52
53
    /**
54
     * The request body
55
     *
56
     * @var StreamInterface
57
     */
58
    private $body;
59
60
    /**
61
     * An array of request body fields that can be appended together
62
     *
63
     * @var array
64
     */
65
    private $fields = [];
66
67
    /**
68
     * An array of arrays of multipart parts, each with name, content, headers, and filename
69
     *
70
     * @var array[]
71
     */
72
    private $parts = [];
73
74
    /**
75
     * Constructor
76
     *
77
     * @param string $method
78
     * @param string $baseUrl
79
     * @param string $uri
80
     * @param array $headers
81
     */
82
    public function __construct(string $method, string $baseUrl, string $uri, array $headers)
83
    {
84
        $this->method = $method;
85
        $this->uri = new Uri($baseUrl.$uri);
86
        $this->headers = $headers;
87
    }
88
89
    /**
90
     * Set the uri base url
91
     *
92
     * @param string $value
93
     */
94
    public function setBaseUrl(string $value): void
95
    {
96
        $uri = new Uri($value);
97
        $this->uri = $this->uri
98
            ->withScheme($uri->getScheme())
99
            ->withHost($uri->getHost())
100
            ->withPort($uri->getPort());
101
    }
102
103
    /**
104
     * Replace a url path placeholder with a value
105
     *
106
     * @param string $name
107
     * @param string $value
108
     */
109
    public function replacePath(string $name, string $value): void
110
    {
111
        $path = rawurldecode($this->uri->getPath());
112
        $path = str_replace(sprintf('{%s}', $name), $value, $path);
113
        $this->uri = $this->uri->withPath($path);
114
    }
115
116
    /**
117
     * Add a query string; if encoded, decodes to be encoded later
118
     *
119
     * @param string $name
120
     * @param string $value
121
     * @param bool $encoded
122
     */
123
    public function addQuery(string $name, string $value, bool $encoded): void
124
    {
125
        if ($encoded === true) {
126
            $name = rawurldecode($name);
127
            $value = rawurldecode($value);
128
        }
129
130
        $this->queries[] = $name.'='.$value;
131
    }
132
133
    /**
134
     * Adds a query string without value; if encoded, decodes to be encoded later
135
     *
136
     * @param string $value
137
     * @param bool $encoded
138
     */
139
    public function addQueryName(string $value, bool $encoded): void
140
    {
141
        if ($encoded === true) {
142
            $value = rawurldecode($value);
143
        }
144
145
        $this->queries[] = $value;
146
    }
147
148
    /**
149
     * Add a header in PSR-7 format
150
     *
151
     * @param string $name
152
     * @param string $value
153
     */
154
    public function addHeader(string $name, string $value): void
155
    {
156
        $name = strtolower($name);
157
        $this->headers[$name][] = $value;
158
    }
159
160
    /**
161
     * Set the request body
162
     *
163
     * @param StreamInterface $body
164
     */
165
    public function setBody(StreamInterface $body): void
166
    {
167
        $this->body = $body;
168
    }
169
170
    /**
171
     * Add a field; if not encoded, encodes first
172
     *
173
     * @param string $name
174
     * @param string $value
175
     * @param bool $encoded
176
     */
177
    public function addField(string $name, string $value, bool $encoded): void
178
    {
179
        if ($encoded === false) {
180
            $name = rawurlencode($name);
181
            $value = rawurlencode($value);
182
        }
183
184
        $this->fields[] = $name.'='.$value;
185
    }
186
187
    /**
188
     * Add a multipart part
189
     *
190
     * @param string $name
191
     * @param StreamInterface $contents
192
     * @param array $headers
193
     * @param null|string $filename
194
     */
195
    public function addPart(string $name, StreamInterface $contents, array $headers = [], ?string $filename = null): void
196
    {
197
        $this->parts[] = [
198
            'name' => $name,
199
            'contents' => $contents,
200
            'headers' => $headers,
201
            'filename' => $filename,
202
        ];
203
    }
204
205
    /**
206
     * Create a PSR-7 request
207
     *
208
     * @return Request
209
     * @throws \LogicException
210
     */
211
    public function build(): Request
212
    {
213
        $uri = $this->uri;
214
        if ($this->queries !== []) {
215
            $query = implode('&', $this->queries);
216
            $uri = $this->uri->getQuery() === ''
217
                ? $this->uri->withQuery($query)
218
                : $this->uri->withQuery($query.'&'.$this->uri->getQuery());
219
        }
220
221
        if ($this->fields !== []) {
222
            if ($this->body !== null) {
223
                throw new LogicException('Retrofit: Cannot mix @Field and @Body annotations.');
224
            }
225
226
            $this->body = Psr7\stream_for(implode('&', $this->fields));
227
        }
228
229
        if ($this->parts !== []) {
230
            if ($this->body !== null) {
231
                throw new LogicException('Retrofit: Cannot mix @Part and @Body annotations.');
232
            }
233
234
            $this->body = new MultipartStream($this->parts);
235
        }
236
237
        return new Request($this->method, $uri, $this->headers, $this->body);
238
    }
239
}
240