1,30 → 1,23 |
<?php |
// declare(encoding='UTF-8'); |
/** |
* classe Url, gérant le découpage des paramètres, leurs modification etc... |
* Traduction et conversion d'une classe (NET_Url2) issue de Pear |
* |
* @category Php5 |
* @package Framework |
* Classe Url, gérant le découpage des paramètres, leurs modification etc... |
* Traduction et conversion d'une classe (NET_Url2) issue de Pear |
* |
* @category Php 5.2 |
* @package Framework |
// Auteur principal |
* @author Christian Schmidt <schmidt@php.net> |
* @author Christian Schmidt <schmidt@php.net> |
// Autre auteurs |
* @author Aurélien PERONNET <aurelien@tela-botanica.org> |
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org> |
* @copyright 2009 Tela-Botanica |
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL |
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL |
* @version SVN: $Id$ |
* @link /doc/framework/ |
* |
* @author Aurélien PERONNET <aurelien@tela-botanica.org> |
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org> |
* @copyright 2009 Tela-Botanica |
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL |
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL |
* @version SVN: $Id$ |
* @link /doc/framework/ |
*/ |
class Url |
{ |
/** |
* Parsing strict dans resoudre() (voir RFC 3986, section 5.2.2). Par défaut |
* à true. |
*/ |
const OPTION_STRICTE = 'strict'; |
class Url { |
|
/** |
* Répresenter les tableaux dans les requêtes en utilisant la notation php []. Par défaut à true. |
54,7 → 47,6 |
* vis à vis de $_GET |
*/ |
private $options = array( |
self::OPTION_STRICTE => true, |
self::OPTION_UTILISER_CROCHETS => true, |
self::OPTION_ENCODER_CLES => true, |
self::OPTION_SEPARATEUR_ENTREE => 'x&', |
133,119 → 125,33 |
$this->fragment = substr($url, 1); |
} |
} |
|
|
/** |
* Retourne le schéma, c.a.d. "http" ou "urn", ou false si aucun schéma n'est |
* spécifié, i.e. l'url est une url relative |
* Met à jour la valeur de l'option spécifiée. |
* |
* @return string|bool |
*/ |
public function getSchema() { |
return $this->schema; |
} |
|
/** |
* @param string|bool $schema |
* @param string $nomOption une des constantes commençant par self::OPTION_ |
* @param mixed $valeur valeur de l'option |
* |
* @return void |
* @see getSchema() |
* @see self::OPTION_STRICTE |
* @see self::OPTION_UTILISER_CROCHETS |
* @see self::OPTION_ENCODER_CLES |
*/ |
public function setSchema($schema) { |
$this->schema = $schema; |
} |
|
/** |
* renvoie la partie user de la partie infoUtilisateur (partie précédant le premier |
* ":"), ou false si aucune partie infoUtilisateur n'est définie. |
* |
* @return string|bool |
*/ |
public function getUtilisateur() { |
return $this->infoUtilisateur !== false ? preg_replace('@:.*$@', '', $this->infoUtilisateur) : false; |
} |
|
/** |
* renvoie la partie mot de passe de la partie infoUtilisateur (partie après le premier |
* ":"), , ou false si aucune partie infoUtilisateur n'est définie (i.e. l'URL ne contient |
* pas de "@" en face du nom d'hôte) ou si la partie infoUtilisateur ne contient pas de ":". |
* |
* @return string|bool |
*/ |
public function getMotDePasse() { |
return $this->infoUtilisateur !== false ? substr(strstr($this->infoUtilisateur, ':'), 1) : false; |
} |
|
/** |
* Renvoie la partie userinfio, ou false si celle-ci n'existe pas, i.e. si la partie |
* autorité ne contient pas de "@" |
* |
* @return string|bool |
*/ |
public function getInfoUtilisateur() { |
return $this->infoUtilisateur; |
} |
|
/** |
* Setteur pour la partie infoUtilisateur. Si deux argument sont passé, ils sont combinés |
* dans la partie infoUtilisateur de cette manière username ":" password. |
* |
* @param string|bool $infoUtilisateur infoUtilisateur ou username |
* @param string|bool $motDePasse |
* |
* @return void |
*/ |
public function setInfoUtilisateur($infoUtilisateur, $motDePasse = false) { |
$this->infoUtilisateur = $infoUtilisateur; |
if ($motDePasse !== false) { |
$this->infoUtilisateur .= ':' . $motDePasse; |
public function setOption($nomOption, $valeur) { |
if (!array_key_exists($nomOption, $this->options)) { |
return false; |
} |
$this->options[$nomOption] = $valeur; |
} |
|
/** |
* Renvoie la partie hôte, ou false s'il n'y a pas de partie autorité, c.a.d. |
* l'URL est relative. |
* |
* @return string|bool |
*/ |
public function getHote() { |
return $this->hote; |
} |
|
/** |
* @param string|bool $hote |
* |
* @return void |
*/ |
public function setHote($hote) { |
$this->hote = $hote; |
} |
|
/** |
* Renvoie le numéro de port, ou false si aucun numéro de port n'est spécifié, |
* i.e. le port par défaut doit utilisé. |
* |
* @return int|bool |
*/ |
public function getPort() { |
return $this->port; |
} |
|
/** |
* @param int|bool $port |
* |
* @return void |
*/ |
public function setPort($port) { |
$this->port = intval($port); |
} |
|
/** |
* Renvoie la partie autorité, i.e. [ infoUtilisateur "@" ] hote [ ":" port ], ou |
* false si celle-ci est absente. |
* |
* @return string|bool |
*/ |
public function getAutorite() { |
private function getAutorite() { |
if (!$this->hote) { |
return false; |
} |
270,7 → 176,7 |
* |
* @return void |
*/ |
public function setAutorite($autorite) { |
private function setAutorite($autorite) { |
$this->user = false; |
$this->pass = false; |
$this->hote = false; |
288,128 → 194,67 |
} |
|
/** |
* Renvoie la partie chemin (chemin) (éventuellement vide). |
* Renvoie vrai ou faux suivant que l'instance en cours représente une URL relative ou absolue. |
* |
* @return string |
* @return bool |
*/ |
public function getChemin() { |
return $this->chemin; |
private function etreAbsolue() { |
return (bool) $this->schema; |
} |
|
|
/** |
* @param string $chemin |
* La suppression des segments à points est décrite dans la RFC 3986, section 5.2.4, e.g. |
* "/foo/../bar/baz" => "/bar/baz" |
* |
* @return void |
*/ |
public function setChemin($chemin) { |
$this->chemin = $chemin; |
} |
|
/** |
* renvoie la chaine de requête (requete string) (sans le premier "?"), ou false si "?" |
* n'est pas présent dans l'url. |
* @param string $chemin un chemin |
* |
* @return string|bool |
* @see self::getVariablesRequete() |
* @return string un chemin |
*/ |
public function getRequete() { |
return $this->requete; |
} |
private static function supprimerSegmentsAPoints($chemin) { |
$sortie = ''; |
|
/** |
* @param string|bool $requete |
* |
* @return void |
* @see self::setVariablesRequete() |
*/ |
public function setRequete($requete) { |
$this->requete = $requete; |
} |
|
/** |
* Renvoie le nom du fragment, ou false si "#" n'est pas present dans l'URL. |
* |
* @return string|bool |
*/ |
public function getFragment() { |
return $this->fragment; |
} |
|
/** |
* @param string|bool $fragment |
* |
* @return void |
*/ |
public function setFragment($fragment) { |
$this->fragment = $fragment; |
} |
|
/** |
* Renvoie la requete string sous forme d'un tableau de variables telles qu'elles apparaitraient |
* dans le $_GET d'un script PHP |
* |
* @return array |
*/ |
public function getVariablesRequete() { |
$pattern = '/' . |
preg_quote($this->getOption(self::OPTION_SEPARATEUR_ENTREE), '/') . |
'/'; |
$parties = preg_split($pattern, $this->requete, -1, PREG_SPLIT_NO_EMPTY); |
$retour = array(); |
|
foreach ($parties as $partie) { |
if (strpos($partie, '=') !== false) { |
list($cle, $valeur) = explode('=', $partie, 2); |
} else { |
$cle = $partie; |
$valeur = null; |
} |
|
if ($this->getOption(self::OPTION_ENCODER_CLES)) { |
$cle = rawurldecode($cle); |
} |
$valeur = rawurldecode($valeur); |
|
if ($this->getOption(self::OPTION_UTILISER_CROCHETS) && |
preg_match('#^(.*)\[([0-9a-z_-]*)\]#i', $cle, $matches)) { |
|
$cle = $matches[1]; |
$idx = $matches[2]; |
|
// On s'assure que c'est bien un tableau |
if (empty($retour[$cle]) || !is_array($retour[$cle])) { |
$retour[$cle] = array(); |
// Assurons nous de ne pas nous retrouver piégés dans une boucle infinie due à un bug de cette méthode |
$j = 0; |
while ($chemin && $j++ < 100) { |
if (substr($chemin, 0, 2) == './') {// Étape A |
$chemin = substr($chemin, 2); |
} else if (substr($chemin, 0, 3) == '../') { |
$chemin = substr($chemin, 3); |
} else if (substr($chemin, 0, 3) == '/./' || $chemin == '/.') {// Étape B |
$chemin = '/' . substr($chemin, 3); |
} else if (substr($chemin, 0, 4) == '/../' || $chemin == '/..') {// Étape C |
$chemin = '/' . substr($chemin, 4); |
$i = strrpos($sortie, '/'); |
$sortie = $i === false ? '' : substr($sortie, 0, $i); |
} else if ($chemin == '.' || $chemin == '..') {// Étape D |
$chemin = ''; |
} else {// Étape E |
$i = strpos($chemin, '/'); |
if ($i === 0) { |
$i = strpos($chemin, '/', 1); |
} |
|
// Ajout des données |
if ($idx === '') { |
$retour[$cle][] = $valeur; |
} else { |
$retour[$cle][$idx] = $valeur; |
if ($i === false) { |
$i = strlen($chemin); |
} |
} elseif (!$this->getOption(self::OPTION_UTILISER_CROCHETS) |
&& !empty($retour[$cle]) |
) { |
$retour[$cle] = (array) $retour[$cle]; |
$retour[$cle][] = $valeur; |
} else { |
$retour[$cle] = $valeur; |
$sortie .= substr($chemin, 0, $i); |
$chemin = substr($chemin, $i); |
} |
} |
|
return $retour; |
return $sortie; |
} |
|
|
/** |
* @param array $tableau (nom => valeur) tableau |
* |
* @return void |
* (Re-)Création de la partie requête de l'URL à partir des données du tableau (passé en paramètre). |
* |
* @param array (nom => valeur) tableau de clés & valeurs pour la partie requête de l'url. |
* @return void (Re-)Création de la partie requête. |
*/ |
public function setVariablesRequete(array $tableau) { |
if (!$tableau) { |
public function setRequete(Array $parametres) { |
if (!$parametres) { |
$this->requete = false; |
} else { |
foreach ($tableau as $nom => $valeur) { |
foreach ($parametres as $nom => $valeur) { |
if ($this->getOption(self::OPTION_ENCODER_CLES)) { |
$nom = rawurlencode($nom); |
} |
416,106 → 261,43 |
|
if (is_array($valeur)) { |
foreach ($valeur as $k => $v) { |
$parties[] = $this->getOption(self::OPTION_UTILISER_CROCHETS) |
? sprintf('%s[%s]=%s', $nom, $k, $v) |
: ($nom . '=' . $v); |
if ($this->getOption(self::OPTION_UTILISER_CROCHETS)) { |
$parties[] = sprintf('%s[%s]=%s', $nom, $k, $v); |
} else { |
$parties[] = $nom.'='.$v; |
} |
} |
} elseif (!is_null($valeur)) { |
} else if (!is_null($valeur)) { |
$parties[] = $nom . '=' . $valeur; |
} else { |
$parties[] = $nom; |
} |
} |
$this->requete = implode($this->getOption(self::OPTION_SEPARATEUR_SORTIE), |
$parties); |
$this->requete = implode($this->getOption(self::OPTION_SEPARATEUR_SORTIE), $parties); |
} |
} |
|
/** |
* @param string $nom |
* @param mixed $valeur |
* |
* @return array |
*/ |
public function setVariableRequete($nom, $valeur) { |
$tableau = $this->getVariablesRequete(); |
$tableau[$nom] = $valeur; |
$this->setVariablesRequete($tableau); |
} |
|
/** |
* @param string $nom |
* |
* @return void |
*/ |
public function unsetVariableRequete($nom) { |
$tableau = $this->getVariablesRequete(); |
unset($tableau[$nom]); |
$this->setVariablesRequete($tableau); |
} |
|
/** |
* @param array $noms tableau des noms de variable à supprimer de l'url. |
* |
* @return void |
* (Re-)Création de la partie requête de l'URL à partir de la fusion du tableau (passé en paramètre) et |
* les valeurs présentes dans $_GET. |
* |
* @param array (nom => valeur) tableau de clés & valeurs pour la partie requête de l'url. |
* @return void (Re-)Création de la partie requête. |
*/ |
public function unsetVariablesRequete($noms) { |
$tableau = $this->getVariablesRequete(); |
foreach ($noms as $nom) { |
unset($tableau[$nom]); |
public function fusionnerRequete(Array $parametres) { |
if ($parametres) { |
$requete = $parametres + $_GET; |
$this->setRequete($requete); |
} |
$this->setVariablesRequete($tableau); |
} |
|
/** |
* Renvoie un représentation sous forme de chaine de l'URL |
* Normalise les données de l'instance d'Url faisant appel à cette méthode. |
* |
* @return string |
* @return void l'instance d'Url courrante est normalisée. |
*/ |
public function getURL() { |
// Voir RFC 3986, section 5.3 |
$url = ""; |
|
if ($this->schema !== false) { |
$url .= $this->schema . ':'; |
} |
|
$autorite = $this->getAutorite(); |
if ($autorite !== false) { |
$url .= '//' . $autorite; |
} |
$url .= $this->chemin; |
|
if ($this->requete !== false) { |
$url .= '?' . $this->requete; |
} |
|
if ($this->fragment !== false) { |
$url .= '#' . $this->fragment; |
} |
|
return $url; |
} |
|
/** |
* Renvoie une représentation de cette URL sous forme de chaine normalisée. Utile pour la |
* comparaison d'URLs |
* |
* @return string |
*/ |
public function getURLNormalisee() { |
$url = clone $this; |
$url->normaliser(); |
return $url->getUrl(); |
} |
|
/** |
* Renvoie une instance normalisée de Url |
* |
* @return Url |
*/ |
public function normaliser() { |
// See RFC 3886, section 6 |
// Voir RFC 3886, section 6 |
|
// les cchémas sont insesibles à la casse |
if ($this->schema) { |
528,10 → 310,7 |
} |
|
// Supprimer le numéro de port par défaut pour les schemas connus (RFC 3986, section 6.2.3) |
if ($this->port && |
$this->schema && |
$this->port == getservbyname($this->schema, 'tcp')) { |
|
if ($this->port && $this->schema && $this->port == getservbyname($this->schema, 'tcp')) { |
$this->port = false; |
} |
|
552,222 → 331,75 |
} |
|
/** |
* Renvoie vrai ou faux suivant que l'instance en cours représente une URL relative ou absolue. |
* Renvoie une instance d'objet Url representant l'URL canonique du script PHP en cours d'éxécution. |
* |
* @return bool |
* @return Url retourne un objet Url ou null en cas d'erreur. |
*/ |
public function etreAbsolue() { |
return (bool) $this->schema; |
} |
|
/** |
* Renvoie une instance de Url représentant une URL absolue relative à |
* cette URL. |
* |
* @param Url|string $reference URL relative |
* |
* @return Url |
*/ |
public function resoudre($reference) { |
if (is_string($reference)) { |
$reference = new self($reference); |
} |
if (!$this->etreAbsolue()) { |
throw new Exception('L\'URL de base doit être absolue !'); |
} |
|
// Un parseur non strict peut choisir d'ignorer un schema dans la référence |
// si celui ci est identique au schéma de base de l'URI. |
if (!$this->getOption(self::OPTION_STRICTE) && $reference->schema == $this->schema) { |
$reference->schema = false; |
} |
|
$cible = new self(''); |
if ($reference->schema !== false) { |
$cible->schema = $reference->schema; |
$cible->setAutorite($reference->getAutorite()); |
$cible->chemin = self::supprimerSegmentsAPoints($reference->chemin); |
$cible->requete = $reference->requete; |
public static function getCanonique() { |
$url = null; |
if (!isset($_SERVER['REQUEST_METHOD'])) { |
trigger_error("Le script n'a pas été appellé à travers un serveur web", E_USER_WARNING); |
} else { |
$autorite = $reference->getAutorite(); |
if ($autorite !== false) { |
$cible->setAutorite($autorite); |
$cible->chemin = self::supprimerSegmentsAPoints($reference->chemin); |
$cible->requete = $reference->requete; |
} else { |
if ($reference->chemin == '') { |
$cible->chemin = $this->chemin; |
if ($reference->requete !== false) { |
$cible->requete = $reference->requete; |
} else { |
$cible->requete = $this->requete; |
} |
} else { |
if (substr($reference->chemin, 0, 1) == '/') { |
$cible->chemin = self::supprimerSegmentsAPoints($reference->chemin); |
} else { |
// Concaténation chemins (RFC 3986, section 5.2.3) |
if ($this->hote !== false && $this->chemin == '') { |
$cible->chemin = '/' . $this->chemin; |
} else { |
$i = strrpos($this->chemin, '/'); |
if ($i !== false) { |
$cible->chemin = substr($this->chemin, 0, $i + 1); |
} |
$cible->chemin .= $reference->chemin; |
} |
$cible->chemin = self::supprimerSegmentsAPoints($cible->chemin); |
} |
$cible->requete = $reference->requete; |
} |
$cible->setAutorite($this->getAutorite()); |
// À partir d'une URL relative |
$url = new self($_SERVER['PHP_SELF']); |
$url->schema = isset($_SERVER['HTTPS']) ? 'https' : 'http'; |
$url->hote = $_SERVER['SERVER_NAME']; |
$port = intval($_SERVER['SERVER_PORT']); |
if ($url->schema == 'http' && $port != 80 || $url->schema == 'https' && $port != 443) { |
$url->port = $port; |
} |
$cible->schema = $this->schema; |
} |
|
$cible->fragment = $reference->fragment; |
|
return $cible; |
return $url; |
} |
|
/** |
* La suppression des segments à points est décrite dans la RFC 3986, section 5.2.4, e.g. |
* "/foo/../bar/baz" => "/bar/baz" |
* Renvoie une instance d'objet Url representant l'URL utilisée pour récupérer la requête en cours. |
* |
* @param string $chemin un chemin |
* |
* @return string un chemin |
* @return Url retourne un objet Url ou null en cas d'erreur. |
*/ |
private static function supprimerSegmentsAPoints($chemin) { |
$sortie = ''; |
|
// Assurons de ne pas nous retrouver piégés dans une boucle infinie due à un bug de |
// cette méthode |
$j = 0; |
while ($chemin && $j++ < 100) { |
// Étape A |
if (substr($chemin, 0, 2) == './') { |
$chemin = substr($chemin, 2); |
} elseif (substr($chemin, 0, 3) == '../') { |
$chemin = substr($chemin, 3); |
|
// Étape B |
} elseif (substr($chemin, 0, 3) == '/./' || $chemin == '/.') { |
$chemin = '/' . substr($chemin, 3); |
|
// Étape C |
} elseif (substr($chemin, 0, 4) == '/../' || $chemin == '/..') { |
$chemin = '/' . substr($chemin, 4); |
$i = strrpos($sortie, '/'); |
$sortie = $i === false ? '' : substr($sortie, 0, $i); |
|
// Étape D |
} elseif ($chemin == '.' || $chemin == '..') { |
$chemin = ''; |
|
// Étape E |
} else { |
$i = strpos($chemin, '/'); |
if ($i === 0) { |
$i = strpos($chemin, '/', 1); |
} |
if ($i === false) { |
$i = strlen($chemin); |
} |
$sortie .= substr($chemin, 0, $i); |
$chemin = substr($chemin, $i); |
} |
public static function getDemande() { |
$url = null; |
if (!isset($_SERVER['REQUEST_METHOD'])) { |
trigger_error("Le script n'a pas été appellé à travers un serveur web", E_USER_WARNING); |
} else { |
// On part d'une URL relative |
$url = new self($_SERVER['REQUEST_URI']); |
$url->schema = isset($_SERVER['HTTPS']) ? 'https' : 'http'; |
// On met à jour les valeurs de l'hôte et si possible du port |
$url->setAutorite($_SERVER['HTTP_hote']); |
} |
|
return $sortie; |
return $url; |
} |
|
|
/** |
* Renvoie une instance de Url representant l'URL canonique du script PHP |
* en cours d'éxécution |
* Renvoie un représentation sous forme de chaine de l'URL. |
* |
* @return string |
* @return string l'url |
*/ |
public static function getCanonique() { |
if (!isset($_SERVER['REQUEST_METHOD'])) { |
// ALERT - pas d'URL en cours |
throw new Exception('Le script n\'a pas été appellé à travers un serveur web'); |
public function getURL() { |
// Voir RFC 3986, section 5.3 |
$url = ""; |
|
if ($this->schema !== false) { |
$url .= $this->schema . ':'; |
} |
|
// on part d'une URL relative |
$url = new self($_SERVER['PHP_SELF']); |
$url->schema = isset($_SERVER['HTTPS']) ? 'https' : 'http'; |
$url->hote = $_SERVER['SERVER_NAME']; |
$port = intval($_SERVER['SERVER_PORT']); |
if ($url->schema == 'http' && $port != 80 || |
$url->schema == 'https' && $port != 443) { |
$autorite = $this->getAutorite(); |
if ($autorite !== false) { |
$url .= '//' . $autorite; |
} |
$url .= $this->chemin; |
|
$url->port = $port; |
if ($this->requete !== false) { |
$url .= '?' . $this->requete; |
} |
return $url; |
} |
|
/** |
* Renvoie l'URL utilisée pour récupérer la requête en cours |
* |
* @return string |
*/ |
public static function getURLDemande() { |
return self::getDemande()->getUrl(); |
} |
|
/** |
* Renvoie une instance de Url representant l'URL utilisée pour |
* récupérer la requête en cours |
* |
* @return Url |
*/ |
public static function getDemande() { |
if (!isset($_SERVER['REQUEST_METHOD'])) { |
// ALERTE - pas d'URL en cours |
throw new Exception('Le script n\'a pas été appellé à travers un serveur web'); |
if ($this->fragment !== false) { |
$url .= '#' . $this->fragment; |
} |
|
// On part d'une URL relative |
$url = new self($_SERVER['REQUEST_URI']); |
$url->schema = isset($_SERVER['HTTPS']) ? 'https' : 'http'; |
// On met à jour les valeurs de l'hote et si possible du port |
$url->setAutorite($_SERVER['HTTP_hote']); |
return $url; |
} |
|
/** |
* Met à jour la valeur de l'option spécifiée. |
* |
* @param string $nomOption une des constantes commençant par self::OPTION_ |
* @param mixed $valeur valeur de l'option |
* |
* @return void |
* @see self::OPTION_STRICTE |
* @see self::OPTION_UTILISER_CROCHETS |
* @see self::OPTION_ENCODER_CLES |
*/ |
function setOption($nomOption, $valeur) { |
if (!array_key_exists($nomOption, $this->options)) { |
return false; |
} |
$this->options[$nomOption] = $valeur; |
} |
|
/** |
* Renvoie la valeur de l'option specifiée. |
* |
* @param string $nomOption Nom de l'option demandée |
* |
* @return mixed |
*/ |
function getOption($nomOption) { |
return isset($this->options[$nomOption]) |
? $this->options[$nomOption] : false; |
} |
|
public function __toString() { |
|
return $this->getURL(); |
} |
} |
?> |