Général

Les PCRE

Les POSIX

Pratique

Linux

Spécial php

Les billets de fred

Structurez vos applications "web"

Lorsque l'on commence à programmer en PHP, on a tendance à manquer de structuration dans l'organisation de ses scripts. Je vous propose donc de comprendre comment moi, je vois les choses.

Attention : Les propos tenus ici n'engagent que moi et ne sont pas paroles d'évangile.

Avant tout, pourquoi faire du PHP, le HTML n'est-il pas suffisant ?
Bien évidemment, si vous vous intéressez à un tel langage, c'est que vous désirez rendre votre site plus ... dynamique. C'est-à-dire que vous cherchez à adapter le contenu de vos pages en réaction à certaines informations données par votre utilisateur et en fonction de données que vous avez accumulées au fil du temps par d'autres utilisateurs et vous-même.

Nous distinguons donc 4 grandes fonctionnalités :
  • traitement et interprétation des données envoyées par les utilisateurs,
  • mémorisation et traitement des données accumulées,
  • traitement et logique de l'application,
  • présentation de l'information.
Détaillons ces fonctionnalités :
Traitement et interprétation des données envoyées par les utilisateurs.

Lorsqu'un utilisateur clique sur un lien, il demande en fait d'afficher une page particulière qui sera le résultat d'un script PHP et de paramètres placés dans ce lien.

Quelques exemples :
Liens Action
/news.php?date=5/11/2004 Lancement du script "news.php" à la racine du site avec le paramètre "date" contenant "5/11/2004"
/?action=news&date=5/11/2004 Dans le cas où le serveur Web est configuré pour lancer le script "index.php" par défaut, ce lien va donc exécuter "index.php" avec les deux paramètres "action" contenant "news" et "date" contenant "5/11/2004"
/?news&date=5/11/2004 Exécution du script "index.php" avec le premier paramètre "news" vide et "date" contenant "5/11/2004"
/news/5/11/2004 Si vous utilisez apache en mode "multiview", cela exécutera le script "news.php" avec le "PATH_INFO" contenant "/5/11/2004"

L'exécution du script est gérée par le serveur web, par contre, le contenu des paramètres et leur interprétation dépendent de vous. En effet, absolument rien ne vous garantit que le contenu de vos paramètres corresponde au format que vous attendiez. Pourquoi ? Parce qu'il y a beaucoup de petits malins sur la toile qui passent leur temps à essayer de pirater vos sites Internet. Regardons le dernier exemple, si notre code consiste à faire ceci :
<?php

// La requête SQL à construire
$sql = 'select date, titre, contenu from news where date = \'%s\'';

// Suppression du premier "/"
$date = substr( $_SERVER['PATH_INFO'], 1);

// On sépare les éléments de la date
list( $jour, $mois, $annee ) = explode('/', $date );

// on construit la date dans un format compris par MySQL
$date = $annee . '-' . $mois . '-' . $jour;

// Pour l'exemple, on affiche simplement la requête
printf( $sql, $date );
?>

En entrant le lien suivant : "/news/5/11/2004" le résultat sera :
select date, titre, contenu from news where date = '2004-11-5'

Maintenant, si on entre le lien suivant : "/news/1/1/1971-01-01' union select '', login, pass from users where login <> '", le résultat sera :
select date, titre, contenu from news where date = '1971-01-01' union select '', login, pass from users where login <> '-1-1'

Si on affiche bêtement, le résultat de la requête, vous allez lister l'ensemble des login/pass de vos utilisateurs. Après, vous savez ce qu'il reste à faire !

Pour éviter ce problème communément appelé "injection SQL", il faut vérifier que la date soit correcte.
Pour ma part, je changerais simplement la ligne qui construit la date au format MySQL par le code suivant :
<?php
$date = sprintf( '%d-%d-%d', $annee, $mois, $jour);
?>

Il en va de même pour le traitement des données envoyées par l'intermédiaire d'un formulaire ou dans un cookie car ces derniers peuvent être modifiés par un pirate.

Bref, il ne faut jamais faire confiance aux données envoyées par l'utilisateur, ce qui vous oblige à les contrôler et à les traiter avant de faire autre chose.

Mémorisation et traitement des données accumulées.

