248 Chapitre 6. Architecture du site : le pattern MVC sont « normalisées » pour annuler l’échappement qui a éventuellement été pratiqué suite au paramétrage à On de magic_quotes_gpc. On peut ensuite développer tout le reste du site en considérant que magic_quotes_gpc vaut Off. Il serait bien sûr plus facile de pouvoir changer la configuration au moment de l’exécution mais ce n’est pas possible pour cette directive. Il est probable qu’elle sera supprimée en PHP 6. Notez enfin qu’on utilise la fonction init_set() pour fixer le paramètre display_errors. Sa valeur est déterminée par la constante DISPLAY_ERRORS, définie dans le fichier Config.php, qu’il faut absolument placer à Off sur un site en production. 6.2.2 Le contrôleur frontal Le contrôleur frontal est une instance de la classe Frontal dont le rôle est de « router » la requête HTTP vers le contrôleur et l’action appropriés. Comme d’ha- bitude avec l’approche orientée-objet, on peut se contenter d’utiliser une classe sans connaître son implantation, ou inspecter cette dernière pour se faire une idée de la manière dont les choses sont traitées. Pour satisfaire votre curiosité, voici le code de la méthode execute() dans Frontal.php (ce fichier se trouve dans lib). function execute () { // D’abord , on récupère les noms du contrôleur et de l ’ action if (isSet($_GET[’controleur ’])) $controleur = ucfirst($_GET [ ’ c o ntr o l e ur ’ ]) . " C t r l " ; else $controleur = "IndexCtrl"; if ( i s S e t ($_GET [ ’ a c t i o n ’ ]) ) $action = lcfirst($_GET[ ’action’]); else $action = "index" ; // Maintenant chargeons la classe $chemin = " c o n t r o l e u r s " . DIRECTORY_SEPARATOR . $ c o n t r o l e u r .".php"; if( file_exists( " a p p l i c a t i o n " . DIRECTORY_SEPARATOR . $chemin )) { require_once($chemin) ; } else { throw new Exception ("Le contrôleur <b>$controleur </b> n ’ existe pas"); } // On instancie un objet eval ("\$ctrl = new $controleur ();"); // Il faut vérifier que l ’action existe if (!method_exists($ctrl , $action)) { 6.2 Structure d’une application MVC : contrôleurs et actions 249 throw new Exception ("L’action <b>$action </b> n’ existe pas "); } // Et pour finir il n’y a plus qu’à exécuter l ’action call_user_func( array($ctrl , $action)); } Essentiellement, elle détermine le contrôleur et l’action en fonction des para- mètres reçus dans la requête HTTP. Conformément à nos conventions, un contrôleur nommé control est implanté par une classe nommée control Ctrl et se trouve dans le fichier control Ctrl.php du répertoire application/controleurs.Onvérifie donc que ce fichier existe, faute de quoi on lève une exeption. On instancie ensuite ce contrôleur avec la fonction eval(), qui permet d’évaluer une expression PHP construite dynamiquement (ce qui est le cas ici puisqu’on ne sait pas à l’avance quel est le nom de la classe à instancier). Finalement on vérifie que l’action demandée est bien implantée par une méthode dans la classe instanciée, et si oui, on l’exécute. Ce fragment de code est un exemple de ce que l’on pourrait appeler « méta- programmation » en PHP : on crée par programmation du code PHP que l’on exé- cute. C’est une pratique assez courante en programmation avancée, car elle permet de résoudre élégamment des problèmes assez difficiles à traiter dans des languages moins souples. En résumé, le contrôleur frontal charge la classe du contrôleur, l’instancie et exécute la méthode correspondant à l’action. Voyons maintenant le contrôleur lui-même. 6.2.3 Créer des contrôleurs et des actions Créer un contrôleur est extrêmement simple : on ajoute un fichier nom Ctrl.php dans application/controleurs,oùnom est le nom du contrôleur. Ce fichier contient une classe qui hérite de Controleur. Voici le code d’un contrôleur servant d’exemple. Exemple 6.2 webscope/application/controleurs/TestCtrl.php : Le contrôleur test <?php /∗∗ ∗ @category webscope ∗ @copyright Philippe Rigaux , 2008 ∗ @license GPL ∗ @package test ∗ / require_once ("Controleur.php"); /∗∗ ∗ Controleur de test , montrant comment implanter ∗ des actions dans un contrôleur . ∗ / 250 Chapitre 6. Architecture du site : le pattern MVC class TestCtrl extends Controleur { /∗∗ ∗ Action par défaut − affichage de la liste des régions ∗ / function index () { // Affichage de la liste des régions $resultat = $this−>bd−>exe cRequ ete ( "SELECT ∗ FROM Region") ; while ($region = $this−>bd−>objetSuivant ( $resultat)) echo "<b>$region−>nom < / b>< b r / > " ; } } ?> Le code est une classe qui sert simplement de « coquille » à une liste de méthodes publiques, sans paramètre, implantant les actions. Ajouter une action revient donc à ajouter une méthode. La seule action disponible ici est index, que l’on appelle avec l’URL : http://serveur/webscope/index.php?ctrl=test&action=index Ou bien, plus simplement http://serveur/webscope/?ctrl=test en tirant parti du fait qu’index est l’action par défaut, et index.php le script par défaut. En étudiant cette action, on constate que l’objet-contrôleur dispose d’une pro- priété $this->bd, qui permet d’exécuter des requêtes. D’où vient cet objet ? De la super-classe Controleur qui instancie automatiquement un objet de la classe BDMySQL dans son constructeur. Tous les contrôleurs, sous-classes de Controleur, héritent de ce constructeur et, automatiquement, on dispose donc d’une connexion avec la base. Voici le code du constructeur de Controleur. function __construct () { /∗ ∗ Le contrôleur initialise plusieurs objets utilitaires : ∗−une instance de BD pour accéder à la base de données ∗−une instance du moteur de templates pour gérer la vue ∗ / // Initialisation de la session PHP session_start () ; // Connexion à la base $this−>bd = new BDMySQL (NOM, PASSE , BASE , SERVEUR) ; 6.3 Structure d’une application MVC : la vue 251 // Instanciation du moteur de templates $this−>vue = new Template ("application" . DIRECTORY_SEPARATOR . " v u e s " ) ; // On charge systématiquement le "layout" du site $this−>vue−>setFile ("page" , "layout . tpl ") ; // et initialisation du contenu et du titre. $this−>vue−>contenu =" " ; $this−>vue−>titre_page = ""; // Recherche de la session $this−>initSession (session_id ()); // Initialisation de la partie du contenu // qui montre soit un formulaire , de connexion , // soit un lien de déconnexion $this−>statutConnexion () ; } On peut noter que le constructeur instancie également un moteur de templates pour gérer la vue, accessible dans $this->vue, ainsi que des informations relatives àlasession.Nousyreviendrons. Au sein d’une action, on programme en PHP de manière tout à fait classique. Il ne s’agit pas vraiment de programmation orientée-objet au sens où nous l’avons vu dans les chapitres précédents. L’approche objet se borne ici à structurer le code, et à bénéficier du mécanisme d’héritage pour initialiser des composants utiles à toutes les actions. Retenez cette approche consistant à définir une super-classe pour définir un com- portement commun à un ensemble d’objets (ici les contrôleurs). Toutes les tâches répétitives d’intialisation de l’environnement, de configuration, de connexion à la base, etc., sont déjà faites une fois pour toutes. Inversement, cela rend très facile l’ajout de nouvelles contraintes, communes à tous les objets, par enrichissement de la super-classe. Un simple exemple : que se passe-t-il si on écrit un contrôleur en oubliant une méthode nommée index() ? Alors le choix par défaut effectué par le contrôleur frontal risque d’entraîner une erreur puisque l’action par défaut, index, n’existe pas. Solution : on définit cette action par défaut dans la super-classe Controleur : elle existe alors, par héritage, dans tous les contrôleurs, et elle est surchargée par toute méthode index() définie au niveau des sous-classes. 6.3 STRUCTURE D’UNE APPLICATION MVC : LA VUE Le code de l’action index() du contrôleur test, présenté précédemment, affiche simplement la sortie avec la commande PHP echo. C’est contraire au principe MVC de séparer la production de la présentation du traitement des données. L’inconvé- nient est de se retrouver à manipuler de très longues chaînes de caractères HTML dans le script, pratique qui mène extrêmement rapidement à un code illisible. 252 Chapitre 6. Architecture du site : le pattern MVC Une solution très simple consisterait à organiser chaque page en trois parties, en-tête, contenu et pied de page, l’en-tête et le pied de page étant systématiquement produits par des fonctions PHP Entete() et PiedDePage(). La figure 6.3 montre le style d’interaction obtenu, chaque action (sur la gauche) produisant les différentes parties de la page. Item 1 Titre Item nItem 2 MySQL PHPcontact Menu Contenu de la page Code PHP/MySQL entete() PiedDePage() Script PHP Fonction Fonction Figure 6.3 — Tout le code HTML est produit avec PHP. Cette méthode est envisageable pour de petits sites pour lesquels la conception graphique est stable et peu compliquée. Elle offre l’avantage de regrouper en un seul endroit (nos deux fonctions) les choix de présentation, et de rendre l’application indépendante de tout outil de production HTML. Pour des projets plus conséquents, il nous faut un composant • gérant la vue, • offrant une séparation claire entre les fragments HTML constituant la présen- tation des pages et le code PHP qui fournit le contenu. L’approche basée sur des templates, ou modèles de présentation, dans lesquels on indique les emplacements où le contenu produit dynamiquement doit être inséré, constitue une solution pratiquée depuis très longtemps. Elle offre plusieurs avantages, et quelques inconvénients. Pour être concret, je vais donner des exemples de la gestion de la vue à base de templates, avant de revenir sur les principes généraux de séparation du code HTML et du code PHP. 6.3.1 Les templates Le système utilisé pour nos exemples est un moteur de templates adapté de la bibliothèque PHPLIB et amélioré grâce aux possibilités de PHP 5. Ce moteur est très représentatif des fonctionnalités des templates (dont il existe de très nombreux représentants) et s’avère simple à utiliser. Les méthodes publiques de la classe sont données dans le tableau 6.1. . vient cet objet ? De la super-classe Controleur qui instancie automatiquement un objet de la classe BDMySQL dans son constructeur. Tous les contrôleurs, sous-classes de Controleur, héritent de ce. commande PHP echo. C’est contraire au principe MVC de séparer la production de la présentation du traitement des données. L’inconvé- nient est de se retrouver à manipuler de très longues chaînes de. page en trois parties, en-tête, contenu et pied de page, l’en-tête et le pied de page étant systématiquement produits par des fonctions PHP Entete() et PiedDePage(). La figure 6.3 montre le style