Passed
Push — main ( 28baaa...23dfd3 )
by Dimitri
04:57
created

Uri::changeSchemeAndPath()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 22
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 6.105

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 6
eloc 10
c 1
b 1
f 0
nc 5
nop 2
dl 0
loc 22
ccs 6
cts 7
cp 0.8571
crap 6.105
rs 9.2222
1
<?php
2
3
/**
4
 * This file is part of Blitz PHP framework.
5
 *
6
 * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
namespace BlitzPHP\Http;
13
14
use BlitzPHP\Exceptions\HttpException;
15
use InvalidArgumentException;
16
use Psr\Http\Message\UriInterface;
17
18
/**
19
 * Abstraction pour un identificateur de ressource uniforme (URI).
20
 *
21
 * @credit CodeIgniter 4 <a href="https://codeigniter.com">CodeIgniter\HTTP\URI</a>
22
 */
23
class Uri implements UriInterface
24
{
25
    /**
26
     * Sous-délimiteurs utilisés dans les chaînes de requête et les fragments.
27
     */
28
    public const CHAR_SUB_DELIMS = '!\$&\'\(\)\*\+,;=';
29
30
    /**
31
     * Caractères non réservés utilisés dans les chemins, les chaînes de requête et les fragments.
32
     */
33
    public const CHAR_UNRESERVED = 'a-zA-Z0-9_\-\.~';
34
35
    /**
36
     * Chaîne d'URI actuelle
37
     *
38
     * @var string
39
     */
40
    protected $uriString;
41
42
    /**
43
     * Liste des segments d'URI.
44
     *
45
     * Commence à 1 au lieu de 0
46
     */
47
    protected array $segments = [];
48
49
    /**
50
     * Schéma
51
     */
52
    protected string $scheme = 'http';
53
54
    /**
55
     * Informations utilisateur
56
     */
57
    protected ?string $user = null;
58
59
    /**
60
     * Mot de passe
61
     */
62
    protected ?string $password = null;
63
64
    /**
65
     * Hôte
66
     */
67
    protected ?string $host = null;
68
69
    /**
70
     * Port
71
     */
72
    protected ?int $port = null;
73
74
    /**
75
     * Chemin.
76
     */
77
    protected ?string $path = null;
78
79
    /**
80
     * Le nom de n'importe quel fragment.
81
     */
82
    protected string $fragment = '';
83
84
    /**
85
     * La chaîne de requête.
86
     */
87
    protected array $query = [];
88
89
    /**
90
     * Default schemes/ports.
91
     */
92
    protected array $defaultPorts = [
93
        'http'  => 80,
94
        'https' => 443,
95
        'ftp'   => 21,
96
        'sftp'  => 22,
97
    ];
98
99
    /**
100
     * Indique si les mots de passe doivent être affichés dans les appels userInfo/authority.
101
     * La valeur par défaut est false car les URI apparaissent souvent dans les journaux
102
     */
103
    protected bool $showPassword = false;
104
105
    /**
106
     * Constructeur.
107
     *
108
     * @throws InvalidArgumentException
109
     */
110
    public function __construct(?string $uri = null)
111
    {
112 12
        $this->setURI($uri);
113
    }
114
115
    /**
116
     * Définit et écrase toute information URI actuelle.
117
     */
118
    public function setURI(?string $uri = null): self
119
    {
120
        if (null !== $uri) {
121 12
            $parts = parse_url($uri);
122
123
            if ($parts === false) {
124 4
                throw HttpException::unableToParseURI($uri);
125
            }
126
127 12
            $this->applyParts($parts);
128
        }
129
130 12
        return $this;
131
    }
132
133
    /**
134
     * {@inheritDoc}
135
     */
136
    public function getScheme(): string
137
    {
138 12
        return $this->scheme;
139
    }
140
141
    /**
142
     * {@inheritDoc}
143
     */
144
    public function getAuthority(bool $ignorePort = false): string
145
    {
146
        if (empty($this->host)) {
147 2
            return '';
148
        }
149
150 12
        $authority = $this->host;
151
152
        if (! empty($this->getUserInfo())) {
153 4
            $authority = $this->getUserInfo() . '@' . $authority;
154
        }
155
156
        // N'ajoute pas de port s'il s'agit d'un port standard pour ce schéma
157
        if (! empty($this->port) && ! $ignorePort && $this->port !== $this->defaultPorts[$this->scheme]) {
158 4
            $authority .= ':' . $this->port;
159
        }
160
161 12
        $this->showPassword = false;
162
163 12
        return $authority;
164
    }
165
166
    /**
167
     * {@inheritDoc}
168
     */
169
    public function getUserInfo(): string
170
    {
171 12
        $userInfo = $this->user ?: '';
172
173
        if ($this->showPassword === true && ! empty($this->password)) {
174 2
            $userInfo .= ':' . $this->password;
175
        }
176
177 12
        return $userInfo;
178
    }
179
180
    /**
181
     * Définit temporairement l'URI pour afficher un mot de passe dans userInfo.
182
     * Se réinitialisera après le premier appel à l'autorité().
183
     */
184
    public function showPassword(bool $val = true): self
185
    {
186 2
        $this->showPassword = $val;
187
188 2
        return $this;
189
    }
190
191
    /**
192
     * {@inheritDoc}
193
     */
194
    public function getHost(): string
195
    {
196 4
        return $this->host ?? '';
197
    }
198
199
    /**
200
     * {@inheritDoc}
201
     */
202
    public function getPort(): ?int
203
    {
204 4
        return $this->port;
205
    }
206
207
    /**
208
     * {@inheritDoc}
209
     */
210
    public function getPath(): string
211
    {
212 12
        return $this->path ?? '';
213
    }
214
215
    /**
216
     * {@inheritDoc}
217
     */
218
    public function getQuery(array $options = []): string
219
    {
220 12
        $vars = $this->query;
221
222
        if (array_key_exists('except', $options)) {
223
            if (! is_array($options['except'])) {
224
                $options['except'] = [$options['except']];
225
            }
226
227
            foreach ($options['except'] as $var) {
228
                unset($vars[$var]);
229
            }
230
        } elseif (array_key_exists('only', $options)) {
231 12
            $temp = [];
232
233
            if (! is_array($options['only'])) {
234
                $options['only'] = [$options['only']];
235
            }
236
237
            foreach ($options['only'] as $var) {
238
                if (array_key_exists($var, $vars)) {
239
                    $temp[$var] = $vars[$var];
240
                }
241
            }
242
243
            $vars = $temp;
244
        }
245
246 12
        return empty($vars) ? '' : http_build_query($vars);
247
    }
248
249
    /**
250
     * {@inheritDoc}
251
     */
252
    public function getFragment(): string
253
    {
254 12
        return $this->fragment ?? '';
255
    }
256
257
    /**
258
     * Renvoie les segments du chemin sous forme de tableau.
259
     */
260
    public function getSegments(): array
261
    {
262 2
        return $this->segments;
263
    }
264
265
    /**
266
     * Renvoie la valeur d'un segment spécifique du chemin URI.
267
     *
268
     * @return string La valeur du segment. Si aucun segment n'est trouvé, lance InvalidArgumentError
269
     */
270
    public function getSegment(int $number, string $default = ''): string
271
    {
272
        if ($number < 1) {
273 2
            throw HttpException::uriSegmentOutOfRange($number);
274
        }
275
        if ($number > count($this->segments) + 1) {
276 2
            throw HttpException::uriSegmentOutOfRange($number);
277
        }
278
279
        // Le segment doit traiter le tableau comme basé sur 1 pour l'utilisateur
280
        // mais nous devons encore gérer un tableau de base zéro.
281 2
        $number--;
282
283 2
        return $this->segments[$number] ?? $default;
284
    }
285
286
    /**
287
     * Définissez la valeur d'un segment spécifique du chemin URI.
288
     * Permet de définir uniquement des segments existants ou d'en ajouter un nouveau.
289
     *
290
     * @param mixed $value (string ou int)
291
     */
292
    public function setSegment(int $number, $value)
293
    {
294
        if ($number < 1) {
295
            throw HTTPException::uriSegmentOutOfRange($number);
296
        }
297
298
        if ($number > count($this->segments) + 1) {
299
            throw HTTPException::uriSegmentOutOfRange($number);
300
        }
301
302
        // Le segment doit traiter le tableau comme basé sur 1 pour l'utilisateur
303
        // mais nous devons encore gérer un tableau de base zéro.
304
        $number--;
305
306
        $this->segments[$number] = $value;
307
        $this->refreshPath();
308
309
        return $this;
310
    }
311
312
    /**
313
     * Renvoie le nombre total de segments.
314
     */
315
    public function getTotalSegments(): int
316
    {
317 2
        return count($this->segments);
318
    }
319
320
    /**
321
     * Autoriser la sortie de l'URI sous forme de chaîne en le convertissant simplement en chaîne
322
     * ou en écho.
323
     */
324
    public function __toString(): string
325
    {
326 4
        $path   = $this->getPath();
327 4
        $scheme = $this->getScheme();
328
329
        // Si les hôtes correspondent, il faut supposer que l'URL est relative à l'URL de base.
330 4
        [$scheme, $path] = $this->changeSchemeAndPath($scheme, $path);
0 ignored issues
show
Deprecated Code introduced by
The function BlitzPHP\Http\Uri::changeSchemeAndPath() has been deprecated: Cette methode pourrait etre supprimer ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

330
        [$scheme, $path] = /** @scrutinizer ignore-deprecated */ $this->changeSchemeAndPath($scheme, $path);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
331
332
        return static::createURIString(
333
            $scheme,
334
            $this->getAuthority(),
335
            $path, // Les URI absolus doivent utiliser un "/" pour un chemin vide
336
            $this->getQuery(),
337
            $this->getFragment()
338 4
        );
339
    }
340
341
    /**
342
     * Construit une représentation de la chaîne à partir des parties du composant.
343
     */
344
    public static function createURIString(?string $scheme = null, ?string $authority = null, ?string $path = null, ?string $query = null, ?string $fragment = null): string
345
    {
346 12
        $uri = '';
347
        if (! empty($scheme)) {
348 12
            $uri .= $scheme . '://';
349
        }
350
351
        if (! empty($authority)) {
352 12
            $uri .= $authority;
353
        }
354
355
        if (isset($path) && $path !== '') {
356
            $uri .= substr($uri, -1, 1) !== '/'
357
                ? '/' . ltrim($path, '/')
358 2
                : ltrim($path, '/');
359
        }
360
361
        if ($query !== '' && $query !== null) {
362 2
            $uri .= '?' . $query;
363
        }
364
365
        if ($fragment !== '' && $fragment !== null) {
366 2
            $uri .= '#' . $fragment;
367
        }
368
369 12
        return $uri;
370
    }
371
372
    /**
373
     * Analyse la chaîne donnée et enregistre les pièces d'autorité appropriées.
374
     */
375
    public function setAuthority(string $str): self
376
    {
377
        $parts = parse_url($str);
378
379
        if (! isset($parts['path'])) {
380
            $parts['path'] = $this->getPath();
381
        }
382
383
        if (empty($parts['host']) && $parts['path'] !== '') {
384
            $parts['host'] = $parts['path'];
385
            unset($parts['path']);
386
        }
387
388
        $this->applyParts($parts);
389
390
        return $this;
391
    }
392
393
    /**
394
     * Définit le schéma pour cet URI.
395
     *
396
     * En raison du grand nombre de schémas valides, nous ne pouvons pas limiter ce
397
     * uniquement sur http ou https.
398
     *
399
     * @see https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
400
     */
401
    public function setScheme(string $str): self
402
    {
403 12
        $str          = strtolower($str);
404 12
        $this->scheme = preg_replace('#:(//)?$#', '', $str);
405
406 12
        return $this;
407
    }
408
409
    /**
410
     * {@inheritDoc}
411
     */
412
    public function withScheme(string $scheme): static
413
    {
414 2
        $uri = clone $this;
415
416 2
        $scheme = strtolower($scheme);
417
418 2
        $uri->scheme = preg_replace('#:(//)?$#', '', $scheme);
419
420 2
        return $uri;
421
    }
422
423
    /**
424
     * Définit la partie userInfo/Authority de l'URI.
425
     *
426
     * @param string $user Le nom d'utilisateur de l'utilisateur
427
     * @param string $pass Le mot de passe de l'utilisateur
428
     */
429
    public function setUserInfo(string $user, string $pass): self
430
    {
431 2
        $this->user     = trim($user);
432 2
        $this->password = trim($pass);
433
434 2
        return $this;
435
    }
436
437
    /**
438
     * {@inheritDoc}
439
     */
440
    public function withUserInfo(string $user, ?string $password = null): static
441
    {
442
        $new = clone $this;
443
444
        $new->setUserInfo($user, $password);
0 ignored issues
show
Bug introduced by
It seems like $password can also be of type null; however, parameter $pass of BlitzPHP\Http\Uri::setUserInfo() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

444
        $new->setUserInfo($user, /** @scrutinizer ignore-type */ $password);
Loading history...
445
446
        return $new;
447
    }
448
449
    /**
450
     * Définit le nom d'hôte à utiliser.
451
     */
452
    public function setHost(string $str): self
453
    {
454 2
        $this->host = trim($str);
455
456 2
        return $this;
457
    }
458
459
    /**
460
     * {@inheritDoc}
461
     */
462
    public function withHost(string $host): static
463
    {
464
        $new = clone $this;
465
466
        $new->setHost($host);
467
468
        return $new;
469
    }
470
471
    /**
472
     * Définit la partie port de l'URI.
473
     */
474
    public function setPort(?int $port = null): self
475
    {
476
        if (null === $port) {
477
            return $this;
478
        }
479
480
        if ($port <= 0 || $port > 65535) {
481 2
            throw HttpException::invalidPort($port);
482
        }
483
484 2
        $this->port = $port;
485
486 2
        return $this;
487
    }
488
489
    /**
490
     * {@inheritDoc}
491
     */
492
    public function withPort(?int $port): static
493
    {
494
        $new = clone $this;
495
496
        $new->setPort($port);
497
498
        return $new;
499
    }
500
501
    /**
502
     * Définit la partie chemin de l'URI.
503
     */
504
    public function setPath(string $path): self
505
    {
506
        $this->path = $this->filterPath($path);
507
508
        $tempPath = trim($this->path, '/');
509
510
        $this->segments = ($tempPath === '') ? [] : explode('/', $tempPath);
511
512
        return $this;
513
    }
514
515
    /**
516
     * {@inheritDoc}
517
     */
518
    public function withPath(string $path): static
519
    {
520
        $new = clone $this;
521
522
        $new->setPath($path);
523
524
        return $new;
525
    }
526
527
    /**
528
     * Définit la partie chemin de l'URI en fonction des segments.
529
     */
530
    private function refreshPath(): self
531
    {
532
        $this->path = $this->filterPath(implode('/', $this->segments));
533
534
        $tempPath = trim($this->path, '/');
535
536
        $this->segments = ($tempPath === '') ? [] : explode('/', $tempPath);
537
538
        return $this;
539
    }
540
541
    /**
542
     * Définit la partie requête de l'URI, tout en essayant
543
     * de nettoyer les différentes parties des clés et des valeurs de la requête.
544
     */
545
    public function setQuery(string $query): self
546
    {
547
        if (str_contains($query, '#')) {
548 2
            throw HTTPException::malformedQueryString();
549
        }
550
551
        // Ne peut pas avoir de début ?
552
        if (! empty($query) && str_starts_with($query, '?')) {
553 2
            $query = substr($query, 1);
554
        }
555
556 2
        parse_str($query, $this->query);
557
558 2
        return $this;
559
    }
560
561
    /**
562
     * {@inheritDoc}
563
     */
564
    public function withQuery(string $query): static
565
    {
566
        $new = clone $this;
567
568
        $new->setQuery($query);
569
570
        return $new;
571
    }
572
573
    /**
574
     * Une méthode pratique pour transmettre un tableau d'éléments en tant que requête
575
     * partie de l'URI.
576
     */
577
    public function setQueryArray(array $query): self
578
    {
579
        $query = http_build_query($query);
580
581
        return $this->setQuery($query);
582
    }
583
584
    /**
585
     * Une méthode pratique pour transmettre un tableau d'éléments en tant que requête
586
     * partie de l'URI.
587
     */
588
    public function withQueryParams(array $query): static
589
    {
590
        $uri = clone $this;
591
592
        $uri->setQueryArray($query);
593
594
        return $uri;
595
    }
596
597
    /**
598
     * Ajoute un seul nouvel élément à la requête vars.
599
     */
600
    public function addQuery(string $key, mixed $value = null): self
601
    {
602
        $this->query[$key] = $value;
603
604
        return $this;
605
    }
606
607
    /**
608
     * Supprime une ou plusieurs variables de requête de l'URI.
609
     */
610
    public function stripQuery(...$params): self
611
    {
612
        foreach ($params as $param) {
613
            unset($this->query[$param]);
614
        }
615
616
        return $this;
617
    }
618
619
    /**
620
     * Filtre les variables de requête afin que seules les clés transmises
621
     * sont gardés. Le reste est supprimé de l'objet.
622
     */
623
    public function keepQuery(...$params): self
624
    {
625
        $temp = [];
626
627
        foreach ($this->query as $key => $value) {
628
            if (! in_array($key, $params, true)) {
629
                continue;
630
            }
631
632
            $temp[$key] = $value;
633
        }
634
635
        $this->query = $temp;
636
637
        return $this;
638
    }
639
640
    /**
641
     * Définit la partie fragment de l'URI.
642
     *
643
     * @see https://tools.ietf.org/html/rfc3986#section-3.5
644
     */
645
    public function setFragment(string $string): self
646
    {
647
        $this->fragment = trim($string, '# ');
648
649
        return $this;
650
    }
651
652
    /**
653
     * {@inheritDoc}
654
     */
655
    public function withFragment(string $fragment): static
656
    {
657
        $new = clone $this;
658
659
        $new->setFragment($fragment);
660
661
        return $new;
662
    }
663
664
    /**
665
     * Encode tous les caractères dangereux et supprime les segments de points.
666
     * Bien que les segments de points aient des utilisations valides selon la spécification,
667
     * cette classe ne les autorise pas.
668
     */
669
    protected function filterPath(?string $path = null): string
670
    {
671 12
        $orig = $path;
672
673
        // Décode/normalise les caractères codés en pourcentage afin que
674
        // nous pouissions toujours avoir une correspondance pour les routes, etc.
675 12
        $path = urldecode($path);
0 ignored issues
show
Bug introduced by
It seems like $path can also be of type null; however, parameter $string of urldecode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

675
        $path = urldecode(/** @scrutinizer ignore-type */ $path);
Loading history...
676
677
        // Supprimer les segments de points
678 12
        $path = self::removeDotSegments($path);
679
680
        // Correction de certains cas de bord de barre oblique...
681
        if (str_starts_with($orig, './')) {
0 ignored issues
show
Bug introduced by
It seems like $orig can also be of type null; however, parameter $haystack of str_starts_with() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

681
        if (str_starts_with(/** @scrutinizer ignore-type */ $orig, './')) {
Loading history...
682 12
            $path = '/' . $path;
683
        }
684
        if (str_starts_with($orig, '../')) {
685 12
            $path = '/' . $path;
686
        }
687
688
        // Encode les caractères
689
        $path = preg_replace_callback(
690
            '/(?:[^' . static::CHAR_UNRESERVED . ':@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/',
691
            static fn (array $matches) => rawurlencode($matches[0]),
692
            $path
693 12
        );
694
695 12
        return $path;
696
    }
697
698
    /**
699
     * Enregistre nos pièces à partir d'un appel parse_url.
700
     */
701
    protected function applyParts(array $parts)
702
    {
703
        if (! empty($parts['host'])) {
704 12
            $this->host = $parts['host'];
705
        }
706
        if (! empty($parts['user'])) {
707 2
            $this->user = $parts['user'];
708
        }
709
        if (! empty($parts['path'])) {
710 12
            $this->path = $this->filterPath($parts['path']);
711
        }
712
        if (! empty($parts['query'])) {
713 2
            $this->setQuery($parts['query']);
714
        }
715
        if (! empty($parts['fragment'])) {
716 2
            $this->fragment = $parts['fragment'];
717
        }
718
719
        if (isset($parts['scheme'])) {
720 12
            $this->setScheme(rtrim($parts['scheme'], ':/'));
721
        } else {
722 2
            $this->setScheme('http');
723
        }
724
725
        if (isset($parts['port']) && null !== $parts['port']) {
726
            // Les numéros de port valides sont appliqués par les précédents parse_url ou setPort()
727 2
            $this->port = $parts['port'];
728
        }
729
730
        if (isset($parts['pass'])) {
731 2
            $this->password = $parts['pass'];
732
        }
733
734
        if (isset($parts['path']) && $parts['path'] !== '') {
735 12
            $tempPath = trim($parts['path'], '/');
736
737 12
            $this->segments = ($tempPath === '') ? [] : explode('/', $tempPath);
738
        }
739
    }
740
741
    /**
742
     * Combine une chaîne d'URI avec celle-ci en fonction des règles définies dans
743
     * RFC 3986 Section 2
744
     *
745
     * @see http://tools.ietf.org/html/rfc3986#section-5.2
746
     */
747
    public function resolveRelativeURI(string $uri): self
748
    {
749
        /*
750
         * REMARQUE : Nous n'utilisons pas removeDotSegments dans cet
751
         * algorithme puisque c'est déjà fait par cette ligne !
752
         */
753
        $relative = new self();
754
        $relative->setURI($uri);
755
756
        if ($relative->getScheme() === $this->getScheme()) {
757
            $relative->setScheme('');
758
        }
759
760
        $transformed = clone $relative;
761
762
        // 5.2.2 Transformer les références dans une méthode non stricte (pas de schéma)
763
        if (! empty($relative->getAuthority())) {
764
            $transformed->setAuthority($relative->getAuthority())
765
                ->setPath($relative->getPath())
766
                ->setQuery($relative->getQuery());
767
        } else {
768
            if ($relative->getPath() === '') {
769
                $transformed->setPath($this->getPath());
770
771
                if ($relative->getQuery() !== '') {
772
                    $transformed->setQuery($relative->getQuery());
773
                } else {
774
                    $transformed->setQuery($this->getQuery());
775
                }
776
            } else {
777
                if (str_starts_with($relative->getPath(), '/')) {
778
                    $transformed->setPath($relative->getPath());
779
                } else {
780
                    $transformed->setPath($this->mergePaths($this, $relative));
781
                }
782
783
                $transformed->setQuery($relative->getQuery());
784
            }
785
786
            $transformed->setAuthority($this->getAuthority());
787
        }
788
789
        $transformed->setScheme($this->getScheme());
790
791
        $transformed->setFragment($relative->getFragment());
792
793
        return $transformed;
794
    }
795
796
    /**
797
     * Étant donné 2 chemins, les fusionnera conformément aux règles énoncées dans RFC 2986, section 5.2
798
     *
799
     * @see http://tools.ietf.org/html/rfc3986#section-5.2.3
800
     */
801
    protected function mergePaths(self $base, self $reference): string
802
    {
803
        if (! empty($base->getAuthority()) && '' === $base->getPath()) {
804
            return '/' . ltrim($reference->getPath(), '/ ');
805
        }
806
807
        $path = explode('/', $base->getPath());
808
809
        if ('' === $path[0]) {
810
            unset($path[0]);
811
        }
812
813
        array_pop($path);
814
        $path[] = $reference->getPath();
815
816
        return implode('/', $path);
817
    }
818
819
    /**
820
     * Utilisé lors de la résolution et de la fusion de chemins pour interpréter et
821
     * supprimer correctement les segments à un ou deux points du chemin selon RFC 3986 Section 5.2.4
822
     *
823
     * @see http://tools.ietf.org/html/rfc3986#section-5.2.4
824
     */
825
    public static function removeDotSegments(string $path): string
826
    {
827
        if (empty($path) || $path === '/') {
828 10
            return $path;
829
        }
830
831 10
        $output = [];
832
833 10
        $input = explode('/', $path);
834
835
        if (empty($input[0])) {
836 10
            unset($input[0]);
837 10
            $input = array_values($input);
838
        }
839
840
        // Ce n'est pas une représentation parfaite de la
841
        // RFC, mais correspond à la plupart des cas et est joli
842
        // beaucoup ce que Guzzle utilise. Devrait être assez bon
843
        // pour presque tous les cas d'utilisation réels.
844
        foreach ($input as $segment) {
845
            if ($segment === '..') {
846 10
                array_pop($output);
847
            } elseif ($segment !== '.' && $segment !== '') {
848 10
                $output[] = $segment;
849
            }
850
        }
851
852 10
        $output = implode('/', $output);
853 10
        $output = ltrim($output, '/ ');
854
855
        if ($output !== '/') {
856
            // Ajouter une barre oblique au début si nécessaire
857
            if (str_starts_with($path, '/')) {
858 10
                $output = '/' . $output;
859
            }
860
861
            // Ajouter une barre oblique à la fin si nécessaire
862
            if (substr($path, -1, 1) === '/') {
863 4
                $output .= '/';
864
            }
865
        }
866
867 10
        return $output;
868
    }
869
870
    /**
871
     * Modifier le chemin (et le schéma) en supposant que les URI ayant le même hôte que baseURL doivent être relatifs à la configuration du projet.
872
     *
873
     * @deprecated Cette methode pourrait etre supprimer
874
     */
875
    private function changeSchemeAndPath(string $scheme, string $path): array
876
    {
877
        // Vérifier s'il s'agit d'un URI interne
878 4
        $config  = (object) config('app');
879 4
        $baseUri = new self($config->base_url);
0 ignored issues
show
Bug introduced by
The property base_url does not seem to exist on BlitzPHP\Config\Config.
Loading history...
880
881
        if (substr($this->getScheme(), 0, 4) === 'http' && $this->getHost() === $baseUri->getHost()) {
882
            // Vérifier la présence de segments supplémentaires
883 4
            $basePath = trim($baseUri->getPath(), '/') . '/';
884 4
            $trimPath = ltrim($path, '/');
885
886
            if ($basePath !== '/' && ! str_starts_with($trimPath, $basePath)) {
887
                $path = $basePath . $trimPath;
888
            }
889
890
            // Vérifier si le protocole HTTPS est forcé
891
            if ($config->force_global_secure_requests) {
0 ignored issues
show
Bug introduced by
The property force_global_secure_requests does not seem to exist on BlitzPHP\Config\Config.
Loading history...
892 4
                $scheme = 'https';
893
            }
894
        }
895
896 4
        return [$scheme, $path];
897
    }
898
}
899