Vous aussi dîtes “non !” aux popups window.open()

mai 23rd, 2009

Parce que window.open c’est le mal, qu’il se fait bloquer par les anti popup des navigateurs, qu’il fait tout le monde quand on les fait s’ouvrir etc., dîtes “non !” à window.open().

Ben euuuuh comment je la fais alors la popup sur monsite ?

Et bien dîtes “oui !” à la popup DHTML qui s’ouvre par dessus le reste du site (pour ceux qui ne voient vraiment pas de quoi je parle, on va dire la popup facebook like ~~).

T’es gentil mais comment je la fais moi ma popup ?

Et bien comme ça :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
</style>
<title>Page de test HTML/JS</title>
</head>
<body>

<button type="button" onclick="popup();">Click</button>

<script type="text/javascript" src="_js/jquery.js"></script>
<script type="text/javascript">
function popup(){
	var div=document.createElement('div'),
		div2=document.createElement('div'),
		width=500,
		height=500,
		top=(Math.round((document.documentElement.clientHeight-height)/2)>0) ? Math.round((document.documentElement.clientHeight-height)/2) : 0,
		left=(Math.round((document.documentElement.clientWidth-width)/2)>0) ? Math.round((document.documentElement.clientWidth-width)/2) : 0,
		css='position:absolute;top:'+top+'px;left:'+left+'px;width:'+width+'px;height:'+height+'px;border:1px dotted #000;z-index:2;padding:5px;display:none;';
	div.style.cssText=css;
	div.id='popup';
	div.innerHTML='<p>Coucou !</p>'
	+'<button type="button" onclick="fermer(\'popup\',\'popup2\');">Fermer</button>';
	document.body.appendChild(div);
	document.body.appendChild(div2);
	$('#'+div.id).fadeIn('normal');
	div2.style.cssText='position:absolute;width:'+document.documentElement.scrollWidth+'px;height:'+document.documentElement.scrollHeight+'px;top:0;left:0;background:#000;z-index:1;opacity:0.6;filter:alpha(opacity=60);display:none;';
	div2.id='popup2';
	$('#'+div2.id).fadeIn('normal');
	window.onresize=function(){
		var popup1=document.getElementById('popup'),
			popup2=document.getElementById('popup2'),
			height=parseInt(popup1.style.height.replace(/^([0-9]+)(px)?$/,'$1')),
			width=parseInt(popup1.style.width.replace(/^([0-9]+)(px)?$/,'$1'));
		popup2.style.width=document.documentElement.scrollWidth+'px';
		popup2.style.height=document.documentElement.scrollHeight+'px';
		popup1.style.top=(Math.round((document.documentElement.clientHeight-height)/2)>0) ? Math.round((document.documentElement.clientHeight-height)/2)+'px' : 0;
		popup1.style.left=(Math.round((document.documentElement.clientWidth-width)/2)>0) ? Math.round((document.documentElement.clientWidth-width)/2)+'px' : 0;
	}
}
function fermer(){
	$('#popup').fadeOut(
		'normal',
		function(){
			document.body.removeChild(document.getElementById('popup'));
		}
	);
	$('#popup2').fadeOut(
		'normal',
		function(){
			document.body.removeChild(document.getElementById('popup2'));
		}
	);
}
</script>
</body>
</html>

Tester en ligne

Explication je rajoute par dessus ma page entière 2 div en position absolute : l’une empêchant le reste du site d’être accessible par un click par exemple, et l’autre étant la popup en soit. Ici j’y ai rajouté de beaux effets JQuery mais vous n’êtes pas obligé de le faire (c’est quand même moins agressif pour le visiteur je trouve ;) ). On rajoute un évènement onresize à window pour replacer/redimensionner les div.

Et pour fermer c’est simple je retire les 2 divs ajoutées du dom et pouf, le reste de la page est de nouveau accessible :)

En espérant que ceci vous aura aidé ;)

La manipulation des cookies avec javascript : équivalent de setcookie et $_COOKIE[]

mai 18th, 2009

Je suppose que vous connaissez tous la fonction setcookie et le tableau $_COOKIE de PHP qui permettent la manipulation de cookies, mais saviez-vous que javascript peut lui aussi manipuler les cookies ? Oui bon, ok, mais vous trouvez pas la syntaxe un peu lourde et rébarbative ?

Aussi je vous offre 2 fonctions JS (en fait 3 mais l’une ne sert qu’à être appelée dans un autre) qui serait les équivalent JS de ce qu’on a en PHP : setCookie() et getCookie().

Voyons les prototype :

void setCookie(string name, object value [, int expires=null [, string path=null [, string domain=null [, boolean secure=false]]]])

object getCookie(string name)

Leur fonction est simple : getCookie retourne la valeur du cookie nommé name et pour setCookie son utilisation est la même que la version PHP à ceci près que l’expiration est un nombre de secondes à partir de maintenant (et non du 01/01/1970)

Voici les dites fonctions :

function getCookieVal(offset)
{
	var endstr=document.cookie.indexOf (";", offset);
	if (endstr==-1)
      		endstr=document.cookie.length;
	return unescape(document.cookie.substring(offset, endstr));
}
function getCookie (name)
{
	var arg=name+"=";
	var alen=arg.length;
	var clen=document.cookie.length;
	var i=0;
	while (i<clen) {
		var j=i+alen;
		if (document.cookie.substring(i, j)==arg)
                        return getCookieVal (j);
                i=document.cookie.indexOf(" ",i)+1;
                        if (i==0) break;}
	return null;
}
function setCookie (name, value)
{
	var today=new Date();
	var argv=setCookie.arguments;
	var argc=setCookie.arguments.length;
	var expires=(argc > 2) ? new Date(today.getTime()+argv[2]) : null;
	var path=(argc > 3) ? argv[3] : null;
	var domain=(argc > 4) ? argv[4] : null;
	var secure=(argc > 5) ? argv[5] : false;
	document.cookie=name+"="+escape(value)+
		((expires==null) ? "" : ("; expires="+expires.toGMTString()))+
		((path==null) ? "" : ("; path="+path))+
		((domain==null) ? "" : ("; domain="+domain))+
		((secure==true) ? "; secure" : "");
}

Télécharger le fichier source

3 fonctions pour se simplifier la vie avec mysql

mai 17th, 2009

Ça fait un moment que cette idée me trotte dans la tête, ce matin je me suis décidé à la mettre en œuvre : faire des fonctions pour se faciliter la vie avec mysql. La première pour se connecter en une seule ligne à la BDD (elle se contente donc, vous l’aurai compris, de faire un mysql_connect() suivi d’un mysql_select_db()), une autre pour fermer la connexion (alors là, encore mieux elle ne fait jamais qu’un mysql_close() ^^’ ) et la dernière par contre vachement utile, qui se charge de faire la requête, avec le traitement et la méthode de votre choix. Pas la peine de réfléchir à des trucs comme ‘ma requête va renvoyer une ou plusieurs lignes ? Je dois faire un while ou pas ?”, la fonction le fait pour vous ! Elle se contente d’exécuter le callback quand il le faut (s’il y en a un).

Voici le prototype des fonctions en question :

ressource connect (string $server, string $user, string $password, string $database)

close (ressource $connexion=null)

boolean query (string $query, function $callback=null, string $method=’array’)

Pour connect() et close() l’utilisation est pas trop compliquée, mais pour query() des détailles s’imposent :

  • callback est donc une fonction qui s’occupe du traitement du retour de la requête SQL
  • method peut prendre pour valeur ‘assoc’, ‘array’, ‘row’ ou ‘object’ qui va donc en fonction de sa valeur utiliser mysql_fetch_assoc, mysql_fetch_array, etc.
  • Cette fonction retourne false s’il y a une erreur, true en cas de succès

Alors les fonctions :

