Introduction par l'exemple à PHP 4


précédentsommaire

VII. Compléments

VII-A. Les outils du développement web

Nous indiquons ici où trouver et comment installer les outils nécessaires au développement web. Certains outils ont vu leurs versions évoluer et il se peut que les explications données ici ne conviennent plus pour les versions les plus récentes. Le lecteur sera alors amené à s'adpater… Dans le cours de programmation web, nous utiliserons essentiellement les outils suivants, tous disponibles gratuitement :

  • un navigateur récent capable d'afficher du XML. Les exemples du cours ont été testés avec Internet Explorer 6.
  • un JDK (Java Development Kit) récent. Les exemples du cours ont été testés avec le JDK 1.4. Ce JDK amène avec lui le Plug-in Java 1.4 pour les navigateurs ce qui permet à ces derniers d'afficher des applets Java utilisant le JDK 1.4.
  • un environnement de développement Java pour écrire des servlets Java. Ici c'est JBuilder 7.
  • des serveurs web : Apache, PWS (Personal Web Server), Tomcat.
    • Apache sera utilisé pour le développement d'applications web en PERL (Practical Extracting and Reporting Language) ou PHP (Personal Home Page)
    • PWS sera utilisé pour le développement d'applications web en ASP (Active Server Pages) ou PHP
    • Tomcat sera utilisé pour le développement d'applications web à l'aide de servlets Java ou de pages JSP (Java Server pages)
  • une application de gestion de base de données : MySQL
  • EasyPHP : un outil qui amène ensemble le serveur Web Apache, le langage PHP et le SGBD MySQL

VII-A-1. Serveurs Web, Navigateurs, Langages de scripts

  1. Serveurs Web principaux
  2. Navigateurs principaux
  3. Langages de scripts côté serveur
  4. Langages de scripts côté navigateur
  • Apache (Linux, Windows)
  • Interner Information Server IIS (NT), Personal Web Server PWS (Windows 9x)
  • Internet Explorer (Windows)
  • Netscape (Linux, Windows)
  • VBScript (IIS, PWS)
  • JavaScript (IIS, PWS)
  • Perl (Apache, IIS, PWS)
  • PHP (Apache, IIS, PWS)
  • Java (Apache, Tomcat)
  • Langages .NET
  • VBScript (IE)
  • Javascript (IE, Netscape)
  • Perlscript (IE)
  • Java (IE, Netscape)

VII-A-2. Où trouver les outils

Netscape http://www.netscape.com/ (lien downloads)
Internet Explorer http://www.microsoft.com/windows/ie/default.asp
PHP http://www.php.net
http://www.php.net/downloads.php (Windows Binaries)
PERL http://www.activestate.com http://www.activestate.com/Products/
http://www.activestate.com/Products/ActivePerl/
Vbscript, Javascript http://msdn.microsoft.com/scripting (suivre le lien windows script)
JAVA http://java.sun.com/
http://java.sun.com/downloads.html (JSE)
http://java.sun.com/j2se/1.4/download.html
Apache http://www.apache.org/
http://www.apache.org/dist/httpd/binaries/win32/
PWS inclus dans NT 4.0 Option pack for Windows 95
inclus dans le CD de Windows 98
http://www.microsoft.com/ntserver/nts/downloads/recommended/NT4OptPk/win95.asp
IIS
(windows NT/2000)
http://www.microsoft.com
Tomcat http://jakarta.apache.org/tomcat/
JBuilder http://www.borland.com/jbuilder/
http://www.borland.com/products/downloads/download_jbuilder.html
EasyPHP http://www.easyphp.org/
http://www.easyphp.org/telechargements.php3

VII-A-3. EasyPHP

Cette application est très pratique en ce qu'elle amène dans un même paquetage :

  1. le serveur Web Apache (1.3.x)
  2. le langage PHP (4.x)
  3. le SGBD MySQL (3.23.x)
  4. un outil d'administration de MySQL : PhpMyAdmin

L'application d'installation se présente sous la forme suivante :

Image non disponible

L'installation d'EasyPHP ne pose pas de problème et une arborescence est créée dans le système de fichiers :

Image non disponible
easyphp.exe l'exécutable de l'application
apache l'arborescence du serveur apache
mysql l'arborescence du SGBD mysql
phpmyadmin l'arborescence de l'application phpmyadmin
php l'arborescence de php
www racine de l'arborescence des pages web délivrées par le serveur apache d'EasyPHP
cgi-bin arborescence où l'on peut palcer des script CGI pour le serveur Apache

L'intérêt principal d'EasyPHP est que l'application arrive préconfigurée. Ainsi Apache, PHP, MySQL sont déjà configurés pour travailler ensemble. Lorsqu'on lance EasyPhp par son lien dans le menu des programmes, une icône se met en place en bas à droite de l'écran.

Image non disponible

C'est le E avec un point rouge qui doit clignoter si le serveur web Apache et la base de données MySQL sont opérationnels. Lorsqu'on clique dessus avec le bouton droit de la souris, on accède à des options de menu :

Image non disponible

L'option Administration permet de faire des réglages et des tests de bon fonctionnement :

Image non disponible

VII-A-3-a. Administration PHP

Le bouton infos php doit vous permettre de vérifier le bon fonctionnement du couple Apache-PHP : une page d'informations PHP doit apparaître :

Image non disponible

Le bouton extensions donne la liste des extensions installées pour php. Ce sont en fait des bibliothèques de fonctions.

Image non disponible

L'écran ci-dessus montre par exemple que les fonctions nécessaires à l'utilisation de la base MySQL sont bien présentes.

Le bouton paramètres donne le login/motdepasse de l'administrateur de la base de données MySQL.

Image non disponible

L'utilisation de la base MySQL dépasse le cadre de cette présentation rapide mais il est clai ici qu'il faudrait mettre un mot de passe à l'administrateur de la base.

VII-A-3-b. Administration Apache

Toujours dans la page d'administration d'EasyPHP, le lien vos alias permet de définir des alias associés à un répertoire. Cela permet de mettre des pages Web ailleurs que dans le répertoire www de l'arborescence d'easyPhp.

Image non disponible

Si dans la page ci-dessus, on met les informations suivantes :

Image non disponible

et qu'on utilise le bouton valider les lignes suivantes sont ajoutées au fichier <easyphp>\apache\conf\httpd.conf :

httpd.conf
CacherSélectionnez

<easyphp> désigne le répertoire d'installation d'EasyPHP. httpd.conf est le fichier de configuration du serveur Apache. On peut donc faire la même chose en éditant directement ce fichier. Une modification du fichier httpd.conf est normalement prise en compte immédiatement par Apache. Si ce n'était pas le cas, il faudrait l'arrêter puis le relancer, toujours avec l'icône d'easyphp :

Image non disponible

Pour terminer notre exemple, on peut maintenant placer des pages web dans l'arborescence e:\data\serge\web :

 
CacherSélectionnez

et demander cette page en utilisant l'alias st :

Image non disponible

Dans cet exemple, le serveur Apache a été configuré pour travailler sur le port 81. Son port par défaut est 80. Ce point est contrôlé par la ligne suivante du fichier httpd.conf déjà rencontré :

 
Sélectionnez
1.
Port 81

VII-A-3-c. Le fichier de configuration d'Apache httpd.conf

Lorsqu'on veut configurer un peu finement Apache, on est obligé d'aller modifier "à la main" son fichier de configuration httpd.conf situé ici dans le dossier <easyphp>\apache\conf :

Image non disponible

Voici quelques points à retenir de ce fichier de configuration :

ligne(s) rôle
ServerRoot "D:/Program Files/Apache Group/Apache" indique le dossier où se trouve l'arborescence de Apache
Port 80 indique sur quel port va travailler le serveur Web. Classiquement c'est 80. En changeant cette ligne, on peut faire travailler le serveur Web sur un autre port
ServerAdmin root@istia.univ-angers.fr l'adresse email de l'administrateur du serveur Apache
ServerName stahe.istia.uang le nom de la machine sur laquelle "tourne" le serveur Apache
ServerRoot "E:/Program Files/EasyPHP/apache" le répertoire d'installation du serveur Apache. Lorsque dans le fichier de configuration, apparaissent des noms relatifs de fichiers, ils sont relatifs par rapport à ce dossier.
DocumentRoot "E:/Program Files/EasyPHP/www" le dossier racine de l'arborescence des pages Web délivrées par le serveur. Ici, l'url http://machine/rep1/fic1.html correspondra au fichier E:\Program Files\EasyPHP\www \rep1\fic1.html
<Directory "E:/Program Files/EasyPHP/www"> fixe les propriétés du dossier précédent
ErrorLog logs/error.log dossier des logs, donc en fait <ServerRoot>\logs\error.log : E:\Program Files\EasyPHP\apache\logs\error.log. C'est le fichier à consulter si vous constatez que le serveur Apache ne fonctionne pas.
ScriptAlias /cgi-bin/ "E:/Program Files/EasyPHP/cgi-bin/" E:\Program Files\EasyPHP\cgi-bin sera la racine de l'arborescence où l'on pourra mettre des scripts CGI. Ainsi l'URL http://machine/cgi-bin/rep1/script1.pl sera l'url du script CGI E:\Program Files\EasyPHP\cgi-bin \rep1\script1.pl.
<Directory "E:/Program Files/EasyPHP/cgi-bin/"> fixe les propriétés du dossier ci-dessus
LoadModule php4_module "E:/Program Files/EasyPHP/php/php4apache.dll"
AddModule mod_php4.c
lignes de chargement des modules permettant à Apache de travailler avec PHP4.
AddType application/x-httpd-php .phtml .pwml .php3 .php4 .php .php2 .inc fixe les suffixes des fichiers à considérer comme des fichiers comme devant être traités par PHP

VII-A-3-d. Administration de MySQL avec PhpMyAdmin

Sur la page d'administration d'EasyPhp, on clique sur le bouton PhpMyAdmin :

Image non disponible
La liste déroulante sous Accueil permet de voir les bases de données actuelles. Image non disponible
Le nombre entre parenthèses est le nombre de tables. Si on choisit une base, les tables de celles-ci s'affichent : Image non disponible

La page Web offre un certain nombre d'opérations sur la base :

Image non disponible

Si on clique sur le lien Afficher de user :

Image non disponible

Il n'y a ici qu'un seul utilisateur : root, qui est l'administrateur de MySQL. En suivant le lien Modifier, on pourrait changer son mot de passe qui est actuellement vide, ce qui n'est pas conseillé pour un administrateur.

Nous n'en dirons pas plus sur PhpMyAdmin qui est un logiciel riche et qui mériterait un développement de plusieurs pages.

VII-A-4. PHP

Nous avons vu comment obtenir PHP au travers de l'application EasyPhp. Pour obtenir PHP directement, on ira sur le site http://www.php.net.

PHP n'est pas utilisable que dans le cadre du Web. On peut l'utiliser comme langage de scripts sous Windows. Créez le script suivant et sauvegardez-le sous le nom date.php :

date.php
CacherSélectionnez

Dans une fenêtre DOS, placez-vous dans le répertoire de date.php et exécutez-le :

 
CacherSélectionnez

VII-A-5. PERL

Il est préférable que Internet Explorer soit déjà installé. S'il est présent, Active Perl va le configurer afin qu'il accepte des scripts PERL dans les pages HTML, scripts qui seront exécutés par IE lui-même côté client. Le site de Active Perl est à l'URL http://www.activestate.comA l'installation, PERL sera installé dans un répertoire que nous appelerons <perl>. Il contient l'arborescence suivante :

 
CacherSélectionnez

L'exécutable perl.exe est dans <perl>\bin. Perl est un langage de scripts fonctionnant sous Windows et Unix. Il est de plus utilisé dans la programmation WEB. Écrivons un premier script :

 
CacherSélectionnez

