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

Exemple avec phpBB 2


précédentsommairesuivant

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
Sélectionnez
<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
Sélectionnez
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
Sélectionnez
<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
Sélectionnez
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
Sélectionnez
<?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
Sélectionnez
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
Sélectionnez
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
Sélectionnez
<?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
Sélectionnez
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
Sélectionnez
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 "&#161;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
Sélectionnez
<?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
Sélectionnez
<?php

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

?>
Fichier : lang_en.php
Sélectionnez
<?php

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

?>
Fichier : lang_es.php
Sélectionnez
<?php

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

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

précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. 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'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.