Algorithmique et PHP

VS

Cédric Avril

Prérequis : PHP, MySQL, HTML, CSS, regex, algorithmique et structure de données, [C++ et POO souhaités].

- Vous avez un projet de site mais vous ne savez pas comment procéder ?

- Votre code est trop brouillon, vous vous perdez dans ses erreurs et souhaitez le reprendre sérieusement ?

- Ou mieux, vous trouvez que votre code est bien conçu, et vous souhaitez faire un dossier expliquant simplement son fonctionnement ?

Alors ce tutoriel est pour vous. Une méthode logique simple et connue pour programmer un site PHP efficacement sans risquer de s'y perdre. Il fera intervenir deux types d'algorithmes, l'un traçant graphiquement le fonctionnement de votre site, l'autre un algorithme normal mais arrangé pour être traduit en PHP. Vous aurez à effectuer un TP à la fin de ce cours pour être opérationnel et apte à écrire un programme rigoureux et solide.

- Titre : Algorithmique et PHP

- Auteur : Cédric Avril

- Parution : 18 mars 2011

- Mise à jour : 26 août 2012

- Public visé :

- Licence : 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 ® 2012 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. L'ALGORITHME GRAPHIQUE

- I-A. Les objets

- I-B. Exemple

- I-C. Les variables

- I-D. Syntaxe utilisée

- I-D-1. Requêtes SQL

- I-D-2. Symboles courants et fonctions

- I-E. Dia - préparation du plan de travail

- I-E-1. Importation des objets de base dans la feuille

- I-E-2. Conseils pour une mise en forme rapide d'un algorithme graphique

- II. PASSAGE D'UN ALGORITHME GRAPHIQUE VERS UN ALGORITHME LITTÉRAL

- II-A. Méthodologie

- II-A-1. Exemple de traduction

- II-A-2. Passage en PHP

- II-B. Énoncé du TP

- II-B-1. Présentation

- II-B-1-a. Connexion

- II-B-1-b. Inscription

- II-B-1-c. Déconnexion

- II-B-2. Fonctionnement

- II-B-2-a. Introduction

- II-B-2-b. Connexion

- II-B-2-c. Inscription

- II-B-2-d. Déconnexion

- II-B-2-e. Oubli d'identifiants

- III. Exemple de codage PHP d'un algorithme graphique (solution du TP)

- III-A. Algorithme graphique

- III-B. Algorithme littéral

- III-C. PHP

- III-D. Remarques et conclusion

- III-D-1. Les cookies

- III-D-2. Les requêtes SQL

- III-E. Les autres fichiers PHP

- III-E-1. debut.php

- III-E-2. menu_a2.php

- III-E-3. messagerie_css.php

- III-F. Remerciements

I - L'ALGORITHME GRAPHIQUE

Nous appellerons vulgairement « algorithme graphique » la schématisation d'un mécanisme informatique. C'est une représentation logique fondamentale et triviale sur laquelle on se base pour faciliter le développement d'un programme dans n'importe quel langage. Une structure algorithmique trop rapidement délaissée naguère au profit de ce qu'on appellera « l'algorithme littéral », communément appelé... algorithme.

Cette façon simpliste de concevoir un programme est la clé d'une programmation rigoureuse et idéale pour mener à bien vos projets.

Terminologie

- Algorithme graphique : organigramme, schéma décisionnel, etc. Ancien terme à n'utiliser que dans ce tutoriel.

- Algorithme littéral : pseudocode, algorithme. Traduction de l'algorithme graphique en français simple.

Pour celles et ceux qui connaissent déjà la façon de faire un algorithme graphique, je vous recommande d'utiliser crayon et feuilles pour mettre les idées à plat dans un premier temps puis d'utiliser Dia.

Il s'agit d'une structure arborescente constituée d'actions consécutives à faire en fonction de certaines données. Nous allons programmer une page contenant un emplacement où l'utilisateur peut s'inscrire, se connecter, se déconnecter, se désabonner et récupérer ses identifiants en utilisant ces méthodes.

Un algorithme graphique est utilisable dans n'importe quelle situation où l'on a : une situation d'origine, et une ou plusieurs situations à atteindre. C'est une espèce de labyrinthe d'actions qui nous envoient chacune dans une direction différente pour aboutir à une situation spécifique. La direction prise pour chaque action effectuée peut dépendre soit du résultat de l'action, soit d'un ou plusieurs paramètres externes à l'action, soit des deux.

I-A - Les objets

Voici les objets de base d'un algorithme graphique :

L'action

La condition

Certaines actions peuvent être accompagnées de paramètres indispensables ou complémentaires à leur exécution. L'action est validée lorsque la transition est vérifiée :

Voici une action avec une variable éventuellement présente :

I-B - Exemple

Illustrons d'un exemple l'algorithme graphique simple d'une personne qui s'en va voir une autre chez elle :

La sombre blague de la transition « porte » est pour vous faire prendre conscience qu'il s'agit de considérer tous les aspects, aussi futiles qu'ils soient, pour sécuriser l'algorithme. Ceci teste si la porte est restée intacte après l'avoir frappée.

À noter qu'on n'a pas utilisé de « =1 » suivi d'un losange conditionnel pour tester le cas où la porte s'effondrait pour ne pas alourdir l'algorithme.

Entraînez-vous avec des exemples simples ou plus compliqués à construire des algorithmes plus pertinents en imaginant ou prenant des exemples au choix dans la vie en général, ou bien dans vos propres sites, ou dans d'autres sites. Faire ses courses, aller à la plage, changer une roue de sa voiture, faire l'algorithme du jeu de l'oie...

I-C - Les variables

Dans tout programme, on fonctionne avec des variables, i.e. on écrit et lit leur contenu. Dans PHP, on a principalement quatre genres de variables :

- les variables normales du style $ma_variable ;

- les variables SQL ;

- les variables fichier ;

- les variables session.

Ce cours n'aborde pas la notion de variable en PHP, mais je vous encourage vivement à consulter ce site pour utiliser des opérateurs de comparaison comme ==, ou même ===, afin d'éviter la frustration de ne pas savoir pourquoi "test" == "te" est vrai. Préférez l'utilisation de === dans ce cas. Vous verrez dans la solution du TP la pertinence de ces opérateurs.

J'ai donc utilisé une couleur différente pour chaque variable utilisée dans l'algorithme graphique. Aussi n + 1 -> n dans l'exemple précédent devenait n + 1 -> n.

I-D - Syntaxe utilisée

I-D-1 - Requêtes SQL

Le langage SQL nous servira à stocker les variables à l'aide de requêtes. On pourra symboliser l'accès à ces données de cette façon :

Variable

Désignation

Table:nom_membres

Tous les éléments de la colonne nom_membre de la table Table

Table[variable].nom_membre

nom_membre de la ligne où le contenu de la variable variable est présent

I-D-2 - Symboles courants et fonctions

Pour tester certaines conditions e.g. faisant intervenir des variables (et donc des requêtes) SQL, j'ai recours aux fonctions et opérateurs suivants :

opérateur ou fonction

Désignation

variableTable:nom_membres

Teste si le contenu de variable est un élément de la colonne nom_membre de la table Table

id_pseudo

Teste si le cookie id_pseudo existe

Membre[id_nick]

Toutes les lignes de la table Membre contenant le contenu de la variable session id_nick

Ajouter Membre(date,id_nick)

Ajoute dans la table Membre les contenus des variables dans les colonnes qui vont bien.

Tout ceci est à titre indicatif, vous pouvez utiliser la syntaxe que vous voulez. C'est juste celle que j'utilise dans ma solution du TP. Cette liste est d'autre part non exhaustive, i.e. elle ne fait qu'expliquer les symboles que j'utilise dans ma solution. Vous pouvez faire intervenir d'autres symboles mathématiques ou fonctions se rapportant à des requêtes SQL si celles-ci vous siéent. Pour plus d'explications sur les symboles que j'utilise, rendez-vous sur cette page.

Cette partie étant assez simple pour beaucoup, prière de ne pas se hâter cependant dans sa lecture, pour être paré pour le TP. Dans la partie suivante, nous construirons les objets des algos graphiques avec Dia puis approfondirons ses outils.

I-E - Dia - préparation du plan de travail

I-E-1 - Importation des objets de base dans la feuille

Lancez DIA.

Familiarisez-vous avec DIA. Nous utiliserons les objets génériques se trouvant en haut à gauche pour faire des lignes, voire des zigzags, ainsi que certains objets de différents types, que vous pouvez trouver dans toutes les autres feuilles. Nous avons besoin des objets correspondant à ceux énoncés dans le chapitre précédent. Il serait aussi judicieux de créer une transition, pour éviter d'avoir à tramer deux lignes et du texte à chaque fois. Voici la procédure pour créer ce nouvel objet dans notre feuille. Elle s'appuie sur le tuto efficace de James Henstridge sur cette page.

Allez dans le dossier Dia dans vos programmes installés. Nous aurons besoin du contenu des dossiers shapes et sheets.

Dans sheets, éditez le ficher texte du même nom que la feuille, ici Algorithme Graphique.sheet. Créez aussi un dossier Algorithme Graphique dans shapes, puis créez et éditez un fichier transition.shape.

Voici le code "custom shape" à insérer dans chacun des fichiers à éditer :

Algorithme Graphique.sheet

<?xml version="1.0" encoding="UTF-8"?>
<sheet xmlns="http://www.lysator.liu.se/~alla/dia/dia-sheet-ns">
<name>Algorithme Graphique</name>
<description>Objets pour algorithmes graphiques</description>
<contents>
<object name="transition">
<description>transition</description>
</object>
</contents>
</sheet>

transition.shape