function connect($host,$user,$pwd,$db){
	$connection=mysql_connect($host,$user,$pwd);
	mysql_select_db($db);
	return $connection;
}
function close($connection=null){
	if(empty($connection)){
		mysql_close();
	}else{
		mysql_close($connection);
	}
}
function query($query,$callback=null,$method='array'){
	if(!empty($query)){
		$methods=Array('array','object','assoc','row');
		if(in_array($method,$methods)){
			$result=mysql_query($query);
			if($result){
				if(!empty($callback)){
					if($method=='array'){
						if(mysql_num_rows($result)==1){
							$callback(mysql_fetch_array($result));
						}else if(mysql_num_rows($result)&gt;1){
							while($array=mysql_fetch_array($result)){
								$callback($array);
							}
						}
						return true;
					}
					if($method=='object'){
						if(mysql_num_rows($result)==1){
							$callback(mysql_fetch_object($result));
						}else if(mysql_num_rows($result)&gt;1){
							while($array=mysql_fetch_object($result)){
								$callback($array);
							}
						}
						return true;
					}
					if($method=='assoc'){
						if(mysql_num_rows($result)==1){
							$callback(mysql_fetch_assoc($result));
						}else if(mysql_num_rows($result)&gt;1){
							while($array=mysql_fetch_assoc($result)){
								$callback($array);
							}
						}
						return true;
					}
					if($method=='row'){
						if(mysql_num_rows($result)==1){
							$callback(mysql_fetch_row($result));
						}else if(mysql_num_rows($result)&gt;1){
							while($array=mysql_fetch_row($result)){
								$callback($array);
							}
						}
						return true;
					}
				}else{
					return true;
				}
			}else{
				return false;
			}
		}else{
			return false;
		}
	}else{
		return false;
	}
}

Télécharger le fichier source

Et maintenant, un exemple :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" >
<head>
<title>Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<style type="text/css">
</style>
</head>
<body>

<?php
require_once 'functions.php';
function traitement($message){
	echo $message['id'].' : '.$message['message'].'<br />';
};
connect('localhost','root','','site_test');
query("SELECT * FROM chat",'traitement','array');
close();
?>

<script type="text/javascript">
</script>
</body>
</html>

Dans cette exemple je me connecte au localhost sous root, à la base site_test. Ensuite je fais un select sur la table ‘chat’ que j’affiche avec la fonction traitement()

Voilà j’espère que ces fonctions vous seront utiles :)

2 classes PHP pour sécuriser vos sites.

mai 16th, 2009

Hop après récolte de données sur des forums et tutos, je me suis fais une compile de 2 classes PHP5 qui vont vous permettre de sécuriser vos sites :

La première fait de l’anti SQL injection et anti XSS et la seconde est un anti brute force qui stock les tentatives non pas dans la bdd mais dans des fichiers (gain de rapidité en évitant d’appeler la base pour rien)

XSS et SQL injection

Alors je vous livre d’abord la classe puis vous explique son fonctionnement :

class Secure{
	public static function bdd($string){
		if(ctype_digit($string)){
			$string = intval($string);
		}else{
			$string = mysql_real_escape_string($string);
			$string = addcslashes($string, '%_');
		}

		return $string;
	}
	public static function html($string){
		return htmlentities($string);
	}
}

Télécharger le fichier source

Elle dispose de 2 fonctions statiques : bdd() et html().

bdd() doit être appelé pour toute insertion de données dans la base et html() avant tout affichage de données fournies, dans les cas, par les utilisateurs du site.

bdd() transforme simplement la chaine en entier si elle n’est composée que d’entier, sinon elle applique à la chaine mysql_real_escape_string() et addslashes sur _ et %, ce qui a pour effet d’écarter tout caractère qui pourrait être potentiellement dangereux.

html() lui se contente de faire un htmlentities() (pour gicler les éventuelles balises <script> par exemple :-°)

Concrètement pour vous servir de cette classe, il suffit de faire :

mysql_query("INSERT INTO maTable VALUES(".Secure::bdd($_POST['val']).")");
echo Secure::html($_POST['login']);

Source : http://www.siteduzero.com/tutoriel-3-64912-securite-php-securiser-les-flux-de-donnees.html

Attaque par brute force

Alors commençons par préciser que je tire ceci d’un tuto du site du zéro mais que j’ai refais à ma sauce version classe, la version proposée à l’origine me semblant à peu lourde à migrer d’un site à l’autre.

Voici la dite classe :

class AntiBruteForce{
	private $rep;
	function __construct($dir=null){
		$this->rep=(empty($dir)) ? 'antibrute' : $dir;
		if(!file_exists($this->rep)){
			mkdir($this->rep);
		}
	}
	function __destruct(){
		return null;
	}
	public function setRep($string){
		if(is_string($string)){
			$this->rep=$string;
		}
	}
	public function test($string){
		if(file_exists($this->rep.'/'.$string.'.tmp')){
			$file=fopen($this->rep.'/'.$string.'.tmp','r+');
			$infos=explode(';',fgets($file));
			if($infos[0]==date('d/m/Y')){
				$tries=intval($infos[1]);
			}else{
				$tries=1;
				$fileStatus='expired';
			}
		}else{
			$tries=1;
			$fileStatus='undefined';
		}
		if(!empty($fileStatus)){
			if($fileStatus=='undefined'){
				$file=fopen($this->rep.'/'.$string.'.tmp','a+');
				fputs($file,date('d/m/Y').';'.$tries);
				fclose($file);
			}else if($fileStatus=='expired'){
				fseek($file,0);
				fputs($file,date('d/m/Y').';'.$tries);
			}
		}else{
			$tries++;
			fseek($file,0);
			fputs($file,date('d/m/Y').';'.$tries);
			fclose($file);
		}
		return $tries;
	}
}

Télécharger le fichier source

Fonctionnement de la bête :

Le constructeur

On peut définir une nouvelle instance en fournissant un paramètre optionelle au constructeur. Ce paramètre sera le répertoire dans lequel seront placés les fichiers de logs de notre classe. Si ne donne pas de paramètre un répertoire ‘antibrute’ est utilisé par défaut.

Le répertoire se créée automatiquement s’il n’existe pas.

La fonction int test(string $string)

C’est cette fonction qui fait tout marcher : on lui donne une chaine en paramètre (le nom de l’utilisateur qui essaie de se connecter) et elle s’occupe de tout. Ouvrir le fichier et annalyser les données, créer le fichier s’il n’existe pas, etc. Elle retourne un entier qui est le nombre de tentatives de connexions pour cette utilisateur pour la date d’aujourd’hui. Ensuite à vous de faire le traitement selon ce nombre.

Je précise que ce script ce base sur un et un seul jour (ben oui si c’est notre vrai utilisateur mais que certains jours exceptionnellement il se trompe, à force il atteindra les quotas et sera coincé ;) )

Exemple d’utilisation

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" >
<head>
<title>Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<style type="text/css">
</style>
</head>
<body>

<?php
$maxEssais=3;
$trimmed=array_map('trim',$_POST);
if(!empty($trimmed['log']) && !empty($trimmed['pwd'])){
	require_once 'functions.php';
	mysql_connect('localhost','root','');
	mysql_select_db('site_test');
	$abf=new AntiBruteForce();
	$essais=$abf->test(Secure::bdd($trimmed['log']));
	if($essais<=$maxEssais){
		$user=mysql_fetch_array(mysql_query("SELECT * FROM login WHERE login='".Secure::bdd($trimmed['log'])."' AND mdp=MD5('".Secure::bdd($trimmed['pwd'])."')"));
		if(is_array($user)){
			echo 'logged<hr />';
		}
	}
}
if(empty($essais)){$essais=1;}
if($essais<=$maxEssais){
		echo '<form action="" method="POST">
				<input type="text" name="log" /><br />
				<input type="password" name="pwd" /><br />
				<input type="submit" name="submitted" value="ok" />
			</form>';
}else{
	echo 'vil pirate';
}
?>

<script type="text/javascript">
</script>
</body>
</html>

Ici on limite donc le nombre de tentatives à 3 mais vous pouvez en mettre bien sûr autant que vous le voulez.

Une amélioration de cette exemple serai d’envoyer un mail à l’adlinistrateur du site si le quota de tentatives est dépassé (pour pouvoir éventuellement banier l’adresse IP attaquante).

Tutoriel : l’URL rewriting

mai 16th, 2009

Hop, je fais le fainéant et je me contente de vous copier/coller le tuto que j’ai écrit pour le site du zéro :-°

Un magnifique tuto sur les secrets de l’URL rewriting et comment en faire même si le moteur de réécriture d’Apache est désactivé, grâce à PHP. Si c’est pas gentil de ma part ça ;)


Ce tutoriel a pour but de faire connaître aux lecteurs du Site du Zéro les secrets de l’obscur URL rewriting.

Monsieur, c’est quoi l’URL rewriting ?

