Url rewriting en php (respecte les régles du mod_rewrite)

Description

Pour ma premiére source c'est un mod_rewrite en PHP (Il y a une source de ce type sur le site mais elle est complétement différente) qui gére les régles originales du mod_rewrite pour Apache, y compris les expressions réguliéres. Le script permet, via la directive ErrorDocument 404 du .htaccess, de rediriger si il le faut la page demandée vers une autre page, sans modifier le contenu de la barre d'adresse (utilisation d'un include et non d'un header)
Le script gére les directives du mod_rewrite (toutes les lignes "RewriteRule"), ainsi que les erreurs 404 si l'url ne correspond à rien, et d'autres redirections

L'installation est simple et rapide : on crée un fichier .htaccess si il n'y en avait pas avant, on le met à la racine du siten et on y insére cette ligne : "ErrorDocument 404 /urlrewriting.php" (sans les " " bien sur) et en remplacant urlrewriting.php par le nom de la page php si vous l'avez renommé

Ensuite il reste à ouvrir le fichier en question et à le configurer :
- config['FichierConf'] désigne le nom du fichier qui contient toutes les régles à appliquer aux urls (ca ne doit PAS etre le fichier .htaccess, qui générerait une ereur si il ne reconnaissait pas les directives RewriteRule / RewriteEngine vu que le mod est desactivé),
- config['ForcerNonVerif'] spécifie si le script doit, ou non, vérifier si la ligne "RewriteEngine On" est présente dans le fichier (utile pour desactiver l'url rewriting quand on veut, mais peut ralentir un peut l'execution)
- config['LangueDefaut'] langue par défaut des erreurs, par défaut les langues existantes sont l'anglais et le français, l'anglais pour la "transparence" du script, les messages d'erreurs sont exactement les memes que ceux affichés en temps normal :)

Le reste des options etant facultatif (Erreurs 404) et bien commenté dans la source :)

Source / Exemple :


<?php
/////////////////////////////////////////////////////////////////////////////
// Nom :          PHPrewrite
//
// Auteur :       Shisui
//
// Version :      1.2   
//
// Description :   Remplacement PHP du mod rewrite d'Apache, permettant une 
//                redirection d'URL meme sur les serveurs n'ayant pas activé
//                ce module.  
//
// Notes :         C'est un code que j'ai trouvé plusieurs fois sur Internet, 
//                mais qui à l'origine était long à mettre en place (une
//                condition par itération).
//                 Cette version ci est donc plus perfectionnée grace à sa 
//                compatibilité avec les expressions du mod rewrite original.
//
// Installation :  L'installation est simple, il suffit de placer toutes les 
//                régles en cohésion avec le mod original dans le fichier 
//                spécifié dans la configuration ci dessous. De configurer 
//                celle ci,puis de mettre dans un .htaccess à la racine
//                de votre site : 
//                      ErrorDocument 404 /urlrewriting.php   
//    
// Changelog :    1.2 - Utilisation d'une classe sur les conseils de XaF
//						Pourquoi une classe ? Tout simplement pour améliorer
//						(un peu) la rapidité et pour que le code soit moins
//						"brouillon", il est mieux structuré et ainsi plus
//						facile à comprendre, et donc à modifier, adapter.
//                    - Mise en place du systéme de casse
//					  - Support de nouveaux flags :
//						. QSA : "Query String Append"
//
//                1.1 - Premiére version de ce script
//                1.0 - Version de base trouvée sur internet
//
// A faire :	- Compréhension des RewriteCond (30%)
//				- Flags :
//					. QSA : "Query String Append", ajouter
//							les paramétres demandés à l'url source
//							vers l'url de sortie.
//				- Debug (Ca restera toujours à faire :])
//				
//////////////////////////////////////////////////////////////////////////////
error_reporting(E_ALL);		// Affichage de toutes les erreurs

class PHPRewrite {
	// Initialisation des variables utilisées dans la classe
	var $config;
	var $erreur;
	var $UrlReq;
	var $ligne;
	var $RespectCasse;
	var $LignesTot;	
	var $url_sortie;
	var $i;
	var $QSA;
	var $Chainage;
	