<?xml version="1.0"?>
<shape xmlns="http://www.daa.com.au/~james/dia-shape-ns" xmlns:svg="http://www.w3.org/2000/svg">
<name>transition</name>
<icon>transition.png</icon>
<default-width>0.2cm</default-width>
<default-height>1cm</default-height>
<connections>
<point x="0" y="0"/>
<point x="0" y="1"/>
</connections>
<textbox x1="0" y1="0.4" x2="0.2" y2="0.6" resize="no" /> <svg: svg>
<svg:line x1="0" y1="0" x2="0" y2="1" />
<svg:line x1="-0.1" y1="0.5" x2="0.1" y2="0.5" /> </svg: svg>
</shape>

Dans Algorithme Graphique, créez une image quelconque pas trop grande de type png et nommez-la transition.png. Ouvrez un nouveau diagramme dans DIA, sélectionnez la feuille Algorithme Graphique, cliquez sur l'icône de l'image transition.png, mettez une transition sur le plan, zoomez jusqu'à voir l'objet à la bonne taille, mettez un texte de transition avec F2 (e.g."var"), et exportez le diagramme en png en remplaçant le fichier transition.png, pour créer l'icône de la transition.

Nous allons à présent nous approprier certains objets de diverses autres feuilles en changeant certains points et les mettre à l'échelle. Pour ce faire, copiez-collez les fichiers suivants dans notre dossier Algorithme Graphique et changez leur nom :

Source

Destination

shapes\Assorted\arrow-pentagon.png
shapes\Assorted\arrow-pentagon.shape

shapes\Algorithme Graphique\fleche.png
shapes\Algorithme Graphique\fleche.shape

shapes\BPMN\Gateway.png
shapes\BPMN\Gateway.shape

shapes\Algorithme Graphique\condition.png
shapes\Algorithme Graphique\condition.shape

shapes\Logic\not.png
shapes\Logic\not.shape

shapes\Algorithme Graphique\non.png
shapes\Algorithme Graphique\non.shape

shapes\SDL\task.png

shapes\SDL\task.shape

shapes\Algorithme Graphique\action.png
shapes\Algorithme Graphique\action_rouge.png
shapes\Algorithme Graphique\action.shape
shapes\Algorithme Graphique\action_rouge.shape

Je vous fais grâce de recoder tous les shapes clonés, car on est vraiment en train de s'éloigner du sujet. Vous pouvez toujours vous pencher sur les cours de James Henstridge si vous le souhaitez, en tout cas lui et son équipe ont toute ma reconnaissance pour avoir pu me donner les clés pour pouvoir créer cette feuille magique.

Voici les codes à éditer :

Algorithme Graphique.sheet

<?xml version= "1. 0" encoding="UTF-8"?>
<sheet xmlns="http://www. lysator. liu. se/~alla/dia/dia-sheet-ns">
<name>Algorithme Graphique</name>
<description>Objets pour algorithmes graphiques</description>
<contents>
<object name="transition">
<description>transition</description>
</object>
<object name="action rouge">
<description>action rouge</description>
</object>
<object name="action">
<description>action</description>
</object>
<object name="non">
<description>non</description>
</object>
<object name="fleche">
<description>fleche</description>
</object>
<object name="condition">
<description>condition</description>
</object>
</contents>
</sheet>

action.shape

<?xml version="1.0"?>
<shape xmlns="http://www.daa.com.au/~james/dia-shape-ns" xmlns:svg="http://www.w3.org/2000/svg">
<name>action</name>
<icon>action.png</icon>
<description>action</description>
<connections>
<point x="2" y="0"/>
<point x="4" y="0"/>
<point x="2" y="2"/>
</connections>
<textbox x1="0" y1="0" x2="4" y2="2"/>
<svg:svg width="2.5" height="2">
<svg:rect style="fill: background" x="0" y="0" width="4" height="2"/>
</svg: svg>
<default-width>4</default-width> <default-height>1</default-height>
</shape>

fleche.shape

<?xml version= "1.0" encoding="UTF-8"?>
<shape xmlns="http: //www.daa.com.au/~james/dia-shape-ns" xmlns:svg="http://www.w3.org/2000/svg">
<name>fleche</name>
<icon>fleche.png</icon>
<default-width>2</default-width>
<default-height>1</default-height>
<connections>
<point x="0" y="1"/>
<point x="3" y="1"/>
</connections>
<textbox x1="0" y1="0" x2="4" y2="2"/>
<aspectratio type="free"/>
<svg: svg>
<svg:polygon style="fill: default" points="0,0 2,0 3,1 2,2 0,2"/>
<svg:polygon style="stroke: foreground" points="0,0 2,0 3,1 2,2 0,2"/>
</svg: svg>
</shape>

Pour le code ci-dessus, cet objet redirigera une branche vers une autre par souci esthétique ou quand on n'a pas trop le choix.

non.shape

<?xml version="1.0"?>
<shape xmlns="http://www.daa.com.au/~james/dia-shape-ns" xmlns:svg="http://www.w3.org/2000/svg">
<name>non</name>
<icon>non.png</icon>
<connections>
<point x="0" y="0.5"/>
<point x="1" y="0.5"/>
</connections>
<aspectratio type="fixed"/>
<svg: svg>
<svg:ellipse style="fill: background" cx="0.5" cy="0.5" rx="0.5" ry="0.5"/>
<svg:ellipse style="stroke: foreground" cx="0.5" cy="0.5" rx="0.5" ry="0.5"/>
</svg: svg>
<default-width>0.1</default-width>
<default-height>0.1</default-height>
</shape>

action_rouge.shape

<?xml version="1.0"?>
<shape xmlns="http: //www.daa.com.au/~james/dia-shape-ns" xmlns:svg="http://www.w3.org/2000/svg">
<name>action rouge</name>
<icon>action_rouge.png</icon>
<connections>
<point x="2" y="0"/>
<point x="4" y="0"/>
<point x="2" y="2"/>
</connections>
<textbox x1="0" y1="0" x2="4" y2="2"/>
<svg:svg width="2.5" height="2">
<svg:rect style="fill: background; stroke: #FF0000" x="0" y="0" width="4" height="2"/>
</svg: svg>
</shape>

Changez la couleur du rectangle en rouge pour action_rouge.png. C'est une action à distinguer des autres pour le codage en PHP, nous verrons de quoi il s'agit plus tard.

condition.shape

<?xml version="1.0"?>
<shape xmlns="http: //www.daa.com.au/~james/dia-shape-ns" xmlns:svg="http://www.w3.org/2000/svg">
<name>condition</name>
<icon>condition.png</icon>
<connections>
<point x="1.25" y="0"/>
<point x="1.25" y="2.5"/>
<point x="2.5" y="1.25"/>
<point x="0" y="1.25"/>
</connections>
<textbox x1="0.65" y1="0.65" x2="1.90" y2="1.90"/>
<svg:svg width="2.5" height="2.5">
<svg:polygon points="1.25,0 2.5,1.25 1.25,2.5 0,1.25" style="fill: default"/>
</svg: svg>
</shape>

Relancez DIA. Notre feuille d'algorithme graphique devrait à présent ressembler à ça :

Vous pouvez si le cœur vous en dit en rajouter, mais je pense que ça suffira largement.

I-E-2 - Conseils pour une mise en forme rapide d'un algorithme graphique

Ces objets rajoutés représentent déjà beaucoup d'efforts en moins. Voici maintenant quelques conseils pour vous aider à optimiser votre travail sur DIA. Vous apprendrez à utiliser les raccourcis clavier par vous-mêmes, mais il convient de spécifier quelques raccourcis incontournables.

- M - Pour zoomer. Maintenez le bouton shift pour le zoom out. Dans le menu zoom dans la barre d'outils, le bouton entre + et -, qui sert à zoomer directement sur l'ensemble du plan ou sur une sélection, est aussi très utile.

- F2 - Pour modifier ou rajouter du texte dans les objets.

- <espace> - Pour revenir au curseur normal de sélection.

- L - Pour les lignes.

- Les deux derniers boutons sur la barre d'outils - J'utilise le snap-to-grid pour une précision coercitive, et le collage d'objet pour les "aimanter" entre leurs points de connexion.

- CTRL + F, CTRL + Z - Fonctionnent ici aussi.

- T - Pour du texte ...

Je travaille personnellement sur des brouillons avec un crayon à papier avant de mettre au propre sur DIA quand je suis sûr de mon coup. Vous pouvez ensuite imprimer votre travail sur DIA et ainsi constituer des dossiers rigoureux de vos projets. On peut se permettre d'appeler ça un élément de cahier des charges. Nous allons passer à présent au premier tour de magie qui nous permet de transformer l'algorithme graphique en algorithme littéral.

II - PASSAGE D'UN ALGORITHME GRAPHIQUE VERS UN ALGORITHME LITTÉRAL

II-A - Méthodologie

Nous voici enfin dans le quasi concret. Nous apprendrons dans ce chapitre comment coder dans un langage accessible, formaté en PHP. C'est-à-dire qu'il prendra en compte la façon dont est parsée une page PHP. L'objet action rouge y est pour quelque chose.

II-A-1 - Exemple de traduction

Dans l'exemple de la porte, il faut prendre en compte que la page PHP qui affichera cet exemple sera au début disons un bouton sur lequel il faudra cliquer pour faire l'action « frapper à la porte ». On aura donc une situation d'origine avec n=0, et un stand-by devant la porte. Cette situation en mode veille sera représentée par une action en rouge. On a d'autres situations de même type dans notre algorithme, et ces dernières seront représentées aussi par des rectangles en rouge. Voilà ce que ça donne :

La traduction en algorithme littéral est tout simplement la lecture à haute voix de cet algorithme graphique, dans un langage qui utilise les si, sinon, et pour les actions : écrire, et demander. On aura aussi les boucles usuelles. Il va sans dire que ça coule de source. Voilà la traduction :

0 -> n ;
répéter
attendre la frappe;
n + 1 -> n ;
attendre 10 secondes;
si réponse = "entrez"
écrire "vous êtes entré" ;
3 -> n ;
sinon
si n > 2
écrire "vous êtes parti" ;
jusqu'à n > 2 ;