Sauvegardez ce script dans un fichier heure.pl. Ouvrez une fenêtre DOS, placez-vous dans le répertoire du script précédent et exécutez-le :

heure.pl
CacherSélectionnez

VII-A-6. Vbscript, Javascript, Perlscript

Ces langages sont des langages de script pour windows. Ils peuvent fonctionner dans différents conteneurs tels

  • Windows Scripting Host pour une utilisation directe sous Windows notamment pour écrire des scripts d'administration système
  • Internet Explorer. Il est alors utilisé au sein de pages HTML auxquelles il amène une certaine interactivité impossible à atteindre avec le seul langage HTML.
  • Internet Information Server (IIS) le serveur Web de Microsoft sur NT/2000 et son équivalent Personal Web Server (PWS) sur Win9x. Dans ce cas, vbscript est utilisé pour faire de la programmation côté serveur web, technologie appelée ASP (Active Server Pages) par Microsoft.

On récupère le fichier d'installation à l'URL : http://msdn.microsoft.com/scripting et on suit les liens Windows Script. Sont installés :

  • le conteneur Windows Scripting Host, conteneur permettant l'utilisation de divers langages de scripts, tels Vbscript et Javascript mais aussi d'autres tel PerlScript qui est amené avec Active Perl.
  • un interpréteur VBscript
  • un interpréteur Javascript

Présentons quelques tests rapides. Construisons le programme vbscript suivant :

 
CacherSélectionnez

Ce programme utilise des objets. Appelons-le objets.vbs (le suffixe vbs désigne un fichier vbscript). Positionnons-nous sur le répertoire dans lequel il se trouve et exécutons-le :

 
CacherSélectionnez

Maintenant construisons le programme javascript suivant qui utilise des tableaux :

 
CacherSélectionnez

Ce programme utilise des tableaux. Appelons-le tableaux.js (le suffixe js désigne un fichier javascript). Positionnons-nous sur le répertoire dans lequel il se trouve et exécutons-le :

tableaux.js
CacherSélectionnez

Un dernier exemple en Perlscript pour terminer. Il faut avoir installé Active Perl pour avoir accès à Perlscript.

 
CacherSélectionnez

Ce programme montre la création et l'utilisation de deux dictionnaires : l'un à la mode Perl classique, l'autre avec l'objet Scripting Dictionary de Windows Script. Sauvegardons ce code dans le fichier dico.wsf (wsf est le suffixe des fichiers Windows Script). Positionnons-nous dans le dossier de ce programme et exécutons-le :

 
CacherSélectionnez

Perlscript peut utiliser les objets du conteneur dans lequel il s'exécute. Ici c'était des objets du conteneur Windows Script. Dans le contexte de la programmation Web, les scripts VBscript, Javascript, Perlscript peuvent être exécutés soit au sein du navigateur IE, soit au sein d'un serveur PWS ou IIS. Si le script est un peu complexe, il peut être judicieux de le tester hors du contexte Web, au sein du conteneur Windows Script comme il a été vu précédemment. On ne pourra tester ainsi que les fonctions du script qui n'utilisent pas des objets propres au navigateur ou au serveur. Même avec cette restriction, cette possibilité reste intéressante car il est en général assez peu pratique de déboguer des scripts s'exécutant au sein des serveurs web ou des navigateurs.

VII-A-7. JAVA

Java est disponible à l'URL : http://www.sun.com (cf début de ce document) et s'installe dans une arborescence qu'on appellera <java> qui contient les éléments suivants :

 
CacherSélectionnez

Dans bin, on trouvera javac.exe, le compilateur Java et java.exe la machine virtuelle Java. On pourra faire les tests suivants :

  • Écrire le script suivant :
 
CacherSélectionnez
  • Sauvegarder ce programme sous le nom heure.java. Ouvrir une fenêtre DOS. Se mettre dans le répertoire du fichier heure.java et le compiler :
 
CacherSélectionnez

Dans la commande ci-dessus c:\jdk1.3\bin\javac doit être remplacé par le chemin exact du compilateur javac.exe. Vous devez obtenir dans le même répertoire que heure.java un fichier heure.class qui est le programme qui va maintenant être exécuté par la machine virtuelle java.exe.

  • Exécuter le programme :
 
CacherSélectionnez

VII-A-8. Serveur Apache

Nous avons vu que l'on pouvait obtenir le serveur Apache avec l'application EasyPhp. Pour l'avoir directement, on ira sur le site d'Apache : http://www.apache.org. L'installation crée une arborescence où on trouve tous les fichiers nécessaires au serveur. Appelons <apache> ce répertoire. Il contient une arborescence analogue à la suivante :

 
CacherSélectionnez
conf dossier des fichiers de configuration d'Apache
logs dossier des fichiers de logs (suivi) d'Apache
bin les exécutables d'Apache

VII-A-8-a. Configuration

Dans le dossier <Apache>\conf, on trouve les fichiers suivants : httpd.conf, srm.conf, access.conf. Dans les dernières versions d'Apache, les trois fichiers ont été réunis dans httpd.conf. Nous avons déjà présenté les points importants de ce fichier de configuration. Dans les exemples qui suivent c'est la version Apache d'EasyPhp qui a servi aux tests et donc son fichier de configuration. Dans celui-ci DocumentRoot qui désigne la racine de l'arborescence des pages Web est e:\program files\easyphp\www.

VII-A-8-b. Lien PHP - Apache

Pour tester, créer le fichier intro.php avec la seule ligne suivante :

 
CacherSélectionnez

et le mettre à la racine des pages du serveur Apache(DocumentRoot ci-dessus). Demander l'URL http://localhost/intro.php. On doit voir une liste d'informations php :

Image non disponible

Le script PHP suivant affiche l'heure. Nous l'avons déjà rencontré :

 
CacherSélectionnez

Plaçons ce fichier texte à la racine des pages du serveur Apache (DocumentRoot ) et appelons-le date.php. Demandons avec un navigateur l'URL http://localhost/date.php. On obtient la page suivante :

Image non disponible

VII-A-8-c. Lien PERL-APACHE

Il est fait grâce à une ligne de la forme : ScriptAlias /cgi-bin/ "E:/Program Files/EasyPHP/cgi-bin/" du fichier <apache>\conf\httpd.conf. Sa syntaxe est ScriptAlias /cgi-bin/ "<cgi-bin>" <cgi-bin> est le dossier où on pourra placer des scripts CGI. CGI (Common Gateway Interface) est une norme de dialogue serveur WEB <--> Applications. Un client demande au serveur Web une page dynamique, c.a.d. une page générée par un programme. Le serveur WEB doit donc demander à un programme de générer la page. CGI définit le dialogue entre le serveur et le programme, notamment le mode de transmission des informations entre ces deux entités.

Si besoin est, modifiez la ligne ScriptAlias /cgi-bin/ "<cgi-bin>"et relancez le serveur Apache. Faites ensuite le test suivant :

  • Écrire le script :
     
    CacherSélectionnez
  • Mettre ce script dans <cgi-bin>\heure.pl<cgi-bin> est le dossier pouvant recevoir des scripts CGI (cf httpd.conf). La première ligne #!c:\perl\bin\perl.exe désigne le chemin de l'exécutable perl.exe. Le modifier si besoin est.
  • Lancer Apache si ce n'est fait
  • Demander avec un navigateur l'URL http://localhost/cgi-bin/heure.pl. On obtient la page suivante :
Image non disponible

VII-A-9. Le serveur PWS

VII-A-9-a. Installation

Le serveur PWS (Personal Web Server) est une version personnelle du serveur IIS (Internet Information server) de Microsoft. Ce dernier est disponible sur les machines NT et 2000. Sur les machines win9x, PWS est normalement disponible avec le paquetage d'installation Internet Explorer. Cependant il n'est pas installé par défaut. Il faut prendre une installation personnalisée d'IE et demander l'installation de PWS. Il est par ailleurs disponibles dans le NT 4.0 Option pack for Windows 95.

VII-A-9-b. Premiers tests

La racine des pages Web du serveur PWS est lecteur:\inetpub\wwwroot lecteur est le disque sur lequel vous avez installé PWS. Nous supposons dans la suite que ce lecteur est D. Ainsi l'url http://machine/rep1/page1.html correspondra au fichier d:\inetpub\wwwroot\rep1\page1.html. Le serveur PWS interprète tout fichier de suffixe .asp (Active Server pages) comme étant un script qu'il doit exécuter pour produire une page HTML.

PWS travaille par défaut sur le port 80. Le serveur web Apache aussi… Il faut donc arrêter Apache pour travailler avec PWS si vous avez les deux serveurs. L'autre solution est de configurer Apache pour qu'il travaille sur un autre port. Ainsi dans le fichier httpd.conf de configuration d'Apache, on remplace la ligne Port 80 par Port 81, Apache travaillera désormais sur le port 81 et pourra être utilisé en même temps que PWS. Si PWS ayant été lancé, on demande l'URL http://localhost, on obtient une page analogue à la suivante :

Image non disponible

VII-A-9-c. Lien PHP - PWS

  • Ci-dessous on trouvera un fichier .reg destiné à modifier la base de registres. Double-cliquer sur ce fichier pour modifier la base. Ici la dll nécessaire se trouve dans d:\php4 avec l'exécutable de php. Modifier si besoin est. Les \ doivent être doublés dans le chemin de la dll.
 
CacherSélectionnez
  • Relancer la machine pour que la modification de la base de registres soit prise en compte.
  • Créer un dossier php dans d:\inetpub\wwwroot qui est la racine du serveur PWS. Ceci fait, activez PWS et prendre l'onglet « Avancé ». Sélectionner le bouton « Ajouter » pour créer un dossier virtuel :

Répertoire/Parcourir : d:\inetpub\wwwroot\php

Alias : php

Cocher la case exécuter.

  • Valider le tout et relancer PWS. Mettre dans d:\inetpub\wwwroot\php le fichier intro.php ayant la seule ligne suivante :
 
CacherSélectionnez
  • Demander au serveur PWS l'URL http://localhost/php/intro.php. On doit voir la liste d'informations php déjà présentées avec Apache.

VII-A-10. Tomcat : servlets Java et pages JSP (Java Server Pages)

Tomcat est un serveur Web permettant de générer des pages HTML grâce à des servlets (programmes Java exécutés par le serveur web) où des pages JSP (Java Server Pages), pages mélangeant code Java et code HTML. C'est l'équivalent des pages ASP (Active Server Pages) du serveur IIS/PWS de Microsoft où là on mélange code VBScript ou Javascript avec du code HTML.

VII-A-10-a. Installation

Tomcat est disponible à l'URL : http://jakarta.apache.org. On récupère un fichier .exe d'installation. Lorsqu'on lance ce programme, il commence par indiquer quel JDK il va utiliser. En effet Tomcat a besoin d'un JDK pour s'installer et ensuite compiler et exécuter les servlets Java. Il faut donc que vous ayez installé un JDK Java avant d'installer Tomcat. Le JD le plus récent est conseillé. L'installation va créer une arborescence <tomcat> :

Image non disponible

consiste simplement à décompresser cette archive dans un répertoire. Prenez un répertoire ne contenant dans son chemin que des noms sans espace (pas par exemple "Program Files"), ceci parce qu'il y a un bogue dans le processus d'installation de Tomcat. Prenez par exemple C:\tomcat ou D:\tomcat. Appelons ce répertoire <tomcat>. On y trouvera dedans un dossier appelé jakarta-tomcat et dans celui-ci l'arborescence suivante :

 
CacherSélectionnez

VII-A-10-b. Démarrage/Arrêt du serveur Web Tomcat

Tomcat est un serveur Web comme l'est Apache ou PWS. Pour le lancer, on dispose de liens dans le menu des programmes :