Les données que vous utilisez proviennent certainement d'une base de données ou de fichiers, mais ceci pourrait ''éventuellement'' changer par la suite (passage de MySQL à Postgres par exemple). De même, certaines données sont utilisées par plusieurs pages. Il faut donc éviter d'écrire plusieurs fois le même code. Pour faire les choses correctement, il est donc recommandé de créer des scripts contenant des fonctions (ou classes avec méthodes) permettant d'accéder à des données particulières comme par exemple la liste des informations à une date précise.
Ces fonctions peuvent également utiliser d'autres fonctions accédant directement à la base de données permettant ainsi de changer de SGBD sans changer tout le code de votre application.

Remarque : Je ne suis pas un grand fan des classes d'abstraction des bases de données car le changement de SGBD est quand même assez rare sauf si on développe une application qui devra s'exécuter sur différentes configurations (cas des applications disponibles sur le Net).

Dans l'exemple précédent, le traitement des paramètres utilisateur s'arrête après la ligne contenant "explode" (en ajoutant peut-être un contrôle de valeur numérique), tandis que la construction de la date au format "MySQL" et de la requête sont à classer dans la catégorie du traitement de la base de données.

Traitement et logique de l'application

En dehors du traitement des paramètres utilisateurs et des données de votre base, vous avez besoin d'effectuer des traitements spécifiques à votre application. Si je prends en exemple le site apéroPHP nouvelle version (non disponible à la sortie de cet article), lorsqu'une personne organise un apéro dans une ville, les personnes inscrites sur le site et ayant indiqué leurs villes de prédilection, vont recevoir un email d'information. Cet envoi d'email n'entre pas dans les catégories ""traitement des paramètres utilisateurs"" et ""accès à la base de données"". Cela fait donc partie de la logique de l'application.

Remarque : Cette catégorie est souvent appelée : "logique métier".

Présentation de l'information.

Une fois toutes les informations traitées, vous allez présenter le résultat à votre utilisateur. Cette présentation est le plus souvent en HTML, mais cela peut être également en PDF, une image JPEG, une animation Flash, un flux XML ... etc.
Pour mener à bien cette tâche, vous pouvez faire appel à un système de gabarit (template) afin de simplifier l'écriture de vos pages, mais une chose est sûre, cette partie de votre application ne doit pas effectuer de traitement des paramètres utilisateur, d'accès direct à la base de données ou envoyer des emails. Elle doit se limiter uniquement à la présentation de l'information.

Le code suivant est un exemple de script séparant les différentes parties (notez bien les répertoires dans lesquels sont placées les classes) :
<?php

// Ce script contient une classe nommée "PathInfo" et qui contient
// un ensemble de méthodes statiques pour traiter la variable
// $_SERVER['PATH_INFO']
require 'preprocess/pathinfo.class.php';
$date = PathInfo::pop_date();

// Ce script contient une classe d'accès aux données des infos.
// Cette classe utilise une autre classe d'accès à la base de
// données permettant de changer le type de base de données sans
// changer les lignes de codes l'utilisant.
require 'data/news.class.php';
$news = News::list_news( $date );

// Ce script contient une classe de statistiques.
// Dans le cas présent, il faut vérifier que l'utilisateur n'ait pas
// déjà lu cette information (donnée membre) afin d'incrémenter son
// compteur perso.
require 'appli/membre.class.php';
Member::news_seen( $date );

// On récupère les préférences de l'utilisateur en terme d'affichage
$display_pref = Member::display_pref();

// Cette classe contient des méthodes permettant d'afficher l'information
// en fonction des préférences de l'utilisateur.
// Dans le cas présent, on affiche les informations d'une date précise,
// deuxième paramètre de "Display()"
require 'layout/display.class.php';
$display = new Display( $display_pref, 'news' );
$display->show( $date, $news );

?>

Comme vous pouvez le constater, ce script (news.php) ne contient aucun code HTML.
Pourquoi segmenter le code de cette manière ?

Tout simplement parce qu'il est plus facile ainsi de changer une logique métier sans toucher à tous les scripts, mais également, cela permet de répartir le travail entre les différents développeurs, certains étant plus spécialisés dans la modélisation de la base de données alors que d'autres maîtrisent plutôt le design et la présentation.

Remarque : Cet article mériterait de détailler certaines parties, mais il deviendrait trop long. Si vous voulez que je précise certains éléments, faites-le moi savoir afin que je puisse rédiger un autre article.

Par Frédéric Bouchery
ADAM Benjamin 2008 | Admin