Subversion Repositories Applications.framework

Rev

Rev 85 | Rev 120 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
5 aurelien 1
<?php
105 aurelien 2
// declare(encoding='UTF-8');
5 aurelien 3
/**
105 aurelien 4
* classe Url, gérant le découpage des paramètres, leurs modification etc...
5
* Traduction et conversion d'une classe (NET_Url2) issue de Pear
6
*
7
* PHP Version 5
8
*
9
* @category  Class
10
* @package   Framework
11
// auteur principal
12
* @author    Christian Schmidt <schmidt@php.net>
13
// autre auteurs
14
* @author    aurelien <aurelien@tela-botanica.org>
15
* @copyright 2009 Tela-Botanica
16
* @license   http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
17
* @license   http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
18
* @version   SVN: $Id: Url.php 105 2009-08-31 15:25:05Z aurelien $
19
* @link      /doc/framework/
20
*
21
*/
85 jpm 22
class Url
5 aurelien 23
{
24
    /**
105 aurelien 25
     * Parsing strict dans resoudre() (voir RFC 3986, section 5.2.2). Par défaut
26
     * à true.
5 aurelien 27
     */
105 aurelien 28
    const OPTION_STRICTE           = 'strict';
5 aurelien 29
 
30
    /**
105 aurelien 31
     * Répresenter les tableaux dans les requêtes en utilisant la notation php []. Par défaut à true.
5 aurelien 32
     */
105 aurelien 33
    const OPTION_UTILISER_CROCHETS     = 'use_brackets';
5 aurelien 34
 
35
    /**
105 aurelien 36
     * URL-encoder les clés des variables dans les requêtes. Par défaut à true.
5 aurelien 37
     */
105 aurelien 38
    const OPTION_ENCODER_CLES      = 'encode_keys';
5 aurelien 39
 
40
    /**
105 aurelien 41
     * Séparateurs de variables lors du parsing de la requête. Chaque caractère
42
     * est considéré comme un séparateur. Par défaut, spécifié par le paramêtre
43
     * arg_separator.input dans php.ini (par défaut "&").
5 aurelien 44
     */
105 aurelien 45
    const OPTION_SEPARATEUR_ENTREE  = 'input_separator';
5 aurelien 46
 
47
    /**
105 aurelien 48
     * Séparateur de variables lors de la génération de la requête. Par défaut, spécifié
49
     * par le paramètre arg_separator.output dans php.ini (par défaut "&").
5 aurelien 50
     */
105 aurelien 51
    const OPTION_SEPARATEUR_SORTIE = 'output_separator';
5 aurelien 52
 
53
    /**
105 aurelien 54
     * Options par défaut correspondant au comportement de php
55
     * vis à vis de $_GET
5 aurelien 56
     */
57
    private $options = array(
105 aurelien 58
        self::OPTION_STRICTE           => true,
59
        self::OPTION_UTILISER_CROCHETS     => true,
60
        self::OPTION_ENCODER_CLES      => true,
61
        self::OPTION_SEPARATEUR_ENTREE  => 'x&',
62
        self::OPTION_SEPARATEUR_SORTIE => 'x&',
5 aurelien 63
        );
64
 
65
    /**
66
     * @var  string|bool
67
     */
105 aurelien 68
    private $schema = false;
5 aurelien 69
 
70
    /**
71
     * @var  string|bool
72
     */
105 aurelien 73
    private $infoUtilisateur = false;
5 aurelien 74
 
75
    /**
76
     * @var  string|bool
77
     */
105 aurelien 78
    private $hote = false;
5 aurelien 79
 
80
    /**
81
     * @var  int|bool
82
     */
83
    private $port = false;
84
 
85
    /**
86
     * @var  string
87
     */
105 aurelien 88
    private $chemin = '';
5 aurelien 89
 
90
    /**
91
     * @var  string|bool
92
     */
105 aurelien 93
    private $requete = false;
5 aurelien 94
 
95
    /**
96
     * @var  string|bool
97
     */
98
    private $fragment = false;
99
 
100
    /**
105 aurelien 101
     * @param string $url     une URL relative ou absolue
5 aurelien 102
     * @param array  $options
103
     */
104
    public function __construct($url, $options = null)
105
    {
105 aurelien 106
        $this->setOption(self::OPTION_SEPARATEUR_ENTREE,
5 aurelien 107
                         ini_get('arg_separator.input'));
105 aurelien 108
        $this->setOption(self::OPTION_SEPARATEUR_SORTIE,
5 aurelien 109
                         ini_get('arg_separator.output'));
110
        if (is_array($options)) {
105 aurelien 111
            foreach ($options as $nomOption => $valeur) {
112
                $this->setOption($nomOption);
5 aurelien 113
            }
114
        }
115
 
116
        if (preg_match('@^([a-z][a-z0-9.+-]*):@i', $url, $reg)) {
105 aurelien 117
            $this->schema = $reg[1];
5 aurelien 118
            $url = substr($url, strlen($reg[0]));
119
        }
120
 
121
        if (preg_match('@^//([^/#?]+)@', $url, $reg)) {
105 aurelien 122
            $this->setAutorite($reg[1]);
5 aurelien 123
            $url = substr($url, strlen($reg[0]));
124
        }
125
 
126
        $i = strcspn($url, '?#');
105 aurelien 127
        $this->chemin = substr($url, 0, $i);
5 aurelien 128
        $url = substr($url, $i);
129
 
130
        if (preg_match('@^\?([^#]*)@', $url, $reg)) {
105 aurelien 131
            $this->requete = $reg[1];
5 aurelien 132
            $url = substr($url, strlen($reg[0]));
133
        }
134
 
135
        if ($url) {
136
            $this->fragment = substr($url, 1);
137
        }
138
    }
139
 
140
    /**
105 aurelien 141
     * Retourne le schéma, c.a.d. "http" ou "urn", ou false si aucun schéma n'est
142
     * spécifié, i.e. l'url est une url relative
5 aurelien 143
     *
144
     * @return  string|bool
145
     */
105 aurelien 146
    public function getSchema()
5 aurelien 147
    {
105 aurelien 148
        return $this->schema;
5 aurelien 149
    }
150
 
151
    /**
105 aurelien 152
     * @param string|bool $schema
5 aurelien 153
     *
154
     * @return void
105 aurelien 155
     * @see    getSchema()
5 aurelien 156
     */
105 aurelien 157
    public function setSchema($schema)
5 aurelien 158
    {
105 aurelien 159
        $this->schema = $schema;
5 aurelien 160
    }
161
 
162
    /**
105 aurelien 163
     * renvoie la partie user de la partie infoUtilisateur (partie précédant le premier
164
     *  ":"), ou false si aucune partie infoUtilisateur n'est définie.
5 aurelien 165
     *
166
     * @return  string|bool
167
     */
105 aurelien 168
    public function getUtilisateur()
5 aurelien 169
    {
105 aurelien 170
        return $this->infoUtilisateur !== false ? preg_replace('@:.*$@', '', $this->infoUtilisateur) : false;
5 aurelien 171
    }
172
 
173
    /**
105 aurelien 174
     * renvoie la partie mot de passe de la partie infoUtilisateur (partie après le premier
175
     *  ":"), , ou false si aucune partie infoUtilisateur n'est définie (i.e. l'URL ne contient
176
     * pas de "@" en face du nom d'hôte) ou si la partie infoUtilisateur ne contient pas de ":".
5 aurelien 177
     *
178
     * @return  string|bool
179
     */
105 aurelien 180
    public function getMotDePasse()
5 aurelien 181
    {
105 aurelien 182
        return $this->infoUtilisateur !== false ? substr(strstr($this->infoUtilisateur, ':'), 1) : false;
5 aurelien 183
    }
184
 
185
    /**
105 aurelien 186
     * Renvoie la partie userinfio, ou false si celle-ci n'existe pas, i.e. si la partie
187
     * autorité ne contient pas de "@"
5 aurelien 188
     *
189
     * @return  string|bool
190
     */
105 aurelien 191
    public function getInfoUtilisateur()
5 aurelien 192
    {
105 aurelien 193
        return $this->infoUtilisateur;
5 aurelien 194
    }
195
 
196
    /**
105 aurelien 197
     * Setteur pour la partie infoUtilisateur. Si deux argument sont passé, ils sont combinés
198
     * dans la partie infoUtilisateur de cette manière username ":" password.
5 aurelien 199
     *
105 aurelien 200
     * @param string|bool $infoUtilisateur infoUtilisateur ou username
201
     * @param string|bool $motDePasse
5 aurelien 202
     *
203
     * @return void
204
     */
105 aurelien 205
    public function setInfoUtilisateur($infoUtilisateur, $motDePasse = false)
5 aurelien 206
    {
105 aurelien 207
        $this->infoUtilisateur = $infoUtilisateur;
208
        if ($motDePasse !== false) {
209
            $this->infoUtilisateur .= ':' . $motDePasse;
5 aurelien 210
        }
211
    }
212
 
213
    /**
105 aurelien 214
     * Renvoie la partie hôte, ou false s'il n'y a pas de partie autorité, c.a.d.
215
     * l'URL est relative.
5 aurelien 216
     *
217
     * @return  string|bool
218
     */
105 aurelien 219
    public function getHote()
5 aurelien 220
    {
105 aurelien 221
        return $this->hote;
5 aurelien 222
    }
223
 
224
    /**
105 aurelien 225
     * @param string|bool $hote
5 aurelien 226
     *
227
     * @return void
228
     */
105 aurelien 229
    public function setHote($hote)
5 aurelien 230
    {
105 aurelien 231
        $this->hote = $hote;
5 aurelien 232
    }
233
 
234
    /**
105 aurelien 235
     * Renvoie le numéro de port, ou false si aucun numéro de port n'est spécifié,
236
     * i.e. le port par défaut doit utilisé.
5 aurelien 237
     *
238
     * @return  int|bool
239
     */
240
    public function getPort()
241
    {
242
        return $this->port;
243
    }
244
 
245
    /**
246
     * @param int|bool $port
247
     *
248
     * @return void
249
     */
250
    public function setPort($port)
251
    {
252
        $this->port = intval($port);
253
    }
254
 
255
    /**
105 aurelien 256
     * Renvoie la partie autorité, i.e. [ infoUtilisateur "@" ] hote [ ":" port ], ou
257
     * false si celle-ci est absente.
5 aurelien 258
     *
259
     * @return string|bool
260
     */
105 aurelien 261
    public function getAutorite()
5 aurelien 262
    {
105 aurelien 263
        if (!$this->hote) {
5 aurelien 264
            return false;
265
        }
266
 
105 aurelien 267
        $autorite = '';
5 aurelien 268
 
105 aurelien 269
        if ($this->infoUtilisateur !== false) {
270
            $autorite .= $this->infoUtilisateur . '@';
5 aurelien 271
        }
272
 
105 aurelien 273
        $autorite .= $this->hote;
5 aurelien 274
 
275
        if ($this->port !== false) {
105 aurelien 276
            $autorite .= ':' . $this->port;
5 aurelien 277
        }
278
 
105 aurelien 279
        return $autorite;
5 aurelien 280
    }
281
 
282
    /**
105 aurelien 283
     * @param string|false $autorite
5 aurelien 284
     *
285
     * @return void
286
     */
105 aurelien 287
    public function setAutorite($autorite)
5 aurelien 288
    {
289
        $this->user = false;
290
        $this->pass = false;
105 aurelien 291
        $this->hote = false;
5 aurelien 292
        $this->port = false;
105 aurelien 293
        if (preg_match('@^(([^\@]+)\@)?([^:]+)(:(\d*))?$@', $autorite, $reg)) {
5 aurelien 294
            if ($reg[1]) {
105 aurelien 295
                $this->infoUtilisateur = $reg[2];
5 aurelien 296
            }
297
 
105 aurelien 298
            $this->hote = $reg[3];
5 aurelien 299
            if (isset($reg[5])) {
300
                $this->port = intval($reg[5]);
301
            }
302
        }
303
    }
304
 
305
    /**
105 aurelien 306
     * Renvoie la partie chemin (chemin) (éventuellement vide).
5 aurelien 307
     *
308
     * @return string
309
     */
105 aurelien 310
    public function getChemin()
5 aurelien 311
    {
105 aurelien 312
        return $this->chemin;
5 aurelien 313
    }
314
 
315
    /**
105 aurelien 316
     * @param string $chemin
5 aurelien 317
     *
318
     * @return void
319
     */
105 aurelien 320
    public function setChemin($chemin)
5 aurelien 321
    {
105 aurelien 322
        $this->chemin = $chemin;
5 aurelien 323
    }
324
 
325
    /**
105 aurelien 326
     * renvoie la chaine de requête (requete string) (sans le premier "?"), ou false si "?"
327
     * n'est pas présent dans l'url.
5 aurelien 328
     *
329
     * @return  string|bool
105 aurelien 330
     * @see     self::getVariablesRequete()
5 aurelien 331
     */
105 aurelien 332
    public function getRequete()
5 aurelien 333
    {
105 aurelien 334
        return $this->requete;
5 aurelien 335
    }
336
 
337
    /**
105 aurelien 338
     * @param string|bool $requete
5 aurelien 339
     *
340
     * @return void
105 aurelien 341
     * @see   self::setVariablesRequete()
5 aurelien 342
     */
105 aurelien 343
    public function setRequete($requete)
5 aurelien 344
    {
105 aurelien 345
        $this->requete = $requete;
5 aurelien 346
    }
347
 
348
    /**
105 aurelien 349
     * Renvoie le nom du fragment, ou false si "#" n'est pas present dans l'URL.
5 aurelien 350
     *
351
     * @return  string|bool
352
     */
353
    public function getFragment()
354
    {
355
        return $this->fragment;
356
    }
357
 
358
    /**
359
     * @param string|bool $fragment
360
     *
361
     * @return void
362
     */
363
    public function setFragment($fragment)
364
    {
365
        $this->fragment = $fragment;
366
    }
367
 
368
    /**
105 aurelien 369
     * Renvoie la requete string sous forme d'un tableau de variables telles qu'elles apparaitraient
370
     * dans le $_GET d'un script PHP
5 aurelien 371
     *
372
     * @return  array
373
     */
105 aurelien 374
    public function getVariablesRequete()
5 aurelien 375
    {
376
        $pattern = '/[' .
105 aurelien 377
                   preg_quote($this->getOption(self::OPTION_SEPARATEUR_ENTREE), '/') .
5 aurelien 378
                   ']/';
105 aurelien 379
        $parties   = preg_split($pattern, $this->requete, -1, PREG_SPLIT_NO_EMPTY);
380
        $retour  = array();
5 aurelien 381
 
105 aurelien 382
        foreach ($parties as $partie) {
383
            if (strpos($partie, '=') !== false) {
384
                list($cle, $valeur) = explode('=', $partie, 2);
5 aurelien 385
            } else {
105 aurelien 386
                $cle   = $partie;
387
                $valeur = null;
5 aurelien 388
            }
389
 
105 aurelien 390
            if ($this->getOption(self::OPTION_ENCODER_CLES)) {
391
                $cle = rawurldecode($cle);
5 aurelien 392
            }
105 aurelien 393
            $valeur = rawurldecode($valeur);
5 aurelien 394
 
105 aurelien 395
            if ($this->getOption(self::OPTION_UTILISER_CROCHETS) &&
396
                preg_match('#^(.*)\[([0-9a-z_-]*)\]#i', $cle, $matches)) {
5 aurelien 397
 
105 aurelien 398
                $cle = $matches[1];
5 aurelien 399
                $idx = $matches[2];
400
 
105 aurelien 401
                // On s'assure que c'est bien un tableau
402
                if (empty($retour[$cle]) || !is_array($retour[$cle])) {
403
                    $retour[$cle] = array();
5 aurelien 404
                }
405
 
105 aurelien 406
                // Ajout des données
5 aurelien 407
                if ($idx === '') {
105 aurelien 408
                    $retour[$cle][] = $valeur;
5 aurelien 409
                } else {
105 aurelien 410
                    $retour[$cle][$idx] = $valeur;
5 aurelien 411
                }
105 aurelien 412
            } elseif (!$this->getOption(self::OPTION_UTILISER_CROCHETS)
413
                      && !empty($retour[$cle])
5 aurelien 414
            ) {
105 aurelien 415
                $retour[$cle]   = (array) $retour[$cle];
416
                $retour[$cle][] = $valeur;
5 aurelien 417
            } else {
105 aurelien 418
                $retour[$cle] = $valeur;
5 aurelien 419
            }
420
        }
421
 
105 aurelien 422
        return $retour;
5 aurelien 423
    }
424
 
425
    /**
105 aurelien 426
     * @param array $tableau (nom => valeur) tableau
5 aurelien 427
     *
428
     * @return void
429
     */
105 aurelien 430
    public function setVariablesRequete(array $tableau)
5 aurelien 431
    {
105 aurelien 432
        if (!$tableau) {
433
            $this->requete = false;
5 aurelien 434
        } else {
105 aurelien 435
            foreach ($tableau as $nom => $valeur) {
436
                if ($this->getOption(self::OPTION_ENCODER_CLES)) {
437
                    $nom = rawurlencode($nom);
5 aurelien 438
                }
439
 
105 aurelien 440
                if (is_array($valeur)) {
441
                    foreach ($valeur as $k => $v) {
442
                        $parties[] = $this->getOption(self::OPTION_UTILISER_CROCHETS)
443
                            ? sprintf('%s[%s]=%s', $nom, $k, $v)
444
                            : ($nom . '=' . $v);
5 aurelien 445
                    }
105 aurelien 446
                } elseif (!is_null($valeur)) {
447
                    $parties[] = $nom . '=' . $valeur;
5 aurelien 448
                } else {
105 aurelien 449
                    $parties[] = $nom;
5 aurelien 450
                }
451
            }
105 aurelien 452
            $this->requete = implode($this->getOption(self::OPTION_SEPARATEUR_SORTIE),
453
                                   $parties);
5 aurelien 454
        }
455
    }
456
 
457
    /**
105 aurelien 458
     * @param string $nom
459
     * @param mixed  $valeur
5 aurelien 460
     *
461
     * @return  array
462
     */
105 aurelien 463
    public function setVariableRequete($nom, $valeur)
5 aurelien 464
    {
105 aurelien 465
        $tableau = $this->getVariablesRequete();
466
        $tableau[$nom] = $valeur;
467
        $this->setVariablesRequete($tableau);
5 aurelien 468
    }
469
 
470
    /**
105 aurelien 471
     * @param string $nom
5 aurelien 472
     *
473
     * @return void
474
     */
105 aurelien 475
    public function unsetVariableRequete($nom)
5 aurelien 476
    {
105 aurelien 477
        $tableau = $this->getVariablesRequete();
478
        unset($tableau[$nom]);
479
        $this->setVariablesRequete($tableau);
5 aurelien 480
    }
481
 
482
    /**
105 aurelien 483
     * Renvoie un représentation sous forme de chaine de l'URL
5 aurelien 484
     *
485
     * @return  string
486
     */
487
    public function getURL()
488
    {
105 aurelien 489
        // Voir RFC 3986, section 5.3
5 aurelien 490
        $url = "";
491
 
105 aurelien 492
        if ($this->schema !== false) {
493
            $url .= $this->schema . ':';
5 aurelien 494
        }
495
 
105 aurelien 496
        $autorite = $this->getAutorite();
497
        if ($autorite !== false) {
498
            $url .= '//' . $autorite;
5 aurelien 499
        }
105 aurelien 500
        $url .= $this->chemin;
5 aurelien 501
 
105 aurelien 502
        if ($this->requete !== false) {
503
            $url .= '?' . $this->requete;
5 aurelien 504
        }
505
 
506
        if ($this->fragment !== false) {
507
            $url .= '#' . $this->fragment;
508
        }
509
 
510
        return $url;
511
    }
512
 
513
    /**
105 aurelien 514
     * Renvoie une représentation de cette URL sous forme de chaine normalisée. Utile pour la
515
     * comparaison d'URLs
5 aurelien 516
     *
517
     * @return  string
518
     */
105 aurelien 519
    public function getURLNormalisee()
5 aurelien 520
    {
521
        $url = clone $this;
105 aurelien 522
        $url->normaliser();
5 aurelien 523
        return $url->getUrl();
524
    }
525
 
526
    /**
105 aurelien 527
     * Renvoie une instance normalisée de Url
5 aurelien 528
     *
105 aurelien 529
     * @return  Url
5 aurelien 530
     */
105 aurelien 531
    public function normaliser()
5 aurelien 532
    {
533
        // See RFC 3886, section 6
534
 
105 aurelien 535
        // les cchémas sont insesibles à la casse
536
        if ($this->schema) {
537
            $this->schema = strtolower($this->schema);
5 aurelien 538
        }
539
 
105 aurelien 540
        // les noms d'hotes sont insensibles à la casse
541
        if ($this->hote) {
542
            $this->hote = strtolower($this->hote);
5 aurelien 543
        }
544
 
105 aurelien 545
        // Supprimer le numéro de port par défaut pour les schemas connus (RFC 3986, section 6.2.3)
5 aurelien 546
        if ($this->port &&
105 aurelien 547
            $this->schema &&
548
            $this->port == getservbyname($this->schema, 'tcp')) {
5 aurelien 549
 
550
            $this->port = false;
551
        }
552
 
105 aurelien 553
        // normalisation dans le cas d'un encodage avec %XX pourcentage (RFC 3986, section 6.2.2.1)
554
        foreach (array('infoUtilisateur', 'hote', 'chemin') as $partie) {
555
            if ($this->$partie) {
556
                $this->$partie  = preg_replace('/%[0-9a-f]{2}/ie', 'strtoupper("\0")', $this->$partie);
5 aurelien 557
            }
558
        }
559
 
105 aurelien 560
        // normalisation des segments du chemin (RFC 3986, section 6.2.2.3)
561
        $this->chemin = self::supprimerSegmentsAPoints($this->chemin);
5 aurelien 562
 
105 aurelien 563
        // normalisation basée sur le schéma (RFC 3986, section 6.2.3)
564
        if ($this->hote && !$this->chemin) {
565
            $this->chemin = '/';
5 aurelien 566
        }
567
    }
568
 
569
    /**
105 aurelien 570
     * Renvoie vrai ou faux suivant que l'instance en cours représente une URL relative ou absolue.
5 aurelien 571
     *
572
     * @return  bool
573
     */
105 aurelien 574
    public function etreAbsolue()
5 aurelien 575
    {
105 aurelien 576
        return (bool) $this->schema;
5 aurelien 577
    }
578
 
579
    /**
105 aurelien 580
     * Renvoie une instance de Url représentant une URL absolue relative à
581
     * cette URL.
5 aurelien 582
     *
105 aurelien 583
     * @param Url|string $reference URL relative
5 aurelien 584
     *
105 aurelien 585
     * @return Url
5 aurelien 586
     */
105 aurelien 587
    public function resoudre($reference)
5 aurelien 588
    {
589
        if (is_string($reference)) {
590
            $reference = new self($reference);
591
        }
105 aurelien 592
        if (!$this->etreAbsolue()) {
593
            throw new Exception('L\'URL de base doit être absolue !');
5 aurelien 594
        }
595
 
105 aurelien 596
        // Un parseur non strict peut choisir d'ignorer un schema dans la référence
597
        // si celui ci est identique au schéma de base de l'URI.
598
        if (!$this->getOption(self::OPTION_STRICTE) && $reference->schema == $this->schema) {
599
            $reference->schema = false;
5 aurelien 600
        }
601
 
105 aurelien 602
        $cible = new self('');
603
        if ($reference->schema !== false) {
604
            $cible->schema = $reference->schema;
605
            $cible->setAutorite($reference->getAutorite());
606
            $cible->chemin  = self::supprimerSegmentsAPoints($reference->chemin);
607
            $cible->requete = $reference->requete;
5 aurelien 608
        } else {
105 aurelien 609
            $autorite = $reference->getAutorite();
610
            if ($autorite !== false) {
611
                $cible->setAutorite($autorite);
612
                $cible->chemin  = self::supprimerSegmentsAPoints($reference->chemin);
613
                $cible->requete = $reference->requete;
5 aurelien 614
            } else {
105 aurelien 615
                if ($reference->chemin == '') {
616
                    $cible->chemin = $this->chemin;
617
                    if ($reference->requete !== false) {
618
                        $cible->requete = $reference->requete;
5 aurelien 619
                    } else {
105 aurelien 620
                        $cible->requete = $this->requete;
5 aurelien 621
                    }
622
                } else {
105 aurelien 623
                    if (substr($reference->chemin, 0, 1) == '/') {
624
                        $cible->chemin = self::supprimerSegmentsAPoints($reference->chemin);
5 aurelien 625
                    } else {
105 aurelien 626
                        // Concaténation chemins (RFC 3986, section 5.2.3)
627
                        if ($this->hote !== false && $this->chemin == '') {
628
                            $cible->chemin = '/' . $this->chemin;
5 aurelien 629
                        } else {
105 aurelien 630
                            $i = strrpos($this->chemin, '/');
5 aurelien 631
                            if ($i !== false) {
105 aurelien 632
                                $cible->chemin = substr($this->chemin, 0, $i + 1);
5 aurelien 633
                            }
105 aurelien 634
                            $cible->chemin .= $reference->chemin;
5 aurelien 635
                        }
105 aurelien 636
                        $cible->chemin = self::supprimerSegmentsAPoints($cible->chemin);
5 aurelien 637
                    }
105 aurelien 638
                    $cible->requete = $reference->requete;
5 aurelien 639
                }
105 aurelien 640
                $cible->setAutorite($this->getAutorite());
5 aurelien 641
            }
105 aurelien 642
            $cible->schema = $this->schema;
5 aurelien 643
        }
644
 
105 aurelien 645
        $cible->fragment = $reference->fragment;
5 aurelien 646
 
105 aurelien 647
        return $cible;
5 aurelien 648
    }
649
 
650
    /**
105 aurelien 651
     * La suppression des segments à points est décrite dans la RFC 3986, section 5.2.4, e.g.
5 aurelien 652
     * "/foo/../bar/baz" => "/bar/baz"
653
     *
105 aurelien 654
     * @param string $chemin un chemin
5 aurelien 655
     *
105 aurelien 656
     * @return string un chemin
5 aurelien 657
     */
105 aurelien 658
    private static function supprimerSegmentsAPoints($chemin)
5 aurelien 659
    {
105 aurelien 660
        $sortie = '';
5 aurelien 661
 
105 aurelien 662
        // Assurons de ne pas nous retrouver piégés dans une boucle infinie due à un bug de
663
        // cette méthode
5 aurelien 664
        $j = 0;
105 aurelien 665
        while ($chemin && $j++ < 100) {
666
            // Étape A
667
            if (substr($chemin, 0, 2) == './') {
668
                $chemin = substr($chemin, 2);
669
            } elseif (substr($chemin, 0, 3) == '../') {
670
                $chemin = substr($chemin, 3);
5 aurelien 671
 
105 aurelien 672
            // Étape B
673
            } elseif (substr($chemin, 0, 3) == '/./' || $chemin == '/.') {
674
                $chemin = '/' . substr($chemin, 3);
5 aurelien 675
 
105 aurelien 676
            // Étape C
677
            } elseif (substr($chemin, 0, 4) == '/../' || $chemin == '/..') {
678
                $chemin = '/' . substr($chemin, 4);
679
                $i = strrpos($sortie, '/');
680
                $sortie = $i === false ? '' : substr($sortie, 0, $i);
5 aurelien 681
 
105 aurelien 682
            // Étape D
683
            } elseif ($chemin == '.' || $chemin == '..') {
684
                $chemin = '';
5 aurelien 685
 
105 aurelien 686
            // Étape E
5 aurelien 687
            } else {
105 aurelien 688
                $i = strpos($chemin, '/');
5 aurelien 689
                if ($i === 0) {
105 aurelien 690
                    $i = strpos($chemin, '/', 1);
5 aurelien 691
                }
692
                if ($i === false) {
105 aurelien 693
                    $i = strlen($chemin);
5 aurelien 694
                }
105 aurelien 695
                $sortie .= substr($chemin, 0, $i);
696
                $chemin = substr($chemin, $i);
5 aurelien 697
            }
698
        }
699
 
105 aurelien 700
        return $sortie;
5 aurelien 701
    }
702
 
703
    /**
105 aurelien 704
     * Renvoie une instance de Url representant l'URL canonique du script PHP
705
     * en cours d'éxécution
5 aurelien 706
     *
707
     * @return  string
708
     */
105 aurelien 709
    public static function getCanonique()
5 aurelien 710
    {
711
        if (!isset($_SERVER['REQUEST_METHOD'])) {
105 aurelien 712
            // ALERT - pas d'URL en cours
713
            throw new Exception('Le script n\'a pas été appellé à travers un serveur web');
5 aurelien 714
        }
715
 
105 aurelien 716
        // on part d'une URL relative
5 aurelien 717
        $url = new self($_SERVER['PHP_SELF']);
105 aurelien 718
        $url->schema = isset($_SERVER['HTTPS']) ? 'https' : 'http';
719
        $url->hote = $_SERVER['SERVER_NAME'];
5 aurelien 720
        $port = intval($_SERVER['SERVER_PORT']);
105 aurelien 721
        if ($url->schema == 'http' && $port != 80 ||
722
            $url->schema == 'https' && $port != 443) {
5 aurelien 723
 
724
            $url->port = $port;
725
        }
726
        return $url;
727
    }
728
 
729
    /**
105 aurelien 730
     * Renvoie l'URL utilisée pour récupérer la requête en cours
5 aurelien 731
     *
732
     * @return  string
733
     */
105 aurelien 734
    public static function getURLDemande()
5 aurelien 735
    {
105 aurelien 736
        return self::getDemande()->getUrl();
5 aurelien 737
    }
738
 
739
    /**
105 aurelien 740
     * Renvoie une instance de Url representant l'URL utilisée pour
741
     * récupérer la requête en cours
5 aurelien 742
     *
105 aurelien 743
     * @return  Url
5 aurelien 744
     */
105 aurelien 745
    public static function getDemande()
5 aurelien 746
    {
747
        if (!isset($_SERVER['REQUEST_METHOD'])) {
105 aurelien 748
            // ALERTE - pas d'URL en cours
749
            throw new Exception('Le script n\'a pas été appellé à travers un serveur web');
5 aurelien 750
        }
751
 
105 aurelien 752
        // On part d'une URL relative
5 aurelien 753
        $url = new self($_SERVER['REQUEST_URI']);
105 aurelien 754
        $url->schema = isset($_SERVER['HTTPS']) ? 'https' : 'http';
755
        // On met à jour les valeurs de l'hote et si possible du port
756
        $url->setAutorite($_SERVER['HTTP_hote']);
5 aurelien 757
        return $url;
758
    }
759
 
760
    /**
105 aurelien 761
     * Met à jour la valeur de l'option spécifiée.
5 aurelien 762
     *
105 aurelien 763
     * @param string $nomOption une des constantes commençant par self::OPTION_
764
     * @param mixed  $valeur      valeur de l'option
5 aurelien 765
     *
766
     * @return void
105 aurelien 767
     * @see  self::OPTION_STRICTE
768
     * @see  self::OPTION_UTILISER_CROCHETS
769
     * @see  self::OPTION_ENCODER_CLES
5 aurelien 770
     */
105 aurelien 771
    function setOption($nomOption, $valeur)
5 aurelien 772
    {
105 aurelien 773
        if (!array_key_exists($nomOption, $this->options)) {
5 aurelien 774
            return false;
775
        }
105 aurelien 776
        $this->options[$nomOption] = $valeur;
5 aurelien 777
    }
778
 
779
    /**
105 aurelien 780
     * Renvoie la valeur de l'option specifiée.
5 aurelien 781
     *
105 aurelien 782
     * @param string $nomOption Nom de l'option demandée
5 aurelien 783
     *
784
     * @return  mixed
785
     */
105 aurelien 786
    function getOption($nomOption)
5 aurelien 787
    {
105 aurelien 788
        return isset($this->options[$nomOption])
789
            ? $this->options[$nomOption] : false;
5 aurelien 790
    }
791
 
792
    public function __toString() {
793
    	return $this->getURL();
794
    }
795
}