Il s'agit ensuite de construire la structure algorithmique de l'algorithme graphique. Nous avons numéroté chaque situation (action effectuée) afin de pouvoir les localiser dans l'algorithme littéral. Nous serons ainsi en possession de toutes les situations possibles déterminées dans l'algorithme graphique. Notre algorithme littéral deviendra ainsi :

algorithme

0 -> n;
0 -> step;
répéter
attendre la frappe;
n + 1 -> n;
attendre 10 secondes;
1 -> step;
si réponse = "entrez"
écrire "vous êtes entré";
3 -> step;
3 -> n;
sinon
2 -> step;
si n > 2
écrire "vous êtes parti";
4 -> step;
3 -> n;
jusqu'à n>2;

Ceci vous permettra aussi avec un CTRL+F + "3" de trouver instantanément l'action numéro 3 dans votre algorithme littéral. Pensons maintenant PHP. Une fois l'action de frapper à la porte effectuée, la page porte.php sera resollicitée et parsée depuis le début. Donc le passage de ce code en PHP ne tient pas. La variable n sera nulle et le résultat escompté sera différent de celui recherché, dès lors qu'on traduit « frapper à la porte » par un bouton de formulaire. Par conséquent, pour se positionner dans la bonne branche de notre algorithme littéral, il faudra alors, avec deux variables POST, positionner le début du parsage à l'endroit correspondant de notre algorithme graphique. Les variables POST indiqueront :

- le numéro de l'action d'où on vient ;

- le numéro de l'action où on va.

Nous allons à présent coder de manière rigoureusement structurée ce programme, en prenant en compte les variables POST. Mais n'allons pas trop vite et restons dans l'algorithme littéral. Créons une fonction action pour traiter chacune des actions en fonction des variables POST step et previous_step. Le corps du programme consistera ensuite tout simplement en une implémentation de la structure de l'arbre.

algorithme

Fonction action(step, previous_step)
{
SELON step, previous_step
CAS step = 0 :
n -> 0;
S -> 0;
NS -> 1;
fin;
CAS step = 1 :
écrire "porte frappée";
n + 1 -> n;
fin;
CAS step = 2 :
attendre 10 secondes;
2 -> S;
3 -> NS;
fin;
CAS step = 3 :
écrire "vous êtes entré.";
0 -> S;
0 -> NS;
fin;
CAS step = 4 :
écrire "vous êtes parti.";
0 -> S;
0 -> NS;
fin;
}

SELON (PS,S)
CAS PS = 0
action(0);
fin;
CAS PS = 2, S = 3 :
CAS PS = 0, S = 1 :
action(1);
action(2);
si réponse = "entrez"
action(3);
sinon
si n>2
action(4);
fin;

Pour la fonction action :
Step : numéro de l'action à effectuer ;
previous_step : action d'où l'on vient. On n'en a aucune utilité ici, mais ça nous servira par exemple pour effectuer la même action avec un message différent ;
NS : POST à envoyer correspondant au numéro de l'action envisagée (« Next Step » abrégé) ;
S : POST à envoyer correspondant au numéro de l'action effectuée.
Pour le corps du programme :
PS : POST récupérée correspondant au numéro de l'action d'où on vient (« Previous Step ») ;
S : POST récupérée correspondant au numéro de l'action où on veut aller.

Fonction action
Je me suis permis d'enlever les variables S et NS pour le cas de « 1 » car l'action effectuée entraine automatiquement la deuxième et on néglige l'absence de la porte. Assez déconseillé car on aura peut-être besoin de changer les algorithmes initiaux à l'avenir, pour un programme plus sérieux.
Pour les cas « 3 » et « 4 », je les ai mis chacun à zéro pour arrêter la boucle. (cas « 0,0 » non traité dans le corps du programme).

Corps du programme
Pour le cas « 2,3 » acheminé vers « 0,1 », il s'agit de la branche faisant reboucler depuis l'action effectuée « 2 » vers l'action à effectuer « 1 », les conditions ayant dirigé depuis leur symbole « non ».

Prière de ne pas prendre le concept d'une variable POST comme un élément intrinsèque d'algorithmique. C'est une variable réservée au langage PHP que je me permets d'utiliser dans un langage de profane. Tout comme la nouveauté du rectangle rouge symbolisant l'action en « stand-by » dans l'algorithme graphique. Ce sont les deux clés magiques des portes situées entre le monde PHPland et algoland.

II-A-2 - Passage en PHP

Pour coder concrètement cet algorithme en PHP, nous utiliserons une variable aléatoire pour tester la réponse de la personne à visiter. Il nous faudra aussi rajouter quelques fioritures pour le confort de l'interface, sans nécessairement avoir à changer les algorithmes graphiques.
Code PHP final :

<?php
Function action($step, $previous_step = false)
{
global $S, $NS, $n, $message;
$chaine_switch = ($previous_step==false) ? $step : $step.",".$previous_step;

switch ($chaine_switch)
{
case "0":
$n = 0;
$S = 0;
$NS = 1;
break;
case "1":
$message .= "Porte frappée. <br />";
$n++;
break;
case "2":
usleep(10000000);        // bon le programme n'est pas parfait mais ce cahier des charges n'a pas beaucoup d'intérêt. ^^
$S = 2;
$NS = 3;
break;
case "3":
$message .= "Vous êtes entré.";
$n = 4;
$S = 0;
$NS = 0;
break;
case "4":
$message .= "Vous êtes parti.";
$n = 4;
$S = 0;
$NS = 0;
break;
}
}

$message = "";
if (isset($_POST['step'])) $PS = $_POST['step'];
else $PS = 0;
if (isset($_POST['next_step'])) $S = $_POST['next_step'];
else $S = 0;
if (isset($_POST['n'])) $n = $_POST['n'];
else $n = 0;

$chaine_switch = $PS.",".$S;

// structure de l'arbre
switch ($chaine_switch)
{
if (isset($_POST['n'])) $n = $_POST['n'];
else $n = 0;
case "0,0" :
action(0);
break;
case "2,3" :
case "0,1" :
action(1);
action(2);
$message_digit = mt_rand(0,1);
$reponse = ($message_digit==1) ? "entrez" : "";
if ($reponse == "entrez")
{
action(3);
}
else
{
$message = "Personne.";
if ($n > 2) action(4);
}
break;
}

echo $message;
?>
<form action='./porte.php' method='post'>
<INPUT TYPE=HIDDEN NAME="step" VALUE="<?php echo $S ?>">
<INPUT TYPE=HIDDEN NAME="next_step" VALUE="<?php echo $NS ?>">
<INPUT TYPE=HIDDEN NAME="n" VALUE="<?php echo $n ?>">
<?php         if ($n<4)
{?>
<input type='submit' value="frapper à la porte"/>
<?php
}?>
</form>

global : ceci est nécessaire pour passer les variables en global, afin de les prendre en compte dans les fonctions.
$n = 4 : le cas « 0,0 » étant traité dans le corps du programme, on a recours à cette affectation pour enlever le bouton, i.e. stopper la boucle.

Ce programme n'ayant qu'une fin théorique, prière de ne le faire fonctionner qu'en local, car j'ai occulté tout ce qui concerne les entêtes HTML et n'ai utilisé aucun CSS.

Voilà on arrive enfin au code PHP. On a fait le plus difficile... avant le TP. Mais rassurez-vous, c'est le même refrain.

II-B - Énoncé du TP

Solidifions tout ça en nous appuyant sur une application pertinente, qui est celle de la partie gestion des identifiants d'un compte personnel sur un site. Je vais vous fournir toutes les données de l'exercice, et les parties qui suivront traiteront d'abord de l'algorithme graphique qui en découle, puis l'algorithme littéral et le code PHP final. Vous serez ainsi en possession de votre code, et de votre dossier depuis lequel il sera miraculeusement facile de changer ou d'ajouter ce que vous voulez sur votre site par cette méthode.

Il s'agit donc de concevoir la partie d'un site s'occupant de gérer l'identification du compte d'un membre. On va voir en détail toutes les données d'entrée à avoir pour restituer les données de sortie en fonction de données externes ou déjà fournies (données de contrôle). Les données de sortie seront donc déterminées selon les données de contrôle et d'entrée. Nous allons construire l'algorithme graphique où seront représentées toutes les situations de sortie possibles (représentées par des actions rouges) dictées par le cahier des charges, i.e. l'énoncé du TP.

II-B-1 - Présentation

Voici les aperçus de ce que doit donner ce gestionnaire de compte. Procédons par onglet. On aura les onglets : Connexion, Inscription, Déconnexion. Ils comprendront plusieurs choix possibles en rapport avec leur thème. Voici les options possibles pour chaque onglet :

II-B-1-a - Connexion

- Connexion automatique - ceci permettra à l'utilisateur de ne pas avoir à entrer ses identifiants à chaque fois qu'il se connecte sur le site ;

- liens - lien direct vers la partie de traitement d'oubli des identifiants ;

- liens du message d'accueil - le message d'accueil sera en fonction d'où on vient et comprendra éventuellement un lien vers l'inscription ou bien vers la section qui traite de l'oubli des identifiants.

II-B-1-b - Inscription

- Liens du message d'accueil - Le message d'accueil sera en fonction d'où on vient et comprendra éventuellement un lien vers la connexion ou l'oubli des identifiants.

II-B-1-c - Déconnexion

- Se déconnecter - bouton menant vers l'onglet connexion après déconnexion de la session.

- Se désinscrire - bouton menant vers l'onglet connexion après la déconnexion/désinscription du membre.

- Mon profil - bouton menant vers la gestion de profil du membre (non traité).

- Liens du message d'accueil - Le message d'accueil sera comme dans l'image et comprendra le lien vers la consultation du profil de l'utilisateur (partie non traitée).

II-B-2 - Fonctionnement

II-B-2-a - Introduction

1. Lorsqu'un individu lambda arrive sur le site, cette partie s'affichera si : Pour les deux cas, on incrémentera une variable entière SQL correspondant au pseudo banni, et on effacera ses cookies.

2. Si pour l'individu, la session existe encore, la connexion sera automatique.

