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ées : 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 URL 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
:
écho '<u>Article "Les vaches"</u> :<br />Meuh'
;
break
;
case
2
:
écho '<u>Article "Le PHP"</u> :<br />écho "Hello World!";'
;
break
;
default
:
écho '<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
:
écho '<u>Article "Les vaches"</u> :<br />Meuh'
;
break
;
case
2
:
écho '<u>Article "Le PHP"</u> :<br />écho "Hello World!";'
;
break
;
default
:
écho '<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▲
Squelette du script :
- 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
:
écho '<u>Article "Les vaches"</u> :<br />Meuh'
;
break
;
case
2
:
écho '<u>Article "Le PHP"</u> :<br />écho "Hello World!";'
;
break
;
default
:
écho '<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
écho $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 :
Squelette du script :
- 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
écho $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
écho $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
écho $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 URL 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 :
- Initialiser la mise en tampon de la sortie standard
- Déterminer dans quelle langue les URL 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'
, 'écho "Bonjour le monde !";'
)
,
(
2
, 2
, 'PHP'
, 'echo "Hello world!";'
)
,
(
2
, 3
, 'El PHP'
, 'écho "¡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 URL 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
écho $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
écho $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
écho $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
écho $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]