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

nette / http / 21830508055

09 Feb 2026 03:10PM UTC coverage: 83.772% (+0.03%) from 83.744%
21830508055

push

github

dg
added RequestFactory::setForceHttps()

8 of 9 new or added lines in 2 files covered. (88.89%)

75 existing lines in 9 files now uncovered.

924 of 1103 relevant lines covered (83.77%)

0.84 hits per line

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

92.68
/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
use function array_change_key_case, base64_decode, count, explode, func_num_args, gethostbyaddr, implode, in_array, preg_match, preg_match_all, rsort, strcasecmp, strtr;
14
use const CASE_LOWER;
15

16

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

38
        /** @var array<string, string> */
39
        private readonly array $headers;
40
        private readonly ?\Closure $rawBodyCallback;
41

42

43
        public function __construct(
1✔
44
                private UrlScript $url,
45
                /** @var array<string, mixed> */
46
                private readonly array $post = [],
47
                /** @var array<string, FileUpload|array<string, FileUpload|array>> */
48
                private readonly array $files = [],
49
                /** @var array<string, string> */
50
                private readonly array $cookies = [],
51
                /** @var array<string, string> */
52
                array $headers = [],
53
                private readonly string $method = 'GET',
54
                private readonly ?string $remoteAddress = null,
55
                private ?string $remoteHost = null,
56
                /** @var ?(\Closure(): string) */
57
                ?callable $rawBodyCallback = null,
58
        ) {
59
                $this->headers = array_change_key_case($headers, CASE_LOWER);
1✔
60
                $this->rawBodyCallback = $rawBodyCallback ? $rawBodyCallback(...) : null;
1✔
61
        }
1✔
62

63

64
        /**
65
         * Returns a clone with a different URL.
66
         */
67
        public function withUrl(UrlScript $url): static
1✔
68
        {
69
                $dolly = clone $this;
1✔
70
                $dolly->url = $url;
1✔
71
                return $dolly;
1✔
72
        }
73

74

75
        /**
76
         * Returns the URL of the request.
77
         */
78
        public function getUrl(): UrlScript
79
        {
80
                return $this->url;
1✔
81
        }
82

83

84
        /********************* query, post, files & cookies ****************d*g**/
85

86

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

97
                return $this->url->getQueryParameter($key);
1✔
98
        }
99

100

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

111
                return $this->post[$key] ?? null;
1✔
112
        }
113

114

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

125

126
        /**
127
         * Returns tree of upload files in a normalized structure, with each leaf an instance of Nette\Http\FileUpload.
128
         * @return array<string, FileUpload|array>
129
         */
130
        public function getFiles(): array
131
        {
132
                return $this->files;
1✔
133
        }
134

135

136
        /**
137
         * Returns a cookie or `null` if it does not exist.
138
         */
139
        public function getCookie(string $key): mixed
1✔
140
        {
141
                return $this->cookies[$key] ?? null;
1✔
142
        }
143

144

145
        /**
146
         * Returns all cookies.
147
         * @return array<string, string>
148
         */
149
        public function getCookies(): array
150
        {
151
                return $this->cookies;
1✔
152
        }
153

154

155
        /********************* method & headers ****************d*g**/
156

157

158
        /**
159
         * Returns the HTTP method with which the request was made (GET, POST, HEAD, PUT, ...).
160
         */
161
        public function getMethod(): string
162
        {
163
                return $this->method;
1✔
164
        }
165

166

167
        /**
168
         * Checks the HTTP method with which the request was made. The parameter is case-insensitive.
169
         */
170
        public function isMethod(string $method): bool
171
        {
UNCOV
172
                return strcasecmp($this->method, $method) === 0;
×
173
        }
174

175

176
        /**
177
         * Returns an HTTP header or `null` if it does not exist. The parameter is case-insensitive.
178
         */
179
        public function getHeader(string $header): ?string
1✔
180
        {
181
                $header = strtolower($header);
1✔
182
                return $this->headers[$header] ?? null;
1✔
183
        }
184

185

186
        /**
187
         * Returns all HTTP headers as associative array.
188
         * @return array<string, string>
189
         */
190
        public function getHeaders(): array
191
        {
192
                return $this->headers;
1✔
193
        }
194

195

196
        /**
197
         * What URL did the user come from? Beware, it is not reliable at all.
198
         * @deprecated  deprecated in favor of the getOrigin()
199
         */
200
        public function getReferer(): ?UrlImmutable
201
        {
UNCOV
202
                return isset($this->headers['referer'])
×
UNCOV
203
                        ? new UrlImmutable($this->headers['referer'])
×
UNCOV
204
                        : null;
×
205
        }
206

207