	function PHPRewrite() {
		/////////////////////////////////
		//// Configuration du script ////
		/////////////////////////////////
		$this->config['FichierConf'] = ".rewritemod"; 	// Nom du fichier ou se trouvent les directives à appliquer ([Nom])
		$this->config['ForcerNonVerif'] = FALSE; 		// Focer la non-vérification de la ligne "RewriteEngine On" (TRUE|FALSE)
		$this->config['LangueDefaut'] = 'en';			// Langue utilisée pour les erreurs, voir ci dessous
		//// Fin de la configuration ////
		
		
		//// Configuration des Erreurs ////	
		$this->erreur['header']['301'] = "HTTP/1.0 301 Permanently Moved";
		$this->erreur['header']['302'] = "HTTP/1.0 302 Temporarily Moved";
		$this->erreur['header']['404'] = "HTTP/1.0 404 Not Found";	// Header à envoyer
		$this->erreur['404']['en']['titre'] = "Not Found";	// Titre de l'erreur
		$this->erreur['404']['en']['expli'] = "The requested URL %u% was not found on this server"; // Description, %u% = url demandée
		
		$this->erreur['header']['403'] = "HTTP/1.0 403 Forbidden";
		$this->erreur['403']['en']['titre'] = "Forbidden";
		$this->erreur['403']['en']['expli'] = "You don't have permission to access %u% on this server";
		
		$this->erreur['header']['410'] = "HTTP/1.0 410 Gone";
		$this->erreur['410']['en']['titre'] = "Gone";
		$this->erreur['410']['en']['expli'] = "The requested resource<br />%u%<br />is no longer available on this server and there is no forwarding address. Please remove all references to this resource.";
		///// Fin de la configuration /////		
		
		$this->Chainage = TRUE;
		$this->RespectCasse = TRUE;
		$this->UrlReq = substr_replace($_SERVER['REQUEST_URI'], '', 0, 1);
	}
	
	function OuvrirFichier() {
		if (!$this->config['ForcerNonVerif'] && !$this->mod_active()) {
			$this->erreur();
			exit;
		}
		else {				
			$handle = fopen($this->config['FichierConf'],"r");
			$texte = fread($handle, filesize($this->config['FichierConf']));
			$this->ligne = preg_split("/\n/", $texte);
			$this->LignesTot = count($this->ligne) - 1;
		}
	}
	
	function RedirectUrl() {
		$i = 0;
		while ($this->i <= $this->LignesTot) {		
			$this->ligne[$this->i] = (!empty($this->ligne[$this->i]))?$this->ligne[$this->i]:"#";	
			if (substr($this->ligne[$this->i],0,1) == "#") {
				if ($this->i == $this->LignesTot) {
					$this->erreur();
					exit;
				}
				$this->i++; 
			}
			else {
				$this->getUrlSortie($this->i);
				if ($this->url_sortie) {
					header("HTTP/1.0 200 OK");
					if (!$this->QSA) { $_GET = ""; }
					$_GET = $this->getPageVars($this->url_sortie);
					include($this->getPageVars($this->url_sortie,"page"));
					exit;
				}
				else {
					if ($this->i == $this->LignesTot) {
						$this->erreur();
						exit;
					}
				}
				$this->i++;
			}
		}	
	}
	
	function getUrlSortie($L) {
		$regle = (!empty($this->ligne[$L]))?$this->regle_info($this->ligne[$L]):FALSE;
		
		if ($regle) {
			if ((!$this->RespectCasse) || ($this->flagPresent("NC",$regle["FLAGS"]))) $Correspondance = eregi($regle['MASQUEE'], $this->UrlReq);
			else $Correspondance = ereg($regle['MASQUEE'], $this->UrlReq); 
			
			if ($Correspondance) {				
				if ($this->Chainage && ($this->flagPresent("C",$regle["FLAGS"]))) {					
					$this->url_sortie = $this->url_rewrite($this->UrlReq,$regle["MASQUEE"],$regle["MASQUES"]);
					$this->Chainage = TRUE;
					$this->UrlReq = $this->url_sortie;
					$this->url_sortie = FALSE;
				}
				else {
					if ($this->flagPresent("F",$regle["FLAGS"])) {
						$this->erreur("403");
					}
					elseif ($this->flagPresent("G",$regle["FLAGS"])) {
						$this->erreur("410");
					}
					elseif ($this->flagPresent("^R(=:alnum:{3})?$",$regle["FLAGS"])) {
						if ($this->flagPresent("^R$",$regle["FLAGS"])) {
							$this->erreur("302","en",$this->url_rewrite($this->UrlReq,$regle['MASQUEE'],$regle['MASQUES']));
						}
						else {
							ereg("^R(=:alnum:{3})?$",$this->flagPresent("^R(=:alnum:{3})?$",$regle["FLAGS"]),$res);
							$res = substr_replace($res[1],'',0,1);
							$this->erreur($res,"en",$this->url_rewrite($this->UrlReq,$regle['MASQUEE'],$regle['MASQUES']));
						}
					}
					else {
						$this->url_sortie = $this->url_rewrite($this->UrlReq,$regle["MASQUEE"],$regle["MASQUES"]);
						if ($this->flagPresent("^N$",$regle["FLAGS"])) {
							$this->UrlReq = $this->url_sortie;
							$this->url_sortie = FALSE;
							$this->i = -1;
						}
						elseif ($this->flagPresent("^C$",$regle["FLAGS"])) {
							$this->Chainage = TRUE;
							$this->UrlReq = $this->url_sortie;
							$this->url_sortie = FALSE;
						}
						elseif ($this->flagPresent("^QSA$",$regle["FLAGS"])) {
							$this->QSA = TRUE;
						}
					}
				}
			}
			else $this->url_sortie =FALSE;
		}		
		else $this->url_sortie = FALSE;	
	}
	
