Subversion Repositories Applications.framework

Rev

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