III. Exemples avec Apache & PHP▲
Ce code est minimaliste. Je n'ai mis aucune mesure de sécurité afin de ne pas nous détourner du sujet. De même, je n'ai prêté aucune attention aux tables créées dans la base de donnée : ce n'est pas l'objet de ce tutoriel. Pour un cas réel, il conviendrait d'appliquer votre stratégie de sécurité habituelle et de structurer correctement les données.
Dans tous les exemples, le fichier .htaccess redirige vers un répertoire que j'ai nommé "urlr" et que j'ai placé à la racine de mon serveur Web : à vous de modifier cette valeur pour qu'elle corresponde à votre configuration.
Ces exemples ont été testés avec succès sur ma machine de développement : j'utilise EasyPHP 1.8, c'est-à -dire Apache 1.3.33 avec PHP 4.3.10 et MySQL 4.1.9. Cela fonctionne malgré ces versions préhistoriques et je pense que c'est une bonne indication de la portabilité de l'URL Rewriting.
III-1. Première partie : traduire les requêtes du client▲
Nos URLs peuvent être réécrites principalement de deux manières : au moyen de noms de fichiers fictifs ou bien en simulant une arborescence.
Traduire une URL fictive en une URL réelle▲
<a href="tous-les-articles.html">Tous les articles</a><br />
<a href="article-1-sur-les-vaches.html">Les vaches</a><br />
<a href="article-2-sur-le-php.html">Le PHP</a><br />
<a href="article-3-sur-le-papier-recycle.html">Le papier recyclé</a><br /><br />
<?php
if(!empty($_GET['a'])){
switch($_GET['a']){
case 1:
echo '<u>Article "Les vaches"</u> :<br />Meuh';
break;
case 2:
echo '<u>Article "Le PHP"</u> :<br />echo "Hello World!";';
break;
default:
echo '<u>Article "Le papier recyclé"</u> :<br />Pensons-y';
break;
}
}
?>DirectoryIndex index.php
RewriteEngine on
RewriteRule article-([0-9]+).* /urlr/index.php?a=$1 [L]
RewriteRule tous-les-articles.* /urlr/index.php [L]Des deux grandes solutions qui s'offrent à nous pour réécrire les liens d'une page Web, celle-ci est ma préférée.
Traduire une URL fictive en une URL réelle en simulant une arborescence▲
<a href="http://localhost/urlr/tous-les-articles.html">Tous les articles</a><br />
<a href="http://localhost/urlr/articles/1-sur-les-vaches.html">Les vaches</a><br />
<a href="http://localhost/urlr/articles/2-sur-le-php.html">Le PHP</a><br />
<a href="http://localhost/urlr/articles/3-sur-le-papier-recycle.html">Le papier recyclé</a><br /><br />
<?php
if(!empty($_GET['a'])){
switch($_GET['a']){
case 1:
echo '<u>Article "Les vaches"</u> :<br />Meuh';
break;
case 2:
echo '<u>Article "Le PHP"</u> :<br />echo "Hello World!";';
break;
default:
echo '<u>Article "Le papier recyclé"</u> :<br />Pensons-y';
break;
}
}
?>DirectoryIndex index.php
RewriteEngine on
RewriteRule articles/([0-9]+).* /urlr/index.php?a=$1 [L]
RewriteRule tous-les-articles.* /urlr/index.php [L]
Cette solution me plaît moins que la précédente mais, puisqu'elle est tellement fréquente, j'ai décidé de la traiter ici. À vous de choisir celle que vous préférez de celle-ci ou de la précédente. Pour ma part, je poursuivrai ma présentation d'exemples avec la solution sans arborescence.
Je dois avouer que c'est ma grande fainéantise qui oriente mon choix... J'imagine que simuler une arborescence a tout un tas d'intérêts d'optimisation ! Si vous en avez le temps, je vous recommande de vous pencher sérieusement sur la question.
III-2. Seconde partie : modifier les pages envoyées au client▲
Utiliser le tampon pour modifier tous les liens plus facilement▲
- Initialiser la mise en tampon de la sortie standard
- Afficher les liens à réécrire
- Afficher le contenu en fonction du paramètre appelé (facultatif)
- Récupérer le tampon et arrêter la mise en cache
- Réécrire les liens
- Afficher la page
<?php
// Initialiser la mise en tampon de la sortie standard
ob_start();
?>
<!-- Afficher les liens à réécrire -->
<a href="index.php">Tous les articles</a><br />
<a href="index.php?a=1">Les vaches</a><br />
<a href="index.php?a=2">Le PHP</a><br />
<a href="index.php?a=3">Le papier recyclé</a><br />
<?php
// Afficher le contenu en fonction du paramètre appelé (facultatif)
if(!empty($_GET['a'])){
switch($_GET['a']){
case 1:
echo '<u>Article "Les vaches"</u> :<br />Meuh';
break;
case 2:
echo '<u>Article "Le PHP"</u> :<br />echo "Hello World!";';
break;
default:
echo '<u>Article "Le papier recyclé"</u> :<br />Pensons-y';
break;
}
}
// Récupérer le tampon et arrêter la mise en cache
$contents = ob_get_contents();
ob_end_clean();
$patterns[] = '<a href="index.php?a=1">Les vaches</a>';
$patterns[] = '<a href="index.php?a=2">Le PHP</a>';
$patterns[] = '<a href="index.php?a=3">Le papier recyclé</a>';
$new_urls[] = '<a href="article-1-les-vaches.html">Les vaches</a>';
$new_urls[] = '<a href="article-2-le-php.html">Le PHP</a>';
$new_urls[] = '<a href="article-3-le-papier-recycle">Le papier recyclé</a>';
// Réécrire les liens
$contents = str_replace($patterns, $new_urls, $contents);
// Afficher la page
echo $contents;
?>DirectoryIndex index.php
RewriteEngine on
RewriteRule article-([0-9]+).* /urlr/index.php?a=$1 [L]
RewriteRule tous-les-articles.* /urlr/index.php [L]
Observez ce qui a changé.
Non seulement les liens pointent vers des fichiers inexistants, mais nous avons également ajouté la propriété title à chacun d'eux. Notez également comme il est possible de mettre n'importe quoi à la suite du numéro d'article.
Et pourtant, cela fonctionne.
Il me paraît limpide que cet exemple peut être amélioré, au vu de la quantité d'information que nous dupliquons...
Étant donné que notre site piochera toutes ses données dans une base de données, autant s'y faire tout de suite !
Utiliser une base de données pour généraliser le script▲
Voici un exemple concret (indépendant de phpBB) utilisant le tampon et une base de données :
- Initialiser la mise en tampon de la sortie standard
- Se connecter à la base de données
- Afficher les liens à réécrire
- Afficher le contenu en fonction du paramètre appelé (facultatif)
- Récupérer le tampon et arrêter la mise en cache
- Récupérer les liens à l'aide d'une expression régulière
- Parcourir les liens et les réécrire à l'aide de la base de données
- Afficher la page
CREATE TABLE `article` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`author` VARCHAR(255) NOT NULL,
`title` VARCHAR(255) NOT NULL,
`text` TEXT NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `article` (`author`, `title`, `text`)
VALUES
('Yogui', 'Les vaches', 'Meuh'),
('Yogui', 'Le PHP', 'echo "Hello world!";'),
('Yogui', 'Le papier recyclé', 'Pensons-y')
;<?php
// Fonction pour nettoyer le titre afin de l'intégrer dans l'URL
function clean($string){
return urlencode($string);
}
// Initialiser la mise en tampon de la sortie standard
ob_start();
// Se connecter à la base de données
mysql_connect('localhost', 'root', '')
or die(__LINE__.' : '.mysql_error());
mysql_select_db('developpez')
or die(__LINE__.' : '.mysql_error());
// Récupérer la liste des articles
$sql = 'SELECT `id`, `title`
FROM `article`';
$result = mysql_query($sql)
or die(__LINE__.' : '.mysql_error());
// Afficher les liens à réécrire
?>
<a href="index.php">Tous les articles</a><br />
<?php
while($article = mysql_fetch_assoc($result)){
?>
<a href="index.php?a=<?php echo $article['id']; ?>"><?php echo $article['title']; ?></a>
<br />
<?php
}
// Afficher le contenu en fonction du paramètre appelé (facultatif)
if(!empty($_GET['a'])){
$sql = 'SELECT `title`, `text`
FROM `article`
WHERE `id` = '.$_GET['a'];
$result = mysql_query($sql)
or die(__LINE__.' : '.mysql_error());
if($article = mysql_fetch_assoc($result)){
?>
<br /><br />
<u>Article "<?php echo $article['title']; ?>"</u> :
<br /><?php echo $article['text']; ?>
<?php
}
}
// Récupérer le tampon et arrêter la mise en cache
$contents = ob_get_contents();
ob_end_clean();
// Récupérer les liens à l'aide d'une expression régulière
if(preg_match_all(
'#<a href="index.php\?a=([0-9]+)">(.+)</a>#Usi',
$contents,
$matches,
PREG_SET_ORDER))
{
// Parcourir les liens et les réécrire à l'aide de la base de données
foreach($matches as $match){
$pattern = $match[0];
$article_id = $match[1];
$anchor = $match[2];
$sql = 'SELECT `title`, `text`
FROM `article`
WHERE `id` = '.$article_id;
$result = mysql_query($sql)
or die(__LINE__.' : '.mysql_error());
if($article = mysql_fetch_assoc($result)){
$new_url =
'<a href="article-'.$article_id.'-sur-'.clean($article['title']).'.html" '
.'title="'.$article['title'].'">'
.$article['title']
.'</a>';
$contents = str_replace($pattern, $new_url, $contents);
}
}
}
// Afficher la page
echo $contents;
?>DirectoryIndex index.php
RewriteEngine on
RewriteRule article-([0-9]+).* /urlr/index.php?a=$1 [L]
RewriteRule tous-les-articles.* /urlr/index.php [L]Utiliser un fichier de langue pour traduire les URLs du site▲
Voici un exemple concret (indépendant de phpBB) utilisant le tampon, une base de données (multilingue) et un fichier de langue :
- Initialiser la mise en tampon de la sortie standard
- Déterminer dans quelle langue les URLs seront traduites
- Se connecter à la base de données
- Afficher les langues disponibles
- Afficher les liens à réécrire
- Afficher le contenu en fonction du paramètre appelé (facultatif)
- Récupérer le tampon et arrêter la mise en cache
- Récupérer les liens à l'aide d'une expression régulière
- Parcourir les liens et les réécrire à l'aide de la base de données et du fichier de langue
- Afficher la page
CREATE TABLE `language` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `language` (`name`)
VALUES
('fr'),
('en'),
('es')
;
CREATE TABLE `article` (
`id` int(11) NOT NULL auto_increment,
`author` VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
INSERT INTO `article` (`author`)
VALUES
('Yogui'),
('Yogui'),
('Yogui')
;
CREATE TABLE `article_lang` (
`article_id` int(11) NOT NULL default '0',
`lang_id` int(11) NOT NULL default '0',
`title` varchar(255) NOT NULL default '',
`text` text NOT NULL,
PRIMARY KEY (`article_id`, `lang_id`)
);
INSERT INTO `article_lang` (`article_id`, `lang_id`, `title`, `text`)
VALUES
(1, 1, 'Les vaches', 'Meuuh'),
(1, 2, 'Cows', 'Muuh'),
(1, 3, 'Las vacas', 'Muu'),
(2, 1, 'Le PHP', 'echo "Bonjour le monde !";'),
(2, 2, 'PHP', 'echo "Hello world!";'),
(2, 3, 'El PHP', 'echo "¡Hola mundo!";'),
(3, 1, 'Le papier recyclé', 'Pensons-y'),
(3, 2, 'Recycled paper', 'Think about it'),
(3, 3, 'Papel reciclado', 'Piensa en ello')
;<?php
// Fonction pour nettoyer le titre afin de l'intégrer dans l'URL
function clean($string){
return urlencode($string);
}
// Initialiser la mise en tampon de la sortie standard
ob_start();
// Déterminer dans quelle langue les URLs seront traduites
if(!empty($_GET['lang']) and is_file('lang_'.$_GET['lang'].'.php')){
define('LANG_ID', $_GET['lang']);
}
else{
define('LANG_ID', 'fr');
}
require('lang_'.LANG_ID.'.php');
// Se connecter à la base de données
mysql_connect('localhost', 'root', '')
or die(__LINE__.' : '.mysql_error());
mysql_select_db('developpez')
or die(__LINE__.' : '.mysql_error());
// Afficher les langues disponibles
$sql = 'SELECT `name`
FROM `language`';
$result = mysql_query($sql)
or die(__LINE__.' : '.mysql_error());
while($language = mysql_fetch_assoc($result)){
?>
[ <a href="index.php?lang=<?php echo $language['name']; ?>">
<?php echo $language['name']; ?>
</a> ]
<?php
}
echo '<br /><br />';
// Récupérer la liste des articles
$sql = 'SELECT a.`id`, a.`author`, al.`title`, al.`text`
FROM `article_lang` AS al
INNER JOIN `article` AS a ON al.`article_id` = a.`id`
INNER JOIN `language` AS l ON al.`lang_id` = l.`id`
WHERE l.`name` = "'.LANG_ID.'"';
$result = mysql_query($sql)
or die(__LINE__.' : '.mysql_error());
// Afficher quelques liens à partir de la base de données
while($article = mysql_fetch_assoc($result)){
?>
<a href="index.php?a=<?php echo $article['id']; ?>"><?php echo $article['title']; ?></a>
<br />
<?php
}
// Afficher le contenu en fonction du paramètre appelé (facultatif)
if(!empty($_GET['a'])){
$sql = 'SELECT a.`id`, a.`author`, al.`title`, al.`text`
FROM `article_lang` AS al
INNER JOIN `article` AS a ON al.`article_id` = a.`id`
INNER JOIN `language` AS l ON al.`lang_id` = l.`id`
WHERE a.`id` = '.$_GET['a'].'
AND l.`name` = "'.LANG_ID.'"';
$result = mysql_query($sql)
or die(__LINE__.' : '.mysql_error());
if($article = mysql_fetch_assoc($result)){
?>
<br /><br />
<u>Article "<?php echo $article['title']; ?>"</u> :
<br /><?php echo $article['text']; ?>
<?php
}
}
// Récupérer le tampon et arrêter la mise en cache
$contents = ob_get_contents();
ob_end_clean();
// Récupérer les liens à l'aide d'une expression régulière
if(preg_match_all(
'#<a href="index.php\?a=([0-9]+)">(.+)</a>#Usi',
$contents,
$matches,
PREG_SET_ORDER))
{
// Parcourir les liens et les réécrire à l'aide de la base de données
foreach($matches as $match){
$pattern = $match[0];
$article_id = $match[1];
$anchor = $match[2];
$sql = 'SELECT a.`id`, a.`author`, al.`title`, al.`text`
FROM `article_lang` AS al
INNER JOIN `article` AS a ON al.`article_id` = a.`id`
INNER JOIN `language` AS l ON al.`lang_id` = l.`id`
WHERE a.`id` = '.$article_id.'
AND l.`name` = "'.LANG_ID.'"';
$result = mysql_query($sql)
or die(__LINE__.' : '.mysql_error());
if($article = mysql_fetch_assoc($result)){
$new_url = sprintf($lang['url'], $article_id, clean($article['title'])).'.html';
$new_title = sprintf($lang['title'], $article['title']);
}
else{
$new_url = $lang['index'].'.html';
$new_title = '';
}
$new_link = '<a href="'.$new_url.'" title="'.$new_title.'">'.$article['title'].'</a>';
$contents = str_replace($pattern,
$new_link,
$contents);
}
}
// Afficher la page
echo $contents;
?><?php
$lang = array();
$lang['index'] = 'tous-les-articles';
$lang['url'] = 'article-%d-sur-%s';
$lang['title'] = 'Article : %s';
?><?php
$lang = array();
$lang['index'] = 'all-the-articles';
$lang['url'] = 'article-%d-about-%s';
$lang['title'] = 'Article: %s';
?><?php
$lang = array();
$lang['index'] = 'todos-los-articulos';
$lang['url'] = 'articulo-%d-sobre-%s';
$lang['title'] = 'ArtÃculo: %s';
?>DirectoryIndex index.php
RewriteEngine on
RewriteRule article-([0-9]+)-sur.* /urlr/index.php?a=$1 [L]
RewriteRule tous-les-articles.* /urlr/index.php [L]
RewriteRule article-([0-9]+)-about.* /urlr/index.php?a=$1&lang=en [L]
RewriteRule all-the-articles.* /urlr/index.php&lang=en [L]
RewriteRule articulo-([0-9]+)-sobre.* /urlr/index.php?a=$1&lang=es [L]
RewriteRule todos-los-articulos.* /urlr/index.php&lang=es [L]

