petit éclairage sur « LAMP »

(PHP + MYSQL)

L'association de quelques logiciels libres a fait trembler les plus gros éditeurs. La possibilité de mettre en place un serveur Web de haut niveau (tant en possibilités, qualité, fiabilité, performances) entièrement en « open source » a conquis la majorité des développeurs. C'est l'association de Linux, Apache, MySQL et PHP (L.A.M.P.) qui est le plus souvent choisie.

Vous trouverez ici quelques notes écrites pour montrer simplement comment créer une base de données sur un serveur, et la consulter par le web. Ce n'est pas du tout un cours complet, il y a des tas de ressources sur le Web pour chaque partie. Je peux néanmoins vous proposer mes différents supports de cours, sujets et corrections de TP, contrôles... du cours programmation et bases de données (UE62) de licence physique et applications (parcours ingénierie). Ici on verra comment on articule chaque partie.

Sur un serveur (ici appelons-le ipst-srv) j'ai installé un gestionnaire de bases de données. Dans la pratique, j'ai utilisé Linux SuSE, lors de l'installation j'ai sélectionné (en plus de l'installation de base) apache, php, mysql. On peut aussi y rajouter phpmyadmin. Je n'ai rien eu de spécial à configurer, SuSE fait tout très bien tout seul. J'ai créé un compte pour les étudiants gérant le projet (supposons que j'ai nommé le compte "iup" avec comme mot de passe "mo2pass").

Pour que mes étudiants puissent facilement travailler sans accéder physiquement (et à plusieurs) sur ipst-srv, j'ai ouvert l'accès ssh (le fait d'installer openssh a suffi) plutôt que telnet (c'est l'option par défaut de SuSE désormais). Ils mettrons leurs pages dans leur home-dir sous le répertoire public_html, ils accéderont à leurs pages à l'URL http://ipst-srv/~iup. Pour transférer les pages facilement (et donc les travailler en local), j'avais validé ftp (sous Yast2, sous network/basic je l'ai démarré dans inetd, j'ai rajouté "-u 023" derrière in.ftp pour qu'ils puissent être en lecture par le serveur web). Je sais que c'est idiot d'utiliser ftp et pas telnet, mais sftp n'était pas encore bien pratique à utiliser directement dans certains gestionnaires de fichiers graphiques dont Windows. Mais les clients putty.exe et Winscp (winscp.sourceforge.net) sont vraiment si faciles à utiliser sous Windows, ainsi que les url « sftp://... » sous Konqueror, que désormais je ferais ainsi.

la base de données

Il y a deux moyens d'y accéder : en direct, on se connecte sur la machine distante, on appelle mysql et on tape les commandes en mode texte. Je le détaille dans ce chapitre. La seconde solution est d'utiliser un logiciel interface. J'utilise phpmyadmin. Une fois phpmyadmin installé sur ipst-srv, on se connecte par un navigateur web : http://ipst-srv/phpmyadmin, puis on peut directement tout faire : créer des tables, les remplir, les modifier,... C'est bien fait, inutile donc que je l'explique ici.

Donc, pour l'accès direct en mode texte, on se loge sous iup (dans un terminal : "ssh iup@ipst-srv", sous Windows j'utilise putty qui est génial et disponible à http://www.chiark.greenend.org.uk/~sgtatham/putty ou ftp-ipst ou ftp).

La première fois j'avais tapé "mysqladmin -hlocalhost -uroot password 'mo3pass'" (Entrée quand il m'a demandé le password actuel) puis je suis passé en su et j'ai créé un fichier .my.cnf dans /root dans lequel j'ai écrit

	[mysqladmin]
	user     = root
	password = mo3pass