Start Tomcat pour lancer Tomcat
Stop Tomcat pour l'arrêter

Lorsqu'on lance Tomcat, une fenêtre Dos s'affiche avec le contenu suivant :

Image non disponible

On peut mettre cette fenêtre Dos en icône. Elle restera présente pendant tant que Tomcat sera actif. On peut alors passer aux premiers tests. Le serveur Web Tomcat travaille sur le port 8080. Une fois Tomcat lancé, prenez un navigateur Web et demandez l'URL http://localhost:8080. Vous devez obtenir la page suivante :

Image non disponible

Suivez le lien Servlet Examples :

Image non disponible

Cliquez sur le lien Execute de RequestParameters puis sur celui de Source. Vous aurez un premier aperçu de ce qu'est une servlet Java. Vous pourrez faire de même avec les liens sur les pages JSP.

Pour arrêter Tomcat, on utilisera le lien Stop Tomcat dans le menu des programmes.

VII-A-11. Jbuilder

Jbuilder est un environnement de développement d'applications Java. Pour construire des servlets Java où il n'y a pas d'interfaces graphiques, il n'est pas indispensable d'avoir un tel environnement. Un éditeur de textes et un JDK font l'affaire. Seulement JBuilder apporte avec lui quelques plus par rapport à la technique précédente :

  • facilité de débogage : le compilateur signale les lignes erronées d'un programme et il est facile de s'y positionner
  • suggestion de code : lorsqu'on utilise un objet Java, JBuilder donne en ligne la liste des propriétés et méthodes de celui-ci. Cela est très pratique lorsqu'on sait que la plupart des objets Java ont de très nombreuses propriétés et méthodes qu'il est difficile de se rappeler.

On trouvera JBuilder sur le site http://www.borland.com/jbuilder. Il faut remplir un formulaire pour obtenir le logiciel. Une clé d'activation est envoyée par mél. Pour installer JBuilder 7, il a par exemple été procédé ainsi :

  • trois fichiers zip ont été obtenus : pour l'application, pour la documentation, pour les exemples. Chacun de ces zip fait l'objet d'un lien séparé sur le site de JBuilder.
  • on a installé d'abord l'application, puis la documentation et enfin les exemples
  • lorsqu'on lance l'application la première fois, une clé d'activation est demandée : c'est celle qui vous a été envoyée par mél. Dans la version 7, cette clé est en fait la totalité d'un fichier texte que l'on peut placer, par exemple, dans le dossier d'installation de JB7. Au moment où la clé est demandée, on désigne alors le fichier en question. Ceci fait, la clé ne sera plus redemandée.

Il y a quelques configurations utiles à faire si on veut utiliser JBuilder pour construire des servlets Java. En effet, la version dite Jbuilder personnel est une version allégée qui ne vient notamment pas avec toutes les classes nécessaires pour faire du développement web en Java. On peut faire en sorte que JBuilder utilise les bibliothèques de classes amenées par Tomcat. On procède ainsi :

  • lancer JBuilder
Image non disponible
  • activer l'option Tools/Configure JDKs
Image non disponible

Dans la partie JDK Settings ci-dessus, on a normalement dans le champ Name un JDK 1.3.1. Si vous avez un JDK plus récent, utilisez le bouton Change pour désigner le répertoire d'installation de ce dernier. Ci-dessus, on a désigné le répertoire E:\Program Files\jdk14 où avait installé un JDK 1.4. Désormais, JBuilder utilisera ce JDK pour ses compilations et exécutions. Dans la partie (Class, Siurce, Documentation) on a la liste de toutes les bibliothèques de classes qui seront explorées par JBuilder, ici les classes du JDK 1.4. Les classes de celui-ci ne suffisent pas pour faire du développement web en Java. Pour ajouter d'autres bibliothèques de classes on utilise le bouton Add et on désigne les fichiers .jar supplémentaires que l'on veut utiliser. Les fichiers .jar sont des bibliothèques de classes. Tomcat 4.x amène avec lui toutes les bibliothèques de classes nécessaires au développement web. Elles se trouvent dans <tomcat>\common\lib<tomcat> est le répertoire d'installation de Tomcat :

Image non disponible

Avec le bouton Add, on va ajouter ces bibliothèques, une à une, à la liste des bibliothèques explorées par JBuilder :

Image non disponible

A partir de maintenant, on peut compiler des programmes java conformes à la norme J2EE, notamment les servlets Java. Jbuilder ne sert qu'à la compilation, l'exécution étant ultérieurement assurée par Tomcat selon des modalités expliquées dans le cours.

VII-B. Code source de programmes

VII-B-1. Le client TCP générique

Beaucoup de services créés à l'origine de l'Internet fonctionnent selon le modèle du serveur d'écho étudié précédemment : les échanges client-serveur se font pas échanges de lignes de texte. Nous allons écrire un client tcp générique qui sera lancé de la façon suivante : java cltTCPgenerique serveur port

Ce client TCP se connectera sur le port port du serveur serveur. Ceci fait, il créera deux threads :

  1. un thread chargé de lire des commandes tapées au clavier et de les envoyer au serveur
  2. un thread chargé de lire les réponses du serveur et de les afficher à l'écran

Pourquoi deux threads? Chaque service TCP-IP a son protocole particulier et on trouve parfois les situations suivantes :

  • le client doit envoyer plusieurs lignes de texte avant d'avoir une réponse
  • la réponse d'un serveur peut comporter plusieurs lignes de texte

Aussi la boucle envoi d'une unique ligne au serveur - réception d'une unique ligne envoyée par le serveur ne convient-elle pas toujours. On va donc créer deux boucles dissociées :

  • une boucle de lecture des commandes tapées au clavier pour être envoyées au serveur. L'utilisateur signalera la fin des commandes avec le mot clé fin.
  • une boucle de réception et d'affichage des réponses du serveur. Celle-ci sera une boucle infinie qui ne sera interrompue que par la fermeture du flux réseau par le serveur ou par l'utilisateur au clavier qui tapera la commande fin.

Pour avoir ces deux boucles dissociées, il nous faut deux threads indépendants. Montrons un exemple d'excécution où notre client tcp générique se connecte à un service SMTP (SendMail Transfer Protocol). Ce service est responsable de l'acheminement du courrier électronique à leurs destinataires. Il fonctionne sur le port 25 et a un protocole de dialogue de type échanges de lignes de texte.

 
CacherSélectionnez

Commentons ces échanges client-serveur :

  1. le service SMTP envoie un message de bienvenue lorsqu'un client se connecte à lui :
 
CacherSélectionnez
  1. certains services ont une commande help donnant des indications sur les commandes utilisables avec le service. Ici ce n'est pas le cas. Les commandes SMTP utilisées dans l'exemple sont les suivantes :
    1. mail from: expéditeur, pour indiquer l'adresse électronique de l'expéditeur du message
    2. rcpt to: destinataire, pour indiquer l'adresse électronique du destinataire du message. S'il y a plusieurs destinataires, on ré-émet autant de fois que nécessaire la commande rcpt to: pour chacun des destinataires.
    3. data qui signale au serveur SMTP qu'on va envoyer le message. Comme indiqué dans la réponse du serveur, celui-ci est une suite de lignes terminée par une ligne contenant le seul caractère point. Un message peut avoir des entêtes séparés du corps du message par une ligne vide. Dans notre exemple, nous avons mis un sujet avec le mot clé Subject:
  2. une fois le message envoyé, on peut indiquer au serveur qu'on a terminé avec la commande quit. Le serveur ferme alors la connexion réseau. Le thread de lecture peut détecter cet événement et s'arrêter.
  3. l'utilisateur tape alors fin au clavier pour arrêter également le thread de lecture des commandes tapées au clavier.

Si on vérifie le courrier reçu, nous avons la chose suivante (Outlook) :

Image non disponible

On remarquera que le service SMTP ne peut détecter si un expéditeur est valide ou non. Aussi ne peut-on jamais faire confiance au champ from d'un message. Ici l'expéditeur machin@univ-angers.fr n'existait pas.

Ce client tcp générique peut nous permettre de découvrir le protocole de dialogue de services internet et à partir de là construire des classes spécialisées pour des clients de ces services. Découvrons le protocole de dialogue du service POP (Post Office Protocol) qui permet de retrouver ses méls stockés sur un serveur. Il travaille sur le port 110.

 
CacherSélectionnez

Les principales commandes sont les suivantes :

  • user login, où on donne son login sur la machine qui détient nos méls
  • pass password, où on donne le mot de passe associé au login précédent
  • list, pour avoir la liste des messages sous la forme numéro, taille en octets
  • retr i, pour lire le message n° i
  • quit, pour arrêter le dialogue.

Découvrons maintenant le protocole de dialogue entre un client et un serveur Web qui lui travaille habituellement sur le port 80 :

 
CacherSélectionnez

Un client Web envoie ses commandes au serveur selon le schéma suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
commande1
commande2
...
commanden
[ligne vide]

Ce n'est qu'après avoir reçu la ligne vide que le serveur Web répond. Dans l'exemple nous n'avons utilisé qu'une commande :

 
CacherSélectionnez

qui demande au serveur l'URL /index.html et indique qu'il travaille avec le protocole HTTP version 1.0. La version la plus récente de ce protocole est 1.1. L'exemple montre que le serveur a répondu en renvoyant le contenu du fichier index.html puis qu'il a fermé la connexion puisqu'on voit le thread de lecture des réponses se terminer. Avant d'envoyer le contenu du fichier index.html, le serveur web a envoyé une série d'entêtes terminée par une ligne vide :

 
CacherSélectionnez

La ligne <html> est la première ligne du fichier /index.html. Ce qui précède s'appelle des entêtes HTTP (HyperText Transfer Protocol). Nous n'allons pas détailler ici ces entêtes mais on se rappellera que notre client générique y donne accès, ce qui peut être utile pour les comprendre. La première ligne par exemple :

 
CacherSélectionnez

indique que le serveur Web contacté comprend le protocole HTTP/1.1 et qu'il a bien trouvé le fichier demandé (200 OK), 200 étant un code de réponse HTTP. Les lignes

 
CacherSélectionnez

disent au client qu'il va recevoir 11251 octets représentant du texte HTML (HyperText Markup Language) et qu'à la fin de l'envoi, la connexion sera fermée.

On a donc là un client tcp très pratique. Il fait sans doute moins que le programme telnet que nous avons utilisé précédemment mais il était intéressant de l'écrire nous-mêmes. Le programme du client tcp générique est le suivant :

 
CacherSélectionnez

VII-B-2. Le serveur Tcp générique

Maintenant nous nous intéressons à un serveur

  • qui affiche à l'écran les commandes envoyées par ses clients
  • leur envoie comme réponse les lignes de texte tapées au clavier par un utilisateur. C'est donc ce dernier qui fait office de serveur.

Le programme est lancé par : java serveurTCPgenerique portEcoute, où portEcoute est le port sur lequel les clients doivent se connecter. Le service au client sera assuré par deux threads :

  • un thread se consacrant exclusivement à la lecture des lignes de texte envoyées par le client
  • un thread se consacrant exclusivement à la lecture des réponses tapées au clavier par l'utilisateur. Celui-ci signalera par la commande fin qu'il clôt la connexion avec le client.

Le serveur crée deux threads par client. S'il y a n clients, il y aura 2n threads actifs en même temps. Le serveur lui ne s'arrête jamais sauf par un Ctrl-C tapé au clavier par l'utilisateur. Voyons quelques exemples.

Le serveur est lancé sur le port 100 et on utilise le client générique pour lui parler. La fenêtre du client est la suivante :

 
CacherSélectionnez

Les lignes commençant par <-- sont celles envoyées du serveur au client, les autres celles du client vers le serveur. La fenêtre du serveur est la suivante :

 
CacherSélectionnez