	function erreur($num = "404",$langue,$redir = FALSE) {
		//D: Fonction servant à afficher un message d'erreur
		if(!isset($langue)) $langue = $this->config['LangueDefaut'];
		if (!isset($this->erreur['header'][$num])) {
			exit("Titre ou description de code introuvable pour l'erreur ou la langue spécifiée");
		}
		else {
			header($this->erreur['header'][$num]);
			if (!$redir) {
				echo '<html><head><title>'.$this->erreur[$num][$langue]['titre'].'</title></head>';
				echo '<body><h1>'.$this->erreur[$num][$langue]['titre'].'</h1>'.str_replace("%u%",$_SERVER['REQUEST_URI'],$this->erreur[$num][$langue]['expli']);
				echo "<p><hr><address>".$_SERVER['SERVER_SIGNATURE']."</address></body></html>";
				exit;
			}
			else {
				header('Location: '.$redir);
				exit;
			}
		}
	}
                                   
	function mod_active() {
		//D: Fonction servant à savoir si Rewrite engine On est présent dans le fichier de régles
		//R: (TRUE|FALSE)
		$handle = fopen($this->config['FichierConf'],"r"); // ouverture du fichier
		$fichier = fread($handle, filesize($this->config['FichierConf'])); // lecture de son contenu
		fclose ($handle); // fermeture du fichier
		
		if (eregi("RewriteEngine On",$fichier)) { // si le ligne se trouve dans le fichier
			return true; // on retourne TRUE
		}
		else {
			return false; // sinon FALSE :)
		}
	}

	function regle_info($ligneregle) {
		//D: Fonction servant à retourner les informations d'une régle sous forme de tableau
		//R: Masque d'entrée (MASQUEE) Masque de sortie (MASQUES) et les Flags (Drapeaux) avec leur nombre
		$regle = str_replace("RewriteRule", '',$ligneregle); // On supprime le "RewriteRule" du début de la ligne
		
		$regle = split(" +",trim($regle)); // On sépare les différentes itérations
		if (count($regle) != 3) {
			return FALSE;
		}
		
		$flags = str_replace("]","",str_replace("[","",$regle[2])); // On enléve les [ et ] entourants les flags
		$flags = explode(",",$flags); // On les séparent
		array_unshift($flags, count($flags)); // Et on ajoute le nombre de flags en position 0
		return array("MASQUEE" => $regle[0], "MASQUES" => $regle[1], "FLAGS" => $flags); // Et on retourne le tableau de valeurs :)
	}

	function url_rewrite($page,$masqueE,$masqueS) {
		// D: Fonction qui retourne la page réécrite en fonction de la page spécifiée et 
		//    du masque d'entrée ou FALSE si ils ne correspondent pas
		// R: Page réécrite ou FALSE
		// Exemple de masque d'entrée : ^page-([0-9]+)-([0-9]+)\.html$
		// Exemple de masque de sortie : article.php?numero=$1&page=$2
		// Exemple de page demandée : article-8125-2.html
		// Page réécrite : article.php?numero=8125&page=2
		if (ereg($masqueE, $page, $vars)) { // On regarde si le masque d'entrée et la page correspondent
			$urlrewrite = $masqueS; // Si oui on définit $urlrewrite
			$i = 0; // on initialise $i
			while ($i <= count($vars) - 1) { // et on fait une boucle, tant que $i sera inférieur au nombre de variables
				$urlrewrite = str_replace("$".$i, $vars[$i], $urlrewrite); // on remplace la variable "$1" par exemple par sa correspondance $vars[1]
				$i++; // on refait un tour ...
			}
			return $urlrewrite; // on retourne l'url réécrite
		}
		else { // si le masque et la page ne correspondent pas ...
			return FALSE; // on retourne FALSE :) 
		}
	}

