• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nette / http / 8877511590

29 Apr 2024 11:05AM UTC coverage: 81.537% (-0.2%) from 81.776%
8877511590

push

github

dg
IRequest, IResponse: added typehints, unification (BC break)

2 of 2 new or added lines in 1 file covered. (100.0%)

53 existing lines in 5 files now uncovered.

870 of 1067 relevant lines covered (81.54%)

0.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.05
/src/Http/Request.php
1
<?php
2

3
/**
4
 * This file is part of the Nette Framework (https://nette.org)
5
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6
 */
7

8
declare(strict_types=1);
9

10
namespace Nette\Http;
11

12
use Nette;
13

14

15
/**
16
 * HttpRequest provides access scheme for request sent via HTTP.
17
 *
18
 * @property-read UrlScript $url
19
 * @property-read array $query
20
 * @property-read array $post
21
 * @property-read array $files
22
 * @property-read array $cookies
23
 * @property-read string $method
24
 * @property-read array $headers
25
 * @property-read UrlImmutable|null $referer
26
 * @property-read bool $secured
27
 * @property-read bool $ajax
28
 * @property-read string|null $remoteAddress
29
 * @property-read string|null $remoteHost
30
 * @property-read string|null $rawBody
31
 */
32
class Request implements IRequest
33
{
34
        use Nette\SmartObject;
35

36
        private readonly array $headers;
37

38
        private readonly ?\Closure $rawBodyCallback;
39

40

41
        public function __construct(
1✔
42
                private UrlScript $url,
43
                private readonly array $post = [],
44
                private readonly array $files = [],
45
                private readonly array $cookies = [],
46
                array $headers = [],
47
                private readonly string $method = 'GET',
48
                private readonly ?string $remoteAddress = null,
49
                private ?string $remoteHost = null,
50
                ?callable $rawBodyCallback = null,
51
        ) {
52
                $this->headers = array_change_key_case($headers, CASE_LOWER);
1✔
53
                $this->rawBodyCallback = $rawBodyCallback
1✔
54
                        ? \Closure::fromCallable($rawBodyCallback)
1✔
55
                        : null;
1✔
56
        }
1✔
57

58

59
        /**
60
         * Returns a clone with a different URL.
61
         */
62
        public function withUrl(UrlScript $url): static
1✔
63
        {
64
                $dolly = clone $this;
1✔
65
                $dolly->url = $url;
1✔
66
                return $dolly;
1✔
67
        }
68

69

70
        /**
71
         * Returns the URL of the request.
72
         */
73
        public function getUrl(): UrlScript
74
        {
75
                return $this->url;
1✔
76
        }
77

78

79
        /********************* query, post, files & cookies ****************d*g**/
80

81

82
        /**
83
         * Returns variable provided to the script via URL query ($_GET).
84
         * If no key is passed, returns the entire array.
85
         */
86
        public function getQuery(?string $key = null): mixed
1✔
87
        {
88
                if (func_num_args() === 0) {
1✔
89
                        return $this->url->getQueryParameters();
1✔
90
                }
91

92
                return $this->url->getQueryParameter($key);
1✔
93
        }
94

95

96
        /**
97
         * Returns variable provided to the script via POST method ($_POST).
98
         * If no key is passed, returns the entire array.
99
         */
100
        public function getPost(?string $key = null): mixed
1✔
101
        {
102
                if (func_num_args() === 0) {
1✔
103
                        return $this->post;
1✔
104
                }
105

106
                return $this->post[$key] ?? null;
1✔
107
        }
108

109

110
        /**
111
         * Returns uploaded file.
112
         * @param  string|string[]  $key
113
         */
114
        public function getFile($key): ?FileUpload
115
        {
116
                $res = Nette\Utils\Arrays::get($this->files, $key, null);
1✔
117
                return $res instanceof FileUpload ? $res : null;
1✔
118
        }
119

120

121
        /**
122
         * Returns tree of upload files in a normalized structure, with each leaf an instance of Nette\Http\FileUpload.
123
         */
124
        public function getFiles(): array
125
        {
126
                return $this->files;
1✔
127
        }
128

129

130
        /**
131
         * Returns a cookie or `null` if it does not exist.
132
         */
133
        public function getCookie(string $key): mixed
1✔
134
        {
135
                return $this->cookies[$key] ?? null;
1✔
136
        }
137

138

139
        /**
140
         * Returns all cookies.
141
         */
142
        public function getCookies(): array
143
        {
144
                return $this->cookies;
1✔
145
        }
146

147

148
        /********************* method & headers ****************d*g**/
149

150

151
        /**
152
         * Returns the HTTP method with which the request was made (GET, POST, HEAD, PUT, ...).
153
         */
154
        public function getMethod(): string
155
        {
156
                return $this->method;
1✔
157
        }
158

159

160
        /**
161
         * Checks the HTTP method with which the request was made. The parameter is case-insensitive.
162
         */
163
        public function isMethod(string $method): bool
164
        {
UNCOV
165
                return strcasecmp($this->method, $method) === 0;
×
166
        }
167

168

169
        /**
170
         * Returns an HTTP header or `null` if it does not exist. The parameter is case-insensitive.
171
         */
172
        public function getHeader(string $header): ?string
1✔
173
        {
174
                $header = strtolower($header);
1✔
175
                return $this->headers[$header] ?? null;
1✔
176
        }
177

178

179
        /**
180
         * Returns all HTTP headers as associative array.
181
         */
182
        public function getHeaders(): array
183
        {
184
                return $this->headers;
1✔
185
        }
186

187

188
        /**
189
         * What URL did the user come from? Beware, it is not reliable at all.
190
         * @deprecated  deprecated in favor of the getOrigin()
191
         */
192
        public function getReferer(): ?UrlImmutable
193
        {
194
                trigger_error(__METHOD__ . '() is deprecated', E_USER_DEPRECATED);
×
195
                return isset($this->headers['referer'])
×
196
                        ? new UrlImmutable($this->headers['referer'])
×
UNCOV
197
                        : null;
×
198
        }
199

200

201
        /**
202
         * What origin did the user come from? It contains scheme, hostname and port.
203
         */
204
        public function getOrigin(): ?UrlImmutable
205
        {
206
                $header = $this->headers['origin'] ?? 'null';
1✔
207
                try {
208
                        return $header === 'null'
1✔
209
                                ? null
1✔
210
                                : new UrlImmutable($header);
1✔
211
                } catch (Nette\InvalidArgumentException $e) {
×
UNCOV
212
                        return null;
×
213
                }
214
        }
215

216

217
        /**
218
         * Is the request sent via secure channel (https)?
219
         */
220
        public function isSecured(): bool
221
        {
222
                return $this->url->getScheme() === 'https';
1✔
223
        }
224

225

226
        /**
227
         * Is the request coming from the same site and is initiated by clicking on a link?
228
         */
229
        public function isSameSite(): bool
230
        {
UNCOV
231
                return isset($this->cookies[Helpers::StrictCookieName]);
×
232
        }
233

234

235
        /**
236
         * Is it an AJAX request?
237
         */
238
        public function isAjax(): bool
239
        {
UNCOV
240
                return $this->getHeader('X-Requested-With') === 'XMLHttpRequest';
×
241
        }
242

243

244
        /**
245
         * Returns the IP address of the remote client.
246
         */
247
        public function getRemoteAddress(): ?string
248
        {
249
                return $this->remoteAddress;
1✔
250
        }
251

252

253
        /**
254
         * Returns the host of the remote client.
255
         */
256
        public function getRemoteHost(): ?string
257
        {
258
                return $this->remoteHost;
1✔
259
        }
260

261

262
        /**
263
         * Returns raw content of HTTP request body.
264
         */
265
        public function getRawBody(): ?string
266
        {
267
                return $this->rawBodyCallback ? ($this->rawBodyCallback)() : null;
1✔
268
        }
269

270

271
        /**
272
         * Returns decoded content of HTTP request body.
273
         */
274
        public function getDecodedBody(): mixed
275
        {
276
                $type = $this->getHeader('Content-Type');
×
277
                return match ($type) {
×
278
                        'application/json' => json_decode($this->getRawBody()),
×
279
                        'application/x-www-form-urlencoded' => $_POST,
×
UNCOV
280
                        default => throw new \Exception("Unsupported content type: $type"),
×
281
                };
282
        }
283

284

285
        /**
286
         * Returns basic HTTP authentication credentials.
287
         * @return array{string, string}|null
288
         */
289
        public function getBasicCredentials(): ?array
290
        {
291
                return preg_match(
1✔
292
                        '~^Basic (\S+)$~',
1✔
293
                        $this->headers['authorization'] ?? '',
1✔
294
                        $t,
295
                )
296
                        && ($t = base64_decode($t[1], strict: true))
1✔
297
                        && ($t = explode(':', $t, 2))
1✔
298
                        && (count($t) === 2)
1✔
299
                        ? $t
1✔
300
                        : null;
1✔
301
        }
302

303

304
        /**
305
         * Returns the most preferred language by browser. Uses the `Accept-Language` header. If no match is reached, it returns `null`.
306
         * @param  string[]  $langs  supported languages
307
         */
308
        public function detectLanguage(array $langs): ?string
1✔
309
        {
310
                $header = $this->getHeader('Accept-Language');
1✔
311
                if (!$header) {
1✔
312
                        return null;
1✔
313
                }
314

315
                $s = strtolower($header);  // case insensitive
1✔
316
                $s = strtr($s, '_', '-');  // cs_CZ means cs-CZ
1✔
317
                rsort($langs);             // first more specific
1✔
318
                preg_match_all('#(' . implode('|', $langs) . ')(?:-[^\s,;=]+)?\s*(?:;\s*q=([0-9.]+))?#', $s, $matches);
1✔
319

320
                if (!$matches[0]) {
1✔
321
                        return null;
1✔
322
                }
323

324
                $max = 0;
1✔
325
                $lang = null;
1✔
326
                foreach ($matches[1] as $key => $value) {
1✔
327
                        $q = $matches[2][$key] === '' ? 1.0 : (float) $matches[2][$key];
1✔
328
                        if ($q > $max) {
1✔
329
                                $max = $q;
1✔
330
                                $lang = $value;
1✔
331
                        }
332
                }
333

334
                return $lang;
1✔
335
        }
336
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc