Subversion Repositories Applications.framework

Rev

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