3. Sinon, s'il avait coché connexion automatique, la connexion sera automatique, et on ajoutera une visite de l'individu dans une table SQL, avec la date.

La partie bannissement d'un utilisateur n'est pas traitée, donc si vous voulez tester les cas bannis, il vous faudra passer par des requêtes SQL sur phpmyadmin du site sur lequel vous travaillez.

II-B-2-b - Connexion

- Si le pseudo rentré est introuvable, recommencer depuis ce même onglet et mettre un message d'erreur comportant des liens vers identifiants oubliés ou inscription.

- Idem pour mot de passe incorrect.

- Si tout est bon, créer une session personnalisée avec le pseudo, puis éventuellement s'en souvenir si connexion automatique est cochée. Enfin, on ajoutera une visite de l'individu dans une table SQL, avec la date.

II-B-2-c - Inscription

- En cas d'erreur, si l'e-mail avait été saisi, ce dernier sera prérempli dans la case e-mail.

- Stipuler si le pseudo ou l'e-mail est déjà présent dans la table des bannis ou des membres, et on recommence. Conformité de l'e-mail à tester.

- Si tout est bon, on demande à l'utilisateur d'activer son compte en consultant le message envoyé sur sa boite mail. Le message contiendra un lien vers cette page, et les identifiants de l'utilisateur. Ce dernier cliquera sur le lien, et son compte sera activé si on sait qu'il vient de sa boite mail.

Ce principe d'activation de compte empêche assez efficacement les spammers de s'inscrire dans votre site. Il est fait d'une URL vers le site faisant intervenir une chaine de caractères aléatoire comme ?var_link=mqsoidunrmqsiocsdfqclnsk.

II-B-2-d - Déconnexion

No comment.

II-B-2-e - Oubli d'identifiants

Les mots de passe des utilisateurs seront cryptés. Il faudra donc :

- rentrer son e-mail via un textbox (en boucle tant que l'e-mail n'est pas le bon avec dans le message d'erreur un lien vers connexion (depuis un autre compte)) ;

- effacer dans la table des membres la ligne correspondant à l'e-mail rentré ;

- procéder de la même manière qu'une inscription normale avec pour identifiants : pseudo = e-mail de l'utilisateur, mot de passe = chaine aléatoire d'un nombre suffisant de caractères.

Cette partie peut paraître un peu en trop vu que l'utilisateur pourrait lui-même se réinscrire. Mais des fonctionnalités supplémentaires (non demandées dans ce TP) telle que la récupération des données de son profil pourront être traitées en passant par là.

Suite : solution du TP : algorithme graphique avec liste des variables utilisées.

III - Exemple de codage PHP d'un algorithme graphique (solution du TP)

Nous voici donc dans la partie qui nous permettra de savoir comment coder un algorithme graphique en PHP en trois étapes, comme on l'a vu : Algorithme graphique -> Algorithme littéral -> Code PHP.

III-A - Algorithme graphique

Voici sans plus attendre la solution que je propose.

Visualisez de préférence le schéma sous pdf, certains navigateurs n'étant pas des plus fines bouches en matière de dessin vectoriel. Sinon, imprimez les images pour étudier de près le fonctionnement de l'algorithme, voire reconstituez le schéma sur DIA (ce que je recommande vivement pour votre propre formation sur ce tutoriel). Je pense qu'il est assez explicite pour ne pas avoir à le commenter.

DIA possède deux versions de plugin pour exporter un fichier vers le format vectoriel SVG (ou bien peut-être n'en possède-t-il qu'un, auquel cas les formats exportés seront peut-être incompatibles avec pas mal de programmes). Utilisez le premier format svg appelé Graphique Vectoriel Cairo. Ne pas confondre X! avec la même chose en mathématiques signifiant littéralement « il existe un unique X tel que... » - j'utilise le « ! » pour prendre le contraire de ce qui le précède, tout comme en programmation en général.

Voici les variables utilisées :

COOKIES

SESSIONS

VARIABLES NORMALES

SQL

id_nick

id_nick

id_nick

TABLE

COLONNE

id_passwd

id_passwd

BANNIS

ip

connexion_auto

IP_Client

nick

temp_nick

var_link

cnt

temp_passwd

temp_nick

email

temp_email

temp_email

MEMBRES

nick

temp_passwd

passwd

temp_date

email

date

TEMP_VARS

nick

passwd

email

date

var_link

VISITES

nick

date

Cette solution marche a priori convenablement. Ce graphe peut être bien sûr schématisé avec des syntaxes différentes, et sa forme peut changer. Mais le fonctionnement sera le même si on respecte les embranchements et le séquencement. Passons à présent à la traduction vers l'algorithme littéral.

III-B - Algorithme littéral

Suivez l'algorithme graphique en lisant l'algorithme littéral suggéré :

Fonction action(step, previous_step = false)
{
SELON step, previous_step
CAS step = 1
[adresse ip du client] -> ip_client; fin;
CAS step = 2
[COOKIE]id_nick = [SESSION]id_nick; fin;
CAS step = 4, previous_step = 2
"vous êtes connecté [*", id_nick,"*]!";
ajouter (VISITES, time(), id_nick);
"deconnexion" -> onglet; fin;
CAS step = 3
effacer ([COOKIE] id_nick, [COOKIE] id_passwd);
exit;
CAS step = 5, previous_step = 1
"Saisissez vos id ou [*inscrivez-vous*]. [*id oubliés*]?";
"connexion" -> onglet;
S = 5;
NS = 6; fin;
CAS step = 7
incrémenter BANNIS[id_nick].cnt;
CAS step = 8
ajouter (MEMBRES.a);
[COOKIE]temp_nick -> id_nick;
temp_passwd -> id_passwd;
effacer ([SQL]a,[COOKIE]a);
ajouter (VISITES, date, id_nick); fin;
CAS step = 5, previous_step = 8
"Vous pouvez maintenant vous connecter. [*id oubliés*]?";
"connexion" -> onglet;
5 -> S;
6 -> NS; fin;
CAS step = 9
effacer ([SQL]a,[COOKIE]a); fin;
CAS step = 5, previous_step = 9
"Problème de confirmation. Reconnectez-vous avec un autre compte ou [*réinscrivez-vous*].";
"connexion" -> onglet;
5 -> S;
6 -> NS; fin;
CAS step = 22
incrémenter (BANNIS[ip_client].cnt);
exit;
CAS step = 10
"Nick incorrect, ",id_nick,". [*id oubliés?*] [*S'inscrire?*]"; fin;
CAS step = 5, previous_step = 10
"connexion" -> onglet;
5 -> S;
6 -> NS; fin;
CAS step = 11
"Bad passwd. [*id oubliés*]? [*S'inscrire*]?"; fin;
CAS step = 5, previous_step = 11
"connexion" -> onglet;
5 -> S;
6 -> NS; fin;
CAS step = 6
id_nick -> [SESSION]id_nick;
enregistrer ([COOKIE]id_nick, [COOKIE]passwd, [COOKIE]connexion_auto); fin;
CAS step = 4, previous_step = 6
"vous êtes connecté ", [*id_nick*],"!";
ajouter (VISITES, date, id_nick);
"deconnexion" -> onglet; FIN;
CAS step = 20
effacer([SESSION]id_nick);
non -> [COOKIE]connexion_auto;
"Saisissez vos id ou [*inscrivez-vous*]. [*id oubliés*]?";
"connexion" -> onglet;
5 -> S;
6 -> NS; fin;
CAS step = 19
effacer(SESSION, COOKIES, [POURTOUT]MEMBRES[id_nick]);
"Saisissez vos id ou [*inscrivez-vous*]. [*id oubliés*]?";
"connexion" -> onglet;
5 -> S;
6 -> NS; fin;
CAS step = 0
"vous êtes connecté ,  [*id_nick*]."; FIN;
CAS step = 0, previous_step = 15
"saisissez infos nécessaires à la préinscription.";
"inscription" -> onglet;
15 -> S;
18 -> NS; fin;
CAS step = 16
"Nick existe. [*id oubliés*]?";
15 -> S;
18 -> NS;
"inscription" -> onglet; fin;
CAS step = 17
"Email existe. [*id oubliés*]?";
15 -> S;
18 -> NS;
"inscription" -> onglet; fin;
CAS step = 18
mail(temp_email);
var_link -> TEMP_VARS.var_link;
temp_nick -> [COOKIE]temp_nick -> TEMP_VARS.temp_nick;
temp_email -> [COOKIE]temp_email -> TEMP_VARS.temp_email;
temp_passwd -> [COOKIE]temp_passwd -> TEMP_VARS.temp_passwd;
temp_date -> TEMP_VARS.temp_date;
"mail envoyé. Activez votre compte.";
"connexion" -> onglet; FIN;
CAS step = 14
"donnez l'email fourni à l'inscription.";
14 -> S;
21 -> NS;
"recup_id" -> onglet; fin;
CAS step = 21
TEMP_VARS.date.[VAR]email -> TEMP_VARS.date;
effacer(COOKIES + COOKIES temp, [POURTOUT]MEMBRES[email] + [POURTOUT]TEMP_VARS[email]);
email -> [COOKIE]temp_email -> [SQL]temp_email -> [COOKIE]temp_nick -> [SQL]temp_nick;
random(8) -> [COOKIE]temp_passwd -> [SQL]temp_passwd;
var_link -> TEMP_VARS.var_link;
"mail envoyé. Réactivez votre compte.";
"connexion" -> onglet; FIN;
CAS step = 23
"saisissez un mot de passe.";
15 -> S;
18 -> NS;
"inscription" -> onglet; fin;
CAS step = 24
"email introuvable.";
14 -> S;
21 -> NS;
"recup_id" -> onglet; fin;
}

si (S [EXISTEPAS]) 0 -> S;
si (PS [EXISTEPAS]) 0 -> NS;

si (G [EXISTE])
SELON (G)
{
CAS "connexion" fin;
CAS "deconnexion"
si ([SESSION]id_nick [EXISTEPAS]) exit;
0 -> PS;
20 -> S; fin;
CAS "desinscription"
si ([SESSION]id_nick [EXISTEPAS]) exit;
0 -> PS;
19 -> S; fin;
CAS "inscription"
si ([SESSION]id_nick [EXISTE]) exit;
0 -> PS;
15 -> S; fin;
CAS "recup_id"
si ([SESSION]id_nick [EXISTE]) exit;
0 -> PS;
14 -> S; fin;
}

sinon
SELON (PS,S)
{
CAS PS = 0, S = 0
si ([SESSION]id_nick [EXISTE]) step(0); FIN;
sinon step(1);
si (ip_client [PASELEMENTDE] BANNIS:ips)
{
si ([COOKIE]temp_nick [EXISTEPAS])
{
si ([COOKIE]connexion_auto = "oui" ET [COOKIE]nick [EXISTE] ET [COOKIE]id_passwd [EXISTE])
{
si ([COOKIE]id_nick [PASELEMENTDE] BANNIS:ids)
{
si (MEMBRES[[COOKIE]id_nick].[COOKIE]id_passwd = MEMBRES[[COOKIE]id_nick].id_passwd)
{
step(2);
step(4,2); FIN;
}
sinon step(3); fin;
}
sinon
{
step(7);
step(3);
}

}
sinon step(5,1);
}
sinon
{
si     (    var_link                |            )
(    [COOKIE]temp_nick            |    TEMP_VARS    )
(    [COOKIE]temp_passwd    =         |            )
(    [COOKIE]temp_email            |            )
{
step(8);
step(5,8);
}
sinon
{
step(9);
step(5,9);
}
}
}
sinon step(22);

CAS PS = 5, S = 6
si (id_nick = '' ou [PASELEMENTDE] MEMBRES:nicks)
{
step(10);
step(5,10);
}
sinon si (id_passwd != MEMBRES[id_nick].passwd)
{
step(11);
step(5,11);
}
sinon
{
step(6);
step(4,6); FIN;
}
CAS PS = 0, S = 20
step(20);
CAS PS = 0, S = 19
step(19);
CAS PS = 0, S = 15
step(0,15);
CAS PS = 15, S = 18
si (temp_nick [ELEMENTDE](MEMBRES:ids, BANNIS:ids)) step(16); FIN;
sinon si (temp_email [ELEMENTDE] (MEMBRES:emails, BANNIS:emails) ou invalide) step(17); FIN;
sinon si (temp_passwd = '') step (23); FIN;
sinon step(18); FIN;
CAS PS = 0, S = 14
step(14); FIN;
CAS PS = 14, S = 21
si (email [ELEMENTDE] (MEMBRES:emails)) step(21); FIN;
sinon step(14); FIN;
}
}
}

