Passed
Push — 1.x ( 05110a...ecbb08 )
by Kevin
02:11
created

HttpOptions::server()   B

Complexity

Conditions 9
Paths 24

Size

Total Lines 33
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 17
c 1
b 0
f 0
nc 24
nop 0
dl 0
loc 33
rs 8.0555
1
<?php
2
3
namespace Zenstruck\Browser;
4
5
/**
6
 * @author Kevin Bond <[email protected]>
7
 */
8
class HttpOptions
9
{
10
    private const EMPTY_JSON_TRIGGER = '__JSON__';
11
    private const DEFAULT_OPTIONS = [
12
        // request headers
13
        'headers' => [],
14
15
        // query parameters
16
        'query' => [],
17
18
        // files to include
19
        'files' => [],
20
21
        // server variables
22
        'server' => [],
23
24
        // raw request body as string
25
        'body' => null,
26
27
        // if set, will json_encode and use as the body and
28
        // set the Content-Type/Accept request headers to application/json
29
        'json' => null,
30
31
        // if true will set the X-Requested-With request header to XMLHttpRequest
32
        'ajax' => false,
33
    ];
34
35
    private array $options;
36
37
    final public function __construct(array $options = [])
38
    {
39
        $this->options = \array_merge(self::DEFAULT_OPTIONS, $options);
40
    }
41
42
    /**
43
     * @param self|array $options
44
     *
45
     * @return static
46
     */
47
    final public static function create($options = []): self
48
    {
49
        if ($options instanceof static) {
50
            return $options;
51
        }
52
53
        return new static($options);
54
    }
55
56
    /**
57
     * @return static
58
     */
59
    final public static function json($body = null): self
60
    {
61
        return static::create()->asJson($body);
62
    }
63
64
    /**
65
     * @return static
66
     */
67
    final public static function ajax(): self
68
    {
69
        return static::create()->asAjax();
70
    }
71
72
    /**
73
     * @return static
74
     */
75
    final public static function jsonAjax($body = null): self
76
    {
77
        return static::json($body)->asAjax();
78
    }
79
80
    /**
81
     * @param self|array $options
82
     *
83
     * @return static
84
     */
85
    final public function merge($options = []): self
86
    {
87
        $other = self::create($options);
88
89
        // merge array options
90
        $this->options['headers'] = \array_merge($this->options['headers'], $other->options['headers']);
91
        $this->options['query'] = \array_merge($this->options['query'], $other->options['query']);
92
        $this->options['files'] = \array_merge($this->options['files'], $other->options['files']);
93
        $this->options['server'] = \array_merge($this->options['server'], $other->options['server']);
94
95
        // override value options only if different from default
96
        if ($other->options['body'] !== self::DEFAULT_OPTIONS['body']) {
97
            $this->options['body'] = $other->options['body'];
98
        }
99
100
        if ($other->options['json'] !== self::DEFAULT_OPTIONS['json']) {
101
            $this->options['json'] = $other->options['json'];
102
        }
103
104
        if ($other->options['ajax'] !== self::DEFAULT_OPTIONS['ajax']) {
105
            $this->options['ajax'] = $other->options['ajax'];
106
        }
107
108
        return $this;
109
    }
110
111
    /**
112
     * @return static
113
     */
114
    final public function withHeader(string $header, string $value): self
115
    {
116
        $this->options['headers'][$header] = $value;
117
118
        return $this;
119
    }
120
121
    /**
122
     * @return static
123
     */
124
    final public function withHeaders(array $headers): self
125
    {
126
        $this->options['headers'] = $headers;
127
128
        return $this;
129
    }
130
131
    /**
132
     * @return static
133
     */
134
    final public function withQuery(array $query): self
135
    {
136
        $this->options['query'] = $query;
137
138
        return $this;
139
    }
140
141
    /**
142
     * @return static
143
     */
144
    final public function withServer(array $server): self
145
    {
146
        $this->options['server'] = $server;
147
148
        return $this;
149
    }
150
151
    /**
152
     * @return static
153
     */
154
    final public function withFiles(array $files): self
155
    {
156
        $this->options['files'] = $files;
157
158
        return $this;
159
    }
160
161
    /**
162
     * @return static
163
     */
164
    final public function withBody(?string $body): self
165
    {
166
        $this->options['body'] = $body;
167
168
        return $this;
169
    }
170
171
    /**
172
     * @param mixed $body Any value that can be json encoded
173
     *
174
     * @return static
175
     */
176
    final public function asJson($body = null): self
177
    {
178
        $this->options['json'] = $body ?? self::EMPTY_JSON_TRIGGER;
179
180
        return $this;
181
    }
182
183
    /**
184
     * @return static
185
     */
186
    final public function asAjax(): self
187
    {
188
        $this->options['ajax'] = true;
189
190
        return $this;
191
    }
192
193
    /**
194
     * @internal
195
     */
196
    final public function query(): array
197
    {
198
        return $this->options['query'];
199
    }
200
201
    /**
202
     * @internal
203
     */
204
    final public function files(): array
205
    {
206
        return $this->options['files'];
207
    }
208
209
    /**
210
     * @co-author Kévin Dunglas <[email protected]>
211
     *
212
     * @internal
213
     */
214
    final public function server(): array
215
    {
216
        $server = $this->options['server'];
217
        $headers = \array_combine(
218
            \array_map(
219
                static fn($header) => \mb_strtoupper(\str_replace('-', '_', $header)),
220
                \array_keys($this->options['headers'])
221
            ),
222
            $this->options['headers']
223
        );
224
225
        if (null !== $this->options['json'] && !\array_key_exists('ACCEPT', $headers)) {
226
            $headers['ACCEPT'] = 'application/json';
227
        }
228
229
        if (null !== $this->options['json'] && !\array_key_exists('CONTENT_TYPE', $headers)) {
230
            $headers['CONTENT_TYPE'] = 'application/json';
231
        }
232
233
        if (false !== $this->options['ajax'] && !\array_key_exists('X_REQUESTED_WITH', $headers)) {
234
            $headers['X_REQUESTED_WITH'] = 'XMLHttpRequest';
235
        }
236
237
        foreach ($headers as $header => $value) {
238
            // content type header cannot have HTTP_ prefix
239
            if ('CONTENT_TYPE' !== $header) {
240
                $header = "HTTP_{$header}";
241
            }
242
243
            $server[$header] = $value;
244
        }
245
246
        return $server;
247
    }
248
249
    /**
250
     * @internal
251
     */
252
    final public function body(): ?string
253
    {
254
        if (null === $this->options['json']) {
255
            return $this->options['body'];
256
        }
257
258
        if (self::EMPTY_JSON_TRIGGER === $this->options['json']) {
259
            return null;
260
        }
261
262
        return \json_encode($this->options['json'], \JSON_THROW_ON_ERROR);
263
    }
264
}
265