208
        /**
209
         * What origin did the user come from? It contains scheme, hostname and port.
210
         */
211
        public function getOrigin(): ?UrlImmutable
212
        {
213
                $header = $this->headers['origin'] ?? '';
1✔
214
                if (!preg_match('~^[a-z][a-z0-9+.-]*://[^/]+$~i', $header)) {
1✔
215
                        return null;
1✔
216
                }
217
                return new UrlImmutable($header);
1✔
218
        }
219

220

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

229

230
        /** @deprecated use isFrom(['same-site', 'same-origin']) */
231
        public function isSameSite(): bool
232
        {
UNCOV
233
                return isset($this->cookies[Helpers::StrictCookieName]);
×
234
        }
235

236

237
        /**
238
         * Checks whether Sec-Fetch headers match the expected values.
239
         * @param string|list<string>|null  $site
240
         * @param string|list<string>|null  $initiator
241
         */
242
        public function isFrom(string|array|null $site = null, string|array|null $initiator = null): bool
1✔
243
        {
244
                $actualSite = $this->headers['sec-fetch-site'] ?? null;
1✔
245
                $actualDest = $this->headers['sec-fetch-dest'] ?? null;
1✔
246

247
                if ($actualSite === null && ($origin = $this->getOrigin())) { // fallback for Safari < 16.4
1✔
248
                        $actualSite = strcasecmp($origin->getScheme(), $this->url->getScheme()) === 0
1✔
249
                                        && strcasecmp(rtrim($origin->getHost(), '.'), rtrim($this->url->getHost(), '.')) === 0
1✔
250
                                        && $origin->getPort() === $this->url->getPort()
1✔
251
                                ? 'same-origin'
1✔
252
                                : 'cross-site';
1✔
253
                }
254

255
                return ($site === null || ($actualSite !== null && in_array($actualSite, (array) $site, strict: true)))
1✔
256
                        && ($initiator === null || ($actualDest !== null && in_array($actualDest, (array) $initiator, strict: true)));
1✔
257
        }
258

259

260
        /**
261
         * Is it an AJAX request?
262
         */
263
        public function isAjax(): bool
264
        {
UNCOV
265
                return $this->getHeader('X-Requested-With') === 'XMLHttpRequest';
×
266
        }
267

268

269
        /**
270
         * Returns the IP address of the remote client.
271
         */
272
        public function getRemoteAddress(): ?string
273
        {
274
                return $this->remoteAddress;
1✔
275
        }
276

277

278
        /**
279
         * Returns the host of the remote client.
280
         */
281
        public function getRemoteHost(): ?string
282
        {
283
                if ($this->remoteHost === null && $this->remoteAddress !== null) {
1✔
284
                        $this->remoteHost = gethostbyaddr($this->remoteAddress);
1✔
285
                }
286

287
                return $this->remoteHost;
1✔
288
        }
289

290

291
        /**
292
         * Returns raw content of HTTP request body.
293
         */
294
        public function getRawBody(): ?string
295
        {
296
                return $this->rawBodyCallback ? ($this->rawBodyCallback)() : null;
1✔
297
        }
298

299

300
        /**
301
         * Returns basic HTTP authentication credentials.
302
         * @return array{string, string}|null
303
         */
304
        public function getBasicCredentials(): ?array
305
        {
306
                return preg_match(
1✔
307
                        '~^Basic (\S+)$~',
1✔
308
                        $this->headers['authorization'] ?? '',
1✔
309
                        $t,
1✔
310
                )
311
                        && ($t = base64_decode($t[1], strict: true))
1✔
312
                        && ($t = explode(':', $t, 2))
1✔
313
                        && (count($t) === 2)
1✔
314
                        ? $t
1✔
315
                        : null;
1✔
316
        }
317

318

319
        /**
320
         * Returns the most preferred language by browser. Uses the `Accept-Language` header. If no match is reached, it returns `null`.
321
         * @param list<string>  $langs supported languages
322
         */
323
        public function detectLanguage(array $langs): ?string
1✔
324
        {
325
                $header = $this->getHeader('Accept-Language');
1✔
326
                if (!$header) {
1✔
327
                        return null;
1✔
328
                }
329

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

335
                if (!$matches[0]) {
1✔
336
                        return null;
1✔
337
                }
338

339
                $max = 0;
1✔
340
                $lang = null;
1✔
341
                foreach ($matches[1] as $key => $value) {
1✔
342
                        $q = $matches[2][$key] === '' ? 1.0 : (float) $matches[2][$key];
1✔
343
                        if ($q > $max) {
1✔
344
                                $max = $q;
1✔
345
                                $lang = $value;
1✔
346
                        }
347
                }
348

349
                return $lang;
1✔
350
        }
351
}
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