Il aurait été intéressant de changer la couleur des variables dans ce code pour pouvoir les distinguer. À la place, on aura des [SQL] [VAR] [COOKIE] [SESSION] au début du nom des variables pour les différencier quand c'est nécessaire. À noter que les variables en MAJUSCULES se réfèrent à SQL. Cet algorithme étant la traduction directe de l'algorithme graphique, La compréhension devrait être aussi simple. J'ai cependant rajouté une petite chose indépendante du diagramme, telle que la gestion d'une variable GET G (les onglets) qui peut forcer le passage à un diagramme de fond bleu. Les liens sont mis entre [* *] mais ces [* *] ne sont pas présents dans l'algorithme de base, ce dernier étant rédigé avec de la couleur, et en utilisant les styles souligné, gras et italique. Aussi vous pouvez vous permettre de copier-coller le code en l'arrangeant par vous-même pour en améliorer la lecture.

III-C - PHP

Nous voici enfin dans le vif du sujet.

Ceci étant la partie principale de ce tuto, elle risque de s'avérer longue à assimiler. Mais elle ne représente que l'aspect technique et l'idée d'ensemble de ce tuto est d'ores et déjà acquise. Vous avez déjà tous les outils théoriques et pratiques pour programmer de la manière la plus paisible.

Il me semblait nécessaire d'appuyer tout ça avec ce TP pour bien vous démontrer la possibilité d'une telle façon de programmer en PHP, dont la priorité est l'ordre et la rigueur, l'efficacité... La simplicité, dans ce monde pas très évident de PHP qui regorge de fonctions parfois inutiles, parfois incompatibles avec certains serveurs, parfois obsolètes, voire... erronées.

Voici donc le fameux code.

<?php
session_start();

include("includes/bbcode.php");
include("includes/identifiants.php");

function random($car) // retourne une chaine aléatoire de $car caractères
{
$string = "";
$chaine = "abcdefghijklmnpqrstuvwxy";
srand((double)microtime()*1000000);
for($i=0; $i<$car; $i++)
{
$string .= $chaine[rand()%strlen($chaine)];
}
return $string;
}
function url_ok($url)
{
return ("[color=#00FF00]".$url."[/color]");
}

