Subversion Repositories Applications.framework

Rev

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