comprendre les bases d'un framework en PHP
IV. Modèle MVC
IV-A. Requêtes HTTP
IV-B. Script de récupération des requêtes
IV-C. Partie contrôleur
IV-D. Conclusion
IV. Modèle MVC
Nous allons maintenant nous atteler à la définition d'un "cadre"
permettant de respecter le modèle MVC. Pour ce faire, il nous
faut séparer présentation (Pages HTML) et code métier (code PHP).
Plusieurs façons de faire sont possible.
Nous allons procéder de la sorte : le code HTML sera dans un
répertoire nommé modèle, le code PHP sera dans un répertoire
nommé vue. Tous deux aurons le même nom de fichier à l'exception
de l'extension : ".php" pour le PHP, ".hmtl" pour le HTML.
Afin d'ouvrir ces fichiers, il nous faudra du code PHP, ce sera,
comme le nomme le modèle MVC, le contrôleur. Cette façon de procéder
nous permet de n'avoir qu'une seule page à ouvrir (exemple : "index.php")
et cette page s'occupera de choisir la vue et le modèle adaptés.
Mais comment allons nous procéder pour savoir, justement, la page adaptée?
c'est là qu'entrent en jeux les requêtes HTTP.
Fort heureusement, nous n'aurons pas a toutes les utiliser. Pour
la navigation, nous allons nous contenter de requêtes "GET". Que
va contenir cette requête? La réponse est : Qu'avons nous
besoin qu'elle contienne, sachant que, pour l'instant, l'inconnue
est le nom des fichiers du modèle et de la vue.
Nous allons donc utiliser la requête "GET", afin de savoir quelle
page afficher. Voyons comment cette requête HTTP s'utilise :
IV-A. Requêtes HTTP
Résumons, nous allons utiliser la requête HTTP "GET" afin
de naviguer de page en page.
Si vous voulez un cours complet sur ce sujet, cette
page
devrait vous plaire, si vous voulez en savoir plus, celle
ci est faite pour vous.
Pour les autres, voici une brève explication :
Vous avez probablement déjà utilisé google
ceci est une url
contenant, en bref :
le nom du protocole : "http"
l'adresse du serveur : "www.google.fr"
l'adresse du fichier sur le serveur : "/search"
les requêtes GET : "hl=fr&q=requete+http+GET&meta="
Les requêtes GET sont toujours introduites par un "?"
Elles constituent en réalité un passage de paramètre du client
(vous) vers le serveur (www.google.fr). Il est évidemment
possible de passer plusieurs paramètres au serveur, chacun
étant séparé d'un "&".
Chaque paramètre est construit comme suit :
- Nom du paramètre
- Signe "="
- Valeur du paramètre
Maintenant que nous savons utiliser une requête GET coté HTML
apprenons a la lire coté serveur :
PHP possède des variables superglobales. La variable $_GET
En est un exemple. Cette variable est un tableau contenant
tous les paramètres GET transmis au serveur. Pour les lire,
il suffit de faire :
| lecture d'un paramètre | $_GET['nom_du_parametre']; |
Par exemple, pour charger le paramètre transmit par l'url suivante : :
| lien avec passage de paramètre | http://www.google.fr/search?q=1-1 |
il suffit de faire, coté PHP, pour le charger dans une
variable nommée $question :
| lecture du parametre | $question = $_GET['q']; |
Ainsi, $question prendra pour valeur "1-1".
Un autre tableau superglobal permet de récupérer les paramètres
GET : $_REQUEST
sa particularité est qu'il récupère aussi les paramètres POST.
Ceux ci sont transmis lorsque l'on utilise un formulaire :
| code HTML d'un formulaire | <form action="test.html" method="post">
Prénom :
<input type="text" name="prenom" value="Mickey" />
<br />
Nom :
<input type="text" name="nom" value="Mouse" />
<br />
<input type="submit" value="OK" />
</form> |
Même si vous ne les connaissez pas, vous en avez déjà utilisés
sans le savoir, en effet, tous les champs vous permettant
d'écrire des données dans une page de votre navigateur est
en réalité un formulaire. Si vous ne me croyez pas, copiez
le code ci dessus dans un fichier texte vierge que vous
renommerez "test.html", puis ouvrez le. Vous constaterez que
bien que vous cliquiez sur le bouton "OK", aucune information
n'est transmise dans la barre d'adresse. Si vous pensez que
c'est du a une erreur de code, testez celui ci :
| le meme code, mais avec la methode GET | <form action="test.html" method="get">
Prénom :
<input type="text" name="prenom" value="Mickey" />
<br />
Nom :
<input type="text" name="nom" value="Mouse" />
<br />
<input type="submit" value="OK" />
</form> |
Et vous constaterez que l'adresse est actualisée, et possède
désormais les paires clef/valeur dans l'url, symptomatiques
du passage de paramètre par la méthode GET. Généralement,
la méthode GET est utilisée pour la navigation, et la méthode
POST pour l'utilisation des formulaires. Cependant, afin de
ne pas prendre de risques, nous allons utiliser la variable
$_REQUEST qui permet de récupérer un paramètre qu'il soit
passé en GET ou en POST.
IV-B. Script de récupération des requêtes
Reprenons google comme exemple, allez sur le site de ce
moteur de recherche, et lancez plusieurs recherches puis
analysez les paramètres affichés dans la barre d'adresse,
vous constaterez que bien que la valeur change en fonction
de vos demandes, c'est toujours la clef "q" qui contient
cette valeur :
La raison est très simple, du coté PHP, il est bien plus
pratique de connaître la variable a lire dans le tableau
$_REQUEST. Faisons de même, et choisissons un nom de variable
(Nous procéderons de cette façon pour toute nouveau type de
variable a lire : fixer un nom).
page semble être un nom tout approprié, il est
facilement compréhensible, non?
Ainsi, une fois ce nom choisit, il suffit, coté PHP, de placer
le code suivant :
| tentative de lecture du paramètre 'page' | $page = $_REQUEST['page']; |
Afin de ne pas avoir a écrire tout le long du code
"$_REQUEST['page']", nous stockons cette valeur dans une
variable "$page" bien plus rapide a écrire. Cependant, si
vous testez ce code, vous réaliserez qu'il n'est pas fonctionnel.
En effet, PHP ne créer l'élément 'page' du tableau
$_REQUEST que si une requête contenant cette clef a
été reçue. De façon plus explicite :
imaginons que vous ayez placé ce code dans un fichier nommé
"test.php", et l'ayez placé dans un répertoire accessible a votre
serveur web. ouvrez ce fichier (vous devriez avoir une URL
ressemblant a :
http://localhost/test/test.php
Vous devriez avoir un beau message d'erreur. La solution est
très simple : tester l'existence de l'élément page
dans le tableau $_REQUEST. Pour ce faire, il existe
la fonction isset
elle retourne vrai lorsque l'élément demandé est définit, faux
sinon.
Reprenons le code :
| récupération d'un paramètre | if ( isset($_REQUEST['page']) )
$page = $_REQUEST['page']; |
Voila, vous avez enfin réussit a récupérer une requête HTTP
sans risque de bug. Cependant, le travail n'est pas terminé,
en effet, nous avons parlés précédemment des formulaires,
sachez que valider un formulaire déclenche le chargement
d'une nouvelle page définie dans le formulaire. Bien entendu,
notre configuration "mono-page" nous permet de toujours faire
recharger la page "index.php", cependant, la page a afficher
(le paramètre GET ayant pour clef la chaîne "page") serait
fastidieux, voir, dans certains cas, impossible a transmettre.
Que faire? il faudrait, au cas où aucune page n'est spécifiée,
savoir quelle est la dernière page qui a été demandée par
le client... PHP est plein de ressources, les
sessions
sont là pour nous le prouver (pour avoir plus de ressources sur les sessions, renseignez vous par la, ou la).
Brièvement, les sessions permettent de conserver des
information propres au client lorsqu'il charge une nouvelle
page, ainsi, a l'aide de ce mécanisme, il est possible de
savoir quelle était la dernière page visitée.
Une page gérant les sessions doit toujours commencer par
l'instruction session_start()
en début de programme. Une fois cette fonction saisie,
vous pourrez accéder aux
variables de session a l'aide du tableau $_SESSION.
Cependant, tout comme pour les autre variables de navigation,
il se peut que le tableau soit vide, il convient donc de le
tester. Par exemple, pour savoir si la variable $page
a été stockée dans la session, il suffit de faire :
| test de définition de la var | if ( isset($_SESSION['page']) ) {
//serie de traitements
} |
En prenant tous les paramètres énumérés précédemment en
compte, il convient de récupérer la page a afficher a l'aide
de $_REQUEST, si cette page n'est pas définie, il faut
lire la page stockée en session, et, si elle n'existe pas...
...Et bien nous n'avons qu'a appeler une page par défaut
(qui sera probablement la page d'accueil). Essayons de coder
tout ça :
| chois de la page en fonction des différentes possibilités | //page a afficher :
switch (true) {
//1er cas : si on a une nouvelle page a afficher
case ( isset($_REQUEST['page']) ) :
$page = $_REQUEST['page'];
$_SESSION['page'] = $page;
break;
//sinon, on verifie si il y en a une stockée dans la session
case ( isset( $_SESSION['page'] ) ) :
$page = $_SESSION['page'];
break;
//sinon, on affiche la page par defaut
default :
$page = "defaut";
break;
}
dbgStore("Page a afficher : '$page'", CONTROLEUR); |
De même que des pages a afficher, il peut arriver qu'une action
de l'utilisateur entraîne un traitement a déclencher (exemple :
pour un forum, ajout d'un nouvel utilisateur). Bien entendu,
nous pouvons intégrer ces traitement aux pages PHP de la vue
(cf. : modèle MVC), cependant, il peut être pratique de
permettre a plusieurs pages d'accéder au même traitement...
Ainsi, une solution simple existe : en prenant
exemple sur le paramètre "page", nous allons récupérer
un paramètre "traitement". Le code a ajouter est :
| code spécifique aux traitements | if (isset($_REQUEST['traitement']) ) {
dbgStore("Traitement a effectuer : '$traitement'", TRAITEMENT);
require_once "traitements/" . $traitement . ".php";
} |
le code est bien plus simple que pour la page, car les traitements
effectués n'ont pas besoin d'être conservés dans la sessions
(si vous en doutez, essayez de trouver une bonne raison pour
les conserver). Nous rencontrons pour la première fois la commande
require_once
qui, d'après le manuel PHP :
"se remplace elle-même par le fichier spécifié, un peu
comme les commandes de préprocesseur C #include"
Ici, le fichier spécifié est obligatoirement dans le répertoire
"traitements" et porte le nom de la valeur du paramètre transmis
auquel on rajoute l'extension ".php". Cependant, un gros souci
apparaît : n'importe qui peut déclencher un traitement! il
suffit qu'il teste plusieurs valeurs pour le paramètre "traitement"
transmis par l'url pour pouvoir déclencher des choses catastrophiques!
Il faut donc sécuriser l'accès aux traitements, et,
pourquoi pas, aux pages. Nous allons donc créer deux fonction
dédiées :
validDroitAcces($pageDemandee)
et
validDroitTraitement($traitementDemande)
dont voici le code :
| nos deux fonction... un peu vides | function validDroitAcces($pageDemandee) {
$pageResultante = $pageDemandee;
return $pageResultante;
}
function validDroitTraitement($traitementDemande) {
$traintementResultant = $traitementDemande;
return $traintementResultant;
} |
Comme vous pouvez le constater, ces fonction sont... un peu
vides! La raison en est simple : elles devront être réécrites
par l'utilisateur de notre framework, en effet, nous ne
pouvons savoir quelle sera sa stratégie de sécurité :'(
a ceci, il peut être intéressant d'ajouter un petit "plus" :
nous avons définit que nos traitements seront dans le
répertoire générique "traitement", pourquoi ne pas rajouter
un sous répertoire permettant de classer plus finement ces
scripts, et par la même d'affiner la stratégie de sécurité :
| version optimisée | function validDroitTraitement($typeTDemande, $traitementDemande) {
$typeTResultant = $typeTDemande;
$traintementResultant = $traitementDemande ;
return array($typeTResultant, $traintementResultant);
} |
Ainsi, notre réception des données se complexifie.
Nous allons en profiter pour la placer dans une fonction :
| fonction recupRequest() | function recupRequest() {
dbgStore("On est dans recupRequest", CONTROLEUR);
//page a afficher :
switch (true) {
//1er cas : si on a une nouvelle page a afficher
case ( isset($_REQUEST['page']) ) :
$page = validDroitAcces($_REQUEST['page']);
if ($page == null) {
//si on est pas autorisé
$page = "accesRefuse";
} else {
//cas normal
$_SESSION['page'] = $page;
//session_register($page);
}
break;
//sinon, on verifie si il y en a une stockée dans la session
case ( isset( $_SESSION['page'] ) ) :
$page = $_SESSION['page'];
break;
//sinon, on affiche la page par defaut
default :
$page = "defaut";
break;
}
dbgStore("Page a afficher : '$page'", CONTROLEUR);
//traitement a effectuer : (execution des fonctionnalitées validées par l'utilisateur)
if (isset($_REQUEST['traitement']) && isset($_REQUEST['typeTraitement']) ) {
list($typeTraitement, $traitement) = validDroitTraitement($_REQUEST['typeTraitement'], $_REQUEST['traitement']);
if ($traitement != null ) {
dbgStore("Traitement a effectuer : '$typeTraitement/$traitement'", TRAITEMENT);
require_once "traitements/" . $typeTraitement . "/" . $traitement . ".php";
}
} else if(isset($_REQUEST['traitement']) ) {
dbgStore("Un traitement a été demandé, mais aucun type de traitement n'a été defini", CONTROLEUR);
} else if(isset($_REQUEST['typeTraitement']) ) {
dbgStore("Un type de traitement a été recus, mais aucun traitement n'a été defini", CONTROLEUR);
}
return $page;
} |
Cette fonction utilise les deux autre fonctions que nous
avons écrits afin de vérifier que l'utilisateur a bien les
droits qu'il prétend, de plus la fonction php
list
fait son apparition. elle permet de récupérer le résultat
d'un tableau dans plusieurs variables.
IV-C. Partie contrôleur
Avant d'écrire le contrôleur, nous allons procéder a un grand
ménage. Si vous avez testés tous les codes qui ont étés
énumérés tout au long de cet article, vous devez avoir une
page PHP complètement incompréhensible!
Nous allons remédier a cela, en utilisant, encore une fois la
commande include_once.
Tout d'abord, nous allons séparer les différentes
fonctionnalités du code en autant de fichiers, que nous
allons placer dans un sous répertoire nommé "includes".
De plus, nous allons suivre une logique de nommage des fichiers :
| Nom du fichier |
fonctions |
| bdd.inc.php |
base de donnée |
| debuggeur.inc.php |
debug |
| droit_d_acces.inc.php |
droit d'accès |
| http_request.inc.php |
récupération des requêtes |
| template.inc.php |
moteur de template (non implémenté pour le moment) |
Nous allons donc nous retrouver avec les répertoires suivants :
| nom du repertoire |
contenu |
| includes |
le code du framework |
| templates |
les fichiers HTML servant de modèle (a définir par l'utilisateur) |
| traitements |
les traitement (a définir par l'utilisateur) |
| vues |
les vues (fichiers PHP) (a définir par l'utilisateur) |
| SQL |
les fichiers SQL utilisés par la base de donnée (a définir par l'utilisateur) |
Une fois tout le code exporté dans des fichiers a inclure,
encore faut il l'inclure!
C'est ce que nous allons faire :
| inclusion du code | // les scripts du TEMPLATE : Partie Modele
require_once "includes/template.inc.php";
// Partie Debuggeur
require_once "includes/debuggeur.inc.php";
// Partie BDD
require_once "includes/bdd.inc.php";
// Partie Controleur
require_once "includes/droit_d_acces.inc.php";
require_once "includes/http_request.inc.php"; |
Ensuite, il ne nous reste plus qu'a écrire notre contrôleur.
Vous allez voir, le plus dur est déjà fait :
| le controleur | //initialisation des info de debug
dbgInit(true);
//recuperation des variables de navigation dont le fichier a afficher
$page = recupRequest();
//
//chargement du template (appel du modele)
$template =& chargeTemplate("templates\\" . $page . ".html");
if ($template == null) {
//si page = null : le fichier de template demandé n'existe pas, on bloque la navigation
echo "Erreure de navigation, le fichier demandé n'existe pas!";
} else {
//chargement de la partie dynamique (appel de la vue)
require_once "vues\\" . $page . ".php";
//affichage du resultat
affiche($template);
//affichage des messages de debug
dbgAffiche(TOUS);
}
//a ce niveau, tous les affichages ont étés fait, on finit donc le programme. |
Vous vous demandez sûrement pourquoi ce code n'est pas
placé dans une fonction? Tout simplement car, lorsqu'on
effectue un "include" le code inclut a la même portée
que le code qui l'a appelé, cette partie ayant une portée
globale, cela permet au code inclut d'en bénéficier, de même,
si le code inclut effectue lui même des include, le chemin
de fichier sera constant (celui de notre contrôleur, et non
celui de localisation du script).
Reste une grande énigme : que peut bien signifier la fonction
chargeTemplate()? Rendez vous au prochain et dernier
chapitre afin de le savoir.
IV-D. Conclusion
Ce chapitre se termine très rapidement, en effet, respecter
l'architecture MVC n'est pas bien compliqué, mais, surtout
est fortement dépendant du chapitre suivant, du moins pour
notre cas : en effet, nous allons utiliser un moteur de template.
Comme toujours, pour finir, vous pouvez retrouver le code
complet de notre contrôleur ci-dessous. Remarquez que les
define se retrouvent en début de programme, et non dans un
fichier inclut, ceci afin de faciliter la création de nouveau
types de messages pour le deboggueur.
| <?php
// on gere les sessions :
session_start();
//definition des constantes :
//pour le debuggeur :
define("TOUS", 0);
define("INDEFINI", 1);//valeur par defaut, si le parametre est oublié, c'est ce niveau qui_ sera affiché
define("FASTBDD", 4);//les requetes rapide : dbInsert...
define( "REQUETE", 5);//tout ce quui est BDD de base
define( "CONTROLEUR", 6);//le controleur
define( "TRAITEMENT", 7);//pour les vues
define( "TEMPLATE", 8);// pour le moteur de template
define( "TEMPLATEDYN", 9);// aussi pour le moteur de template, mais specifique à la fusion
// les scripts du TEMPLATE : Partie Modele
require_once "includes/template.inc.php";
// Partie Debuggeur
require_once "includes/debuggeur.inc.php";
// Partie BDD
require_once "includes/bdd.inc.php";
// Partie Controleur
require_once "includes/droit_d_acces.inc.php";
require_once "includes/http_request.inc.php";
//on ne peut pas mettre le reste dans une fonction car la partie
//dynamique a besoin d'avoir une portée de ses variables globale.
//le code qui suit est donc le point d'entrée du programme :
//initialisation des info de debug
dbgInit(true);
//recuperation des variables de navigation dont le fichier a afficher
$page = recupRequest();
///// affichage de la nouvelle page
//chargement du template (appel du modele)
$template =& chargeTemplate("templates\\" . $page . ".html");
if ($template == null) {
//si page = null : le fichier de template demandé n'existe pas, on bloque la navigation
echo "Erreure de navigation, le fichier demandé n'existe pas!";
} else {
//chargement de la partie dynamique (appel de la vue)
require_once "vues\\" . $page . ".php";
//affichage du resultat
affiche($template);
//affichage des messages de debug
dbgAffiche(TOUS);
}
//a ce niveau, tous les affichages ont etes fait, on finit donc le programme.
?> |
Les sources présentés sur cette pages sont libre 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 .
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érets.
.
|