function action($step, $previous_step = false)
{
//////////////////////////////////////////////////////////////////////////////////
!!      utilisez plutôt des classes - l'utilisation de global n'est ici que     !!
!!                            pour les besoins du TP                            !!
//////////////////////////////////////////////////////////////////////////////////

$ip_client,
$id_nick,
$id_passwd,

$onglet,

$message_boite_connexion,
$message_boite_deconnexion,
$message_boite_inscription,
$message_recuperation_identifiants,

$S, $NS,
$temp_nick,
$temp_passwd,
$temp_email,
$temp_date;

$chaine_switch = ($previous_step==false) ? $step : $step.",".$previous_step;

switch($chaine_switch)
{
case "1": $ip_client = $_SERVER["REMOTE_ADDR"];
break;
case "2": $_SESSION['id_nick'] = $_COOKIE['id_nick'];
break;
case "4,2":
$message_boite_deconnexion = forum_code("Vous êtes connecté, [url=./profile_view.php?id=".$_SESSION['id_nick']."][alt voir
profil]".$_SESSION['id_nick']."[/url].",TRUE);
mysql_query("INSERT INTO universe_visites
(nick_id, date_id)
VALUES ('".$id_nick."', ".time().")")    or die(mysql_error());
$onglet = "deconnexion";
break;
case "3": setCookie('id_nick');
setCookie('id_nick');
unset($_COOKIE['id_nick']);
unset($_COOKIE['id_passwd']);
exit;
break;
case "5,1":
$message_boite_connexion = forum_code("Saisissez vos identifiants pour vous connecter. Si vous n'en avez pas, vous pouvez [url=
./messagerie.php?g=inscription][alt inscription]vous inscrire[/url].","no_pop_up");
$S = 5;
$NS = 6;
$onglet = "connexion";
break;
case "7": mysql_query("UPDATE universe_bannis
SET banni_cnt = banni_cnt + 1
WHERE banni_nick = '".$_SESSION['id_nick']."'") or die(mysql_error());
break;
case "8": mysql_query("INSERT INTO universe_membres
(nick, passwd, email, date_inscription)
VALUES ('".$temp_nick."', '".$temp_passwd."', '".$temp_email."','".$temp_date."')");
$id_nick = $_COOKIE['temp_nick'];
$id_passwd = $_COOKIE['temp_passwd'];
setCookie('temp_nick');
unset($_COOKIE['temp_nick']);
setCookie('temp_passwd');
unset($_COOKIE['temp_passwd']);
setCookie('temp_email');
unset($_COOKIE['temp_email']);
mysql_query("DELETE FROM universe_temp_vars WHERE temp_nick='".$temp_nick."'");
// on en profitera pour nettoyer les vars > 30 jours :
mysql_query("DELETE FROM universe_temp_vars WHERE temp_date +
60*60*24*30 < ".time());
mysql_query("INSERT INTO universe_visites
(nick_id, date_id) VALUES ('".$id_nick."', ".time().")");
break;
case "5,8":
$message_boite_connexion = forum_code("Saisissez vos identifiants pour vous connecter. Si vous n'en avez pas, vous pouvez
[url=./messagerie.php?g=inscription][alt inscription]vous inscrire[/url].","no_pop_up");
$S = 5;
$NS = 6;
$onglet = "connexion";
break;
case "9":
$temp_nick = $_COOKIE['temp_nick'];
setCookie('temp_nick');
unset($_COOKIE['temp_nick']);
setCookie('temp_passwd');
unset($_COOKIE['temp_passwd']);
setCookie('temp_email');
unset($_COOKIE['temp_email']);
mysql_query("DELETE FROM universe_temp_vars WHERE temp_nick='".$temp_nick."'");
// on en profitera pour nettoyer les vars > 30 jours :
mysql_query("DELETE FROM universe_temp_vars WHERE temp_date + 60*60*24*30 < ".time());
break;
case "5,9":
$message_boite_connexion = forum_code("[color=#ff0000]Votre compte n'a pas pu être activé. Veuillez [url=./messagerie.php
?g=inscription][alt inscription]vous réinscrire[/url], ou essayez de vous connecter avec un autre compte.[/color]","no_pop_up");
$S = 5;
$NS = 6;
$onglet = "connexion";
break;
case "22":
mysql_query("UPDATE universe_bannis SET banni_cnt = banni_cnt + 1 WHERE banni_ip = '".$ip_client."'") or die(mysql_error());
exit;
break;
case "10":
$message_boite_connexion = forum_code("[color=#ff0000]Le nick saisi \"$id_nick\" est incorrect. [url=./messagerie.php?g=
recup_id][alt récupération par mail]Récupérez vos identifiants[/url], ou [url=./messagerie.php?g=inscription][alt inscription]
réinscrivrez-vous[/url].[/color]","no_pop_up");
break;
case "5,10":
$S = 5;
$NS = 6;
$onglet = "connexion";
break;
case "11":
$message_boite_connexion = forum_code("[color=#ff0000]Le mot de passe saisi est incorrect. [url=./messagerie.php?g=recup_id]
[alt récupération par mail]Récupérez vos identifiants[/url], ou [url=./messagerie.php?g=inscription][alt inscription]
réinscrivrez-vous[/url].[/color]","no_pop_up");
break;
case "5,11":
$S = 5;
$NS = 6;
$onglet = "connexion";
break;
case "6":
$_SESSION['id_nick'] = $id_nick;
setcookie ("id_nick", $id_nick, time()+60*60*24*30);
setcookie ("id_passwd", $id_passwd, time()+60*60*24*30);
if (isset($_POST['connexion_auto'])) setcookie ("connexion_auto", "oui",time()+60*60*24*30);
break;
case "4,6":
$message_boite_deconnexion = forum_code("Vous êtes connecté, [url=./profile_view.php?id=".$_SESSION['id_nick']."]
[alt voir profil]".$_SESSION['id_nick']."[/url].",TRUE);
mysql_query("INSERT INTO universe_visites
(nick_id, date_id) VALUES ('".$id_nick."', ".time().")");
$onglet = "deconnexion";
break;
case "20":
$_SESSION = array();
session_destroy();
setcookie ("connexion_auto");
unset($_COOKIE['connexion_auto']);
$message_boite_connexion = forum_code("Saisissez vos identifiants pour vous connecter. Si vous n'en avez pas, vous pouvez
[url=./messagerie.php?g=inscription][alt inscription]vous inscrire[/url].","no_pop_up");
$S = 5;
$NS = 6;
$onglet = "connexion";
break;
case "19":
mysql_query("DELETE FROM universe_membres WHERE nick='".$_SESSION['id_nick']."'");
unset($_SESSION['id_nick']);
setcookie ("connexion_auto");
unset($_COOKIE['connexion_auto']);
setCookie('id_nick');
unset($_COOKIE['id_nick']);
setCookie('temp_nick');
unset($_COOKIE['temp_nick']);
setCookie('temp_passwd');
unset($_COOKIE['temp_passwd']);
setCookie('temp_email');
unset($_COOKIE['temp_email']);
$message_boite_connexion = forum_code("Saisissez vos identifiants pour vous connecter. Si vous n'en avez pas, vous pouvez
[url=./messagerie.php?g=inscription][alt inscription]vous inscrire[/url].","no_pop_up");
$S = 5;
$NS = 6;
$onglet = "connexion";
break;
case "0":
$message_boite_deconnexion = forum_code("Vous êtes connecté, [url=./profile_view.php?id=".$_SESSION['id_nick']."]
[alt voir profil]".$_SESSION['id_nick']."[/url].",TRUE);
$onglet = "deconnexion";
break;
case "0,15":
$message_boite_inscription = "Saisissez les informations nécessaires à votre nscription: ";
$onglet = "inscription";
$S = 15;
$NS = 18;
break;
case "16":
$message_boite_inscription = forum_code("[color=#ff0000]Le nick $id_nick que vous avez saisi est invalide ou existe déjà.[/color]");
$S = 15;
$NS = 18;
$onglet = "inscription";
break;
case "17":
$message_boite_inscription = forum_code("[color=#ff0000]L'e-mail que vous avez saisi est invalide ou existe déjà.[/color]");
$S = 15;
$NS = 18;
$onglet = "inscription";
break;
case "23":
$message_boite_inscription = forum_code("[color=#ff0000]Saisissez un mot de passe.[/color]");
$S = 15;
$NS = 18;
$onglet = "inscription";
break;
case "18":
$temp_nick = $_POST['temp_nick'];
$temp_passwd = md5($_POST['temp_passwd']);
$temp_email = $_POST['temp_email'];
$var_link = random(20);
mysql_query("INSERT INTO universe_temp_vars
(temp_nick, temp_passwd, temp_email, temp_date, var_link)
VALUES ('".$temp_nick."', '".$temp_passwd."', '".$temp_email."', '".time()."', '".$var_link."')");
setcookie ("temp_nick", $temp_nick, time()+60*60*24*30);
setcookie ("temp_passwd", $temp_passwd, time()+60*60*24*30);
setcookie ("temp_email", $temp_email, time()+60*60*24*30);
$message_bienvenue = "
Bienvenue sur UNIVERSE.
Veuillez conserver cet e-mail dans vos archives. Les informations
concernant votre compte sont les suivantes :
----------------------------
Nom d'utilisateur : $temp_nick
Mot de passe : ".$_POST['temp_passwd']."
Lien vers le site : http://naytheet.free.fr/UNIVERSE/index.php
----------------------------
Votre compte est actuellement inactif. Vous ne pouvez pas l'utiliser
jusqu'à ce que vous vous rendiez sur le lien suivant :
http://naytheet.free.fr/UNIVERSE/messagerie.php?var_link=$var_link
Veuillez conserver précieusement votre mot de passe car il a été crypté
dans notre base de données et nous ne pourrons pas le retrouver à votre
place. Cependant, si vous oubliez votre mot de passe, vous pouvez en
demander un nouveau qui sera activé de la même manière que ce compte.
Nous vous remercions de votre inscription.";
mail($temp_email, 'Bienvenue sur "Universe"' , $message_bienvenue);
$message_boite_connexion = "Un mail d'activation de votre compte vous a été envoyé à l'adresse e-mail que vous avez fournie.
Vous devez activer votre compte avant de vous connecter.";
$onglet = "connexion";
break;
case "14":
$message_recuperation_identifiants = "Saisissez l'e-mail que vous avez fournie pour votre inscription : ";
$S = 14;
$NS = 21;
$onglet = "recup_id";
break;
case "21":
$temp_email = $_POST['email_recup'];
$requete_email_recup = mysql_query("
SELECT email, date_inscription FROM universe_membres WHERE email = '".$temp_email."'") or die(mysql_error());
$email_recup = mysql_fetch_array($requete_email_recup);
$temp_date = $email_recup['date_inscription'];
setCookie("connexion_auto");
unset($_COOKIE['connexion_auto']);
setCookie('id_nick');
unset($_COOKIE['id_nick']);
setCookie('temp_nick');
unset($_COOKIE['temp_nick']);
setCookie('temp_passwd');
unset($_COOKIE['temp_passwd']);
setCookie('temp_email');
unset($_COOKIE['temp_email']);
mysql_query("DELETE FROM universe_membres WHERE email='".$temp_email."'");
mysql_query("DELETE FROM universe_temp_vars WHERE temp_email='".$temp_email."'");
mysql_query("DELETE FROM universe_temp_vars WHERE temp_date + 60*60*24*30 < ".time())
$temp_passwd = random(8);
$var_link = random(20);
mysql_query("INSERT INTO universe_temp_vars
(temp_nick, temp_passwd, temp_email, temp_date, var_link)
VALUES ('".$temp_email."', '".md5($temp_passwd)."', '".$temp_email."', '".$temp_date."', '". $var_link."')");
setCookie("temp_nick", $temp_email, time()+60*60*24*30);
setCookie("temp_passwd", md5($temp_passwd), time()+60*60*24*30);
setCookie("temp_email", $temp_email, time()+60*60*24*30);
$message_bienvenue = "
Bienvenue sur UNIVERSE.
Veuillez conserver cet e-mail dans vos archives. Les informations
concernant votre compte sont les suivantes :
----------------------------
Nom d'utilisateur : $temp_nick
Mot de passe : ".$_POST['temp_passwd']."
Lien vers le site : http://naytheet.free.fr/UNIVERSE/index.php
----------------------------
Votre compte est actuellement inactif. Vous ne pouvez pas l'utiliser
jusqu'à ce que vous vous rendiez sur le lien suivant :
http://naytheet.free.fr/UNIVERSE/messagerie.php?var_link=$var_link
Veuillez conserver précieusement votre mot de passe car il a été crypté
dans notre base de données et nous ne pourrons pas le retrouver à votre
place. Cependant, si vous oubliez votre mot de passe, vous pouvez en
demander un nouveau qui sera activé de la même manière que ce compte.
Nous vous remercions de votre inscription.";
mail($temp_email, 'Bienvenue sur "Universe"' , $message_bienvenue);
$message_boite_connexion = "Un mail de réactivation de votre compte vous a été envoyé à l'adresse e-mail que vous avez fournie.
Vous devez activer votre compte avant de vous connecter.";
$onglet = "connexion";
break;
case "24":
$message_recuperation_identifiants = "email introuvable.";
$S = 14;
$NS =21;
$onglet = "recup_id";
break;
}
}
if (isset($_POST['step'])) $PS = $_POST['step'];
else $PS = 0;
if (isset($_POST['next_step'])) $S = $_POST['next_step'];
else $S = 0;
if (isset($_GET['g']))
{
$G = $_GET['g'];
switch ($G)
{
case "connexion":
break;

case "deconnexion":
if (!isset($_SESSION['id_nick'])) exit;
$PS = 0;
$S = 20;
break;

case "desinscription":
if (!isset($_SESSION['id_nick'])) exit;
$PS = 0;
$S = 19;
break;

case "inscription":
if (isset($_SESSION['id_nick'])) exit;
$PS = 0;
$S = 15;
break;

case "recup_id":
if (isset($_SESSION['id_nick'])) exit;
$S = 14;
break;
default:
exit;
}
}

if (isset($_POST['sedeconnecter']))
{
$PS = 0; $S = 20;
}
if (isset($_POST['sedesinscrire']))
{
$PS = 0; $S = 19;
}
if (isset($_POST['monprofil']))
{
// redirection
}
$PS_S = $PS.",".$S;
switch ($PS_S)
{
case "0,0":
if (isset($_SESSION['id_nick']))
{
action(0);
break;
} else action(1);
$requete_verification_ip_bannis = mysql_query("SELECT banni_ip FROM universe_bannis
WHERE (banni_ip = '".$ip_client."')") or die(mysql_error());
if (mysql_num_rows($requete_verification_ip_bannis) < 1)
{
if(!isset($_COOKIE['temp_nick']) || ($_COOKIE['temp_nick']) == '')
{
if((($_COOKIE['connexion_auto'])==="oui") && ($_COOKIE['id_nick']!=="") && ($_COOKIE['id_passwd']!==""))
{
$requete_verification_id_bannis = mysql_query("
SELECT banni_nick FROM universe_bannis
WHERE (banni_nick = '".$id_nick."')") or die(mysql_error());
if (mysql_num_rows($requete_verification_id_bannis) < 1)
{
$requete_verification_id = mysql_query("SELECT nick, passwd FROM universe_membres
WHERE (nick = '".$_COOKIE['id_nick']."' AND passwd = '".$_COOKIE['id_passwd']."')") or die(mysql_error());
if (mysql_num_rows($requete_verification_id) > 0)
{
action(2);
action(4,2);
} else
{
action(3);
break;
}
} else
{
action(7);
action(3);
}
} else action(5,1);
} else
{
if ((isset($_GET['var_link']))
&& ($_COOKIE['temp_nick'] !== '')
&& ($_COOKIE['temp_passwd'] !== '')
&& ($_COOKIE['temp_email'] !== ''))
{
$var_link = $_GET['var_link'];
$temp_nick = $_COOKIE['temp_nick'];
$temp_passwd = $_COOKIE['temp_passwd'];
$temp_email = $_COOKIE['temp_email'];
$requete_temp_vars = mysql_query("
SELECT var_link, temp_nick, temp_passwd, temp_email, temp_date
FROM universe_temp_vars
WHERE (var_link = '".$var_link."')") or die(mysql_error());
$temp = mysql_fetch_array($requete_temp_vars);
$temp_date = $temp['temp_date'];
if (($temp_nick == $temp['temp_nick']) && ($temp_passwd == $temp['temp_passwd']) && ($temp_email == $temp['temp_email']))
{
action(8);
action(5,8);
} else
{
action(9);
action(5,9);
}
} else
{
action(9);
action(5,9);
}
}
} else action(22);
break;
case "5,6":
$id_nick = $_POST['id_nick'];
$id_passwd = md5($_POST['id_passwd']);
$requete_identification= mysql_query("
SELECT membre_id, nick, passwd
FROM universe_membres
WHERE nick = '".$id_nick."'") or die(mysql_error());
$id = mysql_fetch_array($requete_identification);

if (mysql_num_rows($requete_identification) < 1)
{
action(10);
action(5,10);
} elseif ($id['passwd'] != $id_passwd)
{
action(11);
action(5,11);
} else
{
action(6);
action(4,6);
}
break;
case "0,20":
action(20);
break;
case "0,19":
action(19);
break;
case "0,15":
action(0,15);
break;
case "15,18":
if (isset($_POST['temp_email'])) $temp_email = $_POST['temp_email'];
if (isset($_POST['temp_nick'])) $temp_nick = $_POST['temp_nick'];
if (isset($_POST['temp_nick']))
{
$requete_verification_nicks = mysql_query("SELECT 'found' FROM
(select count(*) as c1 from universe_membres WHERE nick = '".$temp_nick."') AS t1,
(select count(*) as c2 FROM universe_temp_vars WHERE temp_nick = '".$temp_nick."') AS t2,
(select count(*) as c3 from universe_bannis WHERE banni_nick = '".$temp_nick."') AS t3
WHERE c1 > 0 OR c2 > 0 OR c3 > 0") or die(mysql_error());
if ((mysql_num_rows($requete_verification_nicks) >= 1) || ($temp_nick == ''))
{
action(16);
break;
} else
{
if (isset($_POST['temp_email']))
{
$requete_verification_email_membres = mysql_query("SELECT email FROM universe_membres
WHERE email = '".$temp_email."'") or die(mysql_error());
$requete_verification_email_bannis = mysql_query("SELECT banni_email FROM universe_bannis
WHERE banni_email = '".$temp_email."'") or die(mysql_error());
if ( (mysql_num_rows($requete_verification_email_bannis) >= 1)
|| (mysql_num_rows($requete_verification_email_membres) >= 1)
|| (isset($temp_email) )
&& (!preg_match("#^([a-zA-Z0-9_\-\.]*)[a-zA-Z0-9]@([a-zA-Z0-9\-\.]*)[azA-Z0-9]\.[a-zA-Z]{2,3}$#",$temp_email)))
{
action(17);
break;
}
if ($_POST['temp_passwd']=='') action(23);
else action(18);
}
}
}
break;
case "0,14":
action(14);
break;
case "14,21":
$temp_email = $_POST['email_recup'];
$requete_verification_email_membres = mysql_query("
SELECT email
FROM universe_membres
WHERE email = '".$temp_email."'") or die(mysql_error());
if (mysql_num_rows($requete_verification_email_membres) >= 1) action(21);
else action(24);
break;
}

include("includes/debut.php");

?>

<body style='background-color: black; background-image: url("images/fond.jpg"); margin: auto;
background-repeat: repeat;'>

<div id='A1'>
</div>
<div id='A2'>
<!-- A0 -->
<?php     include("includes/menu_a2.php"); ?>
</div>
<div id='A3'>
</div>
<div id='B1'>
</div>
<div id='B2'>
</div>
<div id='B3'>
</div>
<div id='C1'>
</div>
<div id='C2'>
</div>
<div id='C3'>
</div>
</body>
</html>

À l'exception des variables magiques post et get servant à initialiser la position dans l'algo graphique, l'utilisation de global est ici inappropriée. Prière d'utiliser des classes pour rendre les variables accessibles aux fonctions. La page contenant d'autres div, le nom de la classe utilisée pour cette section pourra s'appeler « B2 », ou plus explicitement « GestionCompte ». Le code de ce TP ne visait qu'à illustrer le concept du passage algo graphique -> PHP, je me suis permis de passer sur les structures de données propres au langage orienté objet.

III-D - Remarques et conclusion

III-D-1 - Les cookies

Les cookies n'étant effacés que lors d'une fin de session du navigateur, les tests d'existence de cookies sont améliorés avec un test de contenu vide ou non s'ils sont présents.

III-D-2 - Les requêtes SQL

mysql_query("DELETE FROM universe_temp_vars WHERE temp_nick='".$temp_nick."'");
// on en profitera pour nettoyer les vars > 30 jours :
mysql_query("DELETE FROM universe_temp_vars WHERE temp_date + 60*60*24*30 < ".time());

En même temps d'activer un compte, on met à jour la table en effaçant ici les comptes mis en attente d'activation de plus de trente jours. Cela peut servir par exemple à effacer des spammers ayant échoué. Il est possible et recommandé de fusionner les requêtes pour un gain de temps. Les requêtes SQL étant sujettes à mettre du temps à s'exécuter, si le site est fréquenté, celui-ci aura du mal à répondre rapidement donc autant diminuer le nombre de requêtes. On pourra alors préférer la requête suivante :

mysql_query("DELETE FROM universe_temp_vars WHERE temp_nick='".$temp_nick."' OR temp_date + 60*60*24*30 < ".time());

case "15,18":
if (isset($_POST['temp_email'])) $temp_email = $_POST['temp_email'];
if (isset($_POST['temp_nick'])) $temp_nick = $_POST['temp_nick'];
if (isset($_POST['temp_nick']))
{
$requete_verification_nicks = mysql_query("
SELECT 'found' FROM
(select count(*) as c1 from universe_membres WHERE nick = '".$temp_nick."') AS t1,
(select count(*) AS c2 FROM universe_temp_vars WHERE temp_nick = '".$temp_nick."') AS t2,
(select count(*) as c3 from universe_bannis WHERE banni_nick = '".$temp_nick."') AS t3
WHERE c1 > 0 OR c2 > 0 OR c3 > 0") or die(mysql_error());
if ((mysql_num_rows($requete_verification_nicks) >= 1) || ($temp_nick == ''))
{
action(16);

Case "15,18" : Pour ce premier if, nous nous trouvons donc ici sur le premier losange testant l'existence du pseudo dans les tables des membres, des membres dont l'activation du compte est en attente, ou des bannis. Je ne suis pas des plus doués en matière de SQL, aussi je vais vous expliquer la démarche de cette requête que j'ai pu trouver en cherchant sur Google. Elle est basée sur trois sous-requêtes t1, t2 et t3. Chacune de ces sous-requêtes compte le nombre de lignes dans lesquelles se trouve le contenu de la variable post temp_nick dans chacune de ces tables. Si on trouve plus de zéro ligne dans au moins une des tables (i.e. si le pseudo est présent dans l'une de ces tables) on aura un résultat de la requête et donc au moins une ligne dans le tableau résultant de la requête. Peu nous importe le résultat de la requête, il est interprété comme un booléen. Cette requête est purement conditionnelle.

III-E - Les autres fichiers PHP

L'include identifiants.php concerne la connexion à votre base de données.

III-E-1 - debut.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/
xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" >
<head>
<?php
// liste des noms de fenêtre correspondant aux fichiers PHP à parser
$titre= array
(
"/" => "Naytheet.fr",
"/UNIVERSE/messagerie.php" => "UNIVERSE - messagerie",
"/NAYTHEET.FR/UNIVERSE/messagerie.php" => "UNIVERSE - messagerie"
);
// récupération du nom de fichier css dynamique - il doit avoir le même nom que le fichier PHP correspondant.
$identification = $_SERVER["REMOTE_ADDR"];
$fichier_css_dynamique = substr($_SERVER['PHP_SELF'], 0, -4).'_css.php';
?>
<title><?php echo $titre[$_SERVER['PHP_SELF']]; ?></title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<link rel="stylesheet" type="text/css" media="screen" href="<?php echo $fichier_css_dynamique?>"
<link rel="SHORTCUT ICON" href="favicon.ico">
</head>

III-E-2 - menu_a2.php

Ceci concerne le fichier appelé pour l'affichage :

<?php
if (!isset($preremplissage_nick)) $preremplissage_nick = '';
if (!isset($preremplissage_email)) $preremplissage_email = '';
$onglet_inscription_active = '
<li class="active">
<a href="messagerie.php?g=inscription"> Inscription </a>
</li>';
$onglet_inscription_passive = '
<li class="passive">
<a href="messagerie.php?g=inscription"> Inscription </a>
</li>';
$onglet_inscription_disabled = '
<li class="disabled">
<a> Inscription </a>
</li>';
$onglet_connexion_active = '
<li class="active">
<a href="messagerie.php?g=connexion"> Connexion </a>
</li>';
$onglet_connexion_passive = '
<li class="passive">
<a href="messagerie.php?g=connexion"> Connexion </a>
</li>';
$onglet_connexion_disabled = '
<li class="disabled">
<a> Connexion </a>
</li>';
$onglet_deconnexion_active = '
<li class="active">
<a href="messagerie.php?g=deconnexion"> Déconnexion </a>
</li>';
$onglet_deconnexion_passive = '
<li class="passive">
<a href="messagerie.php?g=deconnexion"> Déconnexion </a>
</li>';
$onglet_deconnexion_disabled = '
<li class="disabled">
<a> Déconnexion </a>
</li>';
$preremplissage_temp_nick = (isset($_POST['temp_nick'])) ? $_POST['temp_nick'] : '';
$preremplissage_nick = (isset($_POST['nick'])) ? $_POST['nick'] : '';
$preremplissage_temp_email = (isset($_POST['temp_email'])) ? $_POST['temp_email'] : '';
$form_connexion = "
<div id='form_A2'>
<label for='nick'>nick : </label>
<input type='text' name='id_nick' value='$preremplissage_nick'><br/>
<label for='passwd'>mot de passe : </label>
<input type='password' name='id_passwd'><br/>
<label>Connexion automatique </label><input type='checkbox' style='border: none' name='connexion_auto' />
<input type='submit' />
<label for='oubli'>".forum_code("Vous avez oublié vos identifiants? [url=./messagerie.php?g=recup_id][alt récupération par
mail]cliquez ici[/url]","no_pop_up")."</label>
</div>";
$form_deconnexion = "
<div id='form_A2'>
<input type='submit' name='sedeconnecter' value='Se déconnecter' />
<input type='submit' name='sedesinscrire' value='Se désinscrire' />
<input type='submit' name='monprofil' value='Mon profil' />
</div>";
$form_inscription = "
<div id='form_A2'>
<label for='temp_nick'>nick : </label>
<input type='text' name='temp_nick' value='$preremplissage_temp_nick'><br/>
<label for='temp_passwd'>mot de passe : </label>
<input type='text' name='temp_passwd'><br/>
<label for='temp_email'>e-mail : </label>
<input type='text' name='temp_email' value='$preremplissage_temp_email'><br/>
<input type='submit' />
</div>";
$form_recuperation = "
<div id='form_A2'>
<label for='email_recup'>e-mail : </label>
<input type='text' name='email_recup'><br/>
<input type='submit' />
</div>";
if ($onglet == "recup_id")
{
$affichage_onglets = $onglet_inscription_passive.
$onglet_connexion_active.
$onglet_deconnexion_disabled;
$affichage_contenu = "<span class ='message_normal'>$message_recuperation_identifiants</span>$form_recuperation";
}

if ($onglet == "inscription")
{
$affichage_onglets = $onglet_inscription_active.
$onglet_connexion_passive.
$onglet_deconnexion_disabled;
$affichage_contenu = "<span class ='message_normal'>$message_boite_inscription</span>$form_inscription";
}
if ($onglet == "connexion")
{
$affichage_onglets = $onglet_inscription_passive.
$onglet_connexion_active.
$onglet_deconnexion_disabled;
$affichage_contenu = "<span class ='message_normal'>$message_boite_connexion</span>$form_connexion";
}
if ($onglet == "deconnexion")
{
$affichage_onglets = $onglet_inscription_disabled.
$onglet_connexion_disabled.
$onglet_deconnexion_active;
$affichage_contenu = "<span class ='message_normal'>$message_boite_deconnexion</span>$form_deconnexion";
}?>
<ul id="onglets">
<?php echo $affichage_onglets ?>
</ul>
<form action='./messagerie.php' method='post'>
<INPUT TYPE=HIDDEN NAME="step" VALUE="<?php echo $S ?>" >
<INPUT TYPE=HIDDEN NAME="next_step" VALUE="<?php echo $NS ?>" >
<?php echo $affichage_contenu ?>
</form>

Les petits boxes au-dessus des actions sur l'algo graphique sont représentés par les variables de préremplissage. Voici enfin le CSS dynamique utilisé pour paramétrer l'aspect visuel du site :

III-E-3 - messagerie_css.php

<?php header("Content-type: text/css");
// abscisses
$w = 33.29;
// ordonnées
$h = 33.25;
// variables d'initialisations
$INIT_MESSAGERIE = ' position: absolute;
border: solid green 1px;
overflow: auto;';
$INIT_CASES_MESSAGERIE = 'position: absolute;
border: solid green 1px;
width: '.$w.'%;
height: '.$h.'%;
overflow: auto;';
// variables de positionnement pour MESSAGERIE
$MESSAGERIE_width = "99.8%";
$MESSAGERIE_height = "99.8%";
// formulaire A2 (connexion, etc.)
$form_A2_top = "50%";
$form_A2_left = "20%";
?>
/* pas de tableau pour plus de lisibilité du programme */
input
{
border: solid green 1px;
color: white;
background-color: black;
}
.message_normal
{
font-family : courier new;
color : white;
}
#form_A2
{
position: absolute;
top: <?php echo $form_A2_top ?>;
left: <?php echo $form_A2_left ?>;
text-align: right;
color: white;
}
#A1
{
<?php echo $INIT_CASES_MESSAGERIE ?>; /* couleur du cadre; hauteur / largeur. */
}
#A2
{
<?php echo $INIT_CASES_MESSAGERIE ?>;
left: <?php echo $w ?>%; /* positionnement de la cellule. */
#A3
{
<?php echo $INIT_CASES_MESSAGERIE ?>
left: <?php echo $w * 2?>%;
color: white;
}
#B1
{
<?php echo $INIT_CASES_MESSAGERIE ?>
top: <?php echo $h?>%;
}
#B2
{
<?php echo $INIT_CASES_MESSAGERIE ?>
top: <?php echo $h?>%;
left: <?php echo $w?>%;
}
#B3
{
<?php echo $INIT_CASES_MESSAGERIE ?>
top: <?php echo $h?>%;
left: <?php echo $w * 2?>%;
}
#C1
{
<?php echo $INIT_CASES_MESSAGERIE ?>
top: <?php echo $h * 2?>%;
}
#C2
{
<?php echo $INIT_CASES_MESSAGERIE ?>
top: <?php echo $h * 2?>%;
left: <?php echo $w?>%;
}
#C3
{
<?php echo $INIT_CASES_MESSAGERIE ?>
top: <?php echo $h * 2?>%;
left: <?php echo $w * 2?>%;
}
#onglets
{
padding-bottom : 25px;
border-bottom : 1px solid green;
margin-left : 0;
margin-top : 0;
font : 14px Batang, arial, serif;
list-style-type : none; /* pour enlever les puces */
}
#onglets li
{
float: left;
border : 1px solid green;
height : 24px; /* à modifier suivant la taille de la police pour centrer le texte dans l'onglet */
background-color: transparent;
}
#onglets .active a
{
color : #000000;
background-color : #FFFFFF;
}
#onglets .disabled a, #onglets .disabled a:hover
{
display : block;
filter : alpha(opacity=10);
<?php if ( strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox') )
{?>
background-color : #FFFFFF;
<?php }else
{?>
background-color : #444444;
<?php
} ?>
-moz-opacity : 0.3;
opacity : 0.3;
color : #000000;
}
#onglets .passive a
{
display : block;
color : #FFFFFF;
background-color : #000000;
}
#onglets a
{
display: block;
padding : 4px;
color : #000000;
text-decoration : none;
}
#onglets a:hover, #onglets .passive a:hover
{
color : #000000;
background-color : #FFFFFF;
}

Voilà pour le TP qui marque la fin de ce tutoriel. En espérant qu'il motive à coder plus rigoureusement, mais surtout qu'il fasse avancer les débutants dans la manière de coder, cette technique est basée simplement sur une conciliation entre la logique de programmation d'un algorithme et le codage PHP d'une page Web. Pour la suite, la contrainte d'utiliser sans cesse des requêtes nous amènera à plutôt manipuler des fichiers contenant les données directement chez l'utilisateur.

III-F - Remerciements

Ce tutoriel n'aurait pas pu être validé sur developpez.com sans les corrections indispensables effectuées par Claude.
Maxime Gault (_Max_) a pu me guider le long des étapes de validation.
Last but not least, mention spéciale pour deux personnages-clés de ce site :

- Djibril, qui a pu établir la liaison entre mon labo et le QG ;

- Maître Nono40, qui est resté alerte sur le forum privé.

Les modifications qu'ils ont apportées aux outils de rédaction du cours (LaTex, EditeurXML\x{0085}) et leurs explications m'ont permis de rédiger ce cours façon developpez.com.
Je tiens à souligner l'appui fondamental de James Henstridge sans lequel on n'aurait pu construire le plan de travail.
Pour les personnes de mon entourage, dans l'ordre antichronologique :
celles et ceux qui ont lu mon tutoriel avant qu'il ne soit publié et qui se reconnaîtront ;
Catherine Gourion, qui m'a montré une faille de sécurité dans l'algorithme et je la remercie grandement pour son soutien ;
Jean Louis Tutard, qui m'a grandement apporté en CSS, HTML, sitemapping et quincaillerie WEB fondamentale ;
la plupart des professeurs de l'IUT informatique Bordeaux I à Talence... de 1996 à 1999 (M. Duluc, etc.) qui ont réveillé ma passion pour l'algorithmique et l'informatique alors que je programmais déjà (mais bien mal) ;
tous les professeurs en productique (à qui je dédie ce tutoriel), et formateurs en automatisme et informatique industrielle, du lycée Pape Clément (avant 1995) et de l'AFPA de Pau. Vous conviendrez que ces algorithmes graphiques ressemblent assez à des grafcets...