Les lignes commençant par <-- sont celles envoyées du client au serveur. Les lignes N : sont les lignes envoyées du serveur au client n° N. Le serveur ci-dessus est encore actif alors que le client 1 est terminé. On lance un second client pour le même serveur :

 
CacherSélectionnez

La fenêtre du serveur est alors celle-ci :

 
CacherSélectionnez

Simulons maintenant un serveur web en lançant notre serveur générique sur le port 88 :

 
CacherSélectionnez

Prenons maintenant un navigateur et demandons l'URL http://localhost:88/exemple.html. Le navigateur va alors se connecter sur le port 88 de la machine localhost puis demander la page /exemple.html :

Image non disponible

Regardons maintenant la fenêtre de notre serveur :

 
CacherSélectionnez

On découvre ainsi les entêtes HTTP envoyés par le navigateur. Cela nous permet de découvrir peu à peu le protocole HTTP. Lors d'un précédent exemple, nous avions créé un client Web qui n'envoyait que la seule commande GET. Cela avait été suffisant. On voit ici que le navigateur envoie d'autres informations au serveur. Elles ont pour but d'indiquer au serveur quel type de client il a en face de lui. On voit aussi que les entêtes HTTP se terminent par une ligne vide.

Elaborons une réponse à notre client. L'utilisateur au clavier est ici le véritable serveur et il peut élaborer une réponse à la main. Rappelons-nous la réponse faite par un serveur Web dans un précédent exemple :

 
CacherSélectionnez

Essayons de donner une réponse analogue :

 
CacherSélectionnez

Les lignes commençant par 2 : sont envoyées du serveur au client n° 2. La commande fin clôt la connexion du serveur au client. Nous nous sommes limités dans notre réponse aux entêtes HTTP suivants :

 
CacherSélectionnez

Nous ne donnons pas la taille du fichier que nous allons envoyer (Content-Length) mais nous contentons de dire que nous allons fermer la connexion (Connection: close) après envoi de celui-ci. Cela est suffisant pour le navigateur. En voyant la connexion fermée, il saura que la réponse du serveur est terminée et affichera la page HTML qui lui a été envoyée. Cette dernière est la suivante :

 
CacherSélectionnez

L'utilisateur ferme ensuite la connexion au client en tapant la commande fin. Le navigateur sait alors que la réponse du serveur est terminée et peut alors l'afficher :

Image non disponible

Si ci-dessus, on fait View/Source pour voir ce qu'a reçu le navigateur, on obtient :

Image non disponible

c'est-à-dire exactement ce qu'on a envoyé depuis le serveur générique.

Le code du serveur TCP générique est le suivant :

 
CacherSélectionnez

VII-C. Javascript

Nous montrons dans cette partie trois exemples d'utilisation de Javascript dans les pages WEB. Nous nous centrons sur la gestion des formulaires mais Javascript peut faire bien davantage.

VII-C-1. Récupérer les informations d'un formulaire

L'exemple ci-dessous montre comment récupérer au sein du navigateur les données entrées par l'utilisateur au sein d'un formulaire. Cela permet en général de faire des prétraitements avant de les envoyer au serveur.

VII-C-1-a. Le formulaire

On a un formulaire rassemblant les composants les plus courants et d'un bouton Afficher permettant d'afficher les saisies faites par l'utilisateur.

Image non disponible

VII-C-1-b. Le code

 
CacherSélectionnez

VII-C-2. Les expressions régulières en Javascript

Côté navigateur, Javascript peut être utilisé pour vérifier la validité des données entrées par l'utilisateur avant de les envoyer au serveur. Voici un programme de test de ces expressions régulières.

VII-C-2-a. La page de test

Image non disponible

VII-C-2-b. Le code de la page

 
CacherSélectionnez

VII-C-3. Gestion des listes en JavaScript

VII-C-3-a. Le formulaire

Image non disponible

VII-C-3-b. Le code

 
CacherSélectionnez

- Étude de cas -

VII-D. Gestion d'une base d'articles sur le web

Objectifs :

  • écrire une classe de gestion d'une base de données d'articles
  • écrire une application web s'appuyant sur cette classe
  • introduire les feuilles de style
  • proposer un début de méthodologie de développement pour des applications web simples
  • introduire javascript dans le navigateur client

Crédits : L'essence de cette étude de cas a été trouvée dans le livre "Les cahiers du programmeur - PHP/MySQL" de Jean-Philippe Lebœuf aux éditions Eyrolles.

VII-D-1. Introduction

Un commerçant désire gérer les articles qu'il vend en magasin. Il a déjà chez lui une application ACCESS qui fait ce travail mais il est tenté par l'aventure du web. Il a un compte chez un fournisseur d'accès internet, fournisseur autorisant ses clients à installer des scripts PHP dans leurs dossiers personnels. Ceci leur permet de créer des sites web dynamiques. Par ailleurs, ces mêmes clients disposent d'un compte MySQL leur permettant de créer des tables pouvant servir des données à leurs scripts PHP. Ainsi le commerçant a un compte MySQL ayant pour login admarticles et mot de passe mdparticles. Il possède une base dbarticles sur laquelle il a tous les droits. Notre commerçant a donc les éléments suffisants pour mettre sa gestion d'articles sur le web. Aidé par vous qui avez des compétences dans le développement Web, il se lance dans l'aventure.

VII-D-2. La base de données

Notre commerçant fait la maquette suivante de l'interface web d'accueil qu'il souhaiterait :

Image non disponible

Il y aurait deux sortes d'utilisateurs :

  • des administrateurs qui pourraient tout faire sur la table des articles (ajouter, modifier, supprimer, consulter…). Ceux-là pourront utiliser tous les éléments du menu ci-dessus. En particulier, ils pourront émettre toute requête SQL via l'option [Requête SQL].
  • les utilisateurs normaux (pas administrateurs) qui auraient des droits restreints : droits d'ajouter, de modifier, de supprimer, de consulter. Ils peuvent n'avoir que certains de ces droits, le seul droit de consultation par exemple.

Du fait qu'il y a divers types d'utilisateurs de la base n'ayant pas les mêmes droits, il y a nécessité d'une authentification. C'est pourquoi la page d'accueil commence avec celle-ci. Pour savoir qui est qui, et qui a le droit de faire quoi, deux tables USERS et DROITS seront utilisées. La table USERS aurait la structure suivante :

Image non disponible

Le contenu de la table pourrait être le suivant :

Image non disponible

La table DROITS précise les droits qu'ont les utilisateurs non administrateurs présents dans la table USERS. Sa structure est la suivante :

Image non disponible

Le contenu de la table pourrait être le suivant :

Image non disponible

Remarques :

  • Un utilisateur U se trouvant dans la table USERS et absent de la table DROITS n'a aucun droit.
  • Dans notre exemple, les utilisateurs n'auront accès qu'à une seule table, la table ARTICLES. Mais notre commerçant, prévoyant, a néanmoins ajouté le champ table à la structure de la table DROITS afin de se donner la possibilité d'ajouter ultérieurement de nouvelles tables à son application.
  • Pourquoi gérer des droits dans nos propres tables alors qu'on fait l'hypothèse qu'on utilisera une base MySQL capable elle-même (et mieux que nous) de gérer ces droits dans ses propres tables ? Simplement parce que notre commerçant n'a pas les droits d'administration sur la base MySQL qui lui permettraient de créer des utilisateurs et de leur donner des droits. N'oublions pas en effet que la base MySQL est hébergée chez un fournisseur d'accès et que le commerçant n'est qu'un simple utilisateur de celle-ci sans aucun droit d'administration (heureusement). Il possède cependant tous les droits sur une base appelée dbarticles à laquelle il accède pour le moment avec le login admarticles et le mot de passe mdparticles. C'est dans cette base que prennent place toutes les tables de l'application.

La table ARTICLES rassemble les informations sur les articles vendus par le commerçant. Sa structure est la suivante :

Image non disponible

Son contenu utilisé dans un premier temps comme test pourrait être le suivant :

Image non disponible

VII-D-3. Les contraintes du projet

Le commerçant migre ici une application ACCESS locale en une application Web. Il ne sait ce que deviendra celle-ci et comment elle évoluera. Il voudrait cependant que la nouvelle application soit facile à utiliser et évolutive. C'est pour cette raison que son conseiller informatique a imaginé pour lui, lors de la conception des tables, qu'il pouvait y avoir :

  1. divers utilisateurs avec différents droits : cela permettra au commerçant de déléguer certaines tâches à d'autres personnes sans pour autant leur donner des droits d'administration
  2. dans le futur d'autres tables que la table ARTICLES

Le même conseiller fait d'autres propositions :

  • il sait que dans le développement logiciel, il faut séparer nettement les couches présentation et les couches traitement. L'architecture d'une application web est souvent la suivante :
Image non disponible

L'interface utilisateur est ici un navigateur web mais cela pourrait être également une application autonome qui via le réseau enverrait des requêtes HTTP au service web et mettrait en forme les résultats que celui-ci lui envoie. La logique applicative est constituée des scripts traitant les demandes de l'utilisateur, ici des scripts PHP. La source de données est souvent une base de données mais cela peut être aussi un annuaire LDAP ou un service web distant. Le développeur a intérêt à maintenir une grande indépendance entre ces trois entités afin que si l'une d'elles change, les deux autres n'aient pas à changer ou peu. Le conseiller informatique du commerçant fait alors les propositions suivantes :

  • On mettra la logique métier de l'application dans une classe PHP. Ainsi le bloc [Logique applicative] ci-dessus sera constitué des éléments suivants :
Image non disponible