Haha, en voilà une bonne question. L’URL rewriting est ce qui permet aux gentils développeurs de coder leurs pages et leur navigation à grand coup de variables GET (les variables qui apparaissent dans les URL des pages du genre http://www.google.fr/search?hl=fr&q=url+rewriting&btnG=Recherche+Google&meta=&aq=0&oq=url) sans pour autant qu’on se retrouve au final avec un site aux URL “laides”.

L’URL rewriting va donc nous permettre de créer de belles URL, compréhensibles par vos utilisateurs et permettant au passage d’améliorer votre référencement (oui une page du style mes-supers-tutoriels.html sera mieux référencée que index.php?p=2&id=4)

Liens utiles pour ce tutoriel :

  1. Le cours de PHP de M@teo21
  2. Les expressions régulières et PHP par M@teo21
  3. La page Wikipedia dédiée aux expressions régulières
  4. La doc. Apache sur le rewrite engine

Sommaire du tutoriel :


Pourquoi faire de l’URL rewriting et comment bien renommer mes URLs ?

Pourquoi faire de l’URL rewriting ?

Et bien comme dit en introduction, le but de l’URL rewriting dans la plupart des cas est d’obtenir des URLs propres. En effet, les sites dynamiques actuels présentent souvent des URLs complexes présentant des ? et des & pas forcément très compréhensible par les utilisateurs et encore moins par les robots d’indexation :

Exemple :

article.php?id=12&page=2&rubrique=5

Dans cet exemple, le fichier article.php est utilisé pour afficher un article dont le texte vient d’une base de données. C’est un fichier générique, qui peut afficher n’importe quel article, de n’importe quelle rubrique, page par page. Ici on cherche à afficher la page 2 de l’article numéro 12 qui fait partie de la rubrique 5.

Problème : les robots comme celui de Google n’indexent pas ce genre de pages (le googlebot s’arrête par exemple à 2 paramètres, notre exemple est donc oublié par le robot).

Ok mais ça avance à quoi ?

L’URL rewriting va nous permettre de mettre à l’intérieur de notre site des URLs plus “jolies” dans le sens où elles seront compréhensibles et indexables par les robots. Les URLs que nous allons ainsi placés ne correspondent en réalités à aucun fichier mais nous allons voir comment, grâce à l’URL rewriting, nous allons faire correspondre ces URLs factices aux URLs réellement interprétées par le serveur.

D’accord mais alors, à quoi ressemblent ces URLs propres ? Comment puis-je les faire ?

Pour reprendre notre exemple de tout à l’heure, plutôt que d’avoir

article.php?id=12&page=2&rubrique=5

, ne serait-il pas mieux d’avoir

12-2-5-nom-de-l-article.html

?

Vous vous rendez compte ? Grâce à ce type d’URL, le nom de votre article est directement référencé dans l’URL !


Le rewrite engine

Les bases théoriques

Le rewrite engine est un module d’Apache qui gère les réécritures d’URL. Nous verrons plus tard comment peut être chargé de ce dernier.

Étudions en détail les options que nous offre le module :

La directive RewriteEngine

Description : active/désactive le moteur de réécriture.
Syntaxe : RewriteEngine on|off
Par défaut :RewriteEngine off
Contexte : configuration du serveur, serveur virtuel (virtual host), répertoire, .htaccess

La directive RewriteEngine active ou désactive le fonctionnement du moteur de réécriture.

Utilisez cette directive plutôt que de commenter toutes les directives RewriteRule .

A noter que la configuration de réécriture n’est pas héritée par les serveurs virtuels. Cela signifie que vous devez avoir une directive RewriteEngine on pour chaque hôte virtuel dans lequel vous souhaitez utiliser la réécriture d’URL.

La directive RewriteOptions

Description : défini quelques options particulières pour le moteur de réécriture.
Syntaxe : RewriteOptions Options
Contexte : configuration du serveur, serveur virtuel (virtual host), répertoire, .htaccess

La directive RewriteOptions définit certaines options spéciales pour la configuration actuelle du serveur ou répertoire. La chaîne Options ne peut pour l’instant qu’être :
inherit :
Cela force la configuration actuelle à hériter de la configuration du parent. Dans le contexte d’un serveur virtuel, cela signifie que les conditions et règles du serveur principal sont héritées. Dans le contexte d’un répertoire, cela signifie que les conditions et règles du fichier .htaccess du dossier parent sont héritées.

La directive RewriteLog

Description : Définie le nom du fichier utilisé pour journaliser le processus du moteur de réécriture.
Syntaxe : RewriteLog chemin/vers/le/fichier
Contexte : Configuration du serveur, serveur virtuel

La directive RewriteLog définit le nom du fichier dans lequel le serveur journalise toutes les actions de réécriture qu’il réalise. Si le nom ne commence pas par un slash (’/'), alors il est implicite que c’est relatif à la racine du serveur. La directive ne devrait apparaître qu’une fois par configuration de serveur.

Pour désactiver la journalisation des actions de réécriture, il n’est pas recommandé de définir le nom de fichier par /dev/null, car bien que physiquement il n’y a pas de sortie de la journalisation, le serveur créé quand même une journalisation en interne. Cela ralentira le serveur sans aucun avantage pour l’administrateur ! Pour désactiver la journalisation, enlevez ou commentez la directive RewriteLog ou utilisez RewriteLogLevel 0 !
Exemple :
RewriteLog ”/usr/local/var/apache/logs/rewrite.log”

La directive RewriteLogLevel

Description : Définit la pertinence du fichier de journalisation utilisé par le moteur de réécriture
Syntaxe : RewriteLogLevel Niveau
Par défaut : RewriteLogLevel 0
Contexte : configuration du serveur, serveur virtuel

La directive RewriteLogLevel définit le niveau de pertinence du fichier de journalisation de la réécriture. Le niveau par défaut 0 signifie pas de journalisation, alors que 9 ou plus signifie que presque toutes les actions sont journalisées.

Pour désactiver la journalisation des actions de réécriture, définissez simplement Niveau à 0. Cela désactive la journalisation de toute action de réécriture.

Utiliser une haute valeur pour Niveau ralentira de façon significative votre serveur Apache ! Utilisez un fichier de journalisation à un Niveau de plus de 2 uniquement pour le débogage !
Exemple
RewriteLogLevel 3

La directive RewriteBase

Description : Définit l’URL de base pour la réécriture dans un répertoire.
Syntaxe : RewriteBase URL
Par défaut : RewriteBase Chemin/physique/du/dossier
Contexte : répertoire, .htaccess

La directive RewriteBase définie explicitement l’URL de base pour les réécritures dans un répertoire.

Exemple
Nous sommes dans un sous dossier de la racine du serveur Apache. Ce sous dossier contient un fichier .htaccess. Voici le contenu du .htaccess :
Code : Apache

1
2
3
4
5
RewriteEngine on #On active le rewrite engine
RewriteBase / #On définie la racine du serveur comme URL de base
RewriteRule ^existe-pas.html$ index.html
#On définit une règle de réécriture : http://localhost/sous-dossier/existe-pas.html
#correspond désormais à index.html de la racine du serveur soit http://localhost/index.html

La directive RewriteCond

Description : Définit une condition selon laquelle une réécriture sera effectuée.
Syntaxe : RewriteCond TestString CondPattern
Contexte : configuration serveur, serveurs virtuels, répertoire, .htaccess

La directive RewriteCond définit une condition d’application de la règle. Vous ferez précéder une directive RewriteRule par une ou plusieurs directives RewriteCond. La règle de réécriture qui suit ces conditions n’est appliquée que si son motif correspond à l’URI de la requête ET si ces conditions supplémentaires ainsi définies sont remplies.

TestString est une chaîne contenant les constructions suivantes et du texte brut :

  • Rétroréférence sur la RewriteRule : Ce sont des références sous la forme

$N
(1 <= N <= 9) par lesquelles on peut récupérer la valeur de sous-motif (parenthésé !) du motif défini dans la règle RewriteRule associée (celle qui suit le présent groupe de directives RewriteCond), après application du motif.

  • Rétroréférence sur la RewriteCond : Ce sont des références sous la forme

%N
(1 <= N <= 9) par lesquelles on peut récupérer la valeur de sous-motif (parenthésé !) du motif trouvé lors de l’application par la dernière directive RewriteCond du bloc de conditions courant.

La compréhension de ces deux références demande une bonne connaissance des expressions régulières et de leur fonctionnement. Une expression régulière permet de définir un motif de correspondance, appliqué à une portion de texte ASCII. Ce motif peut comprendre des sous-motifs, lesquels seront extraits individuellement lors de l’application du motif complet. Le résultat de ces extractions est usuellement rangé (sous Unix) dans des variables prédéfinies notées $1 à $9. Ces deux références utilisent ce principe pour réutiliser les sous-motifs obtenus par les applications successives des motifs de chaque condition (références %N), ou de la règle associée (références $N).

  • Variables-serveur : Ce sont des variables sous la forme

%{ NOM_DE_VARIABLE}
dans laquelle NOM_DE_VARIABLE peut être une chaîne à prendre dans la liste suivante :

En-têtes HTTP : connexion & requête :
HTTP_USER_AGENT REMOTE_ADDR
HTTP_REFERER REMOTE_HOST
HTTP_COOKIE REMOTE_USER
HTTP_FORWARDED REMOTE_IDENT
HTTP_HOST REQUEST_METHOD
HTTP_PROXY_CONNECTION SCRIPT_FILENAME
HTTP_ACCEPT PATH_INFO
QUERY_STRING
AUTH_TYPE
Variables internes du serveur : Variables système : Variables spéciales :
DOCUMENT_ROOT TIME_YEAR API_VERSION
SERVER_ADMIN TIME_MON THE_REQUEST
SERVER_NAME TIME_DAY REQUEST_URI
SERVER_PORT TIME_HOUR REQUEST_FILENAME
SERVER_PROTOCOL TIME_MIN IS_SUBREQ
SERVER_SOFTWARE TIME_SEC
SERVER_VERSION TIME_WDAY
TIME

Ces variables correspondent toutes aux noms de champs d’en-tête HTTP, aux variables C du serveur Apache ou aux champs de la structure struct tm du système Unix.

Notes spéciales :

  1. Les variables SCRIPT_FILENAME et REQUEST_FILENAME contiennent la même valeur, à savoir, la valeur du champ filename de la structure C interne request_rec du serveur Apache. Le premier nom de variable correspond au nom usuel de la variable CGI tandis que le second est la contrepartie de la variable REQUEST_URI (correspondant au contenu du champ uri dans la structure request_rec).
  2. On pourra utiliser le format spécial : %{ENV:variable} dans lequel variable peut être tout nom de variable d’environnement. Cette variable sera recherchée dans les structures de données internes d’Apache ou (si elle n’y est pas trouvée) via la fonction C getenv() exécutée par le processus serveur d’Apache.
  3. On pourra également utiliser le format spécial : %{HTTP:header} dans lequel header peut être tout nom MIME de champ d’en-tête HTTP. Sa valeur est obtenue à partir de la requête HTTP. Exemple : %{HTTP:Proxy-Connection} récupère la valeur du champ d’en-tête HTTP “Proxy-Connection:”.
  4. On notera un autre format spécial : %{LA-U:url} pour des résolutions internes sur URL. Son utilisation déclenche une sous-requête interne pour connaître l’URL finale pour l’URL url.
  5. On définit un dernier format spécial : %{LA-F:fichier} pour des résolutions internes sur fichier. Son utilisation déclenche une sous-requête interne pour connaître la valeur finale de fichier.

CondPattern est le motif définissant la condition, c’est-à-dire une expression régulière à appliquer à la valeur courante de TestString, c’est à dire que TestString sera évaluée et le motif CondPattern y sera recherché.

Souvenez-vous : CondPattern est une expression régulière étendue standard avec quelques ajouts :

  1. Vous pouvez faire précéder la chaîne de motif par ‘!’ (point d’exclamation) pour inverser le sens du test (la condition est vraie si le motif n’est pas trouvé).
  2. Il existe certaines variantes particulières de CondPatterns. A la place d’une chaîne d’expression régulière, vous pourrez utiliser l’une des expressions suivantes :
    • <CondPattern‘ (est lexicographiquement inférieur à)
      Traite CondPattern comme une chaîne de texte brut et la compare lexicographiquement à TestString. Le résultat est vrai si TestString est lexicographiquement inférieur à CondPattern.
    • >CondPattern‘ (est lexicographiquement supérieur à)
      Traite CondPattern comme une chaîne de texte brut et la compare lexicographiquement à TestString. Le résultat est vrai si TestString est lexicographiquement supérieur à CondPattern.
    • =CondPattern‘ (est lexicographiquement égal à)
      Traite CondPattern comme une chaîne de texte brut et la compare lexicographiquement à TestString. Le résultat est vrai si TestString est lexicographiquement égal à CondPattern, c’est-à-dire que les deux chaînes sont rigoureusement égales (caractère par caractère). Si CondPattern se limite à “” (la chaîne vide), TestString est comparé (logiquement) à la chaîne vide.
    • -d‘ (est un répertoire : directory)
      Considère TestString comme un chemin d’accès et teste si l’objet existe et est un répertoire.
    • -f‘ (est un fichier normal : file)
      Considère TestString comme un chemin d’accès et teste si l’objet existe et est un fichier normal (c’est à dire ni un répertoire, ni un lien symbolique).
    • -s‘ (est un fichier normal non vide : sized file)
      Considère TestString comme un chemin d’accès et teste si l’objet existe et est un fichier normal (c’est à dire ni un répertoire, ni un lien symbolique) et est de taille non nulle.
    • -l‘ (est un lien symbolique : link)
      Considère TestString comme un chemin d’accès et teste si l’objet existe et est un lien symbolique.
    • -F‘ (est un fichier existant, via une sous-requête)
      Vérifie si TestString représente un fichier valide et accessible compte tenues toutes les restrictions d’accès configurées pour ce serveur et ce chemin d’accès. Cette expression lance une sous-requête interne pour tester cet état, et il est prudent de l’utiliser avec parcimonie car elle diminue les performances globales du serveur !
    • -U‘ (is existing URL via subrequest)
      Vérifie si TestString représente une URL valide et accessible compte tenues toutes les restrictions d’accès configurées pour ce serveur et ce chemin d’accès. Cette expression lance une sous-requête interne pour tester cet état, et il est prudent de l’utiliser avec parcimonie car elle diminue les performances globales du serveur !

Note : Tous ces tests peuvent être préfixés du caractère (’!') pour en inverser le sens d’interprétation.

Vous pouvez de plus ajouter certains commutateurs au CondPattern en ajoutant un troisième argument :

[flags]

à la directive RewriteCond. Flags est une liste de commutateurs ci-après définis séparés par des virgules :

  • nocase|NC‘ (Pas de casse : no case)
    La casse est indifférente dans ce cas, c’est-à-dire qu’il n’y aura aucune différence entre les lettres ‘A-Z’ et ‘a-z’, que ce soit dans la chaîne TestString ou CondPattern.
  • ornext|OR‘ (ou condition suivante)
    Utilisez ce commutateur pour lier deux conditions dans un OU local plutôt que le ET implicite. Exemple typique :
    Code : Apache

    1
    2
    3
    4
    RewriteCond %{REMOTE_HOST}  ^hôte1.*  [OR]
    RewriteCond %{REMOTE_HOST}  ^hôte2.*  [OR]
    RewriteCond %{REMOTE_HOST}  ^hôte3.*
    RewriteRule #...la règle vaut pour l'un de ces trois hôtes...
    

Sans l’existence de ce commutateur vous auriez du écrire trois fois l’ensemble des conditions plus la règle.

Exemple
Pour réécrire la Homepage d’un site suivant la valeur du champ User-Agent: de la requête, vous pouvez utiliser les écritures suivantes :
Code : Apache

1
2
3
4
5
6
7
RewriteCond  %{HTTP_USER_AGENT}  ^Mozilla.*
RewriteRule  ^/$                 /homepage.max.html  [L]

RewriteCond  %{HTTP_USER_AGENT}  ^Lynx.*
RewriteRule  ^/$                 /homepage.min.html  [L]

RewriteRule  ^/$                 /homepage.std.html  [L]

Interprétation : si vous utilisez Netscape Navigator (qui s’identifie lui-même sous le nom ‘Mozilla’), alors vous récupérerez la version “haute définition” de la homepage, avec les cadres, les images, etc. Si vous utilisez le navigateur Lynx (basé sur une approche Terminal), alors vous serez routé vers la Homepage “basse définition”, qui est écrite sans images, sans tables, etc. Si vous utilisez n’importe quel autre navigateur, on vous routera sur une version “moyenne”.

La directive RewriteRule

Description : Définit les règles pour le moteur de réécriture.
Syntaxe : RewriteRule motif substitution
Contexte : configuration serveur, hôtes virtuels, répertoire, .htaccess

La directive RewriteRule est le véritable cheval de trait de la réécriture. Elle peut apparaître plus d’une fois. Chaque directive définit alors une règle unique de réécriture. L’ordre dans lequel les définitions de ces règles est faite a une grande importance, car cet ordre conditionne celui dans lequel les règles sont appliquées à l’exécution.

Motif peut être une expression régulière (Système V8 pour Apache 1.1.x et POSIX pour Apache 1.2.x) qui sera appliquée sur l’URL courante. Ici, “courante” signifie la valeur de l’URL au moment ou la règle est appliquée. Celle-ci n’est pas nécessairement l’URL initialement demandée, car plusieurs directives de réécriture auront pu altérer cette URL avant que celle-ci soit applicable.

Voici quelques notes au sujet de la syntaxe d’expressions régulières :

^ Recherche en début de ligne
$ Recherche en fin de ligne
. Un caractère quelque soit sa valeur
[chars] Un caractère dans la liste
[^chars] Un caractère autre que ceux de a liste

? 0 ou 1 fois le caractère qui précède
* 0 à N fois le caractère qui précède
+ 1 à N fois le caractère qui précède

char déspécialise le méta caractère qui suit (ex. pour mentionner les caractères littéraux “.[]()” etc.)

(string) sous-motif (le Nième sous-motif peut être réutilisé dans l’expression de substitution par la variable prédéfinie $N)

En plus des règles standard d’expressions régulières, l’opérateur de négation unaire (’!') peut être préfixé au motif. Ceci permet de demander la condition inverse (logiquement) pour dire par exemple : “si l’URL courante NE CORRESPOND PAS à ce motif“. Ceci peut être utilisé lorsqu’il est plus facile d’exprimer les conditions inverses ou comme dernière règle par défaut.

Lorsque vous utilisez l’opérateur ! pour inverser la signification du motif, vous ne pouvez pas définir de sous motif dans ce motif. C’est effectivement incohérent du fait que comme le motif n’est pas trouvé dans la chaîne analysée, les sous-motifs ne peuvent pas recevoir décemment de contenu. Donc, lorsque vous utilisez cet opérateur, vous ne pouvez utiliser les variables $N dans l’expression de substitution !

L’expression de Substitution d’une règle de réécriture est une chaîne qui définit par quoi est substituée (ou remplacée) l’URL qui correspond au motif. En plus du texte brut, vous pouvez utiliser :

  1. Des rétro-références $N sur des sous-motifs de la condition de règle
  2. Des rétro-références %N sur les sous-motifs de la RewriteCond précédente
  3. Des variables “serveur” de la même façon que dans les expressions de condition (%{VARNAME})

Les rétro-références $N (N=1..9) sont des “variables prédéfinies” qui permettent d’accéder au contenu du Nième sous-motif du motif trouvé. Les variables “serveur” sont les mêmes que celles utilisées dans les directives TestString ou RewriteCond.

Comme il y a déjà été fait allusion, toutes les règles de réécriture sont appliquées à la Substitution précédente (dans l’ordre où elles sont définies dans le fichier de configuration). L’URL est complètement remplacée par l’expression de substitution et le traitement de recomposition de l’URL finale continue tant qu’il reste encore des règles à appliquer (où alors explicitement lorsque le commutateur spécial L est marqué en fin de ligne - voir ci-dessous).

Il est défini une expression de substitution spéciale ‘-’ qui signifie : PAS de substitution ! Bizarre, non ? Non, il est utile de disposer de règles de réécriture qui “déclenchent” sur une certaine URL tout en n’effectuant aucune modification, par exemple, en conjonction avec le commutateur C (chaînage) qui permet à plusieurs motifs successifs d’être testés sur la même URL avant que toute modification n’y soit portée.

Vous pouvez même, par une expression de substitution, réécrire une URL contenant des paramètres de requête. Il suffit pour cela d’ajouter le célèbre point d’interrogation (’?') qui sépare habituellement la partie URI de la partie argument de requête lequel sera passé à la variable QUERY_STRING. Si vous voulez effacer une chaîne de requête déjà présente dans l’URL originale, terminez l’expression de substitution par un point d’interrogation seul.
Voici une fonctionnalité spéciale. Lorsque vous préfixez une expression de substitution par http://cetHôte[:cePort] alors le module mod_rewrite arrête automatiquement son traitement. Cette auto-réduction en cas d’URL redirigées implicitement en externe est une fonction utile et importante lorsqu’elle vient en combinaison d’une fonction de correspondance qui génère la partie “hôte” de l’adresse. Voir le premier exemple dans la section d’exemples ci après pour comprendre pourquoi.
Une redirection externe inconditionnelle vers votre propre serveur ne fonctionnera pas lorsque le préfixe http://cetHôte apparaît, à cause de ce principe. pour effectuer une telle auto-redirection, Vous devrez utiliser le commutateur R (voir ci-dessous).

Vous pouvez de plus ajouter certains commutateurs au champ substitution en ajoutant un troisième argument sous la forme :

[flags]

à la directive RewriteRule. Flags est une liste de commutateurs ci-après définis séparés par des virgules :

  • redirect|R[=code]‘ (force la redirection)
    Préfixez substitution par une chaîne de type http://cetHôte[:cePort]/ (qui fait de cette nouvelle URL une URI) pour forcer une redirection externe. Si aucun code n’est mentionné, un code de réponse HTTP 302 (MOVED TEMPORARILY) sera utilisé par défaut. Si vous souhaitez renvoyer un autre code de réponse, dans les séries 300 ou 400, mentionnez ce code sous forme numérique ou utilisez l’une des constantes symboliques ci-après : temp (défaut), permanent, seeother. Utilisez cette fonction pour des règles qui auraient tendance à canoniser les URL et les renvoyer ainsi au client, ex. qui traduisent “/~” en “/u/” ou ajoutent systématiquement un slash à /u/user, etc.

    Lorsque vous marquez ce commutateur, assurez-vous que l’expression de substitution est bien une URL valide ! Si ce n’est pas le cas, vous redirigez la requête vers un document qui n’existe pas ! Souvenez-vous aussi que l’action de ce commutateur ne fait que préfixer l’URL par http://cetHôte[:cePort]/, et c’est la procédé classique de réécriture qui fait le reste. Vous souhaiterez de plus arrêter, en général, le traitement de réécriture à ce moment et déclencher la redirection immédiatement. Vous devrez pour ce faire marquer en plus le commutateur ‘L’.
  • forbidden|F‘ (force l’URL à apparaître comme interdite : forbidden)
    Ceci force l’URL courante sur l’URL interdite, c’est à dire que le serveur enverra immédiatement une réponse HTTP de code 403 (FORBIDDEN). Utilisez ce commutateur en conjonction avec des directives RewriteConds appropriées pour bloquer l’accès à certaines URL sous certaines conditions.
  • gone|G‘ (force l’URL à apparaître come une redirection définitive : gone)
    Ceci force une réponse HTTP de code 410 (GONE). Utilisez ce commutateur pour marquer que les ressources demandées ont définitivement “déménagé”.
  • proxy|P‘ (force la redirection sur proxy)
    Ce commutateur force l’URL substituée être redirigée en interne au titre de requête proxy (et arrête de ce fait tout processus de réécriture) et passe immédiatement le résultat au module proxy. Vous devez vous assurer que l’expression de substitution est bien une URI valide (ex. typique http://) qui peut être traitée par le module proxy d’Apache. Sinon, vous génèrerez une erreur dans le gestionnaire proxy. Utilisez ce commutateur pour obtenir une implémentation plus puissante de la directive ProxyPass du module mod_proxy, permettant d’intégrer des ressources distantes à l’espace du serveur local.

    Il vous faut nécessairement mentionner ProxyRequests On dans la configuration de votre serveur pour éviter que des requêtes proxy ne se terminent en un “core-dump” du kernel Apache. Si vous n’avez pas compilé le module proxy dans Apache, alors vous n’avez pas ce problème, car le module mod_rewrite vérifie auparavant la disponibilité du module proxy et ignore les redirections proxy si ce module se révèle indisponible.
  • last|L‘ (dernière règle : last rule)
    Arrête le traitement de réécriture en ce point et n’applique plus aucune règle de réécriture postérieure. Ceci correspond à l’instruction Perl last ou au break du C dans une boucle. Utilisez ce commutateur pour éviter que l’URL réécrite par cette règle ne soit à son tour modifiée une nouvelle fois par d’autres règles pour lesquelles le motif pourrait correspondre. Par exemple, vous pouvez l’utiliser pour réécrire l’URL d’accès à root (’/') vers une URL opérationnelle, comme ‘/e/www/’.
  • next|N‘ (rebouclage : next round)
    Ré-exécute le traitement de réécriture (en recommençant par la première règle), mais à partir de l’URL obtenue par application de la règle courante (et non à partir de l’URL originale, d’où on ne sortirait pas). Ceci correspond à l’instruction Perl next ou continue du langage C. Utilisez ce commutateur pour recommencer le traitement de réécriture, c’est-à-dire pour immédiatement reboucler au début de la boucle.

    Faîtes très attention de ne pas créer de boucle infinie !
  • chain|C‘ (chainage à la règle suivante)
    Ce commutateur chaîne la règle courante à la règle suivante (laquelle peut à son tour être chaînée à la règle encore suivante etc.). Ceci à l’effet suivant : si une règle est “déclenchée”, alors le traitement se continue comme d’habitude, c’est-à-dire que ce commutateur n’a pas d’effet particulier. Si la règle n’est pas activée, alors toutes les règles chaînées qui suivent sont ignorées. Par exemple, vous pourriez l’utiliser pour éliminer la partie “.www” dans une règle de contexte répertoire définie pour des cas de redirection externe (où la partie “.www” ne doit pas apparaître !).
  • type|T=type-mime‘ (force le type MIME)
    Force le type MIME du fichier cible à la valeur spécifiée par mime-type. Par exemple, il peut permettre le simuler l’ancienne directive ScriptAlias du module mod_alias par laquelle on attribuerait à tous les fichiers d’un répertoire un type MIME “application/x-httpd-cgi“, quelle que soit leur extension.
  • nosubreq|NS‘ (utilisé uniquement si la requête n’est pas une sous requête interne : no internal sub-request)
    Ce commutateur force le moteur de réécriture à sauter la règle si la requête courante est une sous-requête interne. Un exemple d’utilisation de sous-requêtes intervient lorsque le module mod_include d’Apache essaie de rechercher des informations sur l’existence éventuelle de fichiers par défaut dans les répertoires (index.xxx). Lors de ces sous-requêtes, il n’est pas toujours utile, voir parfois même carrément néfaste d’appliquer une nouvelle fois tout l’ensemble de règles de réécriture. Vous utiliserez alors ce commutateur pour exclure ces règles pouvant conduire à une erreur de traitement.

    Vous pouvez vous appuyer sur la règle suivante pour prendre votre décision : lorsque vous préfixez certaines URL vers des scripts CGI, pour les forcer à exécutées en tant que CGI, il y a de fortes chances que vous tombiez sur un os (ou même sur une défaillance générale) lors des sous-requêtes. Dans ce cas, utilisez ce commutateur.
  • qsappend|QSA‘ (Ajout de chaîne de requête : query string append)
    Ce commutateur force l’ajout d’une chaîne argument de requête dans l’URL substituée à l’argument existant, au lieu de remplacer purement et simplement cet argument comme dans le cas normal. Vous utiliserez ce commutateur lorsque vous voudrez ajouter des paramètres à une requête argumentée par une opération de réécriture.
  • passthrough|PT‘ (pass through)
    Ce commutateur force le moteur de réécriture à renseigner le champ uri de la structure interne request_rec avec la valeur du champ filename. Ce commutateur est juste une “bidouille” pour permettre un post-traitement de la sortie de directives RewriteRule par des directives Alias, ScriptAlias, Redirect, et autres translateurs URI-vers-fichier. Voici un exemple trivial pour en décrire la sémantique :
    Si vous souhaitez translater /abc en /def via le moteur de réécriture du module mod_rewrite puis /def en /ghi par une directive de mod_alias :
    Code : Apache

    1
    2
    RewriteRule ^/abc(.*)  /def$1 [PT]
    Alias       /def       /ghi
    

    Si vous omettez le commutateur PT alors mod_rewrite fera le travail attendu, c’est-à-dire transformera uri=/abc/… en filename=/def/… comme n’importe quel translateur URI-vers-fichier conforme à l’API Apache l’aurait fait. Puis mod_alias intervient et essaie d’effectuer sa translation dans laquelle elle échouera, essayant de se baser sur l’URL originale et non l’URL modifiée par la précédente.

    Vous DEVEZ utiliser ce commutateur si vous souhaitez mélanger des directives de différents modules actionnant des translateurs URL-vers-fichier. L’exemple typique est cette utilisation de mod_alias et mod_rewrite.
  • skip|S=num‘ (sauter la/les prochaine(s) règle(s))
    Ce commutateur force le moteur de réécriture à sauter les num règles suivantes lorsque la règle courante s’applique. Vous pouvez l’utiliser pour simuler des pseudos structures de contrôle de type SI-ALORS-SINON : La dernière règle de la section ALORS doit devenir une règle sautant N nouvelles règles (skip=N), ces N règles suivantes constituant ainsi la section SINON de la structure. (Ceci est différent du comportement du commutateur ‘chain|C’ !).
  • env|E=VAR:VAL‘ (définir une variable d’environnement)
    Force la définition d’une variable d’environnement VAR à la valeur VAL. VAL peut être exprimée par une expression contenant des rétroréférences $N et %N qui seront substituées à l’initialisation de la variable. Vouspouvez utiliser ce commutateur plusieurs fois pour définir plusieurs variables en même temps. Ces variables pourront être par la suite réutilisées dans de nombreuses situations, dont les plus courantes sont à l’intérieur d’un traitement XSSI (via la commande <!–#echo var=”VAR”–>) ou d’un CGI (ex. $ENV{’VAR’}). Vous pourrez aussi l’atteindre dans un motif de RewriteCond ultérieur via l’écriture %{ENV:VAR}. Vous pouvez utiliser cette fonctionnalité lorsque vous souhaitez enlever des portions d’URLs, tout en mémorisant les morceaux enlevés.
N’oubliez jamais que le motif est appliqué à l’URL entière lorsque les conditions sont écrites dans un contexte de configuration serveur. Par contre, dans un contexte de répertoire, le préfixe de chemin d’accès (le chemin partiel vers ce répertoire, qui devrait toujours être le même !) est d’abord retiré de l’URL avant que le motif ne soit appliqué, puis rajouté de nouveau tel que après la substitution. Ce comportement est fondamental dans de nombreuses programmations de réécriture, dans la mesure où, sans cette opération sur le chemin d’accès partiel, il vous faudrait un motif complet prenant en compte l’arborescence aïeule, ce qui n’est pas toujours possible.
Une exception à cela : lorsqu’une expression de substitution commence par “http://” alors le préfixe indiquant le chemin partiel au répertoire courant ne sera pas rajouté, et une redirection externe ou un transfert au proxy (si le commutateur P est marqué !) est opérée.
Pour pouvoir exploiter le moteur de réécriture sur une base de configuration de contexte répertoire, vous devrez inscrire RewriteEngine On dans les fichiers de configuration .htaccess et avoir l’Option FollowSymLinks disponible. Si votre administrateur a désactivé la surcharge des options FollowSymLinks pour votre répertoire utilisateur, vous ne pourrez pas utiliser le moteur de réécriture. Cette restriction est nécessaire pour des raisons de sécurité.

Ci dessous sont données les diverses combinaisons de substitution et leur signification :

Dans une configuration de niveau “serveur” (httpd.conf)
pour une requête “GET /unChemin/uneInfo“:

Règle Substitution résultante
^/unChemin(.*) autreChemin$1 non supporté, car non valide !
^/unChemin(.*) autreChemin$1 [R] non supporté, car non valide !
^/unChemin(.*) autreChemin$1 [P] non supporté, car non valide !
^/unChemin(.*) /autreChemin$1 /autreChemin/uneInfo
^/unChemin(.*) /autreChemin$1 [R] http://cetHôte/autreChemin/uneInfo via une redirection externe
^/unChemin(.*) /autreChemin$1 [P] non supporté !
^/unChemin(.*) http://cetHôte/autreChemin$1 /autreChemin/uneInfo
^/unChemin(.*) http://cetHôte/autreChemin$1 [R] http://cetHôte/autreChemin/uneInfo via une redirection externe
^/unChemin(.*) http://cetHôte/autreChemin$1 [P] non supporté !
^/unChemin(.*) http://autreHôte/autreChemin$1 http://autreHôte/autreChemin/uneInfo via une redirection externe
^/unChemin(.*) http://autreHote/autreChemin$1 [R] http://autreHôte/autreChemin/uneInfo via une redirection externe (le flag [R] est redondant)
^/unChemin(.*) http://autreHôte/autreChemin$1 [P] http://autreHôte/autreChemin/uneInfo via proxy interne

Dans un fichier de configuration pour le répertoire /unChemin
(c.-à-d. un fichier /.htaccess dans le répertoire /chemin/physique/vers/unChemin contenant une directive RewriteBase /unChemin)
pour une requête “GET /somepath/localpath/uneInfo” :

Règle Substitution résultante
^cheminLocal(.*) autreChemin$1 /unChemin/autreChemin/uneInfo
^cheminLocal(.*) autreChemin$1 [R] http://cetHôte/unChemin/autreChemin/uneInfo via une redirection externe
^cheminLocal(.*) autreChemin$1 [P] non supporté !
^cheminLocal(.*) /autreChemin$1 /autreChemin/uneInfo
^cheminLocal(.*) /autreChemin$1 [R] http://cetHôte/autreChemin/uneInfo via une redirection externe
^cheminLocal(.*) /autreChemin$1 [P] non supporté !
^cheminLocal(.*) http://cetHôte/autreChemin$1 /autreChemin/uneInfo
^cheminLocal(.*) http://cetHôte/autreChemin$1 [R] http://cetHôte/autreChemin/uneInfo via une redirection externe
^cheminLocal(.*) http://cetHôte/autreChemin$1 [P] non supporté !
^cheminLocal(.*) http://autreHôte/autreChemin$1 http://autreHôte/autreChemin/uneInfo via une redirection externe
^cheminLocal(.*) http://autreHôte/autreChemin$1 [R] http://autreHôte/autreChemin/uneInfo via une redirection externe (le flag [R] est redondant)
^cheminLocal(.*) http://autreHôte/autreChemin$1 [P] http://autreHôte/autreChemin/uneInfo via proxy interne

Sources : Doc officielle Apache sur mod_rewrite (en)


URL rewriting et .htaccess - Les prérequis

Les prérequis

Le principal prérequis est que votre serveur Apache est son rewrite engine d’activé.

Comment savoir si mon rewrite engine est activé ?
Si vous êtes chez un hébergeur, allez vous renseigner auprès de votre hébergeur, c’est lui qui s’occupe de configurer votre serveur Apache. Si vous hébergez vous-même et que vous avez donc accès à votre configuration Apache, cherchez le fichier nommé httpd.conf et cherchez y la ligne suivante : LoadModule rewrite_module modules/mod_rewrite.so. Normalement, il devrait y avoir un # au début de la ligne, supprimez le. Le # sert en fait à commenter la ligne ce qui fait que le module n’était pas chargé. Une fois le # supprimé, redémarrez votre serveur Apache. S’il n’y avait pas de #, c’est que le module était déjà chargé, vous pouvez passer à la suite.
Si vous n’avez pas accès à la configuration d’Apache et que le module n’est pas activé, reportez-vous à la section L’URL rewriting sans le rewrite engine - utilisation de PHP.

URL rewriting et .htaccess - Mon premier URL rewriting

Mon premier URL rewriting

On va faire un premier exemple d’URL rewriting simple : test.html qui sera en fait la page test.php.

Commençons par créer une page test.php et insérons-y le code suivant :

>?php
echo'mes debuts en url rewriting'
?>

Ensuite il nous faut créer un fichier .htaccess. Pour les personnes sous Windows,
selon les versions, on ne peut pas créer de fichier sans
nom, puisque pour lui .htaccess est une extension alors
qu’en réalité c’est un fichier destiné à être un fichier caché (sous les systèmes UNIX, les fichiers ou dossier commençant par un . sont des fichiers et dossiers cachés). Créez donc un fichier htaccess.txt, ouvrez le avec le bloc note par exemple et faites enregistrer-sous->.htaccess.

Ensuite éditez votre fichier .htaccess avec le bloc note par exemple et mettez y le code suivant :

RewriteEngine On
RewriteRule ^test.html$ /test.php

Voilà c’est fini. Si vous mettez vas fichiers test.php et .htaccess à la racine de votre serveur HTTP (dans le dossier www, par défaut) et que vous faites : http://127.0.0.1/test.html vous devriez obtenir : mes debuts en url rewriting

Si ça ne marche pas :

  1. Vérifiez que vous n’avez pas déjà de page test.html
  2. Faites ce qui suit :

Il se peut que votre serveur soit configuré de manière différente ou ai une version différente. Remplacez le code par le suivant :

RewriteEngine On
RewriteRule ^test.html$ /test.php [L]

Ca ne marche toujours pas ? On essaye avec ça :

RewriteEngine On
RewriteBase /
RewriteRule ^test.html$ test.php [L]

Et si ça ne marche toujours pas, vous êtes sûr que le module de rewrite est chargé ?

URL rewriting avancé

Tout ça c’est super mais je fais comment moi si mes URL sont plus dynamiques que ça et contiennent par exemple des variables GET représentant un identifiant ?

On y vient, on y vient. Reprenons l’exemple précédent : on a un fichier test.php et un fichier .htaccess. Admettons qu’on veuille accéder à la page test.php?p=xx est un nombre. On va pouvoir récupérer ce x pour l’afficher dans notre page test.php

Dans le fichier test.php, insérer ce code :

<?php
echo $_GET['p'];
?>

Et dans le .htaccess :

RewriteEngine On
RewriteRule ^([0-9]+)-test.html$ /test.php?p=$1

Ensuite : http://localhost/1-test.html. Cela devrait vous afficher un 1. Vous pouvez vous amuser à changer le chiffre dans l’URL x-test.html et constater que le nombre affiché est bien celui-là.

Et bien les parenthèses que nous avons mis sont des parenthèses capturantes. Elles permettent de récupérer ce qu’il y a à l’intérieur grâce au $1 que nous avons mis plus loin. On peut bien sûr ajouter des parenthèses pour avoir des $2, $3, etc.


L’URL rewriting sans le rewrite engine - utilisation de PHP

Votre hébergeur ne permet l’URL rewriting car le module de réécriture n’est pas chargé mais vous aimeriez quand même faire de la réécriture ? C’est possible ! Comment ? Avec un poil de .htaccess et surtout du PHP.

En théorie

Alors dans la théorie on va en fait utiliser le fichier .htaccess pour définir une page 404 personnalisée. Voir la page erreur 404 de Wikipedia.
C’est ensuite dans cette page d’erreur que nous définirons les règles de réécriture.

Dans la pratique

Prenons un fichier .htaccess, un fichier test.php et un fichier erreur404.php

Dans le fichier .htaccess mettons ceci :

ErrorDocument 404 /erreur404.php

Dans le fichier test.php :

<?php
echo $_GET['p'];
?>

Dans le fichier erreur404.php:

<?php
if(isset($_SERVER['REQUEST_URI'])){
if(preg_match('#^([0-9]+)-test.html$#',$_SERVER['REQUEST_URI'],$match)){
header('Status: 200 OK';, false, 200);
$_GET['p']=$match[1];
$_REQUEST['p']=$match[1];
include 'test.php';
}else{
?>
<h1>Erreur 404</h1>
<span style="font-family:Arial, Helvetica, sans-serif;font-size:18px;">URL invalide, page introuvable</span>
<?php
}
}else{
?>
<h1>Erreur 404</h1>
<span style="font-family:Arial, Helvetica, sans-serif;font-size:18px;">URL invalide, page introuvable</span>
<?php
}
?>

Tout ceci nous permet d’afficher un message d’erreur personnalisé plutôt que l’erreur 404 classique (ce qui peut être un plus, on peut par exemple mettre alors un formulaire de recherche pour l’utilisateur), et surtout de tester notre règle de réécriture. Ici on utilise directement de l’URL rewriting avancé avec récupération de variable.

Les parenthèses que nous avons mis sont des parenthèses capturantes. Elles permettent de récupérer ce qu’il y a à l’intérieur grâce au $match[1] que nous avons mis plus loin. On peut bien sûr ajouter des parenthèses pour avoir des $match[2], $match[3], etc.


Comment puis-je faire des liens pertinants avec l’URL rewriting ?

Pour générer des URLs “propres” qui seront par la suite traitées avec de l’URL rewriting, ce n’est pas bien compliqué. Un peu de PHP et une bonne règle de réécriture feront l’affaire.

Prenons un exemple. J’ai une page PHP qui me permet d’afficher le descriptif d’un produit à partir de son ID et sa catégorie, le tout passé en variable GET dans les liens de mon site. J’ai donc pour le moment : http://monhôte/produit.php?id=x&cat=y

Comme on l’a dit avant, ce n’est pas terrible ni pour le référencement ni pour la compréhension par le visiteur.

Commençons par définir une règle de réécriture : on décide que désormais cette page aura pour URL : produit-x-y-nom-de-mon-produit.html. Ce qui est bien mieux que ça ne l’était auparavant.

Comment mettre ceci en place ?
Très simple !

Commençons par la règle de réécriture dans notre fichier .htaccess (par exemple) :

RewriteEngine on #Activation du moteur de réécriture
RewriteBase / #On définit la racine du site comme URL de base
RewriteRule ^produit-([0-9]{1,5})-([0-9]{1,5})-[w-]+$ produit.php?id=$1&cat=$2 #On définit la règle

Puis pour créer nos liens en PHP :

<?php
//Connexion à la base de données faite
$result=mysql_query("SELECT * FROM produits"); //On récupère tous les produits
while($produit=mysql_fetch_array($result)){ //Tant qu'il y a des produits, on traite
echo <a href="produit-'.$produit['id'].'-'.$produit['categorie'].'-'.preg_replace('/s+/','-',$produit['nom']).'.html">'.$produit['nom'].'</a><br />';
//On affiche un lien. preg_replace('/s+/','-',$produit['nom']) permet de remplacer les espaces par des '-'
}
?>

Ceci donne comme résultat (pour 3 produits ici) :

<a href="produit-1-1-produit-1.html">Produit 1</a><br />
<a href="produit-1-2-produit-2.html">Produit 2</a><br />
<a href="produit-2-3-produit-3.html">Produit 3</a>

Et voilà c’est donc fini, en 2 coups de cuillère à pot, on a fait de belles URLs qui permettront d’être référencé et d’avoir en plus notre noms de produit référencé directement dans l’URL de la page, ce qui est quand même un petit plus :)

Mon exemple est minimaliste et ne prend pas en compte les accents qu’il faudrait enlever des URL générées avec PHP s’il y en avait. Des fonctions permettant d’enlever les caractères spéciaux d’une chaîne se trouvent facilement.


Voilà, c’est déjà la fin de ce tutoriel qui vous aura, j’espère, été utile.

L’objet en javascript

mai 16th, 2009

Alors cette fois parlons de cette chose que j’ai découvert tout récemment grâce à nod_ (si tu passes par là ^^), j’ai nommé : l’objet javascript

Alors petit rappel : les classes en javascript, ça n’existe pas. En fait on se base sur des prototypes. On créée un premier objet sous forme de variable à qui on va attribuer une fonction anonyme (le constructeur) qui va elle-même contenir variables et méthodes privées ou publiques.

Voici comment ça se passe :

var monObjet=function(attribut1,attribut2){
    var prive1=attribut1 || 0, //si attribut1 est définit prive1 vaut attribut1, 0 sinon
    prive2=attribut2 || 0;

    function methodePrivee(){
        //blabla
    }

    return {
        varPublic1:19,
        varPublic2:'test',
        methodePublic:function(){
            alert(prive1+' et '+this.varPublic1);
        }
    };
};
obj1=monObjet(1,3);
obj1.methodePublic(); //alert &quot;1 et 19&quot;

C’est pas bien compliqué en fait, même si la syntaxe peut dérouter ceux parmi vous qui ont l’habitude d’autre langages orienté objet comme Java ou le C++

Ajax facile

mai 15th, 2009

Hop on commence par un petit script javascript qui va vous permettre d’avoir une fonction universelle pour faire une requête AJAX de la manière la plus simple du monde.

Voici le script :

function getXMLHttpRequest(){
	var xhr=null;
	if(window.XMLHttpRequest || window.ActiveXObject){
		if (window.ActiveXObject){
			try{
				xhr = new ActiveXObject('Msxml2.XMLHTTP');
			}catch(e){
				xhr = new ActiveXObject('Microsoft.XMLHTTP');
			}
		}else{
			xhr = new XMLHttpRequest();
		}
	}else{
		alert('Votre navigateur ne supporte pas l\'objet XMLHTTPRequest...');
		return null;
	}
	return xhr;
}
function ajax(param){
	var xhr=getXMLHttpRequest(),
	url=(typeof param=='string') ? param : (typeof param.url=='string') ? param.url : false,
	params=(typeof param.params=='string') ? param.params : false,
	callback=param.callback ? (typeof param.callback=='function') ? param.callback : alert : false,
	waiting=(typeof param.waiting=='function') ? param.waiting : false,
	xml=(param.xml=='xml') ? true : false,
	method=(param.method=='post') ? 'post' : 'get',
	asynchrone=(typeof param.asynchrone=='boolean') ? param.asynchrone : true;
	if(url){
		if(callback){
			xhr.onreadystatechange=xml?
				function(){
					if(waiting){
						if(xhr.readyState==1){
							waiting();
						}
					}
					if(xhr.readyState==4 &amp;&amp; (xhr.status==200 || xhr.status==0)){
						callback(xhr.responseXML);
					}
				}:function(){
					if(waiting){
						if(xhr.readyState==1){
							waiting();
						}
					}
					if(xhr.readyState==4 &amp;&amp; (xhr.status==200 || xhr.status==0)){
						callback(xhr.responseText);
					}
				}
		}
		if(method=='post'){
			xhr.open('POST',url,asynchrone);
			xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');
			xhr.send(encodeURI(params));
		}else{
			if(/?([^&amp;]=[^&amp;]&amp;)*[^&amp;]=[^&amp;]$/.test(url)){
				url+='&amp;';
			}else if(!/?$/.test(url)){
				url+='?';
			}
			xhr.open('GET',url+encodeURI(params),asynchrone);
			xhr.send(null);
		}
	}else{
		alert('Il faut au moins une URL !');
	}
}

Télécharger le fichier source

Vu comme ça, ça fait un gros script mais en réalité il nous simplifie grandement la vie :D

Son utilisation est des plus simple, il suffit de créer un objet à lui passer en paramètre :

function traitement(txt){
    document.getElementById('test').innerHTML=txt;
}
function enAttente(){
    document.getElementById('test').innerHTML='chargement en cours';
}
var param={
    url:'page.php', //url de la page à appeler
    method:'post', //méthode 'get' ou 'post'
    xml:false, //on reçoit une réponse de type texte (note: cet élément est obsolète, texte étant le type de réponse par défaut)
    params:'var1=truc&amp;var2=chose', //les paramètres à envoyer
    asynchrone:true, //requête synchrone ou asynchrone (asynchrone par défaut)
    callback:traitement, //la fonction qui s'occupe du traitement de la réponse de la requête s'il doit y en avoir un (cette fonction doit prendre un seul paramètre qui est le responseText ou responseXML
    waiting:enAttente //la fonction de traitement de l'attente avant le retour de la réponse (elle ne peut prendre aucun paramètre
};
ajax(param);

Note que si vous n’avez pas de traitement de callback à faire et que vous voulez passer par du GET avec une réponse de type texte, on peut directement lui passer une URL en paramètre :

ajax('test.php?var=truc');

Voilà j’espère que ça pourra vous être utile :)

Si xavierm02 passe par là, merci pour l’idée :D

Bonjour tout le monde !

mai 15th, 2009

Bienvenue sur mon blog dans lequel je vais vous faire partager tout ce que j’apprends quotidiennement sur la programmation Web et que je trouve utile de vous faire partager.

Vous trouverez également des tutoriels et scripts conçus par mes petits doigts musclés :)

Bonne visite.