	function getPageVars($page,$mode = "getvars") {
		// D: Fonction qui retourne un tableau de valeur des variables passées à une page
		// R: Tableau de valeur de type Array( [VAR] => valeur )
		// Exemple : $_GET = get_page_vars("index.php?page=news&id=5");
		// Retournera le tableau $_GET tel que :
		//    $_GET['page'] == "news"
		// et $_GET['id'} == "5"
		//           $page = get_page_vars("index.php?page=news&id=5","page");
		// Retournera "index.php"
		if ($mode == "getvars") {
			if (ereg("(.*)\?(.*)",$page,$vars)) { // On vérifie que l'adresse posséde des variables (page?variables)
												  // et on récupére le bloc de variables
				$get = $vars[2]; //on définit $get comme le bloc de variable
				if (ereg("(.*)\&(.*)",$get,$vars)) { // on regarde si plusieurs vars sont définies (blocvar1&blocvar2)
					array_shift($vars);	// on supprime le $vars[0] qui correspond à $get
					foreach ($vars as $val) {	// pour chaque bloc de variables
						if (ereg("(.*)\=(.*)",$val,$var)) {	// on regarde si ca correspond bien à une déf de var (var1=valeur)
							$GETVARS[$var[1]] = $var[2]; // on définit le tableau
						}
					} 
					return $GETVARS; // que l'on retourne ...
				}
				elseif (ereg("(.*)\=(.*)",$get,$var)) { // il n'y a pas plusieurs blocs de var, donc on regarde si il y en a au moins un
					$GETVARS[$var[1]] = $var[2]; // on définit l'unique variable
					return $GETVARS; // on retourne le tableau
				}
				else { // la page passée n'a pas de variables correctes en parametres
					return FALSE; // on retourne FALSE
				}
			}
			else { // la page passée n'a pas de variables en paramétres
				return FALSE; // on retourne FALSE
			}
		}
		else {
			if (ereg("(.*)\?(.*)",$page,$vars)) { // Si la page est bien consituée de la forme url?vars
				return $vars[1]; // On retourne juste la page
			}
			else {
				return $page; // Sinon on retourne la page passée en paramétres
			}
		}
	}
	
	function flagPresent($flag,$array) {
		//D: Fonction servant à savoir si le flag $flag est précisé dans le tableau $array retourné par regle_info['FLAGS']
		//R: (FALSE|flag)
		if (!is_array($array)) {
			return FALSE;
		}
		else {
			foreach ($array as $v => $k) {
				if (ereg($flag, $k)) {
					return $k;
				}
			}
		}
	}
	
	function BaseServeur($uri) {
		//D: Fonction qui retourne la base d'une URI passée en paramétre
		//R: (uri_base)
		if (substr($uri,0,7) == "http://") { // si l'url posséde le http:// du début
			$url = split("/",$uri); // on coupe $url tout les /
			$url = $url[2]; // on prend le troisiéme
		}
		else {
			$url = split("/",$uri); 
			$url = $url[0];			
		}
		return "http://".$url; // on retourne l'url de base
	}
}

///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// Appel de la classe !
// Sans ca tout ce qu'il y a au dessus ne sert plus à rien :)
///////////////////////////////////////////////////////////////////////
$phpR = new PHPRewrite(); // Création de la classe
$phpR->OuvrirFichier(); // On ouvre le fichier et on voit si il est conforme à la config.
$phpR->RedirectUrl(); // Et hop, on redirige ...
?>

Conclusion :


Voilà les différences entre le mod original et le script :
- Le mod_rewrite d'apache permet aussi des réécritures conditionelles (RewriteCond), le script ne le fait pas encore.
- Pour l'instant c'est tout ce que j'ai trouvé comme différences :)

Voilà les bugs qu'on peut trouver :
- Si le masque de sortie d'une page ne correspond à aucune page réélle, il n'y aura pas d'erreur 404, juste une page blanche (Je vais essayer de corriger ca au plus vite :)
- C'est tout aussi :)

Je suis toujours à la recherche de quelqu'un qui maitrise la réécriture d'url "évoluée" =)

Je bosse actuellement sur la prochaine version qui prend en compte les RewriteCond :)

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.