Dans le bloc [Logique Applicative], on pourra distinguer

  • le bloc [IE=Interface d'Entrée] qui est la porte d'entrée de l'application. Elle est la même quelque soit le type de client.
  • le bloc [Classes métier] qui regroupe des classes nécessaires à la logique de l'application. Elles sont indépendantes du client.
  • le bloc des générateurs des pages réponse [IS1 IS2 … IS=Interface de Sortie]. Chaque générateur est chargé de mettre en forme les résultats fournis par la logique applicative pour un type de client donné : code HTML pour un navigateur ou un téléphone WAP, code XML pour une application autonome…

Ce modèle assure une bonne indépendance vis à vis des clients. Que le client change ou qu'on veuille faire évoluer sa façon de présenter les résultats, ce sont les générateurs de sortie [IS] qu'il faudra créer ou adapter.

  • Dans une application web, l'indépendance entre la couche présentation et la couche traitement peut être améliorée par l'utilisation de feuilles de style. Celles-ci gouvernent la présentation d'une page Web au sein d'un navigateur. Pour changer cette présentation, il suffit de changer la feuille de style associée. Il n'y a pas à toucher à la logique de traitement. On utilisera donc ici une feuille de style.
  • Dans le diagramme ci-dessus, c'est la classe métier qui fera l'interface avec la source de données. Par hypothèse, cette source est ici une base MySQL. Afin de permettre une évolution vers une autre base de données, on utilisera la bibliothèque PEAR qui offre des classes d'accès aux bases de données indépendantes du type réel de celles-ci. Ainsi si notre commerçant s'enrichit au point de pouvoir installer un serveur web IIS de Microsoft dans son entreprise, il pourra remplacer la base MySQL par SQL Server sans avoir (ou très peu) à changer la classe métier.

VII-D-4. La classe articles

La classe articles pourrait être définie de la façon suivante :

 
CacherSélectionnez

Commentaires

  • la classe articles utilise la bibliothèque PEAR::DB pour ses accès à la base de données d'où la commande
 
CacherSélectionnez

Cette inclusion suppose que le script DB.php soit dans l'un des répertoires de l'option include_path du fichier de configuration de PHP.

  • le constructeur a besoin de savoir à quelle base on se connecte et sous quelle identité. Ces informations lui sont données dans le dictionnaire $dDSN. Rappelons que l'hypothèse de départ était que la base s'appelait dbarticles et qu'elle appartenait à un utilisateur appelé admarticles ayant le mot de passe mdparticles. Rappelons aussi que cette application autorise plusieurs utilisateurs ayant différents droits. Il y a là une ambiguïté à lever. La connexion est bien ouverte sous l'identité de admarticles et finalement c'est sous cette identité que seront faites toutes les opérations sur la base dbarticles puisque c'est le seul nom que connaît le SGBD MySQL qui ait les droits suffisants pour gérer la base dbarticles. Pour "simuler" l'existence de différents utilisateurs, on fera travailler l'utilisateur admarticles avec les droits de l'utilisateur dont le login ($sUser) et le mot de passe ($sMdp) sont passés en paramètres au constructeur. Ainsi, avant de faire une opération sur la base des articles, on vérifiera que l'utilisateur ($sUser, $sMdp) a bien les droits de la faire. Si oui, c'est l'utilisateur admarticles qui la fera pour lui.
  • le login et le mot de passe de l'administrateur de la base des articles doivent être passés au constructeur. C'est une saine précaution. Si on inscrivait en "dur" dans le code de la classe ces deux informations, tout utilisateur de la classe pourrait se faire passer aisément pour administrateur de la base des articles. En effet, une classe PHP n'est pas protégée. Aussi l'attribut $bAdmin de la classe qui indique si l'utilisateur ($sUser, $sMdp) pour lequel on travaille est administrateur ou non pourrait très bien être positionné directement de l'extérieur comme dans l'exemple suivant :
 
CacherSélectionnez

PHP n'est pas JAVA ou C# et une classe PHP n'est qu'une structure de données un peu plus évoluée qu'un dictionnaire mais qui n'offre pas la sécurité d'une vraie classe où l'attribut bAdmin aurait été déclaré privé ou protégé rendant impossible sa modification de l'extérieur. Parce que l'utilisateur de la classe doit connaître le login et le mot de passe de l'administrateur de la base des articles, seul ce dernier peut utiliser la classe. L'opération précédente ne présente donc plus aucun intérêt pour lui. La classe est là uniquement pour lui offrir des facilités de développement. Une conséquence importante est qu'il n'y a pas lieu de prendre des précautions de sécurité. Encore une fois, celui qui utilise la classe articles est nécessairement administrateur de la base des articles.

  • la classe gère les erreurs de connexion à la base ou tout autre erreur de façon unique en remplissant l'attribut $aErreurs avec le ou des messages d'erreurs. Après chaque opération, l'utilisateur de la classe doit donc vérifier cette liste.
  • Les méthodes addArticle, updateArticle, deleteArticle, selectArticles, execute découlent directement de la maquette de l'interface web présentée précédemment. Elles correspondent en effet aux options du menu proposé. Les méthodes addArticle et modifyArticle s'appuient sur la méthode vérifierArticle pour vérifier que l'article qui va être ajouté ou être modifié a des données correctes. Toujours dans le même esprit, la méthode existeArticle permet de vérifier qu'on ne s'apprête pas à ajouter un article qui existe déjà. On pourrait se passer de cette méthode si on utilise une table d'articles où le code est clé primaire. C'est alors le SGBD lui-même qui signalera l'échec de l'ajout pour cause de doublon. Il le dira probablement avec un message d'erreur peu lisible et en anglais.
  • Un article à modifier ou à supprimer sera désigné par son code qui est unique. La méthode getCodes permet d'obtenir tous ces codes.
  • La méthode disconnect ferme la connexion à la base, connexion ouverte lors de la construction de l'objet. On ne voit pas l'intérêt ici de la méthode connect qui va recréer une connexion avec la base. Cela va permettre d'ouvrir et fermer cette connexion à volonté avec un même objet. L'intérêt n'apparaît qu'en conjonction avec l'application web. Celle-ci va créer un objet articles qu'elle va mémoriser dans une session. Si celle-ci va être capable de conserver la plupart des attributs de l'objet au fil des échanges client-serveur successifs, elle n'est pas capable cependant de garder l'attribut représentant la connexion ouverte. Celle-ci devra donc être réouverte à chaque nouvel échange client-serveur. On demandera une connexion persistante afin que la connexion ouverte soit stockée dans un pool de connexions et reste ouverte de façon permanente. Ainsi lorsque le script demandera une nouvelle connexion, celle-ci sera récupérée dans le pool de connexions. On arrive donc au même résultat que si la session avait pu mémoriser la connexion ouverte.
  • la méthode existeUser permet au constructeur de savoir si l'utilisateur $sUser identifié par le mot de passe $sMdp existe bien. Si oui, la méthode permet de savoir s'il est administrateur ou non (indiqué dans la table USERS) et mémorise cette information dans l'attribut $bAdmin. S'il n'est pas administrateur, la méthode va récupérer ses droits dans la table DROITS et les met dans l'attribut $dDroits qui est un dictionnaire à double indexation : $dDroits[$table][$droit] vaut 'y' si l'utilisateur $sUser a le droit $droit sur la table $table et vaut 'n' sinon.

Écrire la classe articles. Les accès à la base de données seront faits à l'aide de la bibliothèque PEAR::DB qui permet de s'affranchir du type exact de la base. On trouvera en annexe les éléments de base de cette bibliothèque.

VII-D-5. La structure de l'application WEB

Maintenant que nous avons la classe "métier" de gestion de la base d'articles, nous pouvons utiliser celle-ci dans différents environnements. Il est proposé ici de l'utiliser dans une application web. Découvrons celle-ci au-travers de ces différentes pages :

VII-D-5-a. La page type de l'application

Revenons sur la page d'accueil déjà présentée :

Image non disponible

Toutes les pages de l'application auront la structure ci-dessus, celle d'un tableau à deux lignes et trois colonnes comprenant quatre zones :

  1. la zone 1 forme la première ligne du tableau. Elle est réservée au titre accompagné éventuellement d'une image. Les trois colonnes de la ligne sont ici fusionnées.
  2. la seconde ligne a trois zones, une zone par colonne :
    1. la zone 2 contient les options du menu. Elle contient à son tour un tableau à une colonne et plusieurs lignes. Les options du menu sont placées dans les lignes du tableau.
    2. la zone 3 est vide et ne sert qu'à séparer les zones 2 et 4. On aurait pu procéder différemment pour réaliser cette séparation.
    3. la zone 4 est celle qui contient la partie dynamique de la page. C'est cette partie qui change d'une action à l'autre, les autres restant identiques.

Le script PHP générant cette page type s'appellera main.php et pourrait être le suivant :

 
CacherSélectionnez

Les zones paramétrées de la page ont été mises en relief dans le listing ci-dessus. La page type est paramétrée de plusieurs façons :

  • par un dictionnaire $main ayant les clés suivantes :
    • title : titre à mettre en zone 1 de la page
    • liens : dictionnaires des liens à générer dans la colonne du menu. Ces liens sont associés aux options du menu de la zone 2
    • contenu : url de la page à afficher dans la zone 4
  • par un dictionnaire $dConfig rassemblant des informations tirées d'un fichier de configuration de l'application appelé config.php
  • par des classes faisant partie de la feuille de style utilisée par la page :
 
CacherSélectionnez

La page utilise ici les classes de style suivantes :

  1. menutitle : pour une option principale du menu
  2. menublock : pour une option secondaire du menu

Changer l'un des paramètres change l'aspect de la page. Ainsi changer $main['title'] changera le titre de la zone 1.

VII-D-5-b. Le traitement type d'une demande d'un client

Le client interagit avec l'application grâce aux liens de la zone 2 de la page type. Ces liens seront du type suivant :

 
CacherSélectionnez
action désigne l'action en cours parmi les suivantes :
phase une action peut se faire en plusieurs étapes - désigne l'étape en cours
PHPSESSID jeton de session lorsque celle-ci a démarré - permet au serveur de récupérer des informations stockées dans la session lors des précédents échanges

De même l'attribut action dans les formulaires aura la même forme. Par exemple, dans la page d'accueil il y a un formulaire de login dans la zone 4. La balise HTML de ce formulaire est définie comme suit :

 
CacherSélectionnez

Le traitement de la demande du client est accomplie par le script principal de l'application appelé apparticles.php. Son travail est de construire la réponse au client. Il procèdera toujours de la même façon :

  • grâce au nom de l'action et à la phase en cours, il déléguera la demande à une fonction spécialisée. Celle-ci traitera la demande et génèrera la page réponse adéquate. Pour chaque demande du client, il peut y avoir plusieurs pages réponse possibles : page1, page2…, pagen. Ces pages contiennent des informations qui doivent être calculées par la fonction. Ce sont donc des pages paramétrées. Elles seront générées par des scripts page1.php, page2.php…, pagen.php.
  • par souci d'homogénéité, les parties variables des pages à afficher dans la zone 4 de la page type seront elles-aussi placées dans le dictionnaire $main.

Supposons qu'en réponse à une demande, le serveur doive envoyer la page pagex.php au client. Il procèdera de la façon suivante :

  • il placera dans le dictionnaire $main les valeurs nécessaires à la page pagex.php
  • il mettra dans $main['contenu'] qui désigne l'URL de la page à afficher en zone 4 de la page type, l'URL de pagex.php
  • il demandera l'affichage de le page type avec l'instruction
 
CacherSélectionnez

La page type sera alors affichée avec dans la zone 4 le code du script pagex.php qui sera évalué pour générer le contenu de la zone 4. On se rappellera que celle-ci est une simple cellule d'un tableau. Il ne faut donc pas que le code HTML généré par pagex.php commence par les balises <HTML>, <HEAD>, <BODY>…. Celles-ci ont déja été émises au début de la page type. Voici par exemple à quoi pourrait ressembler le script login.php qui génère la zone 4 de la page d'accueil :

 
CacherSélectionnez

On voit que la page :

  • est réduite à un formulaire
  • est paramétrée à la fois par le dictionnaire $main et la feuille de style.

VII-D-5-c. Le fichier de configuration

On a toujours intérêt à paramétrer le plus possible les applications afin d'éviter d'aller dans le code simplement parce qu'on a décidé par exemple de changer le chemin d'un script ou d'une image. L'application principale apparticles.php chargera donc un fichier de configuration config.php à son démarrage :

 
CacherSélectionnez

On mettra dans ce fichier, des directives de configuration destinées à PHP et des initialisations de variables globales :

 
CacherSélectionnez

VII-D-5-d. La feuille de style associée à la page type

Nous avons vu que la réponse du serveur avait un format unique celui de main.php. On aura pu remarquer que ce script produit une page brute dépourvue d'effets de présentation. C'est une bonne chose pour plusieurs raisons :

  • le développeur n'a pas à se soucier de la présentation graphique de la page qu'il crée. Il n'a en effet pas nécessairement les compétences pour créer des pages graphiques attractives. Il peut ici se concentrer entièrement sur le code.
  • la maintenance des scripts est facilitée. Si ceux-ci comportaient des attributs de présentation, ni la structure du code, ni celle de la présentation n'apparaîtraient clairement. L'aspect graphique des feuilles est souvent délégué à un graphiste. Celui-ci n'aimerait probablement pas à devoir chercher dans un script qu'il ne comprend pas, où sont les attributs de présentation qu'il doit modifier.

Il faut pourtant bien se soucier de l'aspect graphique des pages. En effet, c'est cela qui attire les internautes vers un site. Ici, la présentation est déléguée à une feuille de style. La page main.php indique dans son code la feuille de style à utiliser pour l'afficher :

 
CacherSélectionnez

La feuille de style utilisée dans ce document est la suivante :

 
CacherSélectionnez

Nous n'entrerons pas dans les détails de cette feuille de style. Nous l'accepterons telle-quelle. Un peu plus loin nous verrons comment la construire et la modifier. Il existe des logiciels pour cela. Néanmoins indiquons le rôle des attributs de présentation utilisés dans la feuille :

Attribut : régit la présentation de la balise HTML :
BODY <BODY>
H1 <H1> (Header1)
A <A> (Anchor)
A:HOVER fixe les attributs de présentation de l'ancre lorsque l'utilisateur passe la souris dessus
FIELDSET <FIELDSET> - cette balise n'est pas reconnue par tous les navigateurs
LEGEND <LEGEND> - cette balise n'est pas reconnue par tous les navigateurs
INPUT <INPUT>
INPUT.TEXT <INPUT class="TEXT">
INPUT.SUBMIT <INPUT class="SUBMIT">
TH <TH> (Table Header)
TD.menutitle <TD class="menutitle"> (Table Data)
TD.menublock <TD class="menublock">
TD.libellé <TD class="libellé">

Voyons sur un exemple comment peuvent être écrites ces règles de présentation. Dans cet exemple, nous utiliserons le logiciel TopStyle Lite disponible gratuitement à l'URL http://www.bradsoft.com. Une fois chargée la feuille de style, on a une fenêtre à trois zones :

  • une zone d'édition de texte. Les attributs de présentation peuvent être définis à la main à condition de connaître les règles d'écriture des feuilles de style qui suivent une norme appelée CSS (Cascading Style Sheets).
  • la zone 2 présente les propriétés éditables de l'attribut en cours de construction. C'est la méthode la plus simple. Elle évite d'avoir à connaître le nom exact des attributs de présentation qui sont très nombreux
  • la zone 3 donne l'aspect visuel de l'attribut en cours de construction
Image non disponible

Dans la zone 1 ci-dessus, opérons un copier-coller de l'attribut INPUT.submit vers un attribut INPUT.fantaisie. Cet attribut fixera la présentation de la balise HTML <INPUT class="fantaisie">

Image non disponible

Utilisons la zone 2 pour modifier certaines des propriétés de l'attribut INPUT.fantaisie :

Image non disponible

Désormais, toute balise <INPUT … class="fantaisie"> trouvée dans une page HTML associée à la feuille de style précédente sera présentée comme l'exemple de la zone 3 ci-dessus.

L'intérêt des feuilles de style est grand. Leur utilisation permet de changer le "look" d'une application web en ne modifiant celle-ci qu'en un seul point : sa feuille de style. Les feuilles de style ne sont pas reconnues par les navigateurs anciens. La directive <link ..> ci-dessous sera ignorée par certains d'entre-eux :

 
CacherSélectionnez

Dans notre application, cela donnera la page d'accueil suivante :

Image non disponible

On a là une page minimale sans recherche graphique. On peut avoir pire. Certaines versions de navigateurs reconnaissent les feuilles de style mais les interprètent mal. On peut alors avoir une page défigurée et inutilisable. Se pose donc la question du type du navigateur client. Il existe des techniques qui aident à déterminer le type du navigateur client. Elles ne sont pas totalement fiables. On peut alors écrire différentes feuilles de style pour différents navigateurs voire écrire une version sans feuille de style pour les navigateurs qui les ignorent. Cela alourdit bien sûr la tâche de développement. Ce problème important a été ici ignoré.

Avec les feuilles de style, on peut imaginer offrir un environnement personnalisé aux utilisateurs de notre application. Nous pourrions leur présenter une page leur offrant plusieurs styles de présentation possibles. Ils pourraient choisir celui qui leur convient le mieux. Ce choix pourrait être enregistré dans une base de données. Lorsque l'utilisateur se connecte de nouveau, on pourrait alors démarrer l'application avec la feuille de style qui a reçu sa préférence.

VII-D-5-e. Le module d'entrée de l'application

Les clients ne connaîtront de l'application que son module d'entrée : apparticles.php. Les grandes lignes de son fonctionnement sont les suivantes :

  • La demande du client est récupérée et analysée. Celle-ci est paramétrée ou non. Lorsqu'elle est paramétrée, les paramètres attendus sont les suivants : action=[action]&phase=[phase]&PHPSESSID=[PHPSESSID]
  • Si la demande n'est pas paramétrée ou si les paramètres récupérés ne sont pas ceux attendus, le serveur envoie comme réponse la page d'authentification (login, mot de passe). Dès que l'utilisateur se sera identifié correctement, une session est créée. Elle servira à stocker des informations tout au long des échanges client-serveur.
  • Si une demande est correctement reconnue, elle est traitée par un module qui dépend et de l'action et de la phase en cours.
  • Tous les accès à la base de données se font par l'intermédiaire de la classe métier articles.php.
  • Le traitement d'une demande se termine toujours par l'envoi au client de la page main.php dans laquelle on a précisé dans $main['contenu'] l'URL de la page à mettre dans la zone 4 de la page type.

Le squelette du script apparticles.php pourrait être le suivant :

 
CacherSélectionnez

On notera les points suivants :

  • les fonctions traitant une demande particulière du client se terminent par la génération de la page réponse et par une instruction exit qui termine l'exécution du script apparticles.php. Autrement dit, on ne "revient" pas de ces fonctions.
  • les fonctions admettent un ou deux paramètres :
    • $dConfig est un dictionnaire contenant des informations issues du fichier de configuration config.php. Toutes les fonctions l'utilisent.
    • $dSession est un dictionnaire contenant des informations de session. Il n'existe que lorsque la session a été créée c'est-à-dire après que l'authentification de l'utilisateur a réussi. C'est pourquoi les fonctions authentifier n'ont pas ce paramètre.

VII-D-5-f. La page d'erreurs

Toute application logicielle doit savoir gérer correctement les erreurs qui peuvent survenir. Une application web n'échappe pas à cette règle. Ici, lors d'une erreur, nous placerons la page erreurs.php suivante dans la zone 4 de la page type :

erreurs.php
CacherSélectionnez

Elle présente la liste d'erreurs définie dans $main['erreurs']. Par ailleurs, elle peut proposer un lien de retour, en général vers la page qui a précédé la page d'erreurs. Ce lien sera défini par un libellé $main['lien'] et une URL $main['href']. Pour ne pas avoir ce lien, il suffira de mettre la chaîne vide dans $main['lien']. Voici un exemple de page d'erreurs dans le cas où l'utilisateur s'identifie incorrectement :

Image non disponible

VII-D-5-g. La page d'informations

Parfois on voudra donner en réponse à l'utilisateur une simple information, par exemple que son identification a réussi. On utilisera pour cela la page infos.php suivante :

 
CacherSélectionnez

Pour afficher une information en retour à une demande d'un client, on

  1. mettra l'information dans $main['infos']
  2. mettra l'URL de infos.php dans $main['contenu']

Voici par exemple, l'information retournée lorsque l'utilisateur s'est identifié correctement :

Image non disponible

VII-D-6. Le fonctionnement de l'application

Nous avons maintenant une bonne idée de la structure générale de l'application à écrire. Il nous reste à présenter les cheminement de l'utilisateur dans l'application, les actions qu'il peut faire et les réponses qu'il reçoit du serveur. Ceci fait, nous pourrons écrire les fonctions qui traitent les différentes demandes d'un client. Dans ce qui suit, nous allons présenter le fonctionnement de l'application au travers des pages présentées à l'utilisateur en réponse à certaines de ces actions. Nous préciserons à chaque fois les points suivants :

action utilisateur action initiale de l'utilisateur qui a amené à la réponse affichée
paramètres envoyés les paramètres envoyés par le navigateur client au serveur en réponse à l'action manuelle de l'utilisateur
page réponse le script qui génère la zone 4 de la page type

VII-D-6-a. L'authentification

Avant de pouvoir utiliser l'application, l'utilisateur devra s'identifier à l'aide de la page suivante :

Image non disponible
action utilisateur 1 - demande initiale de l'URL apparticles.php
2 - utilisation de l'option Authentification du menu
3 - demande directe de l'URL articles.php avec des paramètres erronés
paramètres envoyés 1 - pas de paramètres
2 - action=authentifier?phase=0
3 - une liste de paramètres erronés
page réponse login.php

Sur la page d'accueil, le lien [Ajouter un article] est de la forme suivante : action=addarticle?phase=0. Les autres liens sont de la même forme avec action=(authentifier, updatearticle, deletearticle, selectarticle, sql). L'utilisateur remplit le formulaire et utilise le bouton [Connexion] :

Image non disponible

La réponse est la suivante :

Image non disponible
action utilisateur bouton [Connexion]
paramètres envoyés action=authentifier?phase=1
page réponse infos.php

Le titre de la page a été modifié pour indiquer le login de l'utilisateur et ses droits administrateur/utilisateur. Par ailleurs, tous les liens de la zone 2 ont été modifiés pour refléter le fait qu'une session a démarré. Le paramètre PHPSESSID=[PHPSESSID] leur a été ajouté.

Si le serveur n'a pas pu identifier le client, celui-ci aura une réponse différente :

Image non disponible
action utilisateur bouton [Connexion]
paramètres envoyés action=authentifier?phase=1
page réponse erreurs.php

Le lien [Retour à la page de login] est un lien sur l'URL apparticles.php?action=authentifier&phase=2&txtLogin=x. Ce lien ramène le client à la page de login où le champ de login est rempli par la valeur du paramètre txtLogin :

Image non disponible
action utilisateur lien [Retour à la page de login]
paramètres envoyés action=authentifier?phase=2&txtLogin=x
page réponse login.php

VII-D-6-b. Ajout d'un article

Le lien du menu [Ajouter un article] amène la page suivante dans la zone 4 de la page type :

Image non disponible
action utilisateur lien [Ajouter un article]
paramètres envoyés action=addArticle?phase=0&PHPSESSID=[PHPSESSID]
page réponse addarticle.php

L'utilisateur remplit les champs et envoie le tout au serveur avec le bouton [Ajouter] qui est de type submit. Aucune vérification n'est faite côté client. C'est le serveur qui les fait. Il peut envoyer en réponse une page d'erreurs comme sur l'exemple ci-dessous :

Demande Réponse
Image non disponible Image non disponible
action utilisateur bouton [Ajouter]
paramètres envoyés action=addArticle?phase=1&PHPSESSID=[PHPSESSID]
page réponse erreurs.php

Le lien [Retour à la page d'ajout d'article] permet de revenir à la page de saisie :

Demande Réponse
Image non disponible Image non disponible
action utilisateur lien [Retour à la page d'ajout d'article]
paramètres envoyés action=addArticle?phase=2&PHPSESSID=[PHPSESSID]
page réponse article.php

Si l'ajout se fait sans erreurs, l'utilisateur reçoit un message de confirmation :

Demande Réponse
Image non disponible Image non disponible
action utilisateur bouton [Ajouter]
paramètres envoyés action=addArticle?phase=1&PHPSESSID=[PHPSESSID]
page réponse infos.php

VII-D-6-c. Consultation d'articles

Le lien du menu [Lister des articles] amène la page suivante dans la zone 4 de la page type :

Image non disponible
action utilisateur lien du menu [Lister des articles]
paramètres envoyés action=selectArticle?phase=0&PHPSESSID=[PHPSESSID]
page réponse select1.php

Une requête select [colonnes] from articles where [where] orderby [orderby] sera émise sur la table des articles où [colonnes], [where] et [orderby] sont le contenu des champs ci-dessus. Par exemple :

Demande
Image non disponible
Réponse
Image non disponible
action utilisateur bouton [Afficher]
paramètres envoyés action=selectArticle?phase=1&PHPSESSID=[PHPSESSID]
page réponse select2.php

La demande peut être erronée auquel cas le client reçoit une page d'erreurs :

Demande
Image non disponible
Réponse
Image non disponible

Dans les deux cas (erreurs ou pas), le lien [Retour à la page de sélection d'articles] permet de revenir à la page select1.php :

Demande
Image non disponible
Réponse
Image non disponible
action utilisateur lien [Retour à la page de sélection d'articles]
paramètres envoyés action=selectArticle?phase=2&PHPSESSID=[PHPSESSID]
page réponse select1.php

VII-D-6-d. Modification d'articles

Le lien du menu [Modifier un article] amène la page suivante dans la zone 4 de la page type :

Image non disponible
action utilisateur lien de menu [Modifier un article]
paramètres envoyés action=updateArticle?phase=0&PHPSESSID=[PHPSESSID]
page réponse updatearticle1.php

On choisit le code de l'article à modifier dans la liste déroulante et on fait [OK] pour modifier l'article ayant ce code :

Demande Réponse
Image non disponible Image non disponible
action utilisateur bouton [OK]
paramètres envoyés action=updateArticle?phase=1&PHPSESSID=[PHPSESSID]
page réponse updatearticle2.php

Une fois obtenue la fiche de l'article à modifier, l'utilisateur peut faire ses modifications :

Demande Réponse
Image non disponible Image non disponible
action utilisateur bouton [Modifier]
paramètres envoyés action=updateArticle?phase=2&PHPSESSID=[PHPSESSID]
page réponse infos.php

L'utilisateur peut faire des erreurs lors de la modification :

Demande Réponse
Image non disponible Image non disponible

Le lien [Retour à la page de modification d'article] permet de revenir à la page de saisie :

Image non disponible
action utilisateur lien [Retour à la page de modification d'article]
paramètres envoyés action=updateArticle?phase=3&PHPSESSID=[PHPSESSID]
page réponse updatearticle2.php

VII-D-6-e. Suppression d'un article

Le lien du menu [Supprimer un article] amène la page suivante dans la zone 4 de la page type :

Image non disponible
action utilisateur lien du menu [Supprimer un article]
paramètres envoyés action=deleteArticle?phase=0&PHPSESSID=[PHPSESSID]
page réponse deletearticle1.php

L'utilisateur choisit le code de l'article à supprimer dans une liste déroulante :

Demande Réponse
Image non disponible Image non disponible
action utilisateur bouton [OK]
paramètres envoyés action=deleteArticle?phase=1&PHPSESSID=[PHPSESSID]
page réponse deletearticle2.php

L'utilisateur confirme la suppression de l'article avec le bouton [Supprimer] :

Demande Réponse
Image non disponible Image non disponible
action utilisateur bouton [Supprimer]
paramètres envoyés action=deleteArticle?phase=2&PHPSESSID=[PHPSESSID]
page réponse infos.php

VII-D-6-f. Émissions de requêtes administrateur

Le lien du menu [Requête SQL] amène la page suivante dans la zone 4 de la page type :

Image non disponible
action utilisateur lien de menu [Requête SQL]
paramètres envoyés action=sql?phase=0&PHPSESSID=[PHPSESSID]
page réponse sql1.php

On tape le texte de la requête SQL dans le champ de saisie et on utilise le bouton [Exécuter] pour l'exécuter. Seul un administrateur peut émettre ces requêtes comme le montre l'exemple suivant :

Demande Réponse
Image non disponible Image non disponible
action utilisateur bouton [Exécuter]
paramètres envoyés action=sql?phase=1&PHPSESSID=[PHPSESSID]
page réponse erreurs.php

Le lien [Retour à la page d'émission de requêtes SQL] permet de revenir à la page de saisie :

Image non disponible
action utilisateur lien [Retour à la page d'émission de requêtes SQL]
paramètres envoyés action=sql?phase=2&PHPSESSID=[PHPSESSID]
page réponse sql1.php

Si on est administrateur et que la requête est syntaxiquement correcte :

Demande
Image non disponible

on obtient le résultat de la requête :

Réponse
Image non disponible
action utilisateur bouton [Exécuter]
paramètres envoyés action=sql?phase=1&PHPSESSID=[PHPSESSID]
page réponse sql2.php

On peut émettre des requêtes de mise à jour des tables :

Demande
Image non disponible
Réponse
Image non disponible
action utilisateur bouton [Exécuter]
paramètres envoyés action=sql?phase=1&PHPSESSID=[PHPSESSID]
page réponse infos.php

VII-D-6-g. Travail à faire

Écrire les scripts et fonctions nécessaires à l'application :

identifiant type rôle
apparticles.php script le point d'entrée du traitement des demandes des clients
authentifier_0 fonction traite la demande paramétrée action=authentifier&phase=0
authentifier_1 fonction traite la demande paramétrée action=authentifier&phase=1
authentifier_2 fonction traite la demande paramétrée action=authentifier&phase=2
addarticle_0 fonction traite la demande paramétrée action=addArticle&phase=0
addarticle_1 fonction traite la demande paramétrée action=addArticle&phase=1
addarticle_2 fonction traite la demande paramétrée action=addArticle&phase=2
updatearticle_0 fonction traite la demande paramétrée action=updatearticle&phase=0
updatearticle_1 fonction traite la demande paramétrée action=updatearticle&phase=1
updatearticle_2 fonction traite la demande paramétrée action=updatearticle&phase=2
updatearticle_3 fonction traite la demande paramétrée action=updatearticle&phase=3
deletearticle_0 fonction traite la demande paramétrée action=deletearticle&phase=0
deletearticle_1 fonction traite la demande paramétrée action=deletearticle&phase=1
deletearticle_2 fonction traite la demande paramétrée action=deletearticle&phase=2
selectarticle_0 fonction traite la demande paramétrée action=selectarticle&phase=0
selectarticle_1 fonction traite la demande paramétrée action=selectarticle&phase=1
selectarticle_2 fonction traite la demande paramétrée action=selectarticle&phase=2
sql_0 fonction traite la demande paramétrée action=sql&phase=0
sql_1 fonction traite la demande paramétrée action=sql&phase=1
sql_2 fonction traite la demande paramétrée action=sql&phase=2
main.php script génère la page type
login.php script génère la page de login
erreurs.php script génère la page d'erreurs
infos.php script génère la page d'informations
addarticle.php script génère la page d'ajout d'un article
updatearticle1.php script génère la page 1 de la modification d'un article
updatearticle2.php script génère la page 2 de la modification d'un article
deletearticle1.php script génère la page 1 de la suppression d'un article
deletearticle2.php script génère la page 2 de la suppression d'un article
select1.php script génère la page 1 de la sélection d'articles
select2.php script génère la page 2 de la sélection d'articles
sql1.php script génère la page 1 de l'émission de requêtes
sql2.php script génère la page 2 de l'émission de requêtes

VII-D-7. Faire évoluer l'application

Nous avons à ce point une application qui fait ce qu'elle doit faire avec une ergonomie acceptable. Nous allons la faire évoluer sur différents points :

  • le SGBD
  • sa sécurité
  • son look
  • ses performances

VII-D-7-a. Changer le type de la base de données

Notre étude supposait que le SGBD utilisé était MySQL. Changez de SGBD et montrez que la seule modification à faire est dans la définition de la variable $dDSN dans le fichier de configuration config.php.

VII-D-7-b. Améliorer la sécurité

Lors du développement d'une application web, il ne faut jamais faire l'hypothèse que le client est un navigateur et que la demande qu'il nous envoie est contrôlée par le formulaire qu'on lui a envoyé avant cette demande. N'importe quel programme peut être client d'une application web et donc envoyer n'importe quelle demande paramétrée ou non à l'application. Celle-ci doit donc tout vérifier.

Si on se reporte au code du script apparticles.php, on constate

  • qu'aucune action autre que l'authentification ne peut avoir lieu sans session. Celle-ci n'existe que si l'utilisateur a réussi à s'authentifier. Rappelons qu'une session est identifiée par une chaîne de caractères assez longue appelée le jeton de session et qui a la forme suivante : 176a43609572907333118333edf6d1fb. Ce jeton peut être envoyé à l'application de diverses façons, par exemple en utilisant une URL paramétrée :

apparticles.php?PHPSESSID=176a43609572907333118333edf6d1fb.

Un programme qui demanderait, de façon répétée, l'URL précédente en faisant varier le jeton de façon aléatoire dans l'espoir de trouver le bon jeton, a toute chance de mettre de nombreux jours avant de générer la bonne combinaison tellement le nombre des combinaisons possibles est grand. D'ici là, la session étant d'une durée limitée, sera très probablement terminée. Un autre risque serait que le jeton, passant en clair sur le réseau, soit intercepté. Le risque est réel. On peut alors utiliser une connexion cryptée entre le serveur et son client.

  • qu'une fois la session lancée, seules certaines actions sont autorisées. Une URL paramétrée par action=tricher&phase=0&PHPSESSID=[PHPSESSID] serait rejetée car l'action 'tricher' n'est pas une action autorisée. Lorsque les paramètres (action, phase) ne sont pas reconnus, notre application répond par la page d'authentification.

Cependant l'application ne vérifie pas si les actions autorisées s'enchaînent correctement. Par exemple, les deux actions suivantes :

  • action=addArticle&phase=0&PHPSESSID=[PHPSESSID]
  • action=updateArticle&phase=1&PHPSESSID=[PHPSESSID]

sont deux actions autorisées. Cependant l'action 2 n'est pas autorisée à suivre l'action 1.

Comment suivre l'enchaînement des URL demandées par le navigateur client ?

On peut s'aider de deux variables PHP : $_SERVER['REQUEST_URI] et $_SERVER['HTTP_REFERER] qui sont deux informations envoyées par les navigateurs clients dans leurs entêtes HTTP.

$_SERVER['REQUEST_URI] : C'est l'URI demandée par le client. Par exemple

 
CacherSélectionnez

$_SERVER['HTTP_REFERER] : C'est l'URL qui était visualisée dans le navigateur avant la nouvelle URL qu'est en train de demander le navigateur (l'URI précédente). Par exemple, si le navigateur qui a visualisé l'URI évoquée précédemment fait une nouvelle demande à un serveur, la variable $_SERVER['HTTP_REFERER'] de celui-ci aura pour valeur

 
CacherSélectionnez

Pour vérifier que deux actions de notre application se suivent dans l'ordre, on peut procéder ainsi :

Lors de l'action 1 :

  • on note l'URI demandée (URI1) et on la note dans la session

Lors de l'action 2 :

  • on récupère le HTTP-REFERER de l'action 2. On en déduit l'URI (URI2) de l'URL qui était précédemment visualisée dans le navigateur qui fait la demande.
  • on récupère l'URI URI1 qui était mémorisée dans la session et qui est l'URI de l'action demandée précédemment au serveur
  • Si l'action 2 suit l'action 1, alors on doit avoir URI2=URI1. Si ce n'était pas le cas, on refuserait de faire l'action demandée et on présenterait la page d'authentification.
  • on note dans la session l'URI URI2 de l'action en cours pour vérification de l'action suivante. Et ainsi de suite.

Voici un exemple. Après authentification, on choisit le lien [Ajouter un article] :

Image non disponible

L'URL de cette page est :

 
CacherSélectionnez

Directement dans le champ [Adresse] du navigateur, nous modifions l'URL de la façon suivante :

 
CacherSélectionnez

Nous obtenons alors la page d'authentification :

Image non disponible

Cela mérite une explication. Lorsqu'on demande une URL en tapant directement son identité dans le champ adresse du navigateur, celui-ci n'envoie pas l'entête HTTP_REFERER. Notre application n'y trouve donc pas l'URI de l'action précédente, URI qu'elle avait mémorisée dans la session. Elle renvoie alors la page d'authentification en réponse.

Ce mécanisme est efficace pour les navigateurs mais nullement pour un client programmé. Celui-ci peut envoyer l'entête HTTP_REFERER qu'il veut. Il peut donc "tricher" en disant qu'il est bien passé par telle étape alors qu'il ne l'a pas fait. Il faut alors s'assurer que l'enchaînement des étapes est respecté. Ainsi si l'action demandée est action=addArticle&phase=1 (saisie) alors l'action précédente doit être forcément action=deleteArticle&phase=0 (demande initiale de la page de saisie) ou action=addArticle&phase=2 (retour en saisie après ajout erroné). De même si l'action demandée est action=addArticle&phase=2 (ajout) alors l'action précédente doit être action=addArticle&phase=1 (saisie). On peut forcer l'utilisateur à respecter ces enchaînements.

Alors que le premier mécanisme est général et peut s'appliquer à toute application, le second nécessite un codage spécifique à chaque application et est plus lourd : il faut passer en revue toutes les actions possibles de l'utilisateur et leurs enchaînements. On peut mémoriser ces derniers dans un dictionnaire comme le montre le code qui suit :

 
CacherSélectionnez

$dPrec['action']['phase'] est un tableau qui contient les actions qui peuvent précéder l'action et la phase qui servent d'index au dictionnaire. Ces actions précédentes sont elles aussi représentées par un dictionnaire à deux clés 'action' et 'phase'. Si une action peut être précédée par toute action alors $dPrec['action']['phase'] sera un tableau vide. L'absence d'une action dans le dictionnaire signifie qu'elle n'est pas autorisée. Considérons l'action "authentifier" ci-dessus :

 
CacherSélectionnez

Le code ci-dessus signifie que l'action action=authentifier&phase=0 peut être précédée de toute action, que action=authentifier&phase=1 peut être précédée de action=authentifier&phase=0 ou de action=authentifier&phase=2 et que action=authentifier&phase=2 peut être précédée de l'action action=authentifier&phase=1.

Écrire la fonction suivante :

 
CacherSélectionnez

Cette fonction permet à l'application principale de vérifier que l'enchaînement des actions est correct :

 
CacherSélectionnez

VII-D-7-c. Faire évoluer le "look"

Rappelons-nous qu'une des conditions posées lors de l'étude de cette application était qu'elle devait être évolutive. Supposons qu'au bout de quelques semaines, on s'aperçoive que l'ergonomie de l'application doit être améliorée. Modifiez l'application de telle façon que la structure et la présentation de la page type soient changées. Les modifications auront lieu à deux endroits :

  • dans le script main.php qui définit la structure de la page type. Faites évoluer celle-ci.
  • dans la feuille de style qui donne le "look" de l'application. Changez celui-ci.

VII-D-7-d. Améliorer les performances

Pour l'instant, nous avons opté pour un navigateur client léger : il ne fait rien d'autre que de la présentation. On peut lui faire faire du traitement en incluant des scripts dans les pages Web qu'on lui envoie. Ceux-ci peuvent être en différents langages, notablement vbscript et javascript. Internet Explorer et Netscape dominent le marché des navigateurs dans une proportion proche de 60/40. Par ailleurs, IE n'existe que dans le domaine Windows et pas sur Unix par exemple où c'est Netscape qui prédomine. Netscape n'exécute pas nativement les scripts vbscript alors que les deux navigateurs exécutent les scripts javascript. Comme Netscape occupe encore une part significative du marché des navigateurs, les scripts vbscript sont à éviter. C'est donc javascript qui est généralement utilisé dans les scripts côté client.

Sont délégués aux scripts côté client, des traitements où le serveur n'a pas à intervenir. Dans notre application, il serait intéressant que le navigateur client n'envoie une demande au serveur qu'après l'avoir vérifiée. Ainsi il est inutile d'envoyer au serveur une demande d'authentification alors que l'utilisateur a laissé le champ [login] vide dans le formulaire d'authentification. Il est préférable de prévenir l'utilisateur que sa demande est erronée :

Image non disponible

On remarquera que cela n'empêchera pas le serveur de vérifier que le champ login est non vide car son client n'est pas forcément un navigateur et alors la vérification précédente a pu ne pas être faite. Faire l'hypothèse que le client est un navigateur est un risque majeur pour la sécurité de l'application.

Reprenez les différents moments où le navigateur envoie des informations au serveur et lorsque celles-ci peuvent être vérifiées, écrivez une ou des fonctions javascript qui permettront au navigateur de vérifier la validité des informations avant leur envoi au serveur.

Pour reprendre l'exemple précédent, le script login.php qui génère la page d'authentification devient le suivant :

 
CacherSélectionnez

VII-D-8. Pour aller plus loin

Pour terminer, signalons quelques pistes pour approfondir cette étude de cas :

  • il serait intéressant de voir si la page type de cette application ne pourrait pas faire l'objet d'une classe. Celle-ci pourrait être alors utilisée dans d'autres applications.
  • notre application est bien adaptée à des clients de type navigateur mais moins à des clients de type "Application autonome". Celles-ci doivent :
    • créer une connexion tcp avec le serveur
    • lui "parler" HTTP
    • analyser ses réponses HTML pour y trouver l'information désirée puisque le client autonome ne sera probablement pas intéressé par le code HTML de présentation destiné aux navigateurs.

Il serait intéressant que notre application génère du XML plutôt que du HTML. Ses clients pourraient alors être indifféremment des navigateurs (assez récents quand même) ou des applications autonomes. Ces dernières n'auraient aucune difficulté à retrouver l'information qu'elles recherchent puisque la réponse XML du serveur ne contiendrait aucune information de présentation, seulement du contenu.

  • il faudrait très certainement s'intéresser aux accès simultanés à la base d'articles. Il y a au moins deux points à éclaircir :
  • est-ce que le SGBD utilisé par l'application gère correctement l'accès simultané à un même article ? Par exemple, que se passe-t-il si deux utilisateurs modifient le même article au même moment (ils appuient sur le bouton [Modifier] en même temps) ? Cela dépend probablement du SGBD sous-jacent.
  • actuellement notre application ne gère pas les accès simultanés. Cependant, la base devrait rester dans un état cohérent même si des surprises sont à attendre. Prenons la séquence d'événements suivante :
      • l'utilisateur U1 entre en modification d'un article
      • l'utilisateur U2 entre en suppression du même article un peu après
      • chacune des deux actions nécessite des échanges client-serveur. Selon la façon de travailler de chacun, l'utilisateur U2 peut terminer son travail avant U1. Lorsque celui-ci va terminer ses modification et les valider par [Modifier], il aura la page d'informations en réponse, le SGBD lui indiquant que [0 ligne(s) ont été modifiées], ceci parce que la page qu'il voulait modifier a été supprimée entre-temps. L'utilisateur sera sans doute surpris. D'un point de vue ergonomie, il serait sans doute préférable d'afficher une page signalant mieux l'erreur. Par ailleurs, on pourrait envisager d'offrir à l'utilisateur un accès exclusif à un article dès qu'il entrerait en mise à jour de celui-ci. Un autre utilisateur voulant entrer en mise à jour du même article se verrait répondre qu'une autre mise à jour est en cours. Cela posera problème si le premier utilisateur tarde à valider sa mise à jour : les autres seront bloqués. Il y a là des solutions à trouver qui dépendront assez largement des capacités du SGBD utilisé. Oracle a, par exemple, davantage de capacités dans ce domaine que MySQL.

VII-E. PEAR DB: a unified API for accessing SQL-databases

This chapter describes how to use the PEAR database abstraction layer.

DSN -- The data source name

Connect -- Connecting and disconnecting

Query -- Performing a query against a database.

Fetch -- Fetching rows from the query

VII-E-1. DSN

To connect to a database through PEAR::DB, you have to create a valid DSN - data source name. This DSN consists in the following parts:

phptype: Database backend used in PHP (i.e. mysql, odbc etc.)
dbsyntax: Database used with regards to SQL syntax etc.
protocol: Communication protocol to use ( i.e. tcp, unix etc.)
hostspec: Host specification (hostname[:port])
database: Database to use on the DBMS server
username: User name for login
password: Password for login
proto_opts: Maybe used with protocol

The format of the supplied DSN is in its fullest form:

 
CacherSélectionnez

Most variations are allowed:

 
CacherSélectionnez

The currently supported database backends are:

 
CacherSélectionnez

With an up-to-date version of DB, you can use a second DSN format :

 
CacherSélectionnez

VII-E-2. Connect

To connect to a database you have to use the function DB::connect(), which requires a valid DSN as parameter and optional a boolean value, which determines wether to use a persistent connection or not. In case of success you get a new instance of the database class. It is strongly recommened to check this return value with DB::isError(). To disconnect use the method disconnect() from your database class instance.

 
CacherSélectionnez

VII-E-3. Query

To perform a query against a database you have to use the function query(), that takes the query string as an argument. On failure you get a DB Error object, check it with DB::isError(). On succes you get DB_OK (predefined PEAR::DB constant) or when you set a SELECT-statment a DB Result object.

 
CacherSélectionnez

VII-E-4. Fetch

The DB_Result object provides two functions to fetch rows: fetchRow() and fetchInto(). fetchRow() returns the row, null on no more data or a DB_Error, when an error occurs. fetchInto() requires a variable, which be will directly assigned by reference to the result row. It will return null when result set is empty or a DB_Error too.

 
CacherSélectionnez

VII-E-4-a. Select the format of the fetched row

The fetch modes supported are:

  • DB_FETCHMODE_ORDERED (default) : returns an ordered array. The order is taken from the select statment.
 
CacherSélectionnez
  1. DB_FETCHMODE_ASSOC : returns an associative array with the column names as the array keys
 
CacherSélectionnez
  • DB_FETCHMODE_OBJECT  : returns a DB_row object with column names as properties
 
CacherSélectionnez

VII-E-4-b. Set the format of the fetched row

You can set the fetch mode for every call or for your whole DB instance.

 
CacherSélectionnez

VII-E-4-c. Fetch rows by number

The PEAR DB fetch system also supports an extra parameter to the fetch statement. So you can fetch rows from a result by number. This is especially helpful if you only want to show sets of an entire result (for example in building paginated HTML lists), fetch rows in an special order, etc.

 
CacherSélectionnez

VII-E-4-d. Freeing the result set

It is recommended to finish the result set after processing in order to to save memory. Use free() to do this.

 
CacherSélectionnez

VII-E-4-e. Quick data retrieving

PEAR DB provides some special ways to retrieve information from a query without the need of using fetch*() and loop throw results.

getOne() retrieves the first result of the first column from a query

 
CacherSélectionnez

getRow() returns the first row and return it as an array

 
CacherSélectionnez

getCol() returns an array with the data of the selected column. It accepts the column number to retrieve as the second param.

 
CacherSélectionnez

The above sentence could return for example: $all_client_names = array('Stig', 'Jon', 'Colin');

getAssoc() fetches the entire result set of a query and return it as an associative array using the first column as the key.

 
CacherSélectionnez

getAll() fetches all the rows returned from a query

 
CacherSélectionnez

The get*() family methods will do all the dirty job for you, this is: launch the query, fetch the data and free the result. Please note that as all PEAR DB functions they will return a PEAR DB_error object on errors.

VII-E-4-f. Getting more information from query results

With PEAR DB you have many ways to retrieve useful information from query results. These are:

  • numRows(): Returns the total number of rows returned from a "SELECT" query.
 
CacherSélectionnez
  • numCols(): Returns the total number of columns returned from a "SELECT" query.
 
CacherSélectionnez
  • affectedRows(): Returns the number of rows affected by a data manipulation query ("INSERT", "UPDATE" or "DELETE").
 
CacherSélectionnez
  • tableInfo(): Returns an associative array with information about the returned fields from a "SELECT" query.
 
CacherSélectionnez

précédentsommaire

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 © 2001 Serge Tahé. 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.