Accueil
Club Emploi Blogs   TV   Dév. Web PHP XML Python Autres 2D-3D-Jeux Sécurité Windows Linux PC Mac
Accueil Conception Java DotNET Visual Basic  C  C++ Delphi Eclipse MS-Office SQL & SGBD Oracle  4D  Business Intelligence

Tutoriel d'URL Rewriting (réécriture de liens)

Exemple avec phpBB 2

Date de publication : 7 mai 2006 , Date de mise à jour : 5 juillet 2006


III. Exemples avec Apache & PHP
III-1. Première partie : traduire les requêtes du client
Traduire une URL fictive en une URL réelle
Traduire une URL fictive en une URL réelle en simulant une arborescence
III-2. Seconde partie : modifier les pages envoyées au client
Utiliser le tampon pour modifier tous les liens plus facilement
Utiliser une base de données pour généraliser le script
Utiliser un fichier de langue pour traduire les URLs du site


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

Fichier : index.php
<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;
    }
}

?>
Fichier : .htaccess
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

Fichier : index.php
<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;
    }
}

?>
Fichier : .htaccess
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

Squelette du script :
  1. Initialiser la mise en tampon de la sortie standard
  2. Afficher les liens à réécrire
  3. Afficher le contenu en fonction du paramètre appelé (facultatif)
  4. Récupérer le tampon et arrêter la mise en cache
  5. Réécrire les liens
  6. Afficher la page
Fichier : index.php
<?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;

?>
Fichier : .htaccess
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 :

Squelette du script :
  1. Initialiser la mise en tampon de la sortie standard
  2. Se connecter à la base de données
  3. Afficher les liens à réécrire
  4. Afficher le contenu en fonction du paramètre appelé (facultatif)
  5. Récupérer le tampon et arrêter la mise en cache
  6. Récupérer les liens à l'aide d'une expression régulière
  7. Parcourir les liens et les réécrire à l'aide de la base de données
  8. Afficher la page
Code SQL pour créer la base de données
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')
;
Fichier : index.php
<?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;

?>
Fichier : .htaccess
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 :

Squelette du script :
  1. Initialiser la mise en tampon de la sortie standard
  2. Déterminer dans quelle langue les URLs seront traduites
  3. Se connecter à la base de données
  4. Afficher les langues disponibles
  5. Afficher les liens à réécrire
  6. Afficher le contenu en fonction du paramètre appelé (facultatif)
  7. Récupérer le tampon et arrêter la mise en cache
  8. Récupérer les liens à l'aide d'une expression régulière
  9. Parcourir les liens et les réécrire à l'aide de la base de données et du fichier de langue
  10. Afficher la page
Code SQL pour créer la base de données
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')
;
Fichier : index.php
<?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;

?>
Fichier : lang_fr.php
<?php

$lang = array();
$lang['index']  = 'tous-les-articles';
$lang['url']    = 'article-%d-sur-%s';
$lang['title']  = 'Article : %s';

?>
Fichier : lang_en.php
<?php

$lang = array();
$lang['index']  = 'all-the-articles';
$lang['url']    = 'article-%d-about-%s';
$lang['title']  = 'Article: %s';

?>
Fichier : lang_es.php
<?php

$lang = array();
$lang['index']  = 'todos-los-articulos';
$lang['url']    = 'articulo-%d-sobre-%s';
$lang['title']  = 'Artículo: %s';

?>
Fichier : .htaccess
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]
 

Les sources présentés sur cette page sont libres de droits, et vous pouvez les utiliser à votre convenance. Par contre cette page de présentation de ces sources constitue une oeuvre intellectuelle protégée par les droits d'auteurs. Copyright ©2006 Guillaume Rossolini. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD.

Vos questions techniques : forum d'entraide Accueil - Publiez vos articles, tutoriels, cours et rejoignez-nous dans l'équipe de rédaction du club d'entraide des développeurs francophones. Nous contacter - Copyright 2000..2005 www.developpez.com