ci4

Форк
0
/
SiteURI.php 
437 строк · 11.7 Кб
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of CodeIgniter 4 framework.
7
 *
8
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace CodeIgniter\HTTP;
15

16
use BadMethodCallException;
17
use CodeIgniter\Exceptions\ConfigException;
18
use CodeIgniter\HTTP\Exceptions\HTTPException;
19
use Config\App;
20

21
/**
22
 * URI for the application site
23
 *
24
 * @see \CodeIgniter\HTTP\SiteURITest
25
 */
26
class SiteURI extends URI
27
{
28
    /**
29
     * The current baseURL.
30
     */
31
    private readonly URI $baseURL;
32

33
    /**
34
     * The path part of baseURL.
35
     *
36
     * The baseURL "http://example.com/" → '/'
37
     * The baseURL "http://localhost:8888/ci431/public/" → '/ci431/public/'
38
     */
39
    private string $basePathWithoutIndexPage;
40

41
    /**
42
     * The Index File.
43
     */
44
    private string $indexPage;
45

46
    /**
47
     * List of URI segments in baseURL and indexPage.
48
     *
49
     * If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
50
     * and the baseURL is "http://localhost:8888/ci431/public/", then:
51
     *   $baseSegments = [
52
     *       0 => 'ci431',
53
     *       1 => 'public',
54
     *       2 => 'index.php',
55
     *   ];
56
     */
57
    private array $baseSegments;
58

59
    /**
60
     * List of URI segments after indexPage.
61
     *
62
     * The word "URI Segments" originally means only the URI path part relative
63
     * to the baseURL.
64
     *
65
     * If the URI is "http://localhost:8888/ci431/public/index.php/test?a=b",
66
     * and the baseURL is "http://localhost:8888/ci431/public/", then:
67
     *   $segments = [
68
     *       0 => 'test',
69
     *   ];
70
     *
71
     * @var array
72
     *
73
     * @deprecated This property will be private.
74
     */
75
    protected $segments;
76

77
    /**
78
     * URI path relative to baseURL.
79
     *
80
     * If the baseURL contains sub folders, this value will be different from
81
     * the current URI path.
82
     *
83
     * This value never starts with '/'.
84
     */
85
    private string $routePath;
86

87
    /**
88
     * @param         string              $relativePath URI path relative to baseURL. May include
89
     *                                                  queries or fragments.
90
     * @param         string|null         $host         Optional current hostname.
91
     * @param         string|null         $scheme       Optional scheme. 'http' or 'https'.
92
     * @phpstan-param 'http'|'https'|null $scheme
93
     */
94
    public function __construct(
95
        App $configApp,
96
        string $relativePath = '',
97
        ?string $host = null,
98
        ?string $scheme = null
99
    ) {
100
        $this->indexPage = $configApp->indexPage;
101

102
        $this->baseURL = $this->determineBaseURL($configApp, $host, $scheme);
103

104
        $this->setBasePath();
105

106
        // Fix routePath, query, fragment
107
        [$routePath, $query, $fragment] = $this->parseRelativePath($relativePath);
108

109
        // Fix indexPage and routePath
110
        $indexPageRoutePath = $this->getIndexPageRoutePath($routePath);
111

112
        // Fix the current URI
113
        $uri = $this->baseURL . $indexPageRoutePath;
114

115
        // applyParts
116
        $parts = parse_url($uri);
117
        if ($parts === false) {
118
            throw HTTPException::forUnableToParseURI($uri);
119
        }
120
        $parts['query']    = $query;
121
        $parts['fragment'] = $fragment;
122
        $this->applyParts($parts);
123

124
        $this->setRoutePath($routePath);
125
    }
126

127
    private function parseRelativePath(string $relativePath): array
128
    {
129
        $parts = parse_url('http://dummy/' . $relativePath);
130
        if ($parts === false) {
131
            throw HTTPException::forUnableToParseURI($relativePath);
132
        }
133

134
        $routePath = $relativePath === '/' ? '/' : ltrim($parts['path'], '/');
135

136
        $query    = $parts['query'] ?? '';
137
        $fragment = $parts['fragment'] ?? '';
138

139
        return [$routePath, $query, $fragment];
140
    }
141

142
    private function determineBaseURL(
143
        App $configApp,
144
        ?string $host,
145
        ?string $scheme
146
    ): URI {
147
        $baseURL = $this->normalizeBaseURL($configApp);
148

149
        $uri = new URI($baseURL);
150

151
        // Update scheme
152
        if ($scheme !== null && $scheme !== '') {
153
            $uri->setScheme($scheme);
154
        } elseif ($configApp->forceGlobalSecureRequests) {
155
            $uri->setScheme('https');
156
        }
157

158
        // Update host
159
        if ($host !== null) {
160
            $uri->setHost($host);
161
        }
162

163
        return $uri;
164
    }
165

166
    private function getIndexPageRoutePath(string $routePath): string
167
    {
168
        // Remove starting slash unless it is `/`.
169
        if ($routePath !== '' && $routePath[0] === '/' && $routePath !== '/') {
170
            $routePath = ltrim($routePath, '/');
171
        }
172

173
        // Check for an index page
174
        $indexPage = '';
175
        if ($this->indexPage !== '') {
176
            $indexPage = $this->indexPage;
177

178
            // Check if we need a separator
179
            if ($routePath !== '' && $routePath[0] !== '/' && $routePath[0] !== '?') {
180
                $indexPage .= '/';
181
            }
182
        }
183

184
        $indexPageRoutePath = $indexPage . $routePath;
185

186
        if ($indexPageRoutePath === '/') {
187
            $indexPageRoutePath = '';
188
        }
189

190
        return $indexPageRoutePath;
191
    }
192

193
    private function normalizeBaseURL(App $configApp): string
194
    {
195
        // It's possible the user forgot a trailing slash on their
196
        // baseURL, so let's help them out.
197
        $baseURL = rtrim($configApp->baseURL, '/ ') . '/';
198

199
        // Validate baseURL
200
        if (filter_var($baseURL, FILTER_VALIDATE_URL) === false) {
201
            throw new ConfigException(
202
                'Config\App::$baseURL "' . $baseURL . '" is not a valid URL.'
203
            );
204
        }
205

206
        return $baseURL;
207
    }
208

209
    /**
210
     * Sets basePathWithoutIndexPage and baseSegments.
211
     */
212
    private function setBasePath(): void
213
    {
214
        $this->basePathWithoutIndexPage = $this->baseURL->getPath();
215

216
        $this->baseSegments = $this->convertToSegments($this->basePathWithoutIndexPage);
217

218
        if ($this->indexPage !== '') {
219
            $this->baseSegments[] = $this->indexPage;
220
        }
221
    }
222

223
    /**
224
     * @deprecated
225
     */
226
    public function setBaseURL(string $baseURL): void
227
    {
228
        throw new BadMethodCallException('Cannot use this method.');
229
    }
230

231
    /**
232
     * @deprecated
233
     */
234
    public function setURI(?string $uri = null)
235
    {
236
        throw new BadMethodCallException('Cannot use this method.');
237
    }
238

239
    /**
240
     * Returns the baseURL.
241
     *
242
     * @interal
243
     */
244
    public function getBaseURL(): string
245
    {
246
        return (string) $this->baseURL;
247
    }
248

249
    /**
250
     * Returns the URI path relative to baseURL.
251
     *
252
     * @return string The Route path.
253
     */
254
    public function getRoutePath(): string
255
    {
256
        return $this->routePath;
257
    }
258

259
    /**
260
     * Formats the URI as a string.
261
     */
262
    public function __toString(): string
263
    {
264
        return static::createURIString(
265
            $this->getScheme(),
266
            $this->getAuthority(),
267
            $this->getPath(),
268
            $this->getQuery(),
269
            $this->getFragment()
270
        );
271
    }
272

273
    /**
274
     * Sets the route path (and segments).
275
     *
276
     * @return $this
277
     */
278
    public function setPath(string $path)
279
    {
280
        $this->setRoutePath($path);
281

282
        return $this;
283
    }
284

285
    /**
286
     * Sets the route path (and segments).
287
     */
288
    private function setRoutePath(string $routePath): void
289
    {
290
        $routePath = $this->filterPath($routePath);
291

292
        $indexPageRoutePath = $this->getIndexPageRoutePath($routePath);
293

294
        $this->path = $this->basePathWithoutIndexPage . $indexPageRoutePath;
295

296
        $this->routePath = ltrim($routePath, '/');
297

298
        $this->segments = $this->convertToSegments($this->routePath);
299
    }
300

301
    /**
302
     * Converts path to segments
303
     */
304
    private function convertToSegments(string $path): array
305
    {
306
        $tempPath = trim($path, '/');
307

308
        return ($tempPath === '') ? [] : explode('/', $tempPath);
309
    }
310

311
    /**
312
     * Sets the path portion of the URI based on segments.
313
     *
314
     * @return $this
315
     *
316
     * @deprecated This method will be private.
317
     */
318
    public function refreshPath()
319
    {
320
        $allSegments = array_merge($this->baseSegments, $this->segments);
321
        $this->path  = '/' . $this->filterPath(implode('/', $allSegments));
322

323
        if ($this->routePath === '/' && $this->path !== '/') {
324
            $this->path .= '/';
325
        }
326

327
        $this->routePath = $this->filterPath(implode('/', $this->segments));
328

329
        return $this;
330
    }
331

332
    /**
333
     * Saves our parts from a parse_url() call.
334
     */
335
    protected function applyParts(array $parts): void
336
    {
337
        if (! empty($parts['host'])) {
338
            $this->host = $parts['host'];
339
        }
340
        if (! empty($parts['user'])) {
341
            $this->user = $parts['user'];
342
        }
343
        if (isset($parts['path']) && $parts['path'] !== '') {
344
            $this->path = $this->filterPath($parts['path']);
345
        }
346
        if (! empty($parts['query'])) {
347
            $this->setQuery($parts['query']);
348
        }
349
        if (! empty($parts['fragment'])) {
350
            $this->fragment = $parts['fragment'];
351
        }
352

353
        // Scheme
354
        if (isset($parts['scheme'])) {
355
            $this->setScheme(rtrim($parts['scheme'], ':/'));
356
        } else {
357
            $this->setScheme('http');
358
        }
359

360
        // Port
361
        if (isset($parts['port']) && $parts['port'] !== null) {
362
            // Valid port numbers are enforced by earlier parse_url() or setPort()
363
            $this->port = $parts['port'];
364
        }
365

366
        if (isset($parts['pass'])) {
367
            $this->password = $parts['pass'];
368
        }
369
    }
370

371
    /**
372
     * For base_url() helper.
373
     *
374
     * @param array|string $relativePath URI string or array of URI segments.
375
     * @param string|null  $scheme       URI scheme. E.g., http, ftp. If empty
376
     *                                   string '' is set, a protocol-relative
377
     *                                   link is returned.
378
     */
379
    public function baseUrl($relativePath = '', ?string $scheme = null): string
380
    {
381
        $relativePath = $this->stringifyRelativePath($relativePath);
382

383
        $config            = clone config(App::class);
384
        $config->indexPage = '';
385

386
        $host = $this->getHost();
387

388
        $uri = new self($config, $relativePath, $host, $scheme);
389

390
        // Support protocol-relative links
391
        if ($scheme === '') {
392
            return substr((string) $uri, strlen($uri->getScheme()) + 1);
393
        }
394

395
        return (string) $uri;
396
    }
397

398
    /**
399
     * @param array|string $relativePath URI string or array of URI segments
400
     */
401
    private function stringifyRelativePath($relativePath): string
402
    {
403
        if (is_array($relativePath)) {
404
            $relativePath = implode('/', $relativePath);
405
        }
406

407
        return $relativePath;
408
    }
409

410
    /**
411
     * For site_url() helper.
412
     *
413
     * @param array|string $relativePath URI string or array of URI segments.
414
     * @param string|null  $scheme       URI scheme. E.g., http, ftp. If empty
415
     *                                   string '' is set, a protocol-relative
416
     *                                   link is returned.
417
     * @param App|null     $config       Alternate configuration to use.
418
     */
419
    public function siteUrl($relativePath = '', ?string $scheme = null, ?App $config = null): string
420
    {
421
        $relativePath = $this->stringifyRelativePath($relativePath);
422

423
        // Check current host.
424
        $host = $config === null ? $this->getHost() : null;
425

426
        $config ??= config(App::class);
427

428
        $uri = new self($config, $relativePath, $host, $scheme);
429

430
        // Support protocol-relative links
431
        if ($scheme === '') {
432
            return substr((string) $uri, strlen($uri->getScheme()) + 1);
433
        }
434

435
        return (string) $uri;
436
    }
437
}
438

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.