1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
16: class Url
17: {
18: 19: 20: 21:
22: const OPTION_STRICTE = 'strict';
23:
24: 25: 26:
27: const OPTION_UTILISER_CROCHETS = 'use_brackets';
28:
29: 30: 31:
32: const OPTION_ENCODER_CLES = 'encode_keys';
33:
34: 35: 36: 37: 38:
39: const OPTION_SEPARATEUR_ENTREE = 'input_separator';
40:
41: 42: 43: 44:
45: const OPTION_SEPARATEUR_SORTIE = 'output_separator';
46:
47: 48: 49: 50:
51: private $options = array(
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&');
57:
58: 59: 60:
61: private $schema = false;
62:
63: 64: 65:
66: private $infoUtilisateur = false;
67:
68: 69: 70:
71: private $hote = false;
72:
73: 74: 75:
76: private $port = false;
77:
78: 79: 80:
81: private $chemin = '';
82:
83: 84: 85:
86: private $requete = false;
87:
88: 89: 90:
91: private $fragment = false;
92:
93: 94: 95: 96:
97: public function __construct($url, $options = null) {
98: $this->setOption(self::OPTION_SEPARATEUR_ENTREE,
99: Config::get('fw_url_arg_separateur_entree'));
100: $this->setOption(self::OPTION_SEPARATEUR_SORTIE,
101: Config::get('fw_url_arg_separateur_sortie'));
102: if (is_array($options)) {
103: foreach ($options as $nomOption => $valeur) {
104: $this->setOption($nomOption);
105: }
106: }
107:
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: }
112:
113: if (preg_match('@^//([^/#?]+)@', $url, $reg)) {
114: $this->setAutorite($reg[1]);
115: $url = substr($url, strlen($reg[0]));
116: }
117:
118: $i = strcspn($url, '?#');
119: $this->chemin = substr($url, 0, $i);
120: $url = substr($url, $i);
121:
122: if (preg_match('@^\?([^#]*)@', $url, $reg)) {
123: $this->requete = $reg[1];
124: $url = substr($url, strlen($reg[0]));
125: }
126:
127: if ($url) {
128: $this->fragment = substr($url, 1);
129: }
130: }
131:
132: 133: 134: 135: 136: 137:
138: public function getSchema() {
139: return $this->schema;
140: }
141:
142: 143: 144: 145: 146: 147:
148: public function setSchema($schema) {
149: $this->schema = $schema;
150: }
151:
152: 153: 154: 155: 156: 157:
158: public function getUtilisateur() {
159: return $this->infoUtilisateur !== false ? preg_replace('@:.*$@', '', $this->infoUtilisateur) : false;
160: }
161:
162: 163: 164: 165: 166: 167: 168:
169: public function getMotDePasse() {
170: return $this->infoUtilisateur !== false ? substr(strstr($this->infoUtilisateur, ':'), 1) : false;
171: }
172:
173: 174: 175: 176: 177: 178:
179: public function getInfoUtilisateur() {
180: return $this->infoUtilisateur;
181: }
182:
183: 184: 185: 186: 187: 188: 189: 190: 191:
192: public function setInfoUtilisateur($infoUtilisateur, $motDePasse = false) {
193: $this->infoUtilisateur = $infoUtilisateur;
194: if ($motDePasse !== false) {
195: $this->infoUtilisateur .= ':' . $motDePasse;
196: }
197: }
198:
199: 200: 201: 202: 203: 204:
205: public function getHote() {
206: return $this->hote;
207: }
208:
209: 210: 211: 212: 213:
214: public function setHote($hote) {
215: $this->hote = $hote;
216: }
217:
218: 219: 220: 221: 222: 223:
224: public function getPort() {
225: return $this->port;
226: }
227:
228: 229: 230: 231: 232:
233: public function setPort($port) {
234: $this->port = intval($port);
235: }
236:
237: 238: 239: 240: 241: 242:
243: public function getAutorite() {
244: if (!$this->hote) {
245: return false;
246: }
247:
248: $autorite = '';
249:
250: if ($this->infoUtilisateur !== false) {
251: $autorite .= $this->infoUtilisateur . '@';
252: }
253:
254: $autorite .= $this->hote;
255:
256: if ($this->port !== false) {
257: $autorite .= ':' . $this->port;
258: }
259:
260: return $autorite;
261: }
262:
263: 264: 265: 266: 267:
268: public function setAutorite($autorite) {
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: }
277:
278: $this->hote = $reg[3];
279: if (isset($reg[5])) {
280: $this->port = intval($reg[5]);
281: }
282: }
283: }
284:
285: 286: 287: 288: 289:
290: public function getChemin() {
291: return $this->chemin;
292: }
293:
294: 295: 296: 297: 298:
299: public function setChemin($chemin) {
300: $this->chemin = $chemin;
301: }
302:
303: 304: 305: 306: 307: 308: 309:
310: public function getRequete() {
311: return $this->requete;
312: }
313:
314: 315: 316: 317: 318: 319:
320: public function setRequete($requete) {
321: $this->requete = $requete;
322: }
323:
324: 325: 326: 327: 328:
329: public function getFragment() {
330: return $this->fragment;
331: }
332:
333: 334: 335: 336: 337:
338: public function setFragment($fragment) {
339: $this->fragment = $fragment;
340: }
341:
342: 343: 344: 345: 346: 347:
348: public function getVariablesRequete() {
349: $pattern = '/' .
350: preg_quote($this->getOption(self::OPTION_SEPARATEUR_ENTREE), '/') .
351: '/';
352: $parties = preg_split($pattern, $this->requete, -1, PREG_SPLIT_NO_EMPTY);
353: $retour = array();
354:
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: }
362:
363: if ($this->getOption(self::OPTION_ENCODER_CLES)) {
364: $cle = rawurldecode($cle);
365: }
366: $valeur = rawurldecode($valeur);
367:
368: if ($this->getOption(self::OPTION_UTILISER_CROCHETS) &&
369: preg_match('#^(.*)\[([0-9a-z_-]*)\]#i', $cle, $matches)) {
370:
371: $cle = $matches[1];
372: $idx = $matches[2];
373:
374:
375: if (empty($retour[$cle]) || !is_array($retour[$cle])) {
376: $retour[$cle] = array();
377: }
378:
379:
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: }
394:
395: return $retour;
396: }
397:
398: 399: 400: 401: 402:
403: public function setVariablesRequete(array $tableau) {
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: }
411:
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: }
428:
429: 430: 431: 432: 433: 434:
435: public function setVariableRequete($nom, $valeur) {
436: $tableau = $this->getVariablesRequete();
437: $tableau[$nom] = $valeur;
438: $this->setVariablesRequete($tableau);
439: }
440:
441: 442: 443: 444: 445:
446: public function unsetVariableRequete($nom) {
447: $tableau = $this->getVariablesRequete();
448: unset($tableau[$nom]);
449: $this->setVariablesRequete($tableau);
450: }
451:
452: 453: 454: 455: 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: }
464:
465: 466: 467: 468: 469:
470: public function getURL() {
471:
472: $url = "";
473:
474: if ($this->schema !== false) {
475: $url .= $this->schema . ':';
476: }
477:
478: $autorite = $this->getAutorite();
479: if ($autorite !== false) {
480: $url .= '//' . $autorite;
481: }
482: $url .= $this->chemin;
483:
484: if ($this->requete !== false) {
485: $url .= '?' . $this->requete;
486: }
487:
488: if ($this->fragment !== false) {
489: $url .= '#' . $this->fragment;
490: }
491:
492: return $url;
493: }
494:
495: 496: 497: 498: 499: 500:
501: public function getURLNormalisee() {
502: $url = clone $this;
503: $url->normaliser();
504: return $url->getUrl();
505: }
506:
507: 508: 509: 510: 511:
512: public function normaliser() {
513:
514:
515:
516: if ($this->schema) {
517: $this->schema = strtolower($this->schema);
518: }
519:
520:
521: if ($this->hote) {
522: $this->hote = strtolower($this->hote);
523: }
524:
525:
526: if ($this->port &&
527: $this->schema &&
528: $this->port == getservbyname($this->schema, 'tcp')) {
529:
530: $this->port = false;
531: }
532:
533:
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: }
539:
540:
541: $this->chemin = self::supprimerSegmentsAPoints($this->chemin);
542:
543:
544: if ($this->hote && !$this->chemin) {
545: $this->chemin = '/';
546: }
547: }
548:
549: 550: 551: 552: 553:
554: public function etreAbsolue() {
555: return (bool) $this->schema;
556: }
557:
558: 559: 560: 561: 562: 563: 564: 565:
566: public function resoudre($reference) {
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: }
573:
574:
575:
576: if (!$this->getOption(self::OPTION_STRICTE) && $reference->schema == $this->schema) {
577: $reference->schema = false;
578: }
579:
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:
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: }
622:
623: $cible->fragment = $reference->fragment;
624:
625: return $cible;
626: }
627:
628: 629: 630: 631: 632: 633: 634: 635:
636: private static function supprimerSegmentsAPoints($chemin) {
637: $sortie = '';
638:
639:
640:
641: $j = 0;
642: while ($chemin && $j++ < 100) {
643:
644: if (substr($chemin, 0, 2) == './') {
645: $chemin = substr($chemin, 2);
646: } elseif (substr($chemin, 0, 3) == '../') {
647: $chemin = substr($chemin, 3);
648:
649:
650: } elseif (substr($chemin, 0, 3) == '/./' || $chemin == '/.') {
651: $chemin = '/' . substr($chemin, 3);
652:
653:
654: } elseif (substr($chemin, 0, 4) == '/../' || $chemin == '/..') {
655: $chemin = '/' . substr($chemin, 4);
656: $i = strrpos($sortie, '/');
657: $sortie = $i === false ? '' : substr($sortie, 0, $i);
658:
659:
660: } elseif ($chemin == '.' || $chemin == '..') {
661: $chemin = '';
662:
663:
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: }
676:
677: return $sortie;
678: }
679:
680: 681: 682: 683: 684: 685:
686: public static function getCanonique() {
687: if (!isset($_SERVER['REQUEST_METHOD'])) {
688:
689: throw new Exception('Le script n\'a pas été appellé à travers un serveur web');
690: }
691:
692:
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) {
699:
700: $url->port = $port;
701: }
702: return $url;
703: }
704:
705: 706: 707: 708: 709:
710: public static function getURLDemande() {
711: return self::getDemande()->getUrl();
712: }
713:
714: 715: 716: 717: 718: 719:
720: public static function getDemande() {
721: if (!isset($_SERVER['REQUEST_METHOD'])) {
722:
723: throw new Exception('Le script n\'a pas été appellé à travers un serveur web');
724: }
725:
726:
727: $url = new self($_SERVER['REQUEST_URI']);
728: $url->schema = isset($_SERVER['HTTPS']) ? 'https' : 'http';
729:
730: $url->setAutorite($_SERVER['HTTP_hote']);
731: return $url;
732: }
733:
734: 735: 736: 737: 738: 739: 740: 741: 742: 743: 744:
745: function setOption($nomOption, $valeur) {
746: if (!array_key_exists($nomOption, $this->options)) {
747: return false;
748: }
749: $this->options[$nomOption] = $valeur;
750: }
751:
752: 753: 754: 755: 756: 757: 758:
759: function getOption($nomOption) {
760: return isset($this->options[$nomOption])
761: ? $this->options[$nomOption] : false;
762: }
763:
764: public function __toString() {
765: return $this->getURL();
766: }
767: }
768: