• 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

98.18
/src/Http/UrlImmutable.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_slice, explode, http_build_query, implode, ip2long, is_array, is_string, rawurlencode, str_starts_with, strrpos, substr;
14
use const PHP_QUERY_RFC3986;
15

16

17
/**
18
 * Immutable representation of a URL.
19
 *
20
 * <pre>
21
 * scheme  user  password  host  port      path        query    fragment
22
 *   |      |      |        |      |        |            |         |
23
 * /--\   /--\ /------\ /-------\ /--\/------------\ /--------\ /------\
24
 * http://john:x0y17575@nette.org:8042/en/manual.php?name=param#fragment  <-- absoluteUrl
25
 * \______\__________________________/
26
 *     |               |
27
 *  hostUrl        authority
28
 * </pre>
29
 *
30
 * @property-read string $scheme
31
 * @property-read string $user
32
 * @property-read string $password
33
 * @property-read string $host
34
 * @property-read int $port
35
 * @property-read string $path
36
 * @property-read string $query
37
 * @property-read string $fragment
38
 * @property-read string $absoluteUrl
39
 * @property-read string $authority
40
 * @property-read string $hostUrl
41
 * @property-read array $queryParameters
42
 */
43
class UrlImmutable implements \JsonSerializable
44
{
45
        use Nette\SmartObject;
46

47
        private string $scheme = '';
48
        private string $user = '';
49
        private string $password = '';
50
        private string $host = '';
51
        private ?int $port = null;
52
        private string $path = '';
53

54
        /** @var array<string, mixed> */
55
        private array $query = [];
56
        private string $fragment = '';
57
        private ?string $authority = null;
58

59

60
        /**
61
         * @throws Nette\InvalidArgumentException if URL is malformed
62
         */
63
        public function __construct(string|self|Url $url)
1✔
64
        {
65
                $url = is_string($url) ? new Url($url) : $url;
1✔
66
                [$this->scheme, $this->user, $this->password, $this->host, $this->port, $this->path, $this->query, $this->fragment] = $url->export();
1✔
67
        }
1✔
68

69

70
        public function withScheme(string $scheme): static
1✔
71
        {
72
                $dolly = clone $this;
1✔
73
                $dolly->scheme = $scheme;
1✔
74
                $dolly->authority = null;
1✔
75
                return $dolly;
1✔
76
        }
77

78

79
        public function getScheme(): string
80
        {
81
                return $this->scheme;
1✔
82
        }
83

84

85
        /** @deprecated */
86
        public function withUser(string $user): static
1✔
87
        {
88
                $dolly = clone $this;
1✔
89
                $dolly->user = $user;
1✔
90
                $dolly->authority = null;
1✔
91
                return $dolly;
1✔
92
        }
93

94

95
        /** @deprecated */
96
        public function getUser(): string
97
        {
98
                return $this->user;
1✔
99
        }
100

101

102
        /** @deprecated */
103
        public function withPassword(string $password): static
1✔
104
        {
105
                $dolly = clone $this;
1✔
106
                $dolly->password = $password;
1✔
107
                $dolly->authority = null;
1✔
108
                return $dolly;
1✔
109
        }
110

111

112
        /** @deprecated */
113
        public function getPassword(): string
114
        {
115
                return $this->password;
1✔
116
        }
117

118

119
        /** @deprecated */
120
        public function withoutUserInfo(): static
121
        {
122
                $dolly = clone $this;
1✔
123
                $dolly->user = $dolly->password = '';
1✔
124
                $dolly->authority = null;
1✔
125
                return $dolly;
1✔
126
        }
127

128

129
        public function withHost(string $host): static
1✔
130
        {
131
                $dolly = clone $this;
1✔
132
                $dolly->host = $host;
1✔
133
                $dolly->authority = null;
1✔
134
                return $dolly->setPath($dolly->path);
1✔
135
        }
136

137

138
        public function getHost(): string
139
        {
140
                return $this->host;
1✔
141
        }
142

143

144
        public function getDomain(int $level = 2): string
1✔
145
        {
146
                $parts = ip2long($this->host)
1✔
UNCOV
147
                        ? [$this->host]
×
148
                        : explode('.', $this->host);
1✔
149
                $parts = $level >= 0
1✔
150
                        ? array_slice($parts, -$level)
1✔
151
                        : array_slice($parts, 0, $level);
1✔
152
                return implode('.', $parts);
1✔
153
        }
154

155

156
        public function withPort(int $port): static
1✔
157
        {
158
                $dolly = clone $this;
1✔
159
                $dolly->port = $port;
1✔
160
                $dolly->authority = null;
1✔
161
                return $dolly;
1✔
162
        }
163

164

165
        public function getPort(): ?int
166
        {
167
                return $this->port ?: $this->getDefaultPort();
1✔
168
        }
169

170

171
        public function getDefaultPort(): ?int
172
        {
173
                return Url::$defaultPorts[$this->scheme] ?? null;
1✔
174
        }
175

176

177
        public function withPath(string $path): static
1✔
178
        {
179
                return (clone $this)->setPath($path);
1✔
180
        }
181

182

183
        private function setPath(string $path): static
1✔
184
        {
185
                $this->path = $this->host && !str_starts_with($path, '/') ? '/' . $path : $path;
1✔
186
                return $this;
1✔
187
        }
188

189

190
        public function getPath(): string
191
        {
192
                return $this->path;
1✔
193
        }
194

195

196
        public function withQuery(string|array $query): static
1✔
197
        {
198
                $dolly = clone $this;
1✔
199
                $dolly->query = is_array($query) ? $query : Url::parseQuery($query);
1✔
200
                return $dolly;
1✔
201
        }
202

203

204
        public function getQuery(): string
205
        {
206
                return http_build_query($this->query, '', '&', PHP_QUERY_RFC3986);
1✔
207
        }
208

209

210
        public function withQueryParameter(string $name, mixed $value): static
1✔
211
        {
212
                $dolly = clone $this;
1✔
213
                $dolly->query[$name] = $value;
1✔
214
                return $dolly;
1✔
215
        }
216

217

218
        /** @return array<string, mixed> */
219
        public function getQueryParameters(): array
220
        {
221
                return $this->query;
1✔
222
        }
223

224

225
        public function getQueryParameter(string $name): array|string|null
1✔
226
        {
227
                return $this->query[$name] ?? null;
1✔
228
        }
229

230

231
        public function withFragment(string $fragment): static
1✔
232
        {
233
                $dolly = clone $this;
1✔
234
                $dolly->fragment = $fragment;
1✔
235
                return $dolly;
1✔
236
        }
237

238

239
        public function getFragment(): string
240
        {
241
                return $this->fragment;
1✔
242
        }
243

244

245
        /**
246
         * Returns the entire URI including query string and fragment.
247
         */
248
        public function getAbsoluteUrl(): string
249
        {
250
                return $this->getHostUrl() . $this->path
1✔
251
                        . (($tmp = $this->getQuery()) ? '?' . $tmp : '')
1✔
252
                        . ($this->fragment === '' ? '' : '#' . $this->fragment);
1✔
253
        }
254

255

256
        /**
257
         * Returns the [user[:pass]@]host[:port] part of URI.
258
         */
259
        public function getAuthority(): string
260
        {
261
                return $this->authority ??= $this->host === ''
1✔
UNCOV
262
                        ? ''
×
263
                        : ($this->user !== ''
1✔
264
                                ? rawurlencode($this->user) . ($this->password === '' ? '' : ':' . rawurlencode($this->password)) . '@'
1✔
265
                                : '')
1✔
266
                        . $this->host
1✔
267
                        . ($this->port && $this->port !== $this->getDefaultPort()
1✔
268
                                ? ':' . $this->port
1✔
269
                                : '');
1✔
270
        }
271

272

273
        /**
274
         * Returns the scheme and authority part of URI.
275
         */
276
        public function getHostUrl(): string
277
        {
278
                return ($this->scheme === '' ? '' : $this->scheme . ':')
1✔
279
                        . ($this->host === '' ? '' : '//' . $this->getAuthority());
1✔
280
        }
281

282

283
        public function __toString(): string
284
        {
285
                return $this->getAbsoluteUrl();
1✔
286
        }
287

288

289
        public function isEqual(string|Url|self $url): bool
1✔
290
        {
291
                return (new Url($this))->isEqual($url);
1✔
292
        }
293

294

295
        /**
296
         * Resolves relative URLs in the same way as browser. If path is relative, it is resolved against
297
         * base URL, if begins with /, it is resolved against the host root.
298
         */
299
        public function resolve(string $reference): self
1✔
300
        {
301
                $ref = new self($reference);
1✔
302
                if ($ref->scheme !== '') {
1✔
303
                        $ref->path = Url::removeDotSegments($ref->path);
1✔
304
                        return $ref;
1✔
305
                }
306

307
                $ref->scheme = $this->scheme;
1✔
308

309
                if ($ref->host !== '') {
1✔
310
                        $ref->path = Url::removeDotSegments($ref->path);
1✔
311
                        return $ref;
1✔
312
                }
313

314
                $ref->host = $this->host;
1✔
315
                $ref->port = $this->port;
1✔
316

317
                if ($ref->path === '') {
1✔
318
                        $ref->path = $this->path;
1✔
319
                        $ref->query = $ref->query ?: $this->query;
1✔
320
                } elseif (str_starts_with($ref->path, '/')) {
1✔
321
                        $ref->path = Url::removeDotSegments($ref->path);
1✔
322
                } else {
323
                        $ref->path = Url::removeDotSegments($this->mergePath($ref->path));
1✔
324
                }
325
                return $ref;
1✔
326
        }
327

328

329
        /** @internal */
330
        protected function mergePath(string $path): string
1✔
331
        {
332
                $pos = strrpos($this->path, '/');
1✔
333
                return $pos === false ? $path : substr($this->path, 0, $pos + 1) . $path;
1✔
334
        }
335

336

337
        public function jsonSerialize(): string
338
        {
339
                return $this->getAbsoluteUrl();
1✔
340
        }
341

342

343
        /** @internal */
344
        final public function export(): array
345
        {
346
                return [$this->scheme, $this->user, $this->password, $this->host, $this->port, $this->path, $this->query, $this->fragment];
1✔
347
        }
348
}
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