je l'ai caché à tous à cause du mot de passe ("chmod 600 .my.cnf"). Je retourne sous le user iup, je crois que je n'ai plus besoin de passer sous su (attention par contre il existe un utilisateur nommé root dans ma base de données qui n'est pas le su de Linux).

Je peux regarder tous les utilisateurs existants par "mysql -uroot -pmo3pass mysql" puis "SELECT * FROM user;" ou "SELECT * FROM db;" pour voir les bases existantes (pour l'instant il n'y en a pas, mais je vais créer test tout de suite). Attention, dans mes exemples je mets toujours -pmo3passe, mais si l'on ne veut pas que le mot de passe soit dans le bash_history on ne met que -p, il demandera le mot de passe (idem pour la suite). Je mets les mots clef mysql en majuscules mais ça marche aussi bien en minuscules.

Maintenant je crée un autre utilisateur, que je nomme "administrateur" avec un autre mot de passe (mo4pass par exemple), il aura le droit de modifier comme il veut la base de donnée que nous allons créer mais pas les autres bases qu'on pourrait créer sur le même serveur. "mysqladmin -hlocalhost -uadministrateur password 'mo4pass'". J'aurais aimé créer aussi l'utilisateur "lecteur" qui ne pourra qu'interroger la base, mais je n'y suis pas arrivé sur le coup et depuis j'ai oublié de rééssayer.

Bon, maintenant créons une base. Pour cela il faut être le root de mysql : "mysql -uroot -pmo3pass" puis "CREATE DATABASE test;". Maintenant donnons tous les accès à administrateur "GRANT ALL PRIVILEGES ON test.* TO administrateur;" et je quitte "QUIT" pour travailler sous "administrateur" : "mysql -uadministrateur -pmo4pass test".

Dans cette base, définissons une table que je nommerai "infos" :

CREATE TABLE infos ( 
	code SMALLINT UNSIGNED DEFAULT '0' NOT NULL AUTO_INCREMENT,
	nom VARCHAR(20),
	naiss DATE, PRIMARY KEY (code) 
);

Il y a trois champs : code est un petit entier non signé entre 0 et 65535 (il existe aussi les TINYINT (<256), les MEDIUMINT et les BIGINT), nom est une chaîne de 20 caractères maxi, et naiss est une date. code sera la clef primaire de cette table, il faut qu'il soit unique pour chaque enregistrement (c'est pourquoi on l'auto-incrémente automatiquement à chaque création), il nous servira en général pour les relations entre tables.

Il nous faut désormais renseigner la table :

INSERT INTO infos VALUES ('','Patrick Trau', '1958-04-21');
INSERT INTO infos VALUES ('','Napoléon', '1769-8-15');
INSERT INTO infos VALUES ('','Jesus', '0-12-25');

Evidement je ne mets rien comme premier champ. Vérifions si tout va bien : "SELECT * FROM infos;" va m'afficher tous les champs de ma table. Si j'ai oublié le nom des tables : "SHOW TABLES;"

Testons tout de suite une requête : "SELECT nom FROM infos WHERE naiss>1900"

Et quittons (QUIT;)

HTML

Sur notre serveur ipst-srv tourne le serveur Apache, qui permet à tout internaute de regarder nos fichiers. Il faut qu'ils soient écrits en HTML : voir mon mémo sur http://pat.trau.fr/internet/html/memohtml.htm. Mas je vais donner quelques détails sur les formulaires, que nous utiliserons

Un formulaire contient divers champs d'entrée sortie. Il est obligatoirement mis entre <form> et </form>. Les options de form sont method="POST" (ou "GET"qui montre dans l'URL les arguments transmis) et surtout action="URL", avec URL une adresse de page web simple (sans grand intérêt), d'une page PHP (qui peut récupérer les données du formulaire), ou d'une fonction en javascript (qui pourra modifier la page actuelle, voir http://pat.trau.fr/program/javascript/ ). On ne peut avoir qu'une action par formulaire mais plusieurs formulaires sont possibles dans une même page.

Les champs d'un formulaire peuvent être :

un champ simple <INPUT type="text" size="10" name="prix">. La taille est un nombre de caractères. On peut y préparer une valeur par value="val-par-déf". Type="password" lui fait afficher des étoiles plutôt que les caractères tapés, mais ce n'est pas d'une sécurité phénoménale.

des boutons radio (un et un seul choisi) : <INPUT type="radio" name="nom" value="txt_affiché">. Les différents boutons doivent avoir le même nom pour être exclusifs. On rajoute checked comme option du bouton par défaut.

une liste déroulante (ce sera la valeur qui sera transmise, pas le texte de la liste)

  <SELECT name="nom"> 
      <OPTION value="val">txt à afficher
      <OPTION value="val" SELECTED>option par déf si nécessaire
      etc
  </SELECT>

Un champ de texte (comme un champ simple mais pour plusieurs lignes, on peut même mettre des ascenseurs)

  <TEXTAREA name="nom" cols="largeur"50 rows="nb_lignes"> 
     texte par défaut
  </TEXTAREA>

Un bouton. Il n'y a qu'un bouton submit, il lance l'action du formulaire. Le bouton reset vide le formulaire. Les autres boutons peuvent déclencher diverses fonctions javascript par exemple.

<input type="submit" value="Lancer mon programme">
<input type="reset" value="Effacer l'&eacute;cran">
<input type="button" value="texte affiché" onClick="action">

PHP

HTML crée une page statique (définie complètement lors de sa conception). Voyons comment créer des pages dynamiques (dépendantes du contexte lors de l'exécution). Il existe deux solutions pour exécuter un programme dans une page HTML. Soit "côté client" : le programme tourne sur la machine de la personne qui regarde la page (javascript par exemple), et donc a peut-être accès à cette machine; ou "côté serveur" qui nous intéresse plus ici : le programme tourne sur le serveur HTTP, va pouvoir accéder aux données disponibles sur le serveur et créer la page en fonction de ces données. Là encore deux solutions : soit un CGI : programme complètement indépendant (par exemple en C, voir /internet/html/tst-form.htm qui crée complètement une page HTML, soit un langage inséré dans une page web et interprété par serveur web au moment d'envoyer cette page (PHP par exemple).

Le PHP ressemble beaucoup au C (beaucoup de ce qui marche en C fonctionne en PHP), mais est moins rigoureux, et possède aussi d'autres possibilités.

Le serveur web va interpréter comme étant du PHP tout ce qui est dans la balise <?php , jusqu'au ?> (<? et ?> ou <script language=PHP> et </script> peuvent aussi être acceptés). A l'intérieur, tout ce qui doit être transmis à la page web doit l'être par echo (ou print ou printf pour les cas plus compliqués).

<? echo "<P>Hello world</P>"; ?>

La plus grosse différence avec le C est qu'il est inutile de déclarer les variables. Par contre, pour qu'il reconnaisse les variables, leur nom doit commencer par $. Il se débrouille tout seul pour leur type (entier, réel, chaîne, tableau, voire pointeur...).

<? for($i=0;$i<5;$i++) printf("<P>ligne n° %d </P>\n",$i); ?>

remarque pour printf : si vous voulez afficher un ", utilisez \"

Pour déclarer une fonction : function nom(arguments). Si la fonction contient un return, elle retourne quelque chose, sinon elle ne retourne rien. Les variables apparaissant pour la première fois dans un bloc (entre {}, en particulier dans une fonction) sont locales, sinon elles sont globales.

PHP accepte les tableaux (même de contenus différents : t[0]=5; t[1]="coucou";) et même les tableaux associatifs (age["pat"]=45; age["tap"]=27;)

Le passage de données entre deux pages HTML/PHP

Les valeurs des champs d'un formulaire sont transmis comme variables (globales) à la page php.

soit une première page : page.htm

<HTML>
<HEAD>
<TITLE>page de test FORMulaires</TITLE>
</HEAD>

<BODY>
<FORM method="POST" action="result.php">
<P>ENTREZ un nombre entier : <INPUT type="text" size="5" name="nombre">
<INPUT type="submit" value="cliquez ici">
</FORM>

</BODY>
</HTML>

On peut faire une page résultante (ici result.php) qui dépend des valeurs saisies dans le formulaire de la précédente :

<HTML>
<HEAD>
<TITLE>page de résultat du FORMulaire</TITLE>
</HEAD>

<BODY>
<P> bonjour et merci d'avoir cliqué sur le bouton.</P>

<?

$nombre=$_POST["nombre"]; /*il peut même connaître $nombre directement si le serveur accepte les variables globales (non recommandé) */
for($i=0;$i<$nombre;$i++) echo "-";

?>

</BODY>
</HTML>

Attention, il n'y a pas (par défaut) de variables globales entre différentes pages html. Pour garder une valeur entre plusieurs pages, on peut utiliser un cookie : on écrit la valeur sur le disque du client, et on la relit plus tard. Ceci permet à plusieurs ordinateurs de se connecter en même temps au serveur : chacun mémorise ses propres valeurs. Voir l'explication de www.commentcamarche.net. Exemple, à placer avant toute sortie php (echo, printf...) et avant les balises (<HTML>) : setcookie("nomcookie","valcokie".date(ymdhis),now()+(3600*2)); (valide pendant deux heures)

Une autre solution : une première page demande des valeurs, elles sont transmises à la suivante. Celle-ci peut les transmettre à sa suivante en créant un bouton caché : echo "<input type=\"hidden\" name=\"nom\" value=\"$nom\" \n";

PHP et SQL

Dernier point : en fonction de valeurs définies par un formulaire, comment une page PHP qui récupère ces valeurs peut envoyer (localement, du serveur au serveur) une requête, puis en récupérer le résultat pour le mettre en forme proprement et renvoyer le HTML correspondant.

Le mieux est de donner un exemple :

<form method="post" action="requete.php" >
<P> entrez le nom d'utilisateur   &nbsp;  :  &nbsp;&nbsp;
<input type=text size=21 name="nomuser"> </p>

<P> entrez le mot de passe   &nbsp;  :  &nbsp;&nbsp;
<input type=password size=21 name="motdepasse"> </p>

<input type=submit value="interroger la BD">

</form>

et dans le fichier « requete.php »

<HTML>
<BODY>
<?php
$nomuser=$_POST["nomuser"];
$motdepasse=$_POST["motdepasse"];
/* 1) se connecter à la base, sur localhost car mysql et apache tournent sur la même machine */
$db=mysql_connect("localhost",$nomuser,$motdepasse);
if (!$db)
 {
  echo "<BR><B>Erreur connection base de données</B><BR>\n";
  echo "</body></html>";
  exit;
 }
echo "nom d'user et mot de passe acceptés !<br>";
/* une fois connecté, il faut choisir la base sur laquelle on travaille
   je crois qu'il faut le faire même si on n'a qu'une seule base (au 
   chapitre base de données je l'ai appelée "test") */
mysql_select_db("test") or die("erreur de connexion a la base");

/* 2) lancons une requête : supposons qu'existe la table « infos » */
$result=mysql_query("SELECT * FROM infos;");
$err=mysql_errno($db);
if ($err) {	$msg=mysql_error($db);
		echo "<P>ERREUR  accès base TEST : $msg</P></body></html>";
		mysql_close($db);
		exit;
	}
/* 3) traitement du résultat : récup nombre de lignes et colonnes de la réponse */
$nblig=mysql_num_rows($result);
$nbcol=mysql_num_fields($result);
/* il ne reste plus qu'à afficher */
echo "<P>il y a $nblig lignes pour $nbcol colonnes</P>";
echo "<table border align=\"center\" width=\"90%\">";
for ($j=0;$j<$nbcol;$j++) printf("<th>%s</th>",mysql_field_name($result,$j));
for ($i=0;$i<$nblig;$i++)
 {
  $tab=mysql_fetch_row($result);
  printf("<tr>");
  for ($j=0;$j<$nbcol;$j++) echo "<td align=\"center\">$tab[$j]</td>";
  printf("</tr>");
 }
echo "</table>";
/* 4) fermer la connexion (automatique en fin de fichier, si oublié) */
mysql_close($db);
?>
</BODY></HTML>

Je pense avoir dit le principal pour comprendre le fonctionnement d'un système LAMP (et pour s'y lancer). Évidement, chaque chapitre que j'ai traité nécessite de nombreux détails, qu'il vous faudra trouver soit en bibliothèque, soit sur Internet. Quelques adresses utiles : http://www.commentcamarche.net , http://www.phpfacile.com. Un cours SQL par Hédia Mhiri Sellami de l'Institut Supérieur de Gestion de Tunis.


Patrick TRAU, IPST-ULP version mars 2005