Programmation PHP/Version imprimable3

Ceci est la version imprimable de Programmation PHP.
  • Si vous imprimez cette page, choisissez « Aperçu avant impression » dans votre navigateur, ou cliquez sur le lien Version imprimable dans la boîte à outils, vous verrez cette page sans ce message, ni éléments de navigation sur la gauche ou en haut.
  • Cliquez sur Rafraîchir cette page pour obtenir la dernière version du wikilivre.
  • Pour plus d'informations sur les version imprimables, y compris la manière d'obtenir une version PDF, vous pouvez lire l'article Versions imprimables.


Programmation PHP

Une version à jour et éditable de ce livre est disponible sur Wikilivres,
une bibliothèque de livres pédagogiques, à l'URL :
https://fr.wikibooks.org/wiki/Programmation_PHP

Vous avez la permission de copier, distribuer et/ou modifier ce document selon les termes de la Licence de documentation libre GNU, version 1.2 ou plus récente publiée par la Free Software Foundation ; sans sections inaltérables, sans texte de première page de couverture et sans Texte de dernière page de couverture. Une copie de cette licence est incluse dans l'annexe nommée « Licence de documentation libre GNU ».

Exemples/Variables

Utilisation de variables modifier

Un exemple de programme modifier

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Variables en PHP !</title>
  </head>
  <body>

<?php

  for($i = 1; $i <= 10; $i++)
    echo ' <p>Ligne numéro '.$i.'</p>'."\n";
?>

  </body>
</html>

Explications modifier

  • Une variable en php commence par le symbole $. Ici nous utilisons une variable d'identificateur $i.
  • Il n'y a pas de déclaration ni de typage fixe : une variable peut changer dynamiquement de type, ce qui est parfois vu comme un atout, parfois comme une faiblesse !
  • Ce programme comporte une boucle for qui a sa sémantique habituelle. La variable $i va donc prendre successivement les valeurs 1,2,... jusqu'à 10.
  • Dans cet exemple les chaînes de caractères sont entre apostrophes.
  • La concaténation des chaînes de caractères s'effectue grâce à l'opérateur ..
  • Remarque : si on veut qu'une chaîne de caractères contienne une apostrophe droite il faut écrire \' à l'intérieur de la chaîne.

Exécution du programme modifier

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Variables en php</title>
  </head>
  <body>

<p>Ligne numéro 1</p>
<p>Ligne numéro 2</p>
<p>Ligne numéro 3</p>
<p>Ligne numéro 4</p>
<p>Ligne numéro 5</p>
<p>Ligne numéro 6</p>
<p>Ligne numéro 7</p>
<p>Ligne numéro 8</p>
<p>Ligne numéro 9</p>
<p>Ligne numéro 10</p>


  </body>
</html>

Les guillemets modifier

Une chaîne de caractère entre guillemet est assez particulière : si elle contient $a alors $a est remplacé par la valeur de la variable $a. Il y a automatiquement substitution. Si on écrit \$ alors il n'y a plus substitution. De la même manière, pour afficher le caractère guillemet on écrit \".

Exemple 2 : guillemets et variables modifier

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Variables en PHP !</title>
  </head>
  <body>

    <?php
      $a=67+33;
      echo "la variable \$a vaut $a";
    ?>

  </body>
</html>

Explications modifier

Dans ce programme la variable $a vaut 67+33 donc vaut 100. Dans la chaîne de caractères \$a affichera $a et le deuxième $a sera remplacé par la valeur 100. Il s'affichera donc :
la variable $a vaut 100


Exemples/Sommaire

Un sommaire simple modifier

Imaginons un site Web composé de 4 pages entre lesquelles on peut naviguer grâce à un sommaire. Notre sommaire est par exemple à gauche de l'écran et contient 4 liens hypertextes : page 1, page 2, page 3 et page 4. À droite de l'écran, le contenu principal de la page change en fonction de la page affichée. Par contre notre sommaire apparaît lui sur chacune des pages.

Le programme en php modifier

Fichier index.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Sommaire en PHP !</title>
  <style type="text/css">
  #sommaire
  {
  position:absolute;
  background-color:cyan;
  left:10px;
  width:100px;
  }

  #page
  {
  position:absolute;
  background-color:#AAAAAA;
  left : 200px;
  width:500px;
  height:500px;
  }
  </style>
  </head>
  <body>

    <div id="sommaire">
	<h3>Sommaire</h3>
	    <?php for($i = 1; $i <= 4; $i++) :?>
        <a href="index.php?page=<?php echo $i ?>">Page <?php echo $i ?></a><br/>
    	<?php endfor ?>
    </div>

    <div id="page">
        <?php
        if (isset($_GET['page'])) $numero=$_GET['page']; else $numero='1';
        require 'page'.$numero.'.html';
        ?>
    </div>

  </body>
</html>

fichier page1.html

<h1>Page 1</h1>
bla bla bla

fichier page2.html

<h1>Page 2</h1>
ble ble ble

fichier page3.html

<h1>Page 3</h1>
bli bli bli

fichier page4.html

<h1>Page 4</h1>
blo blo blo

Explications modifier

  • Dans ce programme, la page est découpée en 2 parties : à gauche une partie ayant comme id sommaire et à droite une page ayant comme id page.
  • La partie « sommaire » utilise le style #sommaire de la feuille de style et la partie « page », le style #page.
  • Le sommaire est constitué de 4 liens hypertextes appelant respectivement index.php?page=1, index.php?page=2, index.php?page=3 et index.php?page=4. Lorsqu'on met un point d'interrogation après l'URL d'une page, cela signifie qu'on donne une valeur à un paramètre et qu'on envoie cette information au serveur par la méthode GET. Lorsqu'on clique sur l'un des 4 liens hypertextes, on appelle à chaque fois la même page index.php mais à chaque fois la valeur du paramètre nommé page change : il vaut 1, 2, 3 ou 4 selon le lien cliqué.
  • Dans la partie « page » de index.php, la valeur du paramètre page est récupérée de l'url cliquée en écrivant $_GET['page']. Ce paramètre peut très bien ne pas exister : ceci a lieu notamment la première fois qu'on appelle notre page index.php. Dans ce cas, la fonction isset() permet de savoir si une variable existe. La ligne
if (isset($_GET['page']))$numero=$_GET['page']; else $numero='1';
récupère la valeur du paramètre page et la place dans la variable $numero. Si ce paramètre n'existe pas $numero vaut 1.
  • La fonction require'nom_du_fichier'; permet d'insérer un fichier à cet endroit dans le code. Le server insère donc page1.html ou page2.html ou page3.html ou page4.html en fonction de la valeur du paramètre page (et de la variable numero).
  • Notre sommaire est terminé.


Exemples/Formulaire

Interaction avec un formulaire modifier

Les principaux concepts modifier

L'interaction entre une application en PHP et un utilisateur peut s'effectuer par des liens hypertextes ou par l'envoi d'un formulaire. C'est ce cas dernier que nous allons étudier ici.

Le formulaire comporte une balise form qui précise que la méthode utilisée pour envoyer le contenu du formulaire au programme en PHP est la méthode POST. Elle précise également l'action du formulaire, c'est-à-dire à quelle adresse envoyer le contenu du formulaire pour son traitement. Dans notre exemple, après un clic sur le bouton d'envoi, le formulaire déclenchera l'exécution du programme go.php. Le formulaire est composé de 3 éléments graphiques : 2 champs de type texte nommés respectivement nom et prénom et un bouton sur lequel il est écrit envoyer le formulaire. Le formulaire invite donc l'utilisateur à entrer un nom et un prénom et à cliquer sur le bouton « envoyer le formulaire ».

Le programme go.php doit récupérer les valeurs contenues dans le formulaire : pour récupérer la valeur du champ nom, il faut écrire $_POST['nom']. De la même manière, pour récupérer la valeur du champ prénom, il faut écrire $_POST['prenom']. Après un clic sur le bouton « envoyer le formulaire », une autre page s'affiche. Elle contient un message contenant "Bienvenue à" suivi du prénom et du nom définis dans le formulaire rempli. Le programme a bien récupéré la valeur des différents champs du formulaire.

Le programme en php modifier

Le fichier index.html :

<!DOCTYPE html PUBLIC "-//DTD XHTML 2.0 Transitional//EN"
        "http://www.org/TR/xhtml/xhtml-transitional.dtd">

<form action="go.php" method="post">
<p>Votre nom : <input type="text" name="nom" /></p>
<p>Votre prénom : <input type="text" name="prenom" /></p>
<p><input type="submit" value="envoyer le formulaire" /></p>
</form>

Le fichier go.php :

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 1.0 Transitional//EN"
        "http://www.org/xhtml/html-transitional.dtd">
<?php 
$nom = $_POST['nom'];
$prenom = $_POST['prenom'];
echo "<h3>Bienvenue à ".$prenom.' '.$nom,"</h3>";
 
echo "<p><a href='index.html'>Retour au formulaire</a></p>";
?>

Captures d'écran modifier

 


Exemples/BD 1

Afficher le résultat d'une requête modifier

Présentation modifier

Dans cet exemple, on va créer une application qui extrait des données à partir d'une base de données et qui affiche le résultat à l'écran. Notre programme va afficher une liste d'employés d'une entreprise imaginaire : chaque employé est défini par un nom, un prénom et un salaire. La base de données utilisée sera une base mysql. Les données seront dans une table nommée employe. La table employé possède des champs NOM, PRENOM et SALAIRE. La table employé possède de plus un identifiant nommé ID qui est un entier (BIGINT) autoincrémenté et qui est une clé primaire de la table.


Nous étudierons une première version du programme et dans un second temps nous améliorerons le programme en utilisant la méthode GET lors d'une deuxième version.

Descriptif du site modifier

  • La première page affiche 3 liens hypertextes :
    • un lien pour afficher la liste complète des employés,
    • un lien pour afficher la liste dans l'ordre alphabétique,
    • un lien pour afficher par salaire décroissant.
  • Sur chacune des pages, on affiche la liste et il y a un lien pour revenir à la première page.

Création de la base modifier

Les requêtes suivantes permettent la création et l'initialisation des données dans notre table :

Fichier BD.sql :

CREATE TABLE `employe` (
	`ID` BIGINT NOT NULL AUTO_INCREMENT ,
	`NOM` VARCHAR( 20 ) ,
	`PRENOM` VARCHAR( 20 ) ,
	`SALAIRE` DOUBLE DEFAULT '0',
	PRIMARY KEY ( `ID` ) 
); 
INSERT INTO `employe` ( `ID` , `NOM` , `PRENOM` , `SALAIRE` ) 
	VALUES (
	'', 'Dupond', 'Marcel', '8000'
	);
INSERT INTO `employe` ( `ID` , `NOM` , `PRENOM` , `SALAIRE` ) 
	VALUES (
	'', 'Martin', 'Xavier', '4000'
	);
INSERT INTO `employe` ( `ID` , `NOM` , `PRENOM` , `SALAIRE` ) 
	VALUES (
	'', 'Gogol', 'Henri', '3000'
	);
INSERT INTO `employe` ( `ID` , `NOM` , `PRENOM` , `SALAIRE` ) 
	VALUES (
	'', 'Hugo', 'Victor', '2000'
	);
INSERT INTO `employe` ( `ID` , `NOM` , `PRENOM` , `SALAIRE` ) 
	VALUES (
	'', 'Gali', 'Daniel', '6000'
	);
INSERT INTO `employe` ( `ID` , `NOM` , `PRENOM` , `SALAIRE` ) 
	VALUES (
	'', 'Martin', 'Georges', '9000'
	);

La table créée par ces requêtes est la suivante :


IDNOMPRENOMSALAIRE
1DupondMarcel8000
2MartinXavier4000
3GogolHenri3000
4HugoVictor2000
5GaliDaniel6000
6MartinGeorges9000

Configuration de la base de données modifier

La table employe appartient à la base de données toto. L'administrateur de cette base est root. Il n'a pas de mot de passe. Attention ceci est une configuration type utilisée par défaut pour la plateforme de développement EasyPHP : il est vivement recommandé de mettre un vrai mot de passe pour une plateforme en exploitation.

Les requêtes utilisées modifier

  • Pour afficher la liste des employés, on utilisera la requête :

SELECT * FROM employe

  • Pour afficher la liste des employés par ordre alphabétique, on utilisera la requête :

SELECT * FROM employe ORDER BY NOM, PRENOM

  • Pour afficher la liste des employés par salaire décroissant, on utilisera la requête :

SELECT * FROM employe ORDER BY SALAIRE DESC

Comment accéder en php à une base mysql modifier

Les différentes étapes à respecter seront les suivantes :

  1. récupérer les informations suivantes :
    1. le nom de la machine qui héberge le serveur de la base de données (ici localhost)
    2. le nom de la base de données (ici toto)
    3. le nom de l'utilisateur de la base de données (ici root)
    4. le mot de passe de cet utilisateur (ici le mot de passe est vide).
  2. se connecter au serveur de base de données
  3. sélectionner la base de données (ici toto) sur ce serveur.
  4. créer la requête dans une chaîne de caractères
  5. envoyer la requête à la base de données et récupérer le résultat
  6. fermer la connexion avec le serveur de base de données
  7. transformer le résultat de la requête en HTML.

Étape 1 : les paramètres spécifiques au site modifier

Pour réaliser la première étape nous allons créer un fichier params.php qui contient 4 variables $host (le nom de la machine qui héberge la base de données, $user (le nom de l'utilisateur de la base de données), $password (le mot de passe de cet utilisateur) et $base le nom de la base de données. Ces paramètres changent en fonction de la plateforme que vous utilisez. Il est intéressant d'isoler ces paramètres dans un fichier séparé, que ce soit au niveau de la sécurité ou pour porter l'application sur une autre plateforme. Il faut se renseigner au niveau de l'hébergeur sur les caractéristiques de la plateforme utilisée. Les paramètres suivants sont ceux par défaut si on utilise EasyPHP. On a juste créé une base de données appelée toto.

<?php
$host='localhost';
$user='root';
$password='';
$base='toto';
?>

Pour récupérer, ces paramètres il suffit d'inclure ce fichier en utilisant le mot clé require. On écrira require'params.php' . Le fichier sera alors inclus dans le programme en php et le programmeur utilisera les 4 variables.

Étape 2 : connexion à la base de données modifier

La fonction mysql_connect($host,$user,$password) permet de se connecter au serveur de base de données mysql situé sur la machine nommée $host en utilisant le nom d'utilisateur $user ayant comme mot de passe $password. Cette fonction renvoie le booléen true si la connexion est établie et false sinon.

Étape 3 : sélectionner la base de données modifier

Un serveur de base de données peut héberger plusieurs bases de données (une base de données est un ensemble de tables). La fonction mysql_select_db($base) permet de sélectionner la base de données à laquelle on va envoyer les requêtes. Cette fonction renvoie true si tout s'est bien passé et false sinon.

Étape 4 : création de la requête modifier

Il faut ensuite créer la requête dans une chaîne de caractères. Ici la requête va être simple, par exemple $query='SELECT * FROM employe'; : on met la requête dans la variable $query. Parfois, il faudra concaténer des chaînes de caractères pour construire la requête--.

Étape 5 : envoyer la requête et récupérer le résultat modifier

Pour envoyer la requête au serveur, il faut utiliser la fonction $r=mysql_query($query); qui envoie la requête $query et récupère le résultat dans $r.

Étape 6 : fermer la connexion avec le serveur de base de données modifier

Pour fermer la connexion avec le serveur de base de données, il suffit d'appeler la fonction mysql_close();.

Étape 7 : générer du HTML à partir du résultat de la requête modifier

Supposons que le résultat d'une requête soit contenu dans la variable $r. Il est souvent intéressant de parcourir un à un tous les enregistrements contenus dans $r. L'appel de fonction $a=mysql_fetch_object($r) permet de récupérer un à un tous les enregistrements dans la variable $a. Lors du premier appel, on récupère le premier enregistrement dans $a, lors du second appel, on récupère le second enregistrement et ainsi de suite jusqu'au dernier enregistrement. Si on appelle alors une nouvelle fois cette fonction, elle va renvoyer le booléen false. Ceci permet donc de traiter un à un tous les enregistrements en écrivant

while ($a=mysql_fetch_object($r)) {
...
}

Lors de l'exécution de ce while $a va prendre toutes les valeurs des différents enregistrements contenus dans $r, du premier au dernier. À partir de l'objet $a, on pourra récupérer le champs NOM de cet enregistrement en écrivant $a->NOM. On peut ainsi récupérer la valeur de chaque champ d'un enregistrement. Il suffit donc maintennant de parcourir chaque enregistrement et d'afficher du HTML en utilisant la commande echo à partir des différents champs des enregistrements.

Les pages du site (version 1) modifier

Fichier index.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Afficher le résultat d'une requête</title>
  </head>
  <body>

<h1> Choisissez dans le menu ci-dessous</h1>

<a href="liste1.php">Afficher la liste des employés</a><br/>
<a href="liste2.php">Afficher la liste des employés par ordre alphabétique</a><br/>
<a href="liste3.php">Afficher la liste des employés par salaire décroissant</a><br/>
    
  </body>
</html>


Fichier params.php

<?php
$host='localhost';
$user='root';
$password='';
$base='toto';
?>


Fichier liste1.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Afficher la liste des employés</title>
  </head>
  <body>
<h1>Afficher la liste des employés</h1>
<?php
  require 'params.php';
  mysql_connect($host,$user,$password) or die('Erreur de connexion au SGBD.');
  mysql_select_db($base) or die('La base de données n\'existe pas');
  $query='SELECT * FROM employe';
  $r=mysql_query($query);
  mysql_close();
  echo'<table><tr><td>NOM</td><td>PRENOM</td><td>SALAIRE</td></tr>';
  while ($a=mysql_fetch_object($r)) {
    $nom=$a->NOM;
    $prenom=$a->PRENOM;
    $salaire=$a->SALAIRE;
    echo"<tr><td>$nom</td><td>$prenom</td><td>$salaire</td></tr>";
  }
  echo '</table>';
?>
<br/>
<br/>
<a href="index.html">Retour à la page d'accueil</a>
    
  </body>
</html>


Fichier liste2.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Afficher la liste des employés par ordre alphabétique</title>
  </head>
  <body>
<h1>Afficher la liste des employés par ordre alphabétique</h1>
  <?php
  require 'params.php';
  mysql_connect($host,$user,$password) or die('Erreur le connexion au SGBD.');
  mysql_select_db($base) or die('La base de données n\'existe pas');
  $query='SELECT * FROM employe ORDER BY NOM, PRENOM';
  $r=mysql_query($query);
  mysql_close();
  echo'<table><tr><td>NOM</td><td>PRENOM</td><td>SALAIRE</td></tr>';
  while ($a=mysql_fetch_object($r)) {
    $nom=$a->NOM;
    $prenom=$a->PRENOM;
    $salaire=$a->SALAIRE;
    echo"<tr><td>$nom</td><td>$prenom</td><td>$salaire</td></tr>";
  }
  echo '</table>';
  ?>
<br/>
<br/>
<a href="index.html">Retour à la page d'accueil</a>
    
  </body>
</html>


Fichier liste3.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Afficher la liste des employés par salaire décroissant</title>
  </head>
  <body>
<h1>Afficher la liste des employés par ordre alphabétique</h1>
  <?php
  require 'params.php';
  mysql_connect($host,$user,$password) or die('Erreur le connexion au SGBD.');
  mysql_select_db($base) or die('La base de données n\'existe pas');
  $query='SELECT * FROM employe ORDER BY SALAIRE DESC';
  $r=mysql_query($query);
  mysql_close();
  echo'<table><tr><td>NOM</td><td>PRENOM</td><td>SALAIRE</td></tr>';
  while ($a=mysql_fetch_object($r)) {
    $nom=$a->NOM;
    $prenom=$a->PRENOM;
    $salaire=$a->SALAIRE;
    echo"<tr><td>$nom</td><td>$prenom</td><td>$salaire</td></tr>";
  }
  echo '</table>';
  ?>
<br/>
<br/>
<a href="index.html">Retour à la page d'accueil</a>
    
  </body>
</html>

Les pages du site (version 2) modifier

Lorsqu'on étudie les fichiers liste1.php, liste2.php et liste3.php, on s'aperçoit qu'ils sont extrêmement proches : on va donc réécrire l'application en écrivant un seul fichier liste.php et en le paramétrant grâce à la méthode GET.

Le nouveau fichier index.html devient donc :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Afficher le résultat d'un requête</title>
  </head>
  <body>

<h1> Choisissez dans le menu ci-dessous</h1>

<a href="liste.php?l=1">Afficher la liste des employés</a><br/>
<a href="liste.php?l=2">Afficher la liste des employés par ordre alphabétique</a><br/>
<a href="liste.php?l=3">Afficher la liste des employés par salaire décroissant</a><br/>
    
  </body>
</html>

On constate donc que les liens hypertextes, par exemple liste.php?l=1 appelle le même fichier liste.php en lui donnant un paramètre par la méthode GET, ici l=1, ce qui permet de paramétrer le fichier.

Remarque importante : lorsqu'on utilise ce genre de liens hypertextes, il faut toujours se poser la question : que se passe-t-il si l'utilisateur accède à la page liste.php sans passer de paramètres ou alors en écrivant liste.php?l=4 ? Le programmeur doit garantir que dans un tel cas, le site reste cohérent et que ceci n'engendre pas de failles de sécurité.


Le fichier liste.php devient :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Afficher la liste des employés</title>
  </head>
  <body>
<h1>Afficher la liste des employés</h1>
  <?php
  require 'params.php';
  mysql_connect($host,$user,$password) or die('Erreur le connexion au SGBD.');
  mysql_select_db($base) or die('La base de données n\'existe pas');

  if(isset($_GET['l']))
	 $l=$_GET['l'];
	else $l=1;

         if($l==1)$query='SELECT * FROM employe';
    else if($l==2)$query='SELECT * FROM employe ORDER BY NOM, PRENOM';
    else $query='SELECT * FROM employe ORDER BY SALAIRE DESC';

  $r=mysql_query($query);
  mysql_close();
  echo'<table><tr><td>NOM</td><td>PRENOM</td><td>SALAIRE</td></tr>';
  while ($a=mysql_fetch_object($r)) {
    $nom=$a->NOM;
    $prenom=$a->PRENOM;
    $salaire=$a->SALAIRE;
    echo"<tr><td>$nom</td><td>$prenom</td><td>$salaire</td></tr>";
  }
  echo '</table>';
  ?>
<br/>
<br/>
<a href="index.html">Retour à la page d'accueil</a>
    
  </body>
</html>

Le programmeur teste en utilisant isset($_GET['l']) pour savoir si la variable l a été passée par la méthode GET et il récupère sa valeur dans la variable $l. Si ce paramètre n'existe pas, $l est fixé à la valeur 1. On construit ensuite la requête $query en fonction de la valeur de $l. Cette version du programme est plus simple mais îil faut toutefois faire très attention à la sécurité dans un tel cas.


Exemples/BD 2

Modifier une table modifier

Les fichiers de l'application modifier

Le fichier BD.sql modifier

CREATE TABLE `employe` (
	`ID` BIGINT NOT NULL AUTO_INCREMENT ,
	`NOM` VARCHAR( 20 ) ,
	`PRENOM` VARCHAR( 20 ) ,
	`SALAIRE` DOUBLE DEFAULT '0',
	PRIMARY KEY ( `ID` ) 
); 
INSERT INTO `employe` ( `ID` , `NOM` , `PRENOM` , `SALAIRE` ) 
	VALUES (
	'', 'Dupond', 'Marcel', '8000'
	);
INSERT INTO `employe` ( `ID` , `NOM` , `PRENOM` , `SALAIRE` ) 
	VALUES (
	'', 'Martin', 'Xavier', '4000'
	);
INSERT INTO `employe` ( `ID` , `NOM` , `PRENOM` , `SALAIRE` ) 
	VALUES (
	'', 'Gogol', 'Henri', '3000'
	);
INSERT INTO `employe` ( `ID` , `NOM` , `PRENOM` , `SALAIRE` ) 
	VALUES (
	'', 'Hugo', 'Victor', '2000'
	);
INSERT INTO `employe` ( `ID` , `NOM` , `PRENOM` , `SALAIRE` ) 
	VALUES (
	'', 'Gali', 'Daniel', '6000'
	);
INSERT INTO `employe` ( `ID` , `NOM` , `PRENOM` , `SALAIRE` ) 
	VALUES (
	'', 'Martin', 'Georges', '9000'
	);

Le fichier index.php modifier

<?php
session_start();
require_once'Appli/appli.php';
?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Afficher le résultat d'un requête</title>
  </head>
  <body>
<h1> Choisissez dans le menu ci-dessous</h1>
<a href="liste.php?l=1">Afficher la liste des employés</a><br/>
<a href="liste.php?l=2">Afficher la liste des employés par ordre alphabétique</a><br/>
<a href="liste.php?l=3">Afficher la liste des employés par salaire décroissant</a><br/>
<a href="add.php">Ajouter un employé</a><br/>
<a href="delete.php">Supprimer un employé</a><br/>
<?php

if(is_admin())echo '<a href="disconnect.php">Se déconnecter</a><br/>';
?>
  </body>
</html>

Le fichier liste.php modifier

<?php
session_start();
require_once'Appli/appli.php';
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Afficher la liste des employés</title>
  </head>
  <body>
<h1>Afficher la liste des employés</h1>
  <?php
    if(isset($_GET['l'])) {
        $l=$_GET['l'];
    } else {
        $l=1;
    }
    liste($l,false);
  ?>
<br/>
<br/>
<a href="index.php">Retour à la page d'accueil</a>
    
  </body>
</html>

Le fichier add.php modifier

<?php
session_start();
require_once'Appli/appli.php';

$_SESSION['add_NOM']='';
$_SESSION['add_PRENOM']='';
$_SESSION['add_SALAIRE']='';
$_SESSION['add_ERROR']=0;

if (is_admin()) {
    require 'Appli/addForm.php';
} else {
      $_SESSION['connect_target']='Appli/addForm.php';
      $_SESSION['connect_error']=0;
      $_SESSION['connect_login']='';
      require 'Appli/connectForm.php';
}
?>

Le fichier addAction.php modifier

<?php
session_start();
require_once'Appli/appli.php';
if (is_admin()) {
    if(isset($_POST['add'])) {
        $nom=$_POST['nom'];
        $prenom=$_POST['prenom'];
        $salaire=$_POST['salaire'];
        $r=add_liste($nom,$prenom,$salaire);
        if ($r==0) {
            echo '<meta http-equiv="Refresh" content="0;URL=index.php">';
	} else {
            $_SESSION['add_NOM']=$nom;
            $_SESSION['add_PRENOM']=$prenom;
            $_SESSION['add_SALAIRE']=$salaire;
            $_SESSION['add_ERROR']=$r;
            require 'Appli/addForm.php';
        }
    } else {
        echo '<meta http-equiv="Refresh" content="0;URL=index.php">';
    }
}
?>

Le fichier connect.php modifier

<?php
session_start();
require_once'Appli/appli.php';

if (isset($_POST['connect'])) {
    if (isset($_POST['login'])) {
        $login = $_POST['login'];
    } else {
        $login = '';
    }
    if (isset($_POST['password'])) {
        $password = $_POST['password'];
    } else {
        $password = '';
    }
}
if (connect($login, $password)) {
    $target=$_SESSION['connect_target'];
} else {
    $target = 'Appli/connectForm.php';
    $_SESSION['connect_login'] = $login;
    $_SESSION['connect_error'] = 1;
} else {
    $target='index.php';
}
require $target;
?>

Le fichier disconnect.php modifier

<?php
session_start();
require_once'Appli/appli.php';
disconnect();
?>
<meta http-equiv="Refresh" content="0;URL=index.php">

Le fichier delete.php modifier

<?php
session_start();
require_once'Appli/appli.php';

if (is_admin()) {
    require 'Appli/deleteForm.php';
} else {
    $_SESSION['connect_target']='Appli/deleteForm.php';
    $_SESSION['connect_error']=0;
    $_SESSION['connect_login']='';
    require 'Appli/connectForm.php';
}
?>

Le fichier deleteAction.php modifier

<?php
session_start();
require_once'Appli/appli.php';
if (is_admin()) {
    $ID = $_GET['ID'];
    delete($ID);
    require 'Appli/deleteForm.php';
} else {
    echo '<meta http-equiv="Refresh" content="0;URL=index.php">';
}
?>

Le fichier Appli/.htaccess modifier

deny from all

Le fichier Appli/params.php modifier

<?php
$host = 'localhost';
$user = 'root';
$password = '';
$base = 'toto';

$admin_login = 'admin';
$admin_password = 'aligator';
?>

Le fichier Appli/appli.php modifier

<?php
function is_admin()
{
return isset($_SESSION['admin']) and ($_SESSION['admin']==true);
}

function connect($login,$user_password)
{
$r=false;
require 'params.php';
if ($login==$admin_login and $user_password==$admin_password) {
    $r=true;$_SESSION['admin']=true;}
    return $r;
}

function disconnect()
{
    $_SESSION['admin'] = false;
}

function liste($l,$editable)
{
    require 'params.php';
    mysql_connect($host,$user,$password) or die('Erreur le connexion au SGBD.');
    mysql_select_db($base) or die('La base de données n\'existe pas');
    if ($l==1) {
        $query='SELECT * FROM employe';
    } else if ($l==2) {
        $query = 'SELECT * FROM employe ORDER BY NOM, PRENOM';
    } else {
        $query = 'SELECT * FROM employe ORDER BY SALAIRE DESC';
    }
$r=mysql_query($query);
mysql_close();
if (editable==false) {
    echo'<table><tr><td>NOM</td><td>PRENOM</td><td>SALAIRE</td></tr>';
} else {
    echo'<table><tr><td>NOM</td><td>PRENOM</td><td>SALAIRE</td><td>&nbsp;</td></tr>';
}
while ($a=mysql_fetch_object($r)) {
    $nom=$a->NOM;
    $prenom=$a->PRENOM;
    $salaire=$a->SALAIRE;
    $ID=$a->ID;
if ($editable==false) {
    echo"<tr><td>$nom</td><td>$prenom</td><td>$salaire</td></tr>";
} else {
    echo"<tr><td>$nom</td><td>$prenom</td><td>$salaire</td><td><a href=\"deleteAction.php?ID=$ID\">SUPPRIMER</a></td></tr>";
}
echo '</table>';
}

function add_liste($nom,$prenom,$salaire)
{
    $r = 0;
    if ($nom == '') {
        $r = 1;
    } else if ($prenom == '') {
        $r = 2;
    } else if ($salaire == '') {
        $r = 3;
    } else {
        require 'params.php';
        mysql_connect($host,$user,$password) or die('Erreur le connexion au SGBD.');
        mysql_select_db($base) or die('La base de données n\'existe pas');
        $query="INSERT INTO employe (NOM, PRENOM, SALAIRE) VALUES ('$nom', '$prenom', '$salaire')";
        mysql_query($query);
        mysql_close();
    }
    return $r;
}

function delete($ID)
{
    require 'params.php';
    mysql_connect($host,$user,$password) or die('Erreur le connexion au SGBD.');
    mysql_select_db($base) or die('La base de données n\'existe pas');
    $query="DELETE FROM employe WHERE ID=$ID";
    mysql_query($query);
    mysql_close();
}
?>

Le fichier Appli/connectForm.php modifier

<?php
session_start();
require_once'Appli/appli.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Connexion</title>
  </head>
  <body>
<h1> Connexion</h1>
<form method="post" action="connect.php">
<table>
<?php
$value=$_SESSION['connect_login'];
echo "<tr><td><b>LOGIN</b></td> <td><input type=\"text\" name=\"login\" value=\"$value\"/></td></tr>";
?>
<tr><td><b>MOT DE PASSE</b></td> <td><input type="password" name="password"/></td></tr>
<tr><td colspan="2"><input type="submit" value="Se connecter" name="connect"><input type="submit" value="Annuler" name="cancel"></td></tr>
</table>
</form>
<br/>
<?php
$error= $_SESSION['connect_error'];
if($error==1)echo'Erreur de connexion';
$_SESSION['connect_error']=0;
?>
   <p/>
<a href="index.php">Retour</a>
  </body>
</html>

Le fichier Appli/addForm.php modifier

<?php
session_start();
require_once'Appli/appli.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Ajouter</title>
  </head>
  <body>
<h1>Ajouter un employé</h1>
<form method="post" action="addAction.php">
<table>
<?php
$nom=$_SESSION['add_NOM'];
$prenom=$_SESSION['add_PRENOM'];
$salaire=$_SESSION['add_SALAIRE'];
echo '<tr><td><b>NOM</b></td> <td><input type="text" name="nom" value="'.$nom.'"/></td></tr>';
echo '<tr><td><b>PRENOM</b></td> <td><input type="text" name="prenom" value="'.$prenom.'"/></td></tr>';
echo '<tr><td><b>SALAIRE</b></td> <td><input type="text" name="salaire" value="'.$salaire.'"/></td></tr>';
?>
<tr><td colspan="2"><input type="submit" value="Ajouter" name="add"><input type="submit" value="Annuler" name="cancel"></td></tr>
</table>
</form>
<br/>
<?php
$error=$_SESSION['add_ERROR'];
if ($error==1) echo'ERREUR : le nom est vide';
if ($error==2) echo'ERREUR : le prénom est vide';
if ($error==3) echo'ERREUR : le salaire est vide';
?>
   <p/>
<a href="index.php">Retour</a>
  </body>
</html>

Le fichier Appli/deleteForm.php modifier

<?php
session_start();
require_once'Appli/appli.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Suppression</title>
  </head>
  <body>
<h1> Suppression</h1>
<?php
liste(2,true);
?>
   <p/>
<a href="index.php">Retour</a>
  </body>
</html>


Exemples/Livre

Créer le livre modifier

Tout d'abord, il faut créer le livre d'or, c'est-à-dire une table où seront stockés les messages.

Voici un exemple de table, avec un identifiant auto-incrémenté qui sera la clé (pour pouvoir identifier de manière unique chaque message), puis un champ pour le nom, courriel, date, ... et bien sûr le message.

CREATE TABLE `livre` (
	`id` SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
	`nom` VARCHAR( 128 ),
	`email` VARCHAR( 128 ),
	`date` DATE,
	`message` TEXT NOT NULL,
	PRIMARY KEY( `id` )
);

Ajouter un message modifier

Voici la requête SQL qui permet d'insérer les données dans la table:

INSERT INTO `livre` ( `nom` , `email` , `date` , `message` )
 VALUES ( `UnNom` , `Une@` ,  `xx/xx/xxxx` , `blalblablabla...` );

Saisie des données modifier

Voici un exemple de formulaire servant à récupérer les données saisies par l'utilisateur pour les faire suivre vers 'ajout_message.php' qui les traitera. La méthode utilisée est POST, bien que GET marche aussi, mais POST possède l'avantage de ne pas rendre visible dans l'URL les paramètres passés, ce qui rend cette méthode plus « propre » et plus claire.

fichier formulaire.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Formulaire</title>
</head>

<body>
<form name="formulaire" method="post" action="ajout_message.php">
<label for="nom">Nom :</label><input type="text" id="nom" name="nom" /><br />
<label for="email">Courriel :</label><input type="text" id="email" name="email" /><br />
<label for="message">Votre message :</label><br />
<textarea id="message" name="message" cols="50" rows="10">Message par défaut</textarea><br />
<input type="submit" />
</form>
</body>
</html>

Traitement des données modifier

Le fichier « ajout_message.php » va récupérer et traiter les données saisies par l'utilisateur puis effectuer une insertion dans la base (c'est-à-dire ajouter le message dans le livre d'or). Ces données portent le nom qui leur a été donné dans le formulaire, précédé d'un '$' (représente une variable en PHP). Il est fortement conseillé d'effectuer des tests de saisie à ce niveau, pour vérifier que l'utilisateur a rempli correctement les champs (champs non vides, adresse email correcte,...).

fichier params.php

<?php
$host='localhost';
$user='root';
$password='';
$base='toto';
?>

fichier ajout_message.php

<?php
isset($_POST['nom']) or die('Pas de paramètres en entrée');

//récupération des données entrées par l'utilisateur
$nom = $_POST['nom'];
$message = $_POST['message'];
$email= $_POST['email'];

//vérification des données entrées par l'utilisateur
if (($nom == "")||($message == ""))
        die("Paramètres incorrects.
        <a href='formulaire.html'>Cliquez ici pour revenir au formulaire</a>".mysql_error());
        
//connexion à la base
require 'params.php';
$link=mysql_connect($host,$user,$password) or die('Erreur de connexion au SGBD.');
mysql_select_db($base,$link) or die('La base de données n\'existe pas');

//insertion du message

$date=date("Y-m-d"); // on récupère la date
$message = htmlspecialchars($message,ENT_QUOTES); //convertit les caractères spéciaux
$sql="INSERT INTO `livre` ( `id` , `nom` , `email` , `date` , `message` ) VALUES ( NULL , '$nom', '$email', '$date', '$message' );";
$res=mysql_query($sql,$link);
mysql_close($link);
if ($res==NULL) die('Erreur lors de l\'écriture du message'.mysql_error());

print "Message correctement ajouté.<br />Merci d'avoir pris le temps de remplir ce livre d'or";
?>

La fonction htmlspecialchar() permet de convertir les caractères spéciaux qui pourrait être interprétés comme du code HTML par un navigateur. Il existe d'autres fonctions, comme htmlentities(), qui permet en plus de spécifier les caractères de remplacement, ou n12br() qui permet de remplacer les retours chariots par le caractère HTML de retour à la ligne (br).

Récupérer les messages et les faire afficher modifier

Cette partie est beaucoup plus simple à réaliser que la partie précédente.


Voici la requête SQL qui permet de récupérer toutes les informations de la table dans la base de donées:

SELECT * FROM `livre` ORDER BY `id`

Les messages seront alors triés en fonction du champ `id`, et, par défaut, par ordre croissant, donc plus du plus ancien au plus récent. Pour les classer dans l'ordre décroissant, il faut rajouter l'instruction DESC, ce qui donne la requête suivante :

SELECT * FROM `livre` ORDER BY `id` DESC

Après, il suffit d'afficher les informations obtenues grâce à la requête.

fichier affiche_message.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Livre d'or</title>
</head>

<body>
<?php
//connexion à la base
require 'params.php';
$link=mysql_connect($host,$user,$password) or die('Erreur de connexion au SGBD.');
mysql_select_db($base,$link) or die('La base de données n\'existe pas');

//récupération des messages
$sql="SELECT * FROM `livre` ORDER BY `id` DESC";
$rep=mysql_query($sql,$link);
mysql_close($link);
while ( $ligne = mysql_fetch_array($rep))
{
	//on récupère les valeurs des diffèrent champs pour chaque message
	$nom=$ligne['nom'];
	$email=$ligne['email'];
	$date=$ligne['date'];
	$message=$ligne['message'];
	/* on remplace les retours chariots '\n'
	 par le symbole de retour à la ligne en HTML : <br /> */
	$message=nl2br($message);
	
	//on affiche ses valeurs dans un tableau
	print "<table>
	<tr><th>Par :<a href=mailto:$email>$nom</a>/th>
	<th>Le : $date</th></tr>
	<tr><td colspan='2'>$message</td></tr>
	</table>";

}

?>
</body>
</html>

Ceci n'est que le strict minimum pour faire un livre d'or. D'autres fonctionnalités, comme le nombre d'affichage par page, peuvent être ajoutées.


Exemples/MiniForum

Faire un mini forum de discussion modifier

Bon nombre de forums sur internet ont repris des opensources et les ont customisé. Dans cet article, nous verrons comment simplement opposer de meilleures solutions, plus souples et personnelles. Ce chapitre traitera du programme à fournir pour cette solution sans proposer de sécurisation.

Introduction modifier

Voici un forum modulaire, universel, illimité, fonctionnel et très réactif mais à sécuriser avec MySQL en backend et HTML en frontend.

Architecture modifier

À bâtir en MVC, le forum est segmenté comme :

./index.php
./config.inc.php

// STATICAL MODULES

./Objects/
./Objects/frameset.inc.php // main frameset
./Objects/mainPage.inc.php // main pages content

// FUNCTIONNAL MODULES

./Libraries/

Déploiement modifier

La méthodologie processus unifié fonctionne par itérations successives et augmentation, amélioration du code. On obtient donc pour chaque module différentes itérations représentatives de l'état d'avancement de l'application. L'état 0 ou incrément 0 étant l'ébauche et incrément 1, la première itération de l'applicatif.

 Fichier : config.inc.php
<?php
/*  */

define('OBJ', './Objects/');
define('LIB', './Libraries/');

$cnx = array('host'=>'localhost','user'=>'root','db'=>'miniForum','pass'=>'');

?>

increment 0 : index.php

 Fichier : index.php
<?php
/* Mini Forum - main entry 
	
	This is a mini forum 
	
	version : 	1.0
	date : 		2009 07 28
	author : 	zulul
	
*/

require_once "./config.inc.php";
require_once OBJ . "frameset.inc.php";

echo $_mainSet;

?>

increment 1 : index.php

 Fichier : index.php
<?php
/* Mini Forum - main entry 
	
	This is a mini forum 
	
	version : 	1.0
	date : 		2009 07 28
	author : 	zulul
	
*/

@session_start();

require_once "./config.inc.php";
require_once OBJ . "frameset.inc.php";

// lib
require_once LIB . "Connectivity.cla.php";
require_once LIB . "Request.cla.php";
require_once LIB . "utils.fnc.php";

// autho
if(chk_usr($cnx))
	$_SESSION['autho'] = 1;
	
echo $_mainSet;

?>


increment 2 : index.php

 Fichier : index.php
<?php
/* Mini Forum - main entry 
	
	This is a mini forum 
	
	version : 	1.0
	date : 		2009 07 28
	author : 	zulul
	
*/

@session_start();

require_once "./config.inc.php";

// lib
require_once LIB . "Connectivity.cla.php";
require_once LIB . "Request.cla.php";
require_once LIB . "utils.fnc.php";

// page listener
require_once OBJ . "listener.inc.php";

// autho
if(chk_usr($cnx))
{
	$_SESSION['autho'] = 1;
}

// controling application
require_once OBJ . "controler.inc.php";
// variing frameset call
require_once OBJ . "frameset.inc.php";


# OUTPUT
echo $_mainSet;

?>

Objects modifier

 Fichier : frameset.inc.php
 
<?php
/* main page 

*/

require_once OBJ."mainPage.inc.php";

$_mainSet = <<<EOPAGE
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//FR" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html lang="fr" xml:lang="fr">
 <head>
	{$_content['title']}
	{$_content['meta']}
	{$_content['style']}
 </head>
 <body>
	{$_content['main']}
 </body>
</html>
EOPAGE;
?>

increment 3 : frameset.inc.php

 Fichier : frameset.inc.php
 
 <head>
	{$_content['title']}
	{$_content['meta']}
	{$_content['script']}
	{$_content['style']}
 </head>

increment 0 : mainPage.inc.php

 Fichier : mainPage.inc.php
 
<?php
/* Main page content 

*/

$title 		= ':: TITLE ::';
$meta		= '';
$style		= '';


// head content
$_content['title']	= '<title>'.$title.'</title>';
$_content['meta']	= '<meta>'.$meta.'</meta>';
$_content['style']	= '<style>

	body
	{
		margin:0 0 0 0;
		font-family:tahoma;
		font-size:10px;
	}
	
	table td
	{
		border:1px solid;
		padding:5px;
		margin:5px;
	}

	.grey
	{
		background-color:#eee;
	}
	
	.creeper
	{
		border:1px solid;
		height:200px;
	}
	
</style>';

// content contents
$_content['authorize'] = '
	<div id="cAuthorize">
		
		<table align="center" width="100%">
			<tr><td class="grey">pseudo <input type="text" value=""/></td></tr>
			<tr><td class="grey">passwd <input type="text" value=""/></td></tr>
			<tr><td class="grey"><input type="button" value="submit()"/></td></tr>
		</table>
	</div>
	
';

// content contents
$_content['skyCreeper'] = '
	<div>
		<div class="creeper">
		<br/>this is the content
		<br/>this is thec content
		<br/>this is the content	
		</div>
	</div>';

$_content['pager'] = <<<EOPAGE
	<div id="cAuthorize">
		
		<table width="100%">
			<tr valign="top"><td width="50%" height="250" class="grey">:: 1 ::</td><td class="grey">:: 2 ::</td></tr>
			<tr valign="top"><td width="50%" height="250" class="grey">:: 3 ::</td><td class="grey">:: 4 ::</td></tr>
		</table>
		
	</div>
EOPAGE;

// body contents
$_content['main']	= <<<EOPAGE
	<table width="99%" height="600">
		
		<!-- its still better to stay with table instead of divs -->
		<tr>
			<td height="18" colspan="2" width="99%" align="center" class="grey">
				:: THIS IS TOP CONTENT ::			
			</td>
		</tr>
		<tr valign="top">
			<td width="20%">
				{$_content['authorize']}
				{$_content['skyCreeper']}
			</td>
			<td width="79%">
				{$_content['pager']}
			</td>
		</tr>
		<tr>
			<td height="18" colspan="2" width="99%" align="center" class="grey">:: THIS IS BOTTOM CONTENT ::</td>
		</tr>
	</table>
EOPAGE;

?>

increment 1 : listener.inc.php

 Fichier : listener.inc.php
 
<?php
/* application listener

*/

/* flag cases

*/
switch(true)
{
	case(@$_REQUEST['aut']=='0'):
		//action disconnect
		unset($_SESSION['autho'],$_SESSION['userMod']);
		break;
	
	/*
	case ():
		break;
	*/
}

?>


increment 1 : controler.inc.php

 Fichier : controler.inc.php
 
<?php
/* application controler

*/

# INIT

/* admin and mod status control 

*/
if(@$_REQUEST['aut']!='0')
{
	// moderating user
	mod();
}

# PREPARE

/* cases

*/
if(@$_SESSION['userMod'])
{//
	
	if(preg_match('/[0]/',$_SESSION['userMod']['adm']))
		$_mod[0] = 'superAdmin';
		
	if(preg_match('/[1]/',$_SESSION['userMod']['adm']))
		$_mod[0] = 'Admin';
		
	if(preg_match('/[0]/',$_SESSION['userMod']['mod']))
		$_mod[1] = 'superModerator';
		
	if(preg_match('/[1]/',$_SESSION['userMod']['mod']))
		$_mod[1] = 'Moderator';
}
?>

increment 1 : mainPage.inc.php

À cette itération, l'authentification du user est fonctionnelle.
Le snippet content['authorize'] change de mode.
 Fichier : mainPage.inc.php
... 
// content contents
$_content['authorize'] = (!@$_SESSION['autho'])?'
	<div id="cAuthorize">
		<form action="" method="post" name="form">
		<table align="center" width="100%">
			<tr><td colspan="2" class="grey">pseudo <input name="login" type="text" value=""/></td></tr>
			<tr><td colspan="2" class="grey">passwd <input name="pass" type="text" value=""/></td></tr>
			<tr><td class="grey" align="left"><input type="submit" value="submit" /> </td>
			<td align="right"><a href="">forgot password</a><br/><a href="">new registration</a></td></tr>
		</table>
		</form>
	</div>
':'logged';
...

increment 2 : mainPage.inc.php

À cette itération, l'attribution des privilèges du user est fonctionnelle.
Le snippet content['authorize'] affiche le statut user mode.
 Fichier : mainPage.inc.php
 
// content contents
$_content['authorize'] = (!@$_SESSION['autho'])?'
	<div id="cAuthorize">
		<form action="'.$_SERVER['PHP_SELF'].'" method="post" name="form">
		<table align="center" width="100%">
			<tr><td colspan="2" class="grey">pseudo <input name="login" type="text" value=""/></td></tr>
			<tr><td colspan="2" class="grey">passwd <input name="pass" type="text" value=""/></td></tr>
			<tr><td class="grey" align="left"><input type="submit" value="submit" /> </td>
			<td align="right"><a href="">forgot password</a><br/><a href="">new registration</a></td></tr>
		</table>
		</form>
	</div>
':'<div id="cAuthorize"><table align="center" width="100%"><tr><td>logged | 
	<a href="'.$_SERVER['PHP_SELF'].'?aut=0">disconnect</a><br/><br/>Status : '.@$_mod[0] . " " . @$_mod[1] .'</td></tr></table></div>';

// content contents
$_content['containers'] = '
	<table width="100%"><tr><td>:: PARTNER 1 ::</td></tr></table>
	<table width="100%"><tr><td>:: PARTNER 2 ::</td></tr></table>
';

increment 3 : jsScript.inc.php

intégration des javascripts
 Fichier : mainPage.inc.php
<?php

	$script = '
		/* javascript scripts
		
		*/
		
		function showHide(obj)
		{//	>(element)
			
			if(obj.style.display!="none")
				obj.style.display="none";
			else
				obj.style.display="";
		}
	
	';

?>

increment 3 : pages.inc.php

contenus des pages
 Fichier : pages.inc.php
<?php
/* pager contents

*/

// mod 1
if(@$_REQUEST['mod'] == '1' && @$_SESSION['autho']) //
{
	
	// list rubriq
	$_res=Request::SQL($cnx,'SELECT * FROM topics ORDER BY id');
	foreach($_res as $_tmp)
	{
		@$str .= '<br/><form method="post" action="' . $_SERVER['PHP_SELF'] . '?mod=1" name="f3"><input name="iDel" type="hidden" value="' . $_tmp['id'] . '" /><input type="submit" value="[-]"/> <a href="">' . $_tmp['title'] . '</a></form>';
	}
	
	$_content['pager']  = '
	<div class="title">Rubriques existantes | <span class="toAct" onclick="showHide(getElementById(\'cRubriq\'));">Ajouter une rubrique</span><br/>
		<!-- Rubriq adder -->
		<table width="100%">
			<tr valign="top">
				<td>
					<div id="cRubriq">Ajouter une nouvelle rubrique
						<form name="f2" method="post" action="' . $_SERVER['PHP_SELF'] . '?mod=1">
							<p><input name="iRubriq" type="text" size="50" /><input type="submit" value="add"/></p>
						</form>
					</div>
				</td>
			</tr>
			<tr valign="top" height="500"><td><p>' . @$str . '</p></td></tr>
		</table>
	</div>
	';
	
}

// rub 1
if(@$_REQUEST['rub'] == '1') //
{
	
	if(!@$_REQUEST['tId'])
	{
		// list rubriq
		$_res=Request::SQL($cnx,'SELECT * FROM topics ORDER BY id');
		foreach($_res as $_tmp)
		{
			@$str .= '<br/> <a href="'.$_SERVER['PHP_SELF'].'?rub=1&tId=' . $_tmp['id'] . '">' . $_tmp['title'] . '</a>';
		}
		
		$_content['pager']  = '
		<div class="title">Rubriques existantes
			<table width="100%">
				<tr valign="top" height="500"><td><p>' . @$str . '</p></td></tr>
			</table>
		</div>
		';
		
	} else {
		
		// list rubriq
		$_res=Request::SQL($cnx,'SELECT * FROM contents WHERE topicsId=' . @$_REQUEST['tId'] . ' ORDER BY id');
		foreach($_res as $_tmp)
		{
			@$str .= '<br/> <a href="' . $_SERVER['PHP_SELF'] . '?rub=1&tId=' . $_tmp['topicsId'] . '">' . $_tmp['subject'] . '</a>';
		}
		
		$_content['pager']  = '
		<div class="title">Topics existants
			<table width="100%">
			<!-- Topics adder -->
				<tr valign="top">
					<td>
						<div id="cTopics">Ajouter un nouveau sujet
							<form name="f4" method="post" action="' . $_SERVER['PHP_SELF'] . '?rub=1">
								<p><input name="iTopics" type="text" size="50" /><input type="submit" value="add" /></p>
							</form>
						</div>
					</td>
				</tr>
				<tr valign="top" height="500"><td><p>' . @$str . '</p></td></tr>
			</table>
		</div>
		';
		
	}
}
?>


increment 4 : pages.inc.php

contenus des pages complétés
 Fichier : pages.inc.php
<?php
/* pager contents

*/

// mod 1
if(@$_REQUEST['mod'] == '1' && @$_SESSION['autho']) //
{
	
	// list rubriq
	$_res=Request::SQL($cnx,'SELECT * FROM topics ORDER BY id');
	foreach($_res as $_tmp)
	{
		@$str .= '<br/><form method="post" action="' . $_SERVER['PHP_SELF'] . '?mod=1" name="f3"><input name="iDel" type="hidden" value="' . $_tmp['id'] . '" /><input type="submit" value="[-]"/> <a href="">' . $_tmp['title'] . '</a></form>';
	}
	
	$_content['pager']  = '
	<div class="title">Rubriques existantes | <span class="toAct" onclick="showHide(getElementById(\'cRubriq\'));">Ajouter une rubrique</span><br/>
		<!-- Rubriq adder -->
		<table width="100%">
			<tr valign="top">
				<td>
					<div id="cRubriq">Ajouter une nouvelle rubrique
						<form name="f2" method="post" action="' . $_SERVER['PHP_SELF'] . '?mod=1">
							<p><input name="iRubriq" type="text" size="50" /><input type="submit" value="add"/></p>
						</form>
					</div>
				</td>
			</tr>
			<tr valign="top" height="500"><td><p>' . @$str . '</p></td></tr>
		</table>
	</div>
	';
	
}

// rub 1
if(@$_REQUEST['rub'] == '1') //
{
	if(!@$_REQUEST['tId'])
	{
		// list rubriq
		$_res=Request::SQL($cnx,'SELECT * FROM topics ORDER BY id');
		foreach($_res as $_tmp)
		{
			@$str .= '<br/> <a href="'.$_SERVER['PHP_SELF'].'?rub=1&tId=' . $_tmp['id'] . '">' . $_tmp['title'] . '</a>';
		}
		
		$_content['pager']  = '
		<div class="title">Rubriques existantes
			<table width="100%">
				<tr valign="top" height="500"><td><p>' . @$str . '</p></td></tr>
			</table>
		</div>
		';
		
	} else {
		
		// list rubriq
		$_res=Request::SQL($cnx,'SELECT * FROM contents WHERE topicsId="' . @$_REQUEST['tId'] . '" ORDER by id');
		foreach($_res as $_tmp)
		{
			@$str .= '<br/> <a href="' . $_SERVER['PHP_SELF'] . '?aId='.$_tmp['id'].'&art=1&tId=' . $_tmp['topicsId'] . '">' . $_tmp['subject'] . '</a>';
		}
		
		$_content['pager']  = '
		<div class="title">Topics existants
			<table width="100%">
			<!-- Topics adder -->
				<tr valign="top">
					<td>
						<div id="cTopics">Ajouter un nouveau sujet
							<form name="f4" method="post" action="' . $_SERVER['PHP_SELF'] . '?rub=1">
								<p>
								<input name="tId" type="hidden" value="'.$_REQUEST['tId'].'" />
								<input name="iTopics" type="text" size="50" /><input type="submit" value="add" /></p>
							</form>
						</div>
					</td>
				</tr>
				<tr valign="top" height="500"><td><p>' . @$str . '</p></td></tr>
			</table>
		</div>
		';
	}
}

if(@$_REQUEST['art']=='1' && @$_REQUEST['tId'])
{
		// list article
		$_res=Request::SQL($cnx,'SELECT * FROM contents WHERE topicsId= ' .@$_REQUEST['tId'] . ' ORDER BY id');
		foreach($_res as $_tmp)
		{
			@$str .= '<table width="100%">
				<tr><td width="10%" rowspan="2">:: IMAGE ::<br/>:: usrId ' . $_tmp['usrId'] . ' ::</td>
					<td width="90%"><a href="'.$_SERVER['PHP_SELF'].'?aId='.$_tmp['id'].'&art=1&tId=' . $_tmp['topicsId'] . '">' . $_tmp['subject'] . '</a></td>
				</tr>
				<tr><td width="90%">' . $_tmp['content'] . '</td></tr>
				';
		}
		
		$rec = getRecordNfo($cnx,'contents', @$_REQUEST['aId']);
		$_content['pager']  = @$str . '
		<div class="article">
			<table width="100%">
				<tr valign="top">
					<td>
						<div id="cArticle"><b>Introduisez votre commentaire : </b><br/><br/>
							<form name="f5" method="post" action="' . @$_SERVER['PHP_SELF'] . '?art=1&tId=' . @$_REQUEST['tId'] . '>
								<p>
									<input name="tId" type="hidden" value="' . @$_REQUEST['tId'] . '" />
									<input name="aId" type="hidden" value="' . @$_REQUEST['aId'] . '" />
									<input name="iSubject" type="text" value="' . (@$rec['subject']) . '" />
									<textarea name="iContent" rows="5" style="width:95%">' . (@$res['content']) . '</textarea>
									<input type="submit" value="add" />
								</p>
							</form>
						</div>
					</td>
				</tr>
			</table>
		</div>
		';
}

?>

increment 4 : controler.inc.php

controler complété
 Fichier : controler.inc.php
<?php
/* application controler

*/

# INIT

/* admin and mod status control 

*/
if(@$_REQUEST['aut']!='0')
{
	// moderating user
	mod();
}

# PREPARE

/* cases

*/
if(@$_SESSION['userMod'])
{//
	
	if(preg_match('/[0]/',$_SESSION['userMod']['adm']))
		$_mod[0] = 'superAdmin';
		
	if(preg_match('/[1]/',$_SESSION['userMod']['adm']))
		$_mod[0] = 'Admin';
		
	if(preg_match('/[0]/',$_SESSION['userMod']['mod']))
		$_mod[1] = 'superModerator';
		
	if(preg_match('/[1]/',$_SESSION['userMod']['mod']))
		$_mod[1] = 'Moderator';
}

if(@$_REQUEST['mod']=='1')
{// add rubriq
	
	if(@$_REQUEST['iRubriq'])
		Request::SQL($cnx,'INSERT INTO topics (title) values ("' . $_POST['iRubriq'] . '");');
	
	if(@$_REQUEST['iDel'])
	{
		Request::SQL($cnx,'DELETE FROM topics WHERE id='.$_REQUEST['iDel'].';');
	}
}

if(@$_REQUEST['rub']=='1' && @$_REQUEST['tId'])
{
	if(@$_REQUEST['iTopics'])
	{
		Request::SQL($cnx,'INSERT INTO contents (topicsId,subject,usrId) values (' . $_REQUEST['tId'] . ', "' . $_REQUEST['iTopics'] . '",' . $_SESSION['userId'] . ')');
	}
}

if(@$_REQUEST['art']=='1')
{
	if(@$_REQUEST['iContent'])
	{
		Request::SQL($cnx,'UPDATE contents SET content="' . $_REQUEST['iContent'] . '" WHERE id=' . $_REQUEST['aId']);
	}
}

?>
À ce stade on a une ébauche de layout pour notre forum :

   

Constatations modifier

Après le login, on a :

[1] admin
[2] mod
[3] user

[2] & [3] sont similaires, quelques indicateurs à basculer dans la base. Il reste donc deux cas.

L'admin aura sa propre IHM avec fonctions élevées sur le forum.

RUP - Rational Unified Process (ou comment coder ?) modifier

La granularité de la solution s'effectuera par l'affinage des modules.

Il n'est pas intelligent de coder en procédurale ou suivant un développement linéaire dans ce cas. Chaque module ou addon s'interfacera par adjonction de classes et snippets. Ceux-ci étant bien définis quand aux (in/out)put, avec le type précis.

De cette manière l'applicatif sera modulaire et évolutif.

Module administratif modifier

Le forum doit être dédié aux légumes. L'admin doit avoir le choix de créer ces différents sujets principaux.

Par exemple les sections :

  • coups de cœur,
  • espace détente,
  • nouveauté & trouvailles,
  • légumes.

Bibliothèques modifier

Nous aurons d'emblée besoin des deux utilitaires :

[classes]

Les classes usuelles
  • Connectivity
  • Request

[functions]

Les fonctionnalités usuelles


[classes]

 Fichier : Connectivity.cla.php
<?php
/* Connectivity class

	version : 	1.0
	author  :	zulul
	date	:	2009 07 28

*/

class Connectivity 
{//
  var $ident;
  var $sql = array('host'=>'','user'=>'','db'=>'','pass'=>'');

  //INIT
  function DB ($cnx) {
		$this->sql = $cnx;
		$this->connect();
  }
  
  function connect ()  {
     // Connect to MySQL

     $this->ident = mysql_connect($this->sql['host'], $this->sql['user'], $this->sql['pass']);
	 // Select assigned DB
     $this->sql['db']?mysql_select_db($this->sql['db']):0;
  }
	
  function disconnect () {
     // Close the connection
     if (!mysql_close($this->ident)) {
       die("Could not close Database");
     }
  }
}
?>


 Fichier : Request.cla.php
<?php
/* Request class

*/

class Request 
{//
	
	var $res, $Connectivity;

	function SQL($cnx,$sql) {
		
		$DB=new Connectivity($cnx);
		$res = mysql_fetch_all(mysql_query($sql,$DB->ident));
		$DB->disconnect();
	
	return($res);
	}
}
?>


[functions]

 Fichier : utils.fnc.php
<?php
/* utilities functions

*/

/* fetch all db results

*/
function mysql_fetch_all($query, $kind = 'assoc') 
{
   $result = array();
   $kind = $kind === 'assoc' ? $kind : 'row';
   eval('while(@$r = mysql_fetch_'.$kind.'($query)) array_push($result, $r);');
   return $result;
}

/* check if user is on db

*/
function chk_usr($cnx)
{
	if(!isset($_SESSION['autho']))
	{
		if(@$_REQUEST['login']&&@$_REQUEST['pass'])
		{
			return count(Request::SQL($cnx,'SELECT * FROM users WHERE login="'.$_POST['login'].'" and pass="'.$_POST['pass'].'"'))?true:false;
		}
	}else{
		return true;
	}
}

?>

increment 2 : utils.fnc.php

 Fichier : utils.fnc.php
/* dump array 2 html

*/
function print_r_html($data,$return_data=false)
{
    $data = print_r($data,true);
    $data = str_replace( " ","&nbsp;", $data);
    $data = str_replace( "\r\n","<br/>\r\n", $data);
    $data = str_replace( "\r","<br/>\r", $data);
    $data = str_replace( "\n","<br/>\n", $data);
 
    if (!$return_data)
        echo $data;
    else
        return $data;
}

/* adm and mod status checker

*/
function mod()
{
	if(@$_SESSION['userMod'])
	{
		foreach($_SESSION['userMod'] as $_tmp)
		{
			$_stat = explode("/",$_tmp);
			@$_SESSION['userMod'][$_stat[0]]=$_stat[1];
		}
	}
}


increment 4 : utils.fnc.php

add function
 Fichier : utils.inc.ohp
/* get record by id

*/
function getRecordNfo($cnx, $table, $id)
{
	return count($res=Request::SQL($cnx,"SELECT * FROM $table WHERE id=$id"))?$res[0]:false;
}

Modélisation modifier

La modélisation est fonction du métier et des affinités. En voici une fonctionnelle pour notre exemple :

 Fichier : install.sql
-- phpMyAdmin SQL Dump
-- version 3.2.0.1
-- http://www.phpmyadmin.net
--
-- Serveur: localhost
-- Généré le : Mar 28 Juillet 2009 à 19:11
-- Version du serveur: 5.1.36
-- Version de PHP: 5.3.0

SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";

--
-- Base de données: `miniForum`
--

-- --------------------------------------------------------

--
-- Structure de la table `contents`
--

CREATE TABLE IF NOT EXISTS `contents` (
  `usrId` int(11) NOT NULL,
  `topicsId` int(11) NOT NULL,
  `content` text NOT NULL,
  `comment` text NOT NULL,
  `nfo` text NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

--
-- Contenu de la table `contents`
--


-- --------------------------------------------------------

--
-- Structure de la table `topics`
--

CREATE TABLE IF NOT EXISTS `topics` (
  `title` varchar(255) NOT NULL,
  `content` varchar(255) NOT NULL,
  `nfo` varchar(255) NOT NULL,
  `id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

--
-- Contenu de la table `topics`
--


-- --------------------------------------------------------

--
-- Structure de la table `users`
--

CREATE TABLE IF NOT EXISTS `users` (
  `login` varchar(11) NOT NULL,
  `pass` varchar(11) NOT NULL,
  `nfo` varchar(255) NOT NULL,
  `id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;

--
-- Contenu de la table `user`
--

un script d'installation pourrait être :

 Fichier : _install.php
<?php
/* install tables

*/

require_once "./config.inc.php";
require_once LIB."Request.cla.php";
require_once LIB."Connectivity.cla.php";

if(!(new Connectivity($cnx)))
{
	// Créer la base de données
	Request::SQL($cnx,'CREATE DATABASE IF NOT EXISTS miniForum;');

	// table de contenu
	Request::SQL($cnx,'CREATE TABLE IF NOT EXISTS `contents` (
	  `usrId` int(11) NOT NULL,
	  `topicsId` int(11) NOT NULL,
	  `content` text NOT NULL,
	  `comment` text NOT NULL,
	  `nfo` text NOT NULL
	) ENGINE=MyISAM DEFAULT CHARSET=latin1;');

	// table de sujets
	Request::SQL($cnx,'CREATE TABLE IF NOT EXISTS `topics` (
	  `title` varchar(255) NOT NULL,
	  `content` varchar(255) NOT NULL,
	  `nfo` varchar(255) NOT NULL,
	  `id` int(11) NOT NULL AUTO_INCREMENT,
	  PRIMARY KEY (`id`)
	) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;');

	// table des utilisateurs
	Request::SQL($cnx,'CREATE TABLE IF NOT EXISTS `users` (
          `login` varchar(11) NOT NULL,
          `pass` varchar(11) NOT NULL,
          `nfo` varchar(255) NOT NULL,
          `id` int(11) NOT NULL AUTO_INCREMENT,
          PRIMARY KEY (`id`)
        ) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;');
}

?>

Conclusion modifier

Cette façon de coder n'est pas la plus élégante et ne vise pas l'excellence. Bien que l'approche modulaire accroisse la quantité de code, elle a cependant l'avantage d'être lisible pour les longs développements. L'approche itérative et incrémentale de la méthodologie RUP tire fortement parti du développement modulaire.

À faire... 


  • une vue
  • un frameset variable
  • des sujets illimités
  • des catégories illimités
  • un système de classement
  • un système d'administration
  • un système d'authentification
  • un espace myAccount


NICE TO HAVE :

  • tout automatiser (censure / délation / ..)
  • mailer
  • BO admin



Exemples/DomXml

Les documents au format XML ont des imbrications parfois complexes et il n'est pas rare de devoir avoir recours à plusieurs fonctions pour faire le travail de décapsulation du contenu.

À travers cet exemple pratique nous écrirons une fonction pour convertir tout document XML en tableau suivant l'approche du web2 qui tient en deux tags très galvaudés [meta] et [data].

Objectif modifier

  • Élaborer une fonction permettant de convertir en tableaux tout XML bien formé.
  • Créer une classe utilitaire domxml pour la recevoir avec ses petites sœurs.

Écriture d'un XML complexe modifier

  • Écriture d'un document XML valide à imbrications multiples d'éléments hétéroclites comportant des attributs ou non...
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <tag1 m1="attTag1" m2="att2">
    <sous-tag1>texte sous-tag1
      <sous-tag2></sous-tag2>
      texte tag1
    </sous-tag1>
  </tag1>
  <tag1>
    <sous-tag1>texte sous-tag1
      <sous-tag2 att1="att2" att3="att3"></sous-tag2>
    </sous-tag1>
    <tag3>
      <sous-tag1>texte sous-tag1
        <sous-tag2 att1="attribut1"></sous-tag2>
      </sous-tag1>
      <etEncoreUnTagSuperflu>
        <sous-tag1>texte sous-tag1
          <sous-tag2>test</sous-tag2>
        </sous-tag1>
        <tag1>
          <p><b><sous-tag1>texte sous-tag1
            <sous-tag2 att1="attribut1" /></b>
          </sous-tag1>
          ceci</p>est du body texte à extraire
        </tag1>
      </etEncoreUnTagSuperflu>
      text de début ou de fin
    </tag3>
  </tag1>
  <tagNfo id="1" description="description">texteDescription</tagNfo>
</root>
  • Sauvegarde de ce document.xml bien formé dans le même répertoire.

Création de la fonction modifier

On doit maintenant écrire une fonction, la plus optimale possible, pour charger document.xml dans un tableau...

Cette fonction doit :

  • recevoir en entrée un flux xml/rss valide
  • doit migrer les attributs et le contenu dans un tableau

On écrit la fonction récursive qui décapsulera chaque tag en deux sous-tableaux par tag ([meta] ou attributs ) et ([data] ou nœud texte)

Cette fonction doit :

  • tester le type de nœud (texte ou tag)
  • ? si tag >extraire tous ses attributs dans >[meta]
  • ? si texte >extraire le texte dans >[data]
  • comme la structure est imbriquée et non listée :
    • les tags de débuts et de fins ne se suivent pas...
    • la fonction sera donc récursive et s'appellera elle-même pour un output lifo. Elle devra donc se passer son propre résultat en paramètre
    • par soucis du détail technique on fera une fonction getAttribute() pour optimiser le code
   function getAttribute($node)
   {// >((dom)node) ((array)tab)>

     $tab=array();
     foreach($node->attributes() as $k1->$v1)
     {
	$tab[$k1->{''}->name]=$k1->{''}->value;
     }

   return $tab;
   }//

Description :

  • Pour chaque attribut, on place le contenu à une clé du tableau tab à retourner.

On s'attaque ensuite au plus gros du travail de notre convertisseur à savoir domxml2array() :

  function domxml2array($node,&$tab,&$i)
  {// >((dom)node, (array)tab, (int)i) ((array)tab)>
  
    if($next=$node->first_child())        #1
    {
      do
      {
	switch($next->node_type()) #2
        {
	 case 3:
	 $tab['data']=$next->node_value();
	 break;
          case 1:
	 $tab[$next->node_name()."#".++$i]['meta'] = $this->getAttribute($next);
	 $this->domxml2array($next,$tab[$next->node_name()."#".$i],$i);
	 break;
        }
      }while($next=$next->next_sibling()); #3
    }
  return $tab;
  }//

Description :

  1. si le premier enfant existe,
  2. on test le type de nœud,
  3. on passe au nœud suivant.

La fonction utilitaire print_r_html disponible sur php.net permettra de déposer le contenu à l'écran :

function print_r_html($data,$return_data=false)
{
    $data = print_r($data,true);
    $data = str_replace( " ","&nbsp;", $data);
    $data = str_replace( "\r\n","<br/>\r\n", $data);
    $data = str_replace( "\r","<br/>\r", $data);
    $data = str_replace( "\n","<br/>\n", $data);

    if (!$return_data)
        echo $data;
    else
        return $data;
}

Création de la classe modifier

On élabore une classe utilitaire pour php4 à implémenter au fur et à mesure :

  • On la baptise DomTree.
  • On y implémente les fonctions créées...
  • On sauvegarde la classe dans DomTree.Class.php.
<?php

Class DomTree
{

#init
  
  var $tab = array();
  var $domNode;

#constructor 

  function DomTree($xml,$type)
  {// >((string)xml,(int)type) ((dom)node)>
  
    if(!$type)
    {
      $this->domNode = domxml_open_file($xml);
    } else {
      $this->domNode = domxml_open_mem($xml);
    }
  }

#get 

  function getTag($tag)
  {// >((string)tag) ((dom)node)>
			
  return $this->domNode->get_elements_by_tagname($tag);
  }//

  function getAttribute($node)
  {// >((dom)node) ((dom)node)>

    $tab=array();
    foreach($node->attributes() as $k1->$v1)
    {
      $tab[$k1->{''}->name]=$k1->{''}->value;
    }

  return $tab;
  }//

#set

#spec

  function domxml2array($node,&$tab,&$i)
  {// >((dom)node, (array)tab, (int)i) ((array)tab)>
  
    if($next=$node->first_child())
    {
      do
      {
	switch($next->node_type()) 
        {
	 case 3:
	 $tab['data']=$next->node_value(); 
	 break;
          case 1:
	 $tab[$next->node_name()."#".++$i]['meta'] = $this->getAttribute($next);
	 $this->domxml2array($next,$tab[$next->node_name()."#".$i],$i);
	 break;
        }
      }while($next=$next->next_sibling());
    }
  return $this->tab=$tab;
  }//

}

?>

Application modifier

Dans un fichier test.php on instancie la classe et on l'exécute:

<?php

  header("Cache-Control: no-cache, must-revalidate");
  header("Content-Type: text/html");
  
  // appel de la classe
  require_once"DomTree.class.php";

  // création de l'objet
  $doc  = new DomTree('document.xml');

  // sélection du nœud
  $root = $doc->getTag('root');
  
  // conversion du nœud root en tableau
  $tab = $doc->domxml2array($root[0]);

  // affichage du tableau
  print_r_html($tab);

?>

Aperçu modifier

On obtient un arbre structuré easy2use pour le web2

Array
(
    [tag1#1] => Array
        (
            [meta] => Array
                (
                    [m1] => attTag1
                    [m2] => att2
                )

            [sous-tag1#2] => Array
                (
                    [meta] => Array
                        (
                        )

                    [data] => texte tag1
                    [sous-tag2#3] => Array
                        (
                            [meta] => Array
                                (
                                )

                        )

                )

            [data] => 
 
        )

    [data] => 
 
    [tag1#4] => Array
        (
            [meta] => Array
                (
                )

            [sous-tag1#5] => Array
                (
                    [meta] => Array
                        (
                        )

                    [data] => texte sous-tag1
                    [sous-tag2#6] => Array
                        (
                            [meta] => Array
                                (
                                    [att1] => att2
                                    [att3] => att3
                                )

                        )

                )

            [data] => 
 
            [tag3#7] => Array
                (
                    [meta] => Array
                        (
                        )

                    [sous-tag1#8] => Array
                        (
                            [meta] => Array
                                (
                                )

                            [data] => texte sous-tag1
                            [sous-tag2#9] => Array
                                (
                                    [meta] => Array
                                        (
                                        )

                                )

                        )

                    [data] => 
   text de début ou de fin
   
                    [etEncoreUnTagSuperflu#10] => Array
                        (
                            [meta] => Array
                                (
                                )

                            [sous-tag1#11] => Array
                                (
                                    [meta] => Array
                                        (
                                        )

                                    [data] => texte sous-tag1
                                    [sous-tag2#12] => Array
                                        (
                                            [meta] => Array
                                                (
                                                )

                                            [data] => test
                                        )

                                )

                            [data] => 
     
                            [tag1#13] => Array
                                (
                                    [meta] => Array
                                        (
                                        )

                                    [p#14] => Array
                                        (
                                            [meta] => Array
                                                (
                                                )

                                            [b#15] => Array
                                                (
                                                    [meta] => Array
                                                        (
                                                        )

                                                    [sous-tag1#16] => Array
                                                        (
                                                            [meta] => Array
                                                                (
                                                                )

                                                            [data] => texte sous-tag1
                                                            [sous-tag2#17] => Array
                                                                (
                                                                    [meta] => Array
                                                                        (
                                                                            [att1] => attribut1
                                                                        )

                                                                )

                                                        )

                                                )

                                            [data] => 
         ceci
                                        )

                                    [data] => est du body texte à extraire
       
                                )

                        )

                )

        )

    [tagNfo#18] => Array
        (
            [meta] => Array
                (
                    [id] => 1
                    [description] => description
                )

            [data] => texteDescription
        )

)

En bref modifier

On a une fonction fort utile à porter sur php5 ou à optimiser histoire de ne plus avoir d'incréments dans les données du tableau.


Exemples/MVC

Historiquement, PHP est un langage glue, il peut être intégré avec le langage de balisage HTML. L'avantage est cette simplicité de mise-en-œuvre mais l'inconvénient est le mélange entre le traitement et l'affichage. Pour produire une application web claire et facile à entretenir, on peut séparer les différentes parties de l'application selon l'architecture Modèle-Vue-Contrôleur (ou MVC).

  1. Modélisation (Modèle : Partie métier spécifique à l'application)
  2. Visualisation (Vue : Partie visuelle de l'application)
  3. Contrôles (Contrôleur : Partie de gestion des événements de l'application)

De cette façon on peut implémenter son application en sous composantes ce qui augmente légèrement l'analyse de l'application mais fera gagner beaucoup de temps de développement par la suite. Cette architecture est déjà couramment employée dans les applications locales et les applications web l'utilise de plus en plus. On remarquera notamment que beaucoup de Cadriciel web sont basés sur ce principe.

Développement modifier

  • Objectif : Faire un mini système de validation de données saisies.

Pour ce faire on a besoin :

  1. d'un formulaire HTML
  2. d'un module de validation
  3. d'un module de gestion homme-machine
  • Pré-requis conseillés :
  1. html4
  2. php5

Création de la vue modifier

La vue comprendra un formulaire HTML pour la saisie des données utilisateur :

<html>
	<head>
	</head>
	<body>
		<form id="fForm" name="fForm" action="" method="POST">
			<!-- données utilisateur -->
			<input type="text" id="iNom" name="iNom" /><br/>
			<input type="text" id="iPrenom" name="iPrenom" /><br/>
			<input type="text" id="iVisa" name="iVisa" /><p/>
			<input type="submit" value="envoyer" /> 
		</form>
	</body>
</html>

Spécialisation des composantes modifier

Il est mieux de découper son application en sous composante. La vue par exemple pourrait contenir :

  1. le frameset de la page
  2. les containers à afficher

1. Le frameset (vueFrame.inc.php) :

<?php

$page['container']['frameSet'] = '
  <html>
    <head>'
      .$page['css']
      .$page['script'].
    '</head>
    <body>'
      .$page['container']['header']
      .$page['container']['main']
      .$page['container']['footer'].
    '</body>
  </html>';

?>

2. Les formulaires deviendraient (vueFormulaire.inc.php) :

<?php

$page['container']['visa']='
  <div id="cVisa">
    <form id="fForm" name="fForm" action="" method="POST">
      <!-- données utilisateur -->
      <input type="text" id="iNom" name="iNom" value="'.$value['user']['nom'].'"/>
      <span id="msgNom">'
        // communication nom
        .$msg['error']['nom'].'</span><br/>
      <input type="text" id="iPrenom" name="iPrenom" value="'.$value['user']['prenom'].'"/>
      <span id="msgPrenom">'
        // communication prenom
        .$msg['error']['prenom'].'</span><br/>
      <input type="text" id="iVisa" name="iVisa" value="'.$value['user']['visa'].'"/>
      <span id="msgVisa">'
        // communication visa
        .$msg['error']['visa'].'</span><p/>
      <input type="submit" value="envoyer" /> 
    </form>
  </div>';

$page['container']['form1']='
  <div id="">
    <!-- autre formulaire -->
    <form>
    </form>
  </div>';

?>

Création du modèle modifier

Cette bibliothèque utilitaire reprendra les traitements de validation

On a besoin des fonctions suivantes :

  1. check des string alphabétiques
  2. check d'un numéro VISA (4 groupes de 4 chiffres avec le premier groupe commençant par 4 pour simplifier)
<?php

/* modeleValidation

   SYNOPSIS : Cette bibliothèque reprend toutes les fonctions de validation de l'application
*/

/* isAlpha : contrôle de chaîne alphabétique
   @author	: nom
   @date	: 04.11.2007
   @version     : 1
*/
function isAlpha($str)
{// 	>((string)str)-(bool)>

return preg_match('/^([a-zA-Z]*)$/',$str);
}//

/* isVisa : contrôle de numéro VISA
   @author 	: nom
   @date	: 04.11.2007
   @version     : 2
*/
function isVisa($str)
{//	>((string)str)-(bool)>

return preg_match('/^(4)([0-9]{15})$/',$str);
}//

/* DEPRECIEE - isVisa : contrôle de numéro VISA
   @author 	: nom
   @date	: 07.05.2006
   @version     : 1
function isVisa($str)
{//	>((string)str)-(bool)>

return preg_match('/^([0-9]{16})$/',$str);
}//
*/

?>

! Ce modèle modeleValidation.inc.php par exemple comporte une nouvelle fonction isVisa remplaçant celle du 07.05.2006.

Création du contrôleur modifier

Le contrôleur regroupe les traitements de gestion de l'application, c'est à dire ici les événements déclenchés par l'utilisateur ou par le système. On parle souvent d'IHM, voilà un exemple.

On a besoin des contrôles suivants :

  1. validation de saisie
  2. affichage en fonction du statut de la saisie
<?php

/* controlAffichage

   SYNOPSIS : Cette page reprend tous les contrôles pour gérer l'affichage
*/

# INIT
require_once "./Modele/modeleValidation.inc.php";

// var de contrôle de données saisies
$checkSum=3;

# CONTROL
if($_POST)
{
  if(!isAlpha($_POST['iNom']))
  {
    $value['user']['nom'] = $_POST['iNom'];
    $msg['error']['nom']='nom invalide !';
    @$checkSum--;
  }
  if(!isAlpha($_POST['iPrenom']))
  {
    $value['user']['prenom'] = $_POST['iPrenom'];
    $msg['error']['prenom']='prenom invalide !';
    @$checkSum--;
  }
  if(!isVisa($_POST['iVisa']))
  {
    $value['user']['visa'] = $_POST['iVisa'];
    $msg['error']['visa']='visa invalide !';
    @$checkSum--;
  }
}

# CHOIX DE L'OUTPUT
//if(!$checkSum)
if($checkSum==3)
{// formulaire validé
  //$page['container']['main'] = 'Formulaire valide';
  $msg['error']['alert'] = 'Formulaire valide';
}
else
{// formulaire invalide
  //$page['container']['main'] = $page['container']['visa'].'<p> Formulaire invalide !</p>';*/
  $msg['error']['alert'] = $page['container']['visa']
    .'<p> Formulaire invalide !</p>';
}

?>

! utilisation de $page['container']['main'] ne sert a rien ici car sera écrasé dans l'appel de la page principale.

Gestion des containers modifier

Chaque container ou div est ici considéré comme un flux à gérer. Ce qui permettra par la suite d'évoluer simplement vers l'ajax.

Pour bien construire sa page sans mauvaise surprise, il vaut mieux :

  1. charger les containers du plus petit enfant au plus grand parent
  2. ne faire l'output que du frameset

Par ex. :

<?php

// page principale

# INIT
  @session_start();
  

# PREPARE PAGE

  $page['container']['header']='ceci est le header<p/>';
  $page['container']['footer']='ceci est le footer<p/>';

  require_once "./Control/controlAffichage.inc.php";

  require_once './Vue/vueFormulaire.inc.php';
  // preparation du container principal
  $page['container']['main']='
    <div>Veuillez introduire vos données utilisateur :</div>'
    .$page['container']['visa'];
  require_once './Vue/vueFrame.inc.php';
  
# OUTPUT final

  echo $page['container']['frameSet'];

?>

! d'abord $page['container']['main'] et en dernier $page['container']['frameSet']

! $page['container']['main'] de Control/controlAffichage.inc.php sera jamais affiché car il se fait écraser systématiquement avant ./Vue/vueFrame.inc.php' qui passe à l'affichage à proprement parler. Que faire?

Ajout de nouvelles fonctionnalités modifier

On souhaiterait pouvoir gérer les communications dans plusieurs langues. Pour ce faire :

  1. on rajoute un modele messageList.inc.php
  2. on modifie un peu le contrôleur
  3. on crée ou modifie les composantes de la vue

1. messageList.inc.php

<?php

/* messageList

   SYNOPSIS : Cette liste reprend toutes les communications usuelles

   NOTA : Il est préférable d'en faire une table dans une DB
          pour décharger la RAM du serveur et de créer la fonction d'appel du message
*/

// par convention 1=fr et 2=uk

# ERRORS
$msg['error']['alpha'][1] = 'champ alpha invalide';
$msg['error']['alpha'][2] = 'invalid alpha field';
$msg['error']['num'][1] = 'champ numérique invalide';
$msg['error']['num'][2] = 'invalid numeric field';

$msg['error']['invalid'][1] = 'formulaire invalide !';
$msg['error']['invalid'][2] = 'invalid form !';

# SUCCESS
$msg['success']['valid'][1] = 'le formulaire a été validé';
$msg['success']['valid'][2] = 'the form has been validated';

# MESSAGE
$msg['communication']['form'][1] = 'veuillez introduire vos données utilisateur';
$msg['communication']['form'][2] = 'please introduce your user data';
?>

2. controlAffichage.inc.php devient

<?php

/* controlAffichage

   SYNOPSIS : Cette page reprend tous les contrôles pour gérer l'affichage
*/

# INIT
require_once "./Modele/modeleValidation.inc.php";
require_once "./Modele/messageList.inc.php";

// var de controle de données saisies
$_SESSION['checkSum']=3;

# CONTROL
if($_POST)
{
  if(!isAlpha($_POST['iNom']))
  {
    $value['user']['nom'] = $_POST['iNom'];
    $msg['error']['nom']= $msg['error']['alpha'][$lang];           // <--
    @$_SESSION['checkSum']--;
  }
  if(!isAlpha($_POST['iPrenom']))
  {
    $value['user']['prenom'] = $_POST['iPrenom'];
    $msg['error']['prenom']= $msg['error']['alpha'][$lang];        // <--
    @$_SESSION['checkSum']--;
  }
  if(!isVisa($_POST['iVisa']))
  {
    $value['user']['visa'] = $_POST['iVisa'];
    $msg['error']['visa']= $msg['error']['num'][$lang];           // <--
    @$_SESSION['checkSum']--;
  }
}

if($_SESSION['checkSum']==3)
{// formulaire validé
  $page['container']['main'] = $msg['success']['valid'][$lang];    // <--
}
else
{// formulaire invalide
  $page['container']['main'] .= $page['container']['visa']         // <--
    .'<p>'.$msg['error']['invalid'][$lang].'</p>';                 // <--
}

?>

3. ici rien est à faire

Application modifier

L'application se construit ensuite par inclusion des composantes

<?php
 
// Main page
 
# INIT
  @session_start();

  // preparation de la langue choisie (fr par defaut)
  $lang = $_GET['lang']?$_GET['lang']:1;

 
# PREPARE PAGE
 
  $page['container']['header']='ceci est le header<p/>';
  $page['container']['footer']='ceci est le footer<p/>';
 
  // appel de controlAffichage
  // page['container']['main'] va etre remplace si le formulaire est valide
  require_once "./Control/controlAffichage.inc.php";

  require_once './Vue/vueFormulaire.inc.php';
  #region[1] mainPage
  // preparation du container principal
  $page['container']['main']='
    <div><a href="'.$_SERVER['PHP_SELF'].'?lang=1">fr</a> | <a href="'.$_SERVER['PHP_SELF'].'?lang=2">uk</a></div><p/>
    <div>'.$msg['communication']['form'][$lang].'</div>'
    .$page['container']['visa'];
  require_once './Vue/vueFrame.inc.php';
 
  #endRegion[1]

# OUTPUT final
 
  echo $page['container']['frameSet'];
 
?>

! Il y a encore moyen d'optimiser la region[1] pour ne pas avoir à initialiser le container main avant l'inclusion et de faire de controlAffichage.inc.php un contrôleur ne comprenant aucun autre traitement que l'appel pour affichage.

En bref modifier

Cette façon de programmer permet de construire des applications plus souples et évolutives et ne demande pas plus de méthode que celle de choisir correctement ses variables lors de l'analyse. L'utilisation de classes permet d'optimiser plus encore le développement et faciliter l'ergonomie.

Il existe de nombreuses implémentations de MVC dans des cadriciels web écrit en PHP.


Exemples/Webservice

Introduction modifier

Le langage XML se propage peu à peu dans le système d'information. Il est devenu nécessaire de connaître ce standard. Il permet de développer des applications sous plateforme JEE, .Net ou PHP et de s'affranchir des problèmes de portabilité. Les webservices sont basés sur XML, permettant de créer des composants logiciels distribués, de les utiliser indépendamment du langage d'implémentation. SOAP, WSDL, UDDI et WS-Inspection sont les technologies standard qui rendent possibles la construction et la publication de ces services.

Dans nos exemples, nous aborderons l'utilisation de SOAP. Zend propose dans son framework quelques utilitaires de la technique SOAP et REST.

Qu'est-ce que SOAP (Simple Object Access Protocol) ? modifier

Il s'agit d'un protocole d'échange permettant d'invoquer des applications sur différents types de réseaux, en faisant appel, à distance, à des méthodes. Il utilise différents protocoles de transport tel que HTTP mais aussi le protocole POP ou SMTP pour transmettre des données sous forme de messages.

SOAP repose sur une approche RPC (Remote Procedure Call), basée donc sur des messages dont le contenu est structuré en XML.

Webservice PHP4 modifier

Utilisation de la librairie NuSOAP modifier

Pour mettre en place un service web utilisant le protocole SOAP sous technologie PHP, il vous faut récupérer la librairie NUSOAP sous licence GNU en PHP4. La librairie a été développée par NuSphere et Dietrich Ayala. elle permet de créer des services web basés sur SOAP 1.1, WSDL 1.1 et HTTP 1.0/1.1.

Vous pouvez la télécharger sur le site suivant: http://sourceforge.net/projects/nusoap/

L'utilisation de cette librairie ne nécessite pas la mise en place d'extensions PHP spécifiques ce qui est un avantage pour la mise en place de ce système.

Mise en place du webservice modifier

Une fois la librairie téléchargée, et placée dans un sous répertoire où va se trouver votre fichier webservice, nous allons pouvoir commencer a voir comment créer votre webservice.

Vous devez créer un fichier pour votre webservice, nous allons le nommer par exemple webservice.php.

<?php

  // On inclut la librairie nécessaire pour mettre en place le webservice
  require_once("lib/nusoap.php");
  // On initialise un nouvel objet serveur
  $server = new soap_server();
  // On configure en donnant un nom et un Namespace
  $server -> configureWSDL('nomDuWebservice','Namespace');
  // On spécifie l'emplacement du namespace
  $server -> wsdl->schemaTargetNamespace = 'http://emplacementDuNamespace';

?>

Votre webservice est créé, il vous faut maintenant ajouter des méthodes, et le faire communiquer avec les différents clients.

Création des méthodes modifier

Nous allons voir ici comment ajouter des méthodes dans votre webservice en prenant un exemple simple. Nous allons créer une méthode qui prend en argument une chaîne de caractères et qui la renvoie.

Dans votre fichier webservice.php, à la suite du code déjà écrit, nous allons rajouter les lignes suivantes :

<?php

  //on enregistre la méthode grâce à register()
  $server->register('ReturnChaine',array('ChaineString'=>'xsd:string'),   
  array('return'=>'xsd:string'),'Namespace');

  //nous créons ici la fonction ReturnChaine() qui correspond à la méthode créée
  function ReturnChaine($ChaineString) {
     return new soapval('return','string',$ChaineString);   
  }

  $HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
  $server->service($HTTP_RAW_POST_DATA);

?>

Nous avons vu dans cet exemple comment retourner une chaîne de caractère, un exemple assez simple. Il est aussi possible de renvoyer des tableaux grâce aux méthodes lorsqu'on souhaite extraire des éléments d'une base de données.

Webservice PHP5 modifier

On utilise ici la bibliothèque SOAP[1].

WSDL modifier

<?xml version="1.0"?>
<!-- partie 1 : Definitions -->
<definitions 	name="HelloYou" 
		targetNamespace="urn:HelloYou" 
		xmlns:typens="urn:HelloYou" 
		xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
		xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
		xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
		xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
		xmlns="http://schemas.xmlsoap.org/wsdl/">
		
	<!-- partie 2 : Types-->
<types>
    	<xsd:schema 	xmlns="http://www.w3.org/2001/XMLSchema" 
			targetNamespace="urn:HelloYou">
 	</xsd:schema>
 </types> 
	
	
	<!-- partie 3 : Message -->
	<message name="getHelloRequest">
		<part name="prenom" type="xsd:string"/>
		<part name="nom" type="xsd:string"/>
	</message>
	<message name="getHelloResponse">
		<part name="return" type="xsd:string"/>
	</message>
	
	<!-- partie 4 : Port Type -->
	<portType name="HelloYouPort">
		<!-- partie 5 : Operation -->
		<operation name="getHello">
			<input message="typens:getHelloRequest"/>
			<output message="typens:getHelloResponse"/>
		</operation>
	</portType>

	<!-- partie 6 : Binding -->
	<binding name="HelloYouBinding" type="typens:HelloYouPort">
		<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
		<operation name="getHello">
			<soap:operation soapAction="HelloYouAction"/>
			<input name="getHelloRequest">
				<soap:body 	use="encoded" 	
						namespace="urn:HelloYou" 	
						encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
			</input>
			<output name="getHelloResponse">
				<soap:body 	use="encoded" 	
						namespace="urn:HelloYou" 
						encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
			</output>
		</operation>
	</binding>

	<!-- partie 7 : Service -->
	<service name="HelloYouService">
		<documentation>Retourne une phrase simple </documentation>
		<!-- partie 8 : Port -->
		<port name="HelloYouPort" binding="typens:HelloYouBinding">
			<soap:address location="http://monDns:monPort/monChemin/server.php"/> <!-- modifier ce chemin vers server.php -->
		</port>
	</service>
</definitions>

SERVER modifier

Pour que soap soit actif, il faut décommenter extension=php_soap.dll dans php.ini

<?php

// première étape : désactiver le cache lors de la phase de test
ini_set("soap.wsdl_cache_enabled", "0");

// on indique au serveur à quel fichier de description il est lié
$serveurSOAP = new SoapServer('HelloYou.wsdl');

// ajouter la fonction getHello au serveur
$serveurSOAP->addFunction('getHello');

// lancer le serveur
if ($_SERVER['REQUEST_METHOD'] == 'POST')

{
	$serveurSOAP->handle();
}
else
{
	echo 'désolé, je ne comprends pas les requêtes GET, veuillez seulement utiliser POST';
}

function getHello($prenom, $nom)
{
	return 'Hello ' . $prenom . ' ' . $nom;
}
?>

Client modifier

<?php

// première étape : désactiver le cache lors de la phase de test
ini_set("soap.wsdl_cache_enabled", "0");

// lier le client au fichier WSDL
$clientSOAP = new SoapClient('HelloYou.wsdl');

// exécuter la méthode getHello
echo $clientSOAP->getHello('Marc','Assin');

?>

Conclusion modifier

Nous avons pu voir dans cet article comment développer un webservice en PHP. Comme pour les autres technologies dans lesquelles sont développés les webservices, il est possible de construire des méthodes plus complexes,avec accès aux bases de données et un véritable traitement de l'information.

Références modifier


Exemples/Vérification RIO

Le relevé d'identité opérateur (en abrégé RIO) est un identifiant unique attribué à chaque contrat de téléphonie mobile en France.

Le fragment de code PHP ci-dessous permet de vérifier si celui-ci est correct.

explications modifier

  • le champ coderio doit être renseigné avec le code RIO sans blanc.
  • le champ mobile doit être renseigné avec un numéro de téléphone mobile, sans blanc.

code modifier

$rio = isset($_POST["coderio"]) ? strtoupper(trim($_POST["coderio"])) : null ;
$mobile = isset($_POST["mobile"]) ? $_POST["mobile"] : null ;
if ($rio === null  OR $mobile === null ) 
{
   echo "Un des champs est vide";
}
else if(strlen($rio) !=12)
{
  echo "Le code RIO doit contenir 12 caractères exactement";
}
else
{
    $operateur=substr($rio,0,2);
    $typecontrat=substr($rio,2,1);
    $refclient=substr($rio,3,6);
	
    if($typecontrat != "P" && $typecontrat != "E") 
    {
       echo "Le code RIO est erroné, l'identification du contrat est faux";
       exit;
    }
    $ordre="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+";
    $res = array(0,0,0);
    $tmp=$operateur.$typecontrat.$refclient.$mobile;
    for($n=0;$n<19;$n++) {
        $pos=strpos($ordre,substr($tmp,$n, 1));
        $res[0]=($res[0]+$pos)%37;
        $res[1]=((2*$res[1])+$pos)%37;
        $res[2]=((4*$res[2])+$pos)%37;
    }
	
    $clecalculee = substr($ordre,$res[0],1).substr($ordre,$res[1],1).substr($ordre,$res[2],1);
    if(substr($rio,9) != $clecalculee) 
     { 
        echo "Le code RIO est erroné";
     }
      else 
      {
         echo "<em>!!! Le code RIO est BON !!!</em>";	
      }
}


Ajax/Sommaire

Ajax : comment créer un sommaire modifier

Intérêt de l'utilisation d'Ajax modifier

Lorsqu'on écrit un sommaire en PHP classique, à chaque fois qu'on clique sur un lien la totalité de la page est affichée. Sur un sommaire, cela crée un effet de clignotement indésirable et d'autant plus important que la page est lourde. De plus, comme il faut régénérer toute la page, la tendance est à surcharger le serveur avec des requêtes inutiles.

Avec la technologie AJAX, seule la partie qui est modifiée dans la page est rechargée. On diminue ainsi à la fois la charge du serveur, celle du réseau et l'effet de clignotement.

  • Sans la technologie AJAX, on observe un effet de clignotement.
  • Avec Ajax, plus de clignotement et un site plus rapide.

Remarque :

  • ici on voit très peu la différence (quasiment pas d'ailleurs) car le site est super simple. Si le site était plus complexe notamment avec des accès à une base de données, la différence serait beaucoup plus nette !
  • Regardez aussi la différence au niveau de l'utilisation des retours en arrière.

Les fichiers modifier

Le fichier index.html modifier

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Sommaire en PHP !</title>
  <style type="text/css">
  #sommaire
  {
  position:absolute;
  background-color:cyan;
  left:10px;
  width:100px;
  }

  #page
  {
  position:absolute;
  background-color:#AAAAAA;
  left : 200px;
  width:500px;
  height:500px;
  }
  </style>

<script type='text/JavaScript'>
var xhr = null; 
function getXhr()
{
     if(window.XMLHttpRequest)xhr = new XMLHttpRequest(); 
else if(window.ActiveXObject)
  { 
  try{
     xhr = new ActiveXObject("Msxml2.XMLHTTP");
     } catch (e) 
     {
     xhr = new ActiveXObject("Microsoft.XMLHTTP");
     }
  }
else 
  {
  alert("Votre navigateur ne supporte pas les objets XMLHTTPRequest..."); 
  xhr = false; 
  } 
}

function ShowPage(page)
{
getXhr();
xhr.onreadystatechange = function()
    {
     if(xhr.readyState == 4 && xhr.status == 200)
     {
     document.getElementById('page').innerHTML=xhr.responseText;
     }
    }
xhr.open("GET","ajax.php?page="+page,true);
xhr.send(null);
}

</script>

</head>

<body onLoad="ShowPage(1)">

    <div id="sommaire">
        <h3>Sommaire</h3>
        <a href="#" onClick="ShowPage(1)">Page 1</a><br/>
        <a href="#" onClick="ShowPage(2)">Page 2</a><br/>
        <a href="#" onClick="ShowPage(3)">Page 3</a><br/>
        <a href="#" onClick="ShowPage(4)">Page 4</a><br/>
    </div>

    <div id="page">
    </div>

  </body>
</html>

Le fichier ajax.php modifier

<?php

$page=$_GET['page'];
     if($page==1)require 'page1.html';
else if($page==2)require 'page2.html';
else if($page==3)require 'page3.html';
else require 'page4.html';

?>

Le fichier page1.html modifier

<h1>Page 1</h1>
bla bibib blan

Le fichier page2.html modifier

<h1>Page 2</h1>
bonjour

Le fichier page3.html modifier

<h1>Page 3</h1>
bli bli bli

Le fichier page4.html modifier

<form method="get" action="http://www.google.com/search"><fieldset style="border: 1px solid black;"><legend style="font-family:verdana;font-weight:bold;font-size:1em;color:orange;">Recherche Google</legend><TABLE><tr><td align="center"><div style="text-align: center;">
<A HREF="http://www.google.fr">
<IMG SRC="http://www.google.com/logos/Logo_40wht.gif" border="0" 
ALT="Google" align="middle"></A></div></td></tr>
<tr><td align="center"><div style="text-align: center;"><INPUT TYPE=text name=q size=20 value="">
<INPUT TYPE=hidden name=hl value=fr></div></td></tr>
<tr><td align="center" colspan="2"><div style="text-align: center;"><INPUT style="border: 2px outset purple;color:white;background-color:purple;font-weight:bold;font-family:verdana;" type=submit name=btnG VALUE="Recherche"></div>
</td></tr></TABLE>
</FORM></fieldset>


Ajax/Date

Démonstration modifier

Le résultat est visible sur http://xavier.merrheim.free.fr/date.

Les fichiers modifier

Le fichier index.html modifier

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>Sommaire en PHP !</title>
  <style type="text/css">
  #page1
  {
  position:absolute;
  background-color:#AAAAAA;
  left : 200px;
  width:500px;
  top:10px;
  height:200px;
  }

  #page2
  {
  position:absolute;
  background-color:cyan;
  left:200px;
  width:500px;
  height:200px;
  top:250px;
  }
  </style>

<script type='text/JavaScript'>
var xhr = null; 
var n=0;
function getXhr()
{
     if(window.XMLHttpRequest)xhr = new XMLHttpRequest(); 
else if(window.ActiveXObject)
  { 
  try{
     xhr = new ActiveXObject("Msxml2.XMLHTTP");
     } catch (e) 
     {
     xhr = new ActiveXObject("Microsoft.XMLHTTP");
     }
  }
else 
  {
  alert("Votre navigateur ne supporte pas les objets XMLHTTPRequest..."); 
  xhr = false; 
  } 
}

function init()
{
loop();
}

function loop()
{
setTimeout('loop();',3000);
ShowPage();
}

function ShowPage()
{
getXhr();
xhr.onreadystatechange = function()
    {
     if(xhr.readyState == 4 && xhr.status == 200)
     {
     document.getElementById('page1').innerHTML=xhr.responseText;
     }
    }

xhr.open("GET","ajax.php",true);
xhr.send(null);
}

</script>

</head>

<body onLoad="init()">

    <div id="page1">
    </div>

    <div id="page2">
        Ce cadre n'est pas mis à jour.<br/>
    <H1>Bienvenue chers amis</h1>
    </div>
  </body>
</html>

Le fichier ajax.php modifier

Ce fichier peut contenir tout script php dont le résultat doit être mis à jour régulièrement.

Pour cet exemple :

<?php

echo "(Cadre mise à jour le ".date("r").")";

?>


Exemples/MiniCMS

Les CMS ou Content Management Systems (système de gestion de contenu) sont répandus sur les toiles intra/extra(nets). Leur objectif étant d'assister la publication de contenu. L'article décrit comment en concevoir un, ajaxifié côté client et modulaire côté serveur (backend).


Définissons quelques fonctionnalités et appelons le package "myTinyCMS".



Aller au livre


Exemples/MiniCMS/Faire un miniCMS

Faire un miniCMS modifier

Les CMS ou Content Management Systems sont répandus sur les toiles intra/extra(nets). Leur objectif étant d'assister la publication de contenu. L'article décrit comment en concevoir un, ajaxifié côté client et modulaire côté serveur (backend). Définissons quelques fonctionnalités et appelons le package "myTinyCMS".

Les modules développés modifier

Ne seront développés et intégrés que les modules nécessaires à la démonstration.

Ces modules peuvent s'organiser à desseins.

Backend(s)manager
Users
Contents
Customizations
FrontEnds Manager
Templates
Controlers
Front-Ends
Templates

Analyse modifier

Les schémas suivants décrivent le déploiement des composants.


        ./index.php

	<Utilities>_____<Backend>	
		     |
	[Functions]  |  [ContentModule]	
	[Classes]    |  [TemplatesModule]	
	[Objects]    |  [UsersModule]
		     |
		     |__<Frontend>
		     
			[Outputer]
			[AjaxLayer]

On a ici trois couches à distribuer en MVC.

Déploiements modifier

Les parties "clientes" et "administratives" sont séparées par simplicité. On prend un déploiement admins vs customers sur deux répertoires.


     ./Root
           
           main.php
           config.inc.php

           /Cms
                   /Functions
                               /common.inc.php
                   /Classes
                               /contentPane.class.php
                               /dataManager.class.php
                               /templateControler.class.php
                               /utilities.class.php
                               /snippetActer.class.php
                   /Images
                   /Objects
                               /Backend
                                           /authorize.inc.php
                                           /services.inc.php
                                           /webservices.inc.php
                                           /responder.inc.php
                               /Frontend
                                           /entry.inc.php
                                           /admin.inc.php
           /Owners
                   <AdminAccount>
                   /root 
                               /Contents
                                           /page.xml
                                           /user.xml
                                           /...
                               /Templates
                                           /admin.xml
                                           /default.xml
                               /Users
                   <Customers>
                   /customer1
                               /Contents
                               /Templates
                               /Users
                   /customer2
                   /...

Les composantes back&front ends sont dans cette version des packages ou sous composantes. Les données étant distinctement séparées du backOffice.

Technologies modifier

  • Les données : Xmlisation
  • Le code : Php5 Object
  • Dumping & archiving : mysql en soutien pour la recherche indexée et le harvesting

Méthodologie modifier

  • RUP et patterns
  • pas de frameworks (zend ou autre...) ou rapatriement total des snippets


Exemples/MiniCMS/Développement

Développement modifier

  • Les couches choisies
  • Code de service vs Code métier
  • Modélisation
  • Implémentation
  • Déploiement de la vue
  • Ajaxification
  • Intégration des gestionnaires dans un premier noyau
  • Fournir les données
  • Interface Homme - Machine


Exemples/MiniCMS/Les couches choisies

  • Couche de services
Fonctionnalités
Patterns
  • Couche métier
Orientations
Modularités
  • Couche d'accès aux données
Connectiques
  • Couche de présentation
Modèles (Templates)


Il y aura peu de métier et beaucoup de service dans cette version, l'essentiel du travail reposant sur la modélisation, la sérialisation et la modularisation à faible granularité.


Avec l'expérience, en développant de manière modulaire, l'écriture du code se fait sans difficulté, hormis les problèmes liés aux limitations de la solution. Il faut s'efforcer de rester le moins limitatif possible (sauf développements business).


Exemples/MiniCMS/Code de service vs Code métier

Le choix de l'orientation est déterminé par le fait de coder pour soi-même ou pour un client. À moins de devoir développer un code polyvalent au client, il est déconseillé de délivrer du code de service pour la simple et bonne raison, qu'il peut s'approprier le code et le faire devenir propriétaire.


En effet, le code de service, libéré des contraintes business, doit être "déposé et protégé", en particulier quand il propose un service considérable. Au mieux il doit être sous licence d'utilisation.


Exemples/MiniCMS/Modélisation

La modélisation se veut simple et polyvalente.

Champs de données modifier

[champs contenus]

Sous customer1/contents, les champs ou "espaces de données" sont organisés en paquets d'enregistrements. D'autres modèles sont possibles.

<?xml version="1.0" encoding="UTF-8"?>
<!-- content dataChunk model -->
<crop id="" number="" scope="" links="">
    
    <topic/>
    <assert>
        <!-- HERE COMES FRONT END CODE -->
    </assert>

    <!-- Content records -->
    <fields>
        <field id="" number="" subject="">
            <topic/>
            <assert/>
            <content></content>
        </field>
    </fields>

</crop>

Distribués ou non, les paquets s'accèdent par du service pour ne pas dépendre d'un développement purement métier.


On peut opérer la distinction service / métier, en disant que le service est moins spécialisé.


La méthode d'accession aux données sera par exemple un :

  • getCropById ou byScope (prendreChampParId ou parEtendue)
  • getFieldNearSubject (prendreDonnéePrèsDuSujet)

L'id permettant une extraction directe de contenu.


[champs utilisateurs]


Même logique de service :

<?xml version="1.0" encoding="UTF-8"?>
<!-- users dataChunk model -->
<users id="" number="" links="">

   <!-- u:usr / m:mod / a:adm --> 
   <user login="" pass="" lastname="" forname="" rights="u|m|a" />

</users>

Mêmes accès que pour le modèle précédent.


[Champs page.xml]

L'indexation des pages pointeront sur des données data ou layout

<?xml version="1.0" encoding="UTF-8"?>
<pages count="4">
    <page number="1" id="standard" entry="data:1|1|rootEntry"/>
    <page number="2" id="vaisseau" entry="data:1|2|information_2"/>
    <page number="3" id="espace" entry="data:1|3|doorEntry"/>
    <page number="4" id="admin" entry="data:1|1|Error"/>
    <page number="6" id="potiron" entry="data:1|1|content" num="5"/>
    <page number="7" id="er" entry="e"/>
    <page number="8" id="terere" entry="re"/>
</pages>


[Champs templates]

La version suivante a une complétion par points d'ancrages. Cette version va orienter le projet.


L'autorisation de snippets php exécutables rendra possible l'intégration côté client (customer). L'adjonction du contenu à l'output se faisant par injection aux points d'ancrage {{inc:nomDeCle[idDeCle]}} par la méthode servant à compléter le flux avant sa sortie, comme :

  • getFeedByTagName ou getPartByName ...
  • incomes(header/content) à l'injection

Le contenu peut être sorti côté php ou ajaxifié comme ici par le javascript de fin de flux (frameset/content) feedContent().


Exemples/MiniCMS/Implémentation

Nous avons besoin des fonctionnalités :

TODO

[1] Complétion des données
(none) Create
(done) Read
(done) Update
(done) Delete
[2] Gestion des users
[3] Complétion du frameset via templates
(done) injection point replacer
[4] Un webService pour les inputs&query distants
(none) common web services
[5] Parser de snippet
(do ) php snippet acter
(none) class writer & executer
(do ) single actions executer
[6] Vue xhtml fonctionnelle ajaxifiée
(done) xhtml template "nested"


NICE TO HAVE

[1.1] Versionning sur données
(todo) new crop creating passing current to old


Le but étant de fournir un maximum de code de service pour les héritages futur le métier n'est approché qu'en vue de personnaliser le CMS client.


Concernant l'approche modulaire :


La segmentation en composantes (classes ou scriptlets) permets un découpage net et précis des différentes parties du projets développable séparément les unes des autres. L'association au sein d'un IHM ou couche de présentation via contrôleur, venant :

  • avant (pour orienter le développement)
  • pendant (pour stabiliser le développement)
  • après (pour finaliser le développement)


L'interface utilisatrice devant tirer parti des composantes et non l'inverse. La richesse des fonctionnalités est dégagée par le service et enclavé par un code orienté solution client. Il va donc de soi de privilégier proprement, pour enrichir ses propres librairies, de chercher le plus possible à développer ses classes et composantes plutôt que de perdre son temps en métier.

Concevoir du code de haute qualité signifie souvent d'optimiser le code vers une solution à logique 'customer' càd spécialisée, ce qui n'est pas recherché lorsque l'on doit se développer de l'expérience sur le terrain. Au possible, il faut adapté sa modularité et péricliter le service en business pour le client.

Concevoir du code modulaire permets de développer concrètement sans trop d'efforts d'analyse pour les développement XP et sur un grand délais. Il est utile d'avoir conscience de ces deux approches sur le terrain ou lorsque l'on décide d'ouvrir son code au monde. La majorité des développeur ne faisant trop souvent qu'intégrer pour l'argent ou la renommée chez le client le fruit du travail des autres.

'Classes & Fermes de fonctions' contre 'scriptlets'

Les premières sont :

  • plus simple à coder,
  • plus simple à utiliser,
  • plus simple à entretenir,
  • plus simple à spécialiser,
  • plus facilement réutilisable,
  • plus orienté objet,
  • plus susceptible d'être récupéré.

Les secondes sont :

  • plus cryptiques,
  • plus optimisée pour le client,
  • aisément traductible en classes et fermes,
  • plus fonctionnelle,
  • plus procédurales,
  • plus chaotiques[1].
  1. produire un code obscure voir chaotique présente de nombreux avantages. D'autres analystes et collègues y perdront leur temps d'une part, les piping inhérents au code permettant l'implémentation, le hacking, le reroutage et la divergence fonctionnelle de script. Ce qui n'est pas sans avantages.

[1] Complétion des données modifier

Pour disposer des fonctionnalités de gestion de données.

  • On a besoin de Getter/Setter de données :
- depuis la vue
- depuis le webservice
- depuis l'URL
  • Pour les fichiers xml


Les fonctionnalités d'appels sont regroupées ci-dessous

<?php

class dataManager
{//
	private $pattern;

	protected $stack, $domDoc;
	//
	public static 
	
		$crop
				= array(
					"std"=> '<?xml version="1.0" encoding="UTF-8"?>
					<crop><topic /><fields /></crop>'
					,""=>''
				),
				
		$data
				= array(
					"root"=>"/miniCMS/Owners/"
				);
				
# TODO GETTER/SETTER	
	
	
	public static function getContentByData($pData)
	/**
	 * get the content from data
	 */
	{}
	
	public static function setContentByData($pData)
	/**
	 * set the content to data
	 */
	{}
	
	public function setCropForCustomer($pPath, $pCrop)
	/**
	 * set crop to customer directory
	 */
	{
		file_put_contents($pPath, $pCrop);
	}
	
# STATE
	
	public function dataManager()
	{
		$this->initialize();			
	}
	
	public function initialize()
	/**
	 * init
	 */
	{
		$this->domDoc = new DOMDocument();
		if(@$this->data['path'])
		{
			$this->domDoc->load( $this->data['path'] );
		}
		
	return $this->domDoc;
	}
	
	public function saveDocument()
	/**
	 * save document
	 */
	{
		$this->domDoc->save($this->data['path']) ;
	}
}

?>


On implémente ces fonctions utilitaires à utilities.class.php

	public static function searchNodesByContent($pDocument, $pQueries)
	/**
	 * return node matching request
	 */
	{
		
		$_fields = $pDocument->getElementsByTagName('fields');

		/* ceci est un exemple de mauvais code de service, 
                   il ne fonctionne que pour un nombre limité de structure de crop
		   c'est donc un code métier à surcharger. */
		foreach ($_fields->item(0)->childNodes as $u)
		{
			if ($u->nodeType != XML_TEXT_NODE) 
				foreach ( $u->childNodes as $v  )
				{
					if( $v->nodeName == $pQueries['node'] 
                                             && ! utilities::isContent ( $pQueries['value'] 
                                             , $v->nodeValue ) )
					
                                              $_fields->item(0)->removeChild($u) ;
				}
		}
		
		/* OBSOLÈTE - Malheureusement, ce code ne marche pas une fois appelée par getNodes 
		for($i=0; $i<count($pNodeSet); $i++)
		{
			$_content = $pNodeSet[$i]->childNodes ;
			
			// cette fonction ne descend qu'à un niveau 
			foreach($_content as $v)
			{
				if( $v->nodeName == $pQueries['node'] 
                                     && ! utilities::isContent ( $pQueries['value'] 
                                     , $v->nodeValue ) )
				{
					$pNodeSet[$i]->parentNode->removeChild($pNodeSet[$i]) ;
				}
			}
		}
		*/		

	return $pDocument ;
	}

	public static function searchNodesByAttribute($pDocument, $pQueries)
	/**
	 * return node matching request by attribute
	 */
	{
		
		$_fields = $pDocument->getElementsByTagName('fields');
		
		foreach ($_fields->item(0)->childNodes as $u)
		{
			if ($u->nodeType != XML_TEXT_NODE)
				if( !($u->getAttribute($pQueries['attribute']) 
                                         == $pQueries['value'])  ) // 1:1 match
				      $_fields->item(0)->removeChild($u) ;
		}

	return $pDocument ;
	}


On implémente la méthode getContentByData du dataManager

	public static function getContentByData($pDocument, $pData)
	/**
	 * get the content from data
	 */
	{

                /* DEPRECIEE - code pour l'appel à getNodes déprécié pour searchNodes
		if($pData['node'])
		{
			
			if($pData['node'] != "*")
			{
				
				$expr = '//'.$pData['node'].'/..' ;
				
			} else {
				
				return $pDocument ;
				
			}
			
		} else {
			
			return false ;
			
		}
		*/

	return utilities::searchNodes($pDocument, $pData) ;
	}

#     Qui devient ici capable de distinguer 
#     recherche de contenu d'avec les attributs de nœuds

	public static function getContentByData($pDocument, $pData)
	/**
	 * get the content from data
	 */
	{
		if(@$pData['attribute'])
		{
			return utilities::searchNodesByAttribute($pDocument, $pData) ;
		}
		else
		{	
			return utilities::searchNodesByContent($pDocument, $pData) ;	
		}
	}


Un test du getContentByData nous rend nominal pour cette phase dans cette modélisation métier. Le refactoring du business en services serait apprécié...


<?php

require_once "./Cms/Classes/dataManager.class.php";
require_once "./Cms/Classes/utilities.class.php";

//echo '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';

$_manager = new dataManager();

//
$_manager->data['path'] =  $_manager->data['root']  . "Customer1/Contents/crop1.xml"; //

// init manager
$_document = $_manager->initialize();

# QUERY TESTS
 	
	// setting de valeur à récupérer
	$_manager->data['query_1'] = array("node"=>"subject","value"=>"potager");
	$_manager->data['query_2'] = array("node"=>"content","value"=>"molle2");
	
	// queries test
	$res = dataManager::getContentByData(
		dataManager::getContentByData($_document, $_manager->data['query_1'])
		,$_manager->data['query_22']);

# OUTPUT
echo $res->saveXML();

?>


-> Sur l'input crop1.xml
<?xml version="1.0" encoding="UTF-8"?>
<crop>
    <topic>Cultiver son potager</topic>
    <scope>légumes potager soupe</scope>
    <fields>
        <field id="k_01">
            <subject>les légumes du potager</subject>
            <content>Le potager contient une terre molle. 
                     Parsemé ça et là, on trouve carottes, 
                     petits poids et potirons
            </content>
        </field>
        <field id="k_02">
            <subject>les légumes du test</subject>
            <content>Le potager contient une terre molle. 
                     Parsemé ça et là, on Test carottes, 
                     petits poids et potirons</content>
        </field>
        <field id="k_03">
            <subject>les légumes du potager</subject>
            <content>Le potager contient une terre molle2. 
                     Parsemé ça et là, on trouve carottes, 
                     petits poids et potirons</content>
        </field>
    </fields>
</crop>


-> On a pour résultat :
<?xml version="1.0" encoding="UTF-8" ?> 
<crop>
  <topic>Cultiver son potager</topic> 
  <scope>légumes potager soupe</scope> 
  <fields>
    <field id="k_03">
      <subject>les légumes du potager</subject> 
      <content>Le potager contient une terre molle2. 
               Parsemé ça et là, on trouve carottes, petits poids et potirons
      </content> 
    </field>
  </fields>
</crop>


Le deuxième test de get par l'id est également concluant

# QUERY TESTS
 	
	// setting de valeur à récupérer
	$_manager->data['query_1'] = array("node"=>"subject", "value"=>"potager");
	$_manager->data['query_2'] = array("attribute"=>"id", "value"=>"k_03");
	
	// query test
	$res = dataManager::getContentByData(
		dataManager::getContentByData($_document, $_manager->data['query_1'])
		,$_manager->data['query_2']);
	
# OUTPUT
echo $res->saveXML();


Son résultat étant :

<?xml version="1.0" encoding="UTF-8" ?> 
<crop>
  <topic>Cultiver son potager</topic> 
  <scope>légumes potager soupe</scope> 
  <fields>
    <field id="k_03">
      <subject>les légumes du potager</subject> 
      <content>Le potager contient une terre molle2. 
               Parsemé ça et là, on trouve carottes, 
               petits poids et potirons
      </content> 
    </field>
  </fields>
</crop>


On implémente à utilities.class.php les méthodes :

	public static function writeContentByAttribute($pDocument, $pQueries)
	/**
	 * write node content matching request by attribute
	 */
	{
		
		$_fields = $pDocument->getElementsByTagName('fields');
		$_flg = false ;
		foreach ($_fields->item(0)->childNodes as $u)
		{
			if ($u->nodeType != XML_TEXT_NODE)
			{
				if( $u->getAttribute($pQueries['attribute']) 
                                       == $pQueries['value']  ) // 1:1 match
				{
					$v = $u->getElementsByTagName($pQueries['node']) ;
					$v->item(0)->nodeValue = $pQueries['replacement'] ;
					$_flg = true ;
				}
			}
		}
		
	return $_flg ;
	}
	
	public static function deleteContentByContent($pDocument, $pQueries)
	/**
	 * delete node matching request by attribute
	 */
	{
		
		$_fields = $pDocument->getElementsByTagName('fields');
		$_flg = false ;
		foreach ($_fields->item(0)->childNodes as $u)
		{
			if ($u->nodeType != XML_TEXT_NODE)
			{
				foreach ( $u->childNodes as $v  )
				{
					if( $v->nodeName == $pQueries['node'] 
                                               && utilities::isContent ( $pQueries['value'] , 
                                               $v->nodeValue ) )
					{
						$_fields->item(0)->removeChild($u) ;
						$_flg = true ;
					}
				}
			}
		}
		
	return $_flg ;
	}

	public static function deleteContentByAttribute($pDocument, $pQueries)
	/**
	 * delete  node matching request by attribute
	 */
	{
		$_fields = $pDocument->getElementsByTagName('fields');
		$_flg = false ;
		foreach ($_fields->item(0)->childNodes as $u)
		{
			if ($u->nodeType != XML_TEXT_NODE)
				if( $u->getAttribute($pQueries['attribute']) 
                                       == $pQueries['value'] ) // 1:1 match
				{
					$_fields->item(0)->removeChild($u) ;
					$_flg = true ;
				}
		}
	
	return $_flg ;
	}


et à la classe dataManager.class.php

	public function setContentByData($pDocument,$pData)
	/**
	 * set the content to data
	 */
	{
		if(@$pData['attribute'])
		{
			utilities::writeContentByAttribute($pDocument, $pData) ;
		}
		else
		{
			utilities::writeContentByContent($pDocument, $pData) ;
		}
		$this->saveDocument();
	}


Le test suivant permet de valider la phase d'écriture :

# QUERY TESTS
 	
	// setting de valeur à récupérer
	$_manager->data['query_1'] = array("attribute"=>"id", "value"=>"k_03","node"=>"subject",
         "replacement"=>"this is the new subject");
	
	// query test
	$_manager->setContentByData($_document, $_manager->data['query_1']);


avec le resultat

        <field id="k_03">
            <subject>this is the new subject</subject>
            <content>Le potager contient une terre molle2. 
                     Parsemé ça et là, on trouve carottes, 
                     petits poids et potirons
            </content>
        </field>
Liste des défauts modifier
  • searchContents / getContentByData est du code métier
-> le transcrire en code de service
  • pour la suppression, delete reste à implémenter correctement
-> à dataManager

[2] Gestion des utilisateurs modifier

La gestion des utilisateurs suivant le modèle ne demande que l'extraction de la ligne utilisateur pour recueillir ses infos.


Dans la classe utilities.class.php on rajoute ces deux méthodes :

	public static function getAttributesContents($pNode)
	/**
	 * retourne les attributs dans un tableau
	 */
	{
		foreach ($pNode->attributes as $attrName => $attrNode)
		{
		        $_tab[$attrName] = $attrNode->value ;
		}
		    
	return $_tab ;
	}
	
	public static function getLineByAttribute($pDocument, $pQueries)
	/**
	 * return node matching request by attribute
	 */
	{
		
		$_users = $pDocument->getElementsByTagName('user');
		
		foreach ($_users as $u)
		{
			if ($u->nodeType != XML_TEXT_NODE)
			{
				$_flg = true ;
				foreach($pQueries as $_qa )
				{
					if( !($u->getAttribute($_qa['attribute']) 
                                                == $_qa['value']) || !$_flg  )
					{ // 1:1 match
						
						$_flg = false ; 
						
					}
				}
				if($_flg)
					return utilities::getAttributesContents($u) ;
			}
		}
		
	return false ;
	}


Un test concluant de ces méthodes nous rend opérationnel pour cette phase

<?php

require_once "./Cms/Classes/dataManager.class.php";
require_once "./Cms/Classes/utilities.class.php";

$_manager = new dataManager();

//
$_manager->data['path'] =  $_manager->data['root']  . "Customer1/Contents/user1.xml"; //

# QUERY TESTS
 	
	$_manager->data['query_1'] = array(
			array("attribute"=>"login", "value"=>"user2")
			, array("attribute"=>"pass", "value"=>"pass2"));
	
	// query test
	$_res = utilities::getLineByAttribute($_manager->initialize(), $_manager->data['query_1']) ;
	
# OUTPUT
print_r_html($_res);

?>


Résultat :

Array
(
    [login] => user2
    [pass] => pass2
    [lastname] => name2
    [forname] => forName2
    [rights] => u
    [customer] => customer2
)
Liste des défauts modifier

Cette gestion des utilisateurs ne prend actuellement que les attributs de nœuds...

-> il serait utile de pourvoir des données supplémentaires dans le nœud user.

[3] Complétion du frameset modifier

  • Pour commencer nous déclarons une classe utilities.class.php qui reprendra toutes les fonctionnalités utiles pour notre solution


<?php 

function print_r_html($data,$return_data=false)
{
    $data = print_r($data,true);
    $data = str_replace( " ","&nbsp;", $data);
    $data = str_replace( "\r\n","<br/>\r\n", $data);
    $data = str_replace( "\r","<br/>\r", $data);
    $data = str_replace( "\n","<br/>\n", $data);
 
    if (!$return_data)
        echo $data;
    else
        return $data;
}

class utilities
{
	
	
   public static function getNodes($pDomDoc, $pXpathString)
   /**
    * get the node from DomDoc with XpathString
    */
   {
       $xp = new DOMXPath($pDomDoc);
       $xp->registerNamespace('x', 'http://www.w3.org/1999/xhtml');
       $xp->registerNamespace('xhtml', 'http://www.w3.org/1999/xhtml');
       $xp->registerNamespace('i18n', 'http://apache.org/cocoon/i18n/2.1');
	
       $ret = array();
       $nodes = $xp->query($pXpathString);
       foreach ($nodes as $node)
       {
           array_push($ret, $node);
       }
   }
	
   public static function isContent($pPattern, $pFeed)
   /**
   * check is pattern in feed
   */
   {
       return preg_match('/('.$pPattern.')/',$pFeed)?true:false;
   }

}

?>
  • La classe templateControler regroupe les fonctions sur les templates. Le contenu à publier est segmenté en flux.
  • Son objectif est de :
- reformater,
- assembler,
- publier.
<?php

class templateControler
{
/**
 * Control the templates
 *
 * @var (mixed) ($domDoc, $data, $stack)
 */
	
	// 
	private $pattern 
		= array(
			'inc'=>'/{{inc:[a-zA-Z]*[\[\]\*]*}}/'		// inclusion patterns
			,''=>''
		);
	
	protected $domDoc, $stack;
	
	//
	public $data;
	
	/*
	public function __construct()
	{//
		$this->domDoc = new DOMDocument();
		$this->initialize();
	}
	*/
	
	/**
	 * Enter description here...
	 *
	 * @param unknown_type $pVar
	 * @return templateControler
	 */
	public function templateControler($pVar)
	{
		$this->domDoc = new DOMDocument();
		$this->setter($pVar);
		$this->initialize();
	}
	
	/**
	 * Enter description here...
	 *
	 * @param unknown_type $pVar
	 */
	public function setter($pVar)
	{//
		 $this->data = $pVar;
	}
	
// TODO GETTER
	
	public function initialize()
	{//
		$this->domDoc->load( $this->data['path'] );
	}
	
	/**
	 * Enter description here...
	 *
	 */
	public function getNode($pXPath) //"//html/body/*[@class='text']";
	{//
		return $this->data['feed'] = utilities::getNodes($this->domDoc, $pXPath);
	}

	public function getAnchors($pFeed)
	{// 
		preg_match_all($this->pattern['inc'], $pFeed, $this->data['result']);
		
	return $this->data['result']; // on prefere les données centralisées dans la classe
	}
	
	public function publicTemplate()
	{//
		return $this->domDoc->saveXML();
	}
	
}

Le test suivant permet de voir si on prend bien les ancres d'injection, par exemple, pour le nœud frameset/content

//
$tmp = new templateControler(array("path"=>"object.xml"));
$tst = $tmp->getNode("//template/frameset/content");

print_r_html($tmp->getAnchors($tst[0]->nodeValue));
Array
(
    [0] => Array
        (
            [0] => {{inc:styles[*]}}
            [1] => {{inc:scripts[*]}}
            [2] => {{inc:bodies[*]}}
        )

)

On implémente ces deux nouvelles fonctions à templateControler

	/**
	 * getContent retourne le contenu du/des nœuds demandés
	 * 
	 * > (nodeName, nodeId) - (string) >
	 */
	public function getContent($nodeName,$nodeId)
	{//
		// set expression
		$_expr = "//" . $nodeName . ($nodeId&&$nodeId!='*'?"[id='$nodeId']":"");
		$set = $this->getNode($_expr);
		
		$content=null;
		foreach($set as $k=>$v)
		{
			$content .= "<" . $nodeName . ">" . trim($v->nodeValue) . "</" . $nodeName . ">";
		}
		
	return $content?$content:"";
	}
	
	/**
	 * replace anchor by content
	 */
	public function anchorContentReplacer()
	{//
		
		$i=0;
		foreach($this->data['result'][0] as $k=>$v)
		{
			// formatting anchor's syllabes
			$v = str_replace(array('{','[','}',']'),array('',' ','',''),$v);
			$tmp = explode(":",$v);$_ = explode(' ',$tmp[1]);
			
			// replacing
			$this->data['result'][0][$i++] 
                             = array("node"=>$_[0],"value"=>$this->getContent($_[0], $_[1]));
		}
	}
	
// TODO : getNodeById pour disposer d'un getElementById moins spécialisé et fonctionnel ici.

Le test suivant permet de voir si on remplace bien l'ancre par son contenu :

require_once "templateControler.class.php";

//
$tmp = new templateControler(array("path"=>"template.xml"));

$tmp->getNode("//template/frameset/content");

$tmp->getAnchors(
		$tmp
			->data['feed'][0]
			->nodeValue);

$tmp->anchorContentReplacer();

print_r_html(
	$tmp->data['result']
	);
Array
(
    [0] => Array
        (
            [0] => Array
                (
                    [node] => styles
                    [value] => 
                
                
                    body
                    {
                        background-color:red;
                        font-family:verdana;
                        
                        padding:10px;
                    }
                    
                    .style_01
                    {
                        /* some style in */
                    }
                
            
                )

            [1] => Array
                (
                    [node] => scripts
                    [value] => 
                
                
            
                )

            [2] => Array
                (
                    [node] => bodies
                    [value] => 
                )

        )

)

Le contenu est bien localisé dans le template, par conséquent l'objectif de cette phase est atteint. On est opérationnel pour l'injection.


  • La classe snippetActer regroupe les fonctions de computation des snippets.
class SnippetActer
{//
 	
	protected $_type,$_content,$_stack;
 	
# GET/SET
	
	public function setType()
	{//
		list($_1,$_2)=explode('/',$this->_type);
		$this->actCode(array($_1,$_2));
	}
 	
	public function setContent($pContent)
	{//
		$this->_content = $pContent;
	}
 	
# STATE

	/*
	 * act statements
	 */
	public function actCode($pType)
	{//
		switch(true)
		{
			case $pType[0]=="text"&&$pType[0]=="php":
//				
				$this->write();
				break;
 
			case $pType[0]=="text"&&$pType[0]=="javascript":
				$this->inject();
//
				break;
 
			case $pType[0]=="action"&&$pType[0]=="php":
				$this->unstack();$this->act();
				break;
		}
	}
 
	/*
	 * stack statements
	 */
	public function unstack()
	{//
		$_this->_stack = explode(";",$this->_content);
	}
 
	/*
	 * execute statements
	 */
	public function act()
	{//
		foreach($this->_stack as $v)
			eval($v);
	}

}
Liste des défauts modifier
  • Les snippets actifs à la volée peuvent ralentir l'output
-> l'ajaxification peut fournir le contenu en deux temps (données avant/après traitements)
  • Cette classe reste à faire...

[4] Un webservice modifier

Le [[../../webService|webService]] propose par défaut une synergie avec d'autres solutions, sites, applications. Des fonctionnalités distantes permettent l'interfaçage logicielle avec le serveur de données, option qui deviendra basique pour les systèmes orientés données.

<?php
/*
	webservice servker
*/
	
# no chache in testing
		ini_set("soap.wsdl_cache_enabled", "0");
		 
# wsdl
		$serveurSOAP = new SoapServer('service.wsdl');
		 
# provided services

		/* inqueries services 
			in :
			out :
		*/
		$serveurSOAP->addFunction('request');
		// ...
		
		/* high level services 
			in :
			out :
		*/
		$serveurSOAP->addFunction('harvest');
		// ...
		
		/* low level services 
			in : 
			out :
		*/
		$serveurSOAP->addFunction('deploy');
		$serveurSOAP->addFunction('f_01');
		$serveurSOAP->addFunction('f_02');
		// ...
		 
# server start 
		if ($_SERVER['REQUEST_METHOD'] == 'POST')
		{
			$serveurSOAP->handle();
		}
		 
# services integration
		require_once "./Services/request.srv.php";
		require_once "./Services/service_01.srv.php";
		require_once "./Services/service_02.srv.php";
		
# online status checker
		function listen($pIn)
		{
			return 'Copy : ' . $pIn;
		}
		
?>
Liste des défauts modifier

Exemples/MiniCMS/Déploiement de la vue

Le frontOffice modifier

Le front office est orienté ajax.

<?xml version="1.0" encoding="UTF-8"?>
<!-- templateChunk model -->
<template id="nested" number="" topic="" links="">
    
    <!-- HEADER CONTENT -->
    <header>
        
        <assert>
            
            <![CDATA[
                
                :: HERE COMES SERVER/CLIENT SIDE SNIPPETS ::
                
            ]]>
            
        </assert>
        
        <!--  -->
        <content>
                
                <style id="" />
                
                <style id="standard">
                    
                    body
                        {
                            /* background-color:red; */
                            font-family:verdana;
                            font-size:10px;
                            margin:0px 0px 0px 0px;
                            padding:0px;
                        }
                        
                    .click
                        {
                            cursor:pointer;
                        }
                        
                    .style_01
                        {
                            /* some style in */
                        }
                    
                    .encoder
                        {
                            font-size:11px;
                            font-family:verdana;
                            width:95%;
                        }
                     
                     .title td
                         {
                             font-weight:bold;
                         }
                     
                     .specButton
                        {
                            font-weight:bold;
                            cursor:pointer;
                            font-size:10px;
                            font-family:verdana;
                        }
                        
                    div
                        {
                            /* border: grey 1px solid ; */
                           
                        }
                    
                    .dirPath
                        {
                            background-color:yellow;
                        }
                    
                    .red
                        {
                            color:red;
                        }
                    
                    .green
                        {
                            color:green;
                        }
                    
                    .dark
                        {
                            padding:2px;
                            background:#bbb;
                            /* font-weight:bold; */
                            color:white;
                        }
                    
                    .panel 
                        {
                            position: fixed;
                            background:#eee;
                            top: 1em;
                            right: 2%;
                            border: 1px solid #000000;
                            padding: 1em;
                            z-index: 10;
                            width: 200;
                        }
                        
                    .adminMenu 
                        {
                            position: fixed;
                            background:#eee;
                            top: 1em;
                            left: 2%;
                            border: 1px solid #000000;
                            padding: 1em;
                            z-index: 10;
                            width: 200;
                        }
                    
                    .bottomTool 
                        {
                            position: fixed;
                            background:#eee;
                            bottom: 1em;
                            left: 2%;
                            border: 1px solid #000000;
                            padding: 1em;
                            z-index: 10;
                            /* width: 75%; */
                            overflow:auto;
                        }
                    
                    /* */
                    input
                        {
                            width:80%;
                        }
                    
                </style>
               
                <script id="prototype" number="" >
                    
                    <![CDATA[
                    
                        <script src="./Cms/Objects/prototype.js"></script> 
                    
                    ]]>
                    
                </script>
                
            <script id="editor" number="">
                
                <![CDATA[
                
                // inject special tags in the textPad as bold, strike...
                // input	target place, start position, end position
                // output	nonde
                function txtInject(trg,repdeb, repfin)
                {//
                	document.all('txt#content').focus();
                	/* pour l'Explorer Internet */
                	if(typeof document.selection != 'undefined')
                	{
                		/* Insertion du code de formatage */
                		var range = document.selection.createRange();
                		var insText = range.text;
                		range.text = repdeb + insText + repfin;
                		/* Ajustement de la position du curseur */
                		range = document.selection.createRange();
                		if (insText.length == 0)
                		{
                			range.move('character', -repfin.length);
                		} else {
                			range.moveStart('character', repdeb.length + insText.length + repfin.length);
                		}
                		range.select();
                	}
                	/* pour navigateurs plus récents basés sur Gecko*/
                	else if(typeof input.selectionStart != 'undefined')
                	{
                		/* Insertion du code de formatage */
                		var start = input.selectionStart;
                		var end = input.selectionEnd;
                		var insText = input.value.substring(start, end);
                		input.value = input.value.substr(0, start) + repdeb + insText + repfin + input.value.substr(end);
                		/* Ajustement de la position du curseur */
                		var pos;
                		if (insText.length == 0)
                		{
                			pos = start + repdeb.length;
                		} else {
                			pos = start + repdeb.length + insText.length + repfin.length;
                		}
                		input.selectionStart = pos;
                		input.selectionEnd = pos;
                	} else {
                		/* requête de la position d'insertion */
                		var pos;
                		var re = new RegExp('^[0-9]{0,3}$');
                		while(!re.test(pos))
                		{
                			pos = prompt("Insertion à la position (0.." + input.value.length + "):", "0");
                		}
                		if(pos > input.value.length)
                		{
                			pos = input.value.length;
                		}
                		/* Insertion du code de formatage */
                		var insText = prompt("Veuillez entrer le texte à formater:");
                		input.value = input.value.substr(0, pos) + repdeb + insText + repfin + input.value.substr(pos);
                	}
                }//
                                
                // sort a text pad in the main gui if double clicked on the content zone
                // input	content string, text container id
                // output	reformated string
                function tinyTextPad(str,txt_id)
                {// text editor
                
                	// set the utilities images on the content zone of the container
                	str= "<div id='txtPad' align='left'><img src='./Cms/Images/bold.gif' style='cursor:pointer;' onclick='trg=parentNode.nextSibling;txtInject(trg,\"<b>\",\"</b>\");' /><img src='./Cms/Images/italic.gif' onclick='trg=parentNode.nextSibling;txtInject(trg,\"<i>\",\"</i>\");' /><img src='./Cms/Images/underline.gif' style='cursor:pointer;' onclick='trg=parentNode.nextSibling;txtInject(trg,\"<u>\",\"</u>\");' /><img src='./Cms/Images/stroke.gif' style='cursor:pointer;' onclick='trg=parentNode.nextSibling;txtInject(trg,\"<s>\",\"</s>\");' /><img src='./Cms/Images/p.gif' style='cursor:pointer;' onclick='trg=parentNode.nextSibling;txtInject(trg,\"<p>\",\"</p>\");' /> | <img src='./Cms/Images/ul.gif' style='cursor:pointer;' onclick='trg=parentNode.nextSibling;txtInject(trg,\"<ul>\",\"</ul>\");' /><img src='./Cms/Images/li.gif' style='cursor:pointer;' onclick='trg=parentNode.nextSibling;txtInject(trg,\"<li>\",\"</li>\");' /></div><textarea id='"+txt_id+"' rows='15'>"+str+"</textarea>";
                
                return str;
                }//
                ]]>
                
            </script>
                
                <script id="topScript" number="">
                
                <![CDATA[
                    
                    function reformat( pContent )
                        {
                            return pContent.replace(/=/gi,'``i').replace(/\"/gi,'``o').replace(/\?/g,'``q').replace(/&/g,'``e').replace(/%/g,'``u').replace(/#/g,'``a');
                        }
                    
                    function trim( content )
                        {
                        	return content.replace(/^\s+|\s+$/g,"");
                        }
                  
                    function collapse(pDom)
                      {
                           switch(pDom.innerHTML)
                               {
                                  case "[_]":
                                      pDom.innerHTML = "[+]";
                                      break;
                                  case "[+]":
                                      pDom.innerHTML = "[_]";
                                      break;
                              }
                      }
                  
                    function showHide(pDom,pFlg)
                        {
                          if(pDom.style.display=='none')
                                  pDom.style.display='' ;
                          else
                                  pDom.style.display='none' ;
                         }
                  
                   function doHtml( content )
                       {
                          content = content.replace(/&lt;/g, "<").replace(/&gt;/g,">");
                          
                       return content ;
                       }
                   
                    function feedContainer( pDom )
                        /* 
                            
                        */
                        {
                            var ctc = pDom.innerHTML ;
                            cpt = document.getElementById('iCounter') ;
                            var nbr = Number((cpt.value)) ;
                            if(trim(ctc).substring(0,4) != "<tex" && trim(ctc).substring(0,4) != "<TEX" )
                                {
                                    cpt.value = nbr +1 ;
                                    pDom.innerHTML = "<textarea id='t_"+nbr+"' style='width:99%' rows='10' cols='20'>" + trim(ctc) + "</textarea>" ; 
                                }
                            else 
                                {
                                    cpt.value = nbr -1 ;
                                    placeContent( pDom.firstChild.value, pDom.id ) ;
                                }
                        }
                    
                    var _ = function() {} ;
                    
                    // object access
                    _.prototype.oa = {
                        
                        tab:new Array(),
                        
                        getNodesById:function(pDom, pId)
                            /**
                                getNodes by id function
                            */
                            {
                                 if(pDom.getElementsByTagName)
                                     {
                                         all = pDom.getElementsByTagName("*") ;
                                             for(g=0; g<all.length; g++)
                                             {
                                                 if( all[g].getAttribute && all[g].getAttribute("id") == pId )
                                                     {
                                                         this.tab.push(all[g]) ;
                                                     }
                                             }
                                             
                                     return this.tab ;
                                     }
                            },
                        
                        getNodesWithId:function(pDom, pId)
                            /**
                                getNodes by id function
                            */
                            {
                                 if(pDom.getElementsByTagName)
                                     {
                                         var reg=new RegExp("("+pId+")","g") ;
                                         all = pDom.getElementsByTagName("*") ;
                                         for(g=0; g<all.length; g++)
                                             {
                                                 if( all[g].getAttribute && reg.test(all[g].getAttribute("id")) )
                                                     {
                                                         this.tab.push(all[g]) ;
                                                     }
                                             }
                                         
                                     return this.tab ? this.tab : false ;
                                     }
                            },
                        
                        each:function() // pAction
                            /**
                            
                            */
                            {
                                for(var i=0;i<this.tab.length;i++)
                                    {
                                        // 
                                    }
                            }
                    }
                    
                    _.prototype.Remote = {

                    	getConnector: function() 
                        	{
                        		var connectionObject = null ;
                        		if (window.XMLHttpRequest)
                            		{
                            			connectionObject = new XMLHttpRequest() ;
                            		} 
                        		else if (window.ActiveXObject) 
                            		{
                            			connectionObject = new ActiveXObject('Microsoft.XMLHTTP') ;
                            		}
                        		return connectionObject ;
                        	},
                    
                    	configureConnector: function(connector, callback) 
                        	{
                        		connector.onreadystatechange = function() 
                            		{
                            			if (connector.readyState == 4) 
                                			{
                                				if (connector.status == 200) 
                                    				{
                                    					callback.call(connector, {
                                        					text: connector.responseText,
                                        					xml: connector.responseXML
                                    					});
                                    				}
                                			}
                            		}
                        	},
                    	
                    	load: function(request) 
                        	{
                        		var url = request.url || "" ;
                        		var callback = request.callback || function() {} ;
                                
                        		var connector = this.getConnector() ;
                        		if (connector) 
                            		{
                            			this.configureConnector(connector, callback) ;
                            			connector.open("GET", url, true) ;
                            			connector.send("") ;
                            		}
                        	},
                    	
                    	save: function(request) 
                        	{
                        		var url = request.url || "" ;
                        		var callback = request.callback || function() {} ;
                        		var data = request.data || "" ;
                        		var connector = this.getConnector() ;
                        		if (connector) 
                            		{
                            			this.configureConnector(connector, callback);
                            			connector.
                            			    open("POST", url, true);
                            			connector.
                            			    setRequestHeader("Content-type", "application/x-www-form-urlencoded") ;
                            			connector.
                            				setRequestHeader("Content-length", data.length) ;
                            			connector.
                            				setRequestHeader("Connection", "close") ;
                            			connector.
                            				send(data);
                            		}
                        	}
                    }
                    
                    function placeContent ( pContent, pIdTarget )
                        /**
                               set the content to targetted container 
                        */
                        {
                             _.Remote.save(
                                 {
                                    url: "./main.php",
                                    data: "qry=2&id=" + pIdTarget + "&content=" + reformat(pContent),
                                    callback: function(response)
                                        {
                                           var nfo = segment( response.text ) ;
                                           var node = document.getElementById( nfo[0] ) ;

                                           node.innerHTML = nfo[2] ;
                                           
                                           if($('contentKey').value==1)
                                           {
                                                setNodes(node);   
                                           }
                                           
                                           _.oa.tab = new Array() ;
                                           feedContent() ;
                                        }
                                  }
                             );
                        }
                    
                ]]>
               
                </script>
                
                <script id="endScript" number="">
                    
                    <![CDATA[
                    
                        _ = new _() ;
                        
                        function segment(pStr)
                            /**
                            
                            */
                            {
                                var reg=new RegExp("~~", "g") ;
                                
                            return pStr.split(reg) ;
                            }
                        
                        function isToFeed()
                            /**
                                check is still to feed
                            */
                            {
                                return  (set = _.oa.getNodesWithId(document.body,"lay") )?set:false  ;
                            }
                        
                        function feedLayout()
                            /**
                                feed the layout
                            */
                            {
                                    var ctc = _.oa.getNodesWithId(document.body,"lay") ;
                                    if(ctc[0])
                                       _.Remote.save({
                                            url: "./main.php",
                                            data: "qry=1&id="+ctc[0].id+"&owner=" + document.getElementById("customerName").value,
                                            callback: function(response)
                                            {
                                               var nfo = segment( response.text ) ;
                                               var node = document.getElementById( nfo[0] ) ;
                                               node.innerHTML = nfo[2] ;
                                               node.id = nfo[1] ;
                                               
                                               _.oa.tab = new Array() ;
                                               feedContent() ;
                                            }
                                        });
                                }
                        
                        function feedData()
                            /**
                                feed the data
                            */
                            {
                                var ctc = _.oa.getNodesWithId(document.body,"data") ;
                                if(ctc[0])
                                   _.Remote.save({
                                        url: "./main.php",
                                        data: "qry=1&id="+ctc[0].id+"&request=1&owner=" + document.getElementById("customerName").value,
                                        callback: function(response)
                                        {
                                           var nfo = segment( response.text ) ;
                                           var node = document.getElementById( nfo[0] ) ;
                                           node.innerHTML = nfo[2] ;
                                           node.id = nfo[1] ;
                                           
                                           _.oa.tab = new Array() ;
                                           feedContent() ;
                                        }
                                   });
                            }
                        
                        function feedContent()
                            /**
                                feed the content of the page by tag expr and Id
                            */
                            {
                                feedLayout() ;
                                feedData() ;

                              if($('contentKey').value!=0)
                                {
                                    if($('mainContent'))
                                    {
                                       $('mainContent').ondblclick = function (e) {feedContainer(this);};
                                    }
                                }
                            }

                    ]]>
                    
                </script>
                
            <script id="pagerScript" number="">
                
              <![CDATA[
              
                  function cropPage()
                  /*
                      get the crop page
                      
                      */
                      {
                             _.Remote.save(
                                 {
                                    url: "./main.php",
                                    data: "qry=7",
                                    callback: function(response)
                                        {
                                           var nfo = segment( response.text ) ;
                                           document.getElementById("mainContent").innerHTML = nfo[2];
                                        }
                                  });
                      }
                      
                  function itemPage()
                  /*
                      get the item page
                      
                      */
                      {
                             _.Remote.save(
                                 {
                                    url: "./main.php",
                                    data: "qry=8",
                                    callback: function(response)
                                        {
                                           var nfo = segment( response.text ) ;
                                           document.getElementById("mainContent").innerHTML = nfo[2];
                                        }
                                  });
                      }
                      
                  function pagePage()
                  /*
                      get the page page
                      
                      */
                      {
                             _.Remote.save(
                                 {
                                    url: "./main.php",
                                    data: "qry=9",
                                    callback: function(response)
                                        {
                                           var nfo = segment( response.text ) ;
                                           document.getElementById("mainContent").innerHTML = nfo[2];
                                        }
                                  });
                      }
              
              ]]>
                
            </script>
                
            <script id="adminScript" number="">
                
               <![CDATA[
                   
                 // $('mainContent').onDblClick = "feedContainer(this);";
                   
               ]]>
                
            </script>
                
        </content>
        
    </header>
    
    <!-- HEADER CONTENT -->
    <main>
        
        <assert>
            
            <![CDATA[
                
                :: HERE COMES SERVER/CLIENT SIDE SNIPPETS ::
                
            ]]>
            
        </assert>
        
        <!--  -->
        <content>
            
            <page id="standard">
                
                <![CDATA[
                
                    <input type="hidden" id="iCounter" name="nCounter" value="0" />
                    <div id="lay:frameContent" class="toFeed" >> mainEntry container</div>
                 
                ]]>
                
            </page>
            
            <page id="frameContent">
                
                <![CDATA[
                        
                        <div id="lay:topContent"></div>
                        <div id="lay:mainContent"></div>
                        <div  id="lay:bottomContent"></div>
                        
                ]]>
                
            </page>
            
            <page id="topContent">
                
                <![CDATA[
                
                   <div> > My Tiny CMS | TOP CONTENT</div>
                
                ]]>
                
            </page>
            <page id="loginPane">
                
                <![CDATA[
                        
                        <div>
                            
                            <input id="iUsr" type="text" value=" " style="width:96%;" /><br/>
                            <input id="iButton" type="button" value="log on"  style="width:100%;" />
                            
                       </div>
                        
                ]]>
                
            </page>
            
            <page id="menuPane">
                
                <![CDATA[
                    
                    <div class="panel">
                        <span onclick="showHide($('cMenu'),1);collapse(this);">[+]</span>
                        <span id="cMenu">  !.crop | !.item | !.page
                            <p />
                            <span onclick="showHide($('loginPane'),2);">:: Login Pane ::</span>
                            <span><div id="lay:loginPane"></div></span>

                        </span>
                    </div>
                    
                ]]>
                
            </page>
            
            <page id="mainContent">
                
                <![CDATA[

                    <div id="mainContent">
                        
                        <div id="data:1|1|rootEntry"></div>
                        
                    </div>
                ]]>
                
            </page>
            
            <!-- 
            
                The bottom tool may contain all the moderator and admin utilities
            
            -->
            <page id="bottomTool">
                
                <![CDATA[
                
                    <!--
                    <div class="bottomTool">
                        <span onclick="showHide($('cBottom'),1);collapse(this);">[+]</span>
                        <span id="cBottom">:: Bottom Tool ::
                        <div id="cBottomPane"></div>
                        </span>
                    </div>
                    -->
                    
                ]]>
                
            </page>
            
            <page id="bottomContent">
                
                <![CDATA[
                    
                    <div> myTinyCS @ | version 1.0 </div>
                    
                ]]>
                
            </page>
            
            <page id="lay_01" />
            <page id="lay_02" />  
            <page id="lay_03" />

<!-- 

!.crop | !.item | !.page

-->

        </content>
        
    </main>
    
    <!-- PAGE CONTENT -->
    <frameset>
        
        <assert>
            
            <code type="text/javascript" id="scr_01">
                
                <![CDATA[
 
                    function lastScript()
                    {
                        return "this is last script";
                    }
 
                ]]>
                
            </code>

        </assert>
        
        <content><!--  -->
            
            <![CDATA[
            
                <?xml version="1.0" encoding="UTF-8" ?>
                {{inc:token[xhtmlTransitional]}}
                <html>
                    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
                    <head>
                        
                        <style>{{inc:style[standard]}}</style>
                        
                        <script>{{inc:script[topScript]}}</script>
                        <script>{{inc:script[pagerScript]}}</script>
                        
                        {{inc:script[prototype]}}
                        
                    </head>
                    
                    <body onload="feedContent()">
                    
                        {{inc:customer}}
                        
                        {{inc:menuPane}}
                        {{inc:bottomPane}}
                        
                        <!-- Page entry -->
                        <div id='mainContent'>
                        
                            {{inc:page[standard]}}
                        
                        </div>
                        
                    </body>
                    
                    <script>
                    
                         {{inc:script[endScript]}}
                         
                    </script>
                    
                </html>
             
            ]]>
            
        </content>
        
    </frameset>
    
    <tokens>
	
		<token id="xhtmlTransitional">
		
			<![CDATA[
			
				<!DOCTYPE html PUBLIC 
					"-//W3C//DTD XHTML 1.0 Transitional//EN" 
					"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
			
			]]>
		
		</token>
		
        <token id="HTML4">
            
            <![CDATA[
                 
                    <!DOCTYPE HTML PUBLIC 
                        "-//W3C//DTD HTML 4.01 Transitional//EN" 
                        "http://www.w3.org/TR/html4/loose.dtd">
                 
           ]]>
           
        </token>
        
        <token id="xhtmlstrict">
            
            <![CDATA[
                 
                 <!DOCTYPE html PUBLIC
                     "-//W3C//DTD XHTML 1.0 Strict//EN" 
                     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
                     
           ]]>
           
        </token>
        
        <token id="xhtml_xmlns">
            
            <![CDATA[
                
                http://www.w3.org/1999/xhtml
                
            ]]>
            
        </token>
        
    </tokens>
    
</template>

Le backOffice modifier

  • Config.inc.php
<?php 

/**
 * 	configure my tiny CMS
 */

define("ROOT","") ;

define("CMS"	,ROOT."/Cms") ;
define("OWN"	,ROOT."/Owners") ;

define("CLA"	,CMS . "/Classes") ;
define("FUN"	,CMS . "/Functions") ;

// 
define("OBJ"	,CMS . "/Objects") ;

	define("BCK"	,OBJ . "/Backend") ;
	define("FNT"	,OBJ . "/Frontend") ;

?>


  • main.php
point d'entrée back&front end
un switch distingue les appels entrants avec et sans paramètres
<?php

require_once "./config.inc.php" ;

if( !@$_REQUEST['qry'] )
/*
 *   output type switch for ajax layer
 */
	{ 
		include_once FNT."/entry.inc.php" ;
	}
else
	{
		include_once BCK."/responder.inc.php" ;
	}

?>


  • responder.inc.php
appels entrant avec interrogation
<?php 
	
     # TESTING 1.0
	echo $_REQUEST["id"] . "~@~" . $_REQUEST['id'] . " / " . $_REQUEST["name"] . " " . $_REQUEST["surname"] ;
	
?>


  • entry.inc.php
appels entrant sans interrogation
<?php

//
//require_once CLA . "/dataManager.class.php" ;
require_once CLA . "/utilities.class.php" ;
require_once CLA . "/templateControler.class.php" ;

//
$tmp = new templateControler(array("path"=>"./Owners/Root/Templates/default.xml"));

# STATE
	$tmp->initFrame(
		$tmp
			->getNode("//template/frameset/content")
		 ) ;
	
	$tmp->getAnchors( $tmp->data['feed'][0]->nodeValue ) ;
	$tmp->anchorContentReplacer() ;
	
	$tmp->setFrame() ;

# OUTPUT
	echo $tmp->data['frameset'] ;

?>


Exemples/MiniCMS/Ajaxification

L'ajaxification à implémenter côté serveur et client permet une plus grande souplesse d'interaction entre la vue et le backend. Le séquencement du contenu en flux offre une plus grande réactivité.

Pour ce faire :

Dans le template des owners, on implémente la couche AJAX suivante, "topScript" nourrit le début de document et "bottomScript" l'exécution de fin de document.

               <script id="topScript" number="">
               
                    <![CDATA[
                    
                    var $ = function() {};
                    
                    // object access
                    $.prototype.oa = {
                        
                        tab:new Array(),
                        
                        getNodesById:function(pDom, pId)
                        /**
                            getNodes by id function
                        */
                        {
                             if(pDom.getElementsByTagName)
                             {
                                 all = pDom.getElementsByTagName("*");
                                 for(g=0; g<all.length; g++)
                                 {
                                     if( all[g].getAttribute 
                                         && all[g].getAttribute("id") 
                                         == pId )
                                     {
                                         this.tab.push(all[g]);
                                     }
                                 }
                                 
                             return this.tab;
                             }
                        },
                        
                        getNodesWithId:function(pDom, pId)
                        /**
                            getNodes by id function
                        */
                        {
                             if(pDom.getElementsByTagName)
                             {
                                 var reg=new RegExp("("+pId+")","g");
                                 all = pDom.getElementsByTagName("*");
                                 for(g=0; g<all.length; g++)
                                 {
                                     if( all[g].getAttribute &&                     
                                         reg.test(all[g].getAttribute("id")) )
                                     {
                                         this.tab.push(all[g]);
                                     }
                                 }
                                 
                             return this.tab;
                             }
                        },
                        
                        each:function() // pAction
                        /**
                            TODO
                        */
                        {
                            for(var i=0; i<this.tab.length; i++)
                            {
                                // 
                            }
                        }
                        
                    }
                    
                    $.prototype.Remote = {

                    	getConnector: function() 
                    	{
                    		var connectionObject = null;
                    		if (window.XMLHttpRequest)
                    		{
                    			connectionObject = new XMLHttpRequest();
                    		} else if (window.ActiveXObject) {
                    			connectionObject = new ActiveXObject('Microsoft.XMLHTTP');
                    		}
                    		return connectionObject;
                    	},
                    
                    	configureConnector: function(connector, callback) 
                    	{
                    		connector.onreadystatechange = function() 
                    		{
                    			if (connector.readyState == 4) 
                    			{
                    				if (connector.status == 200) 
                    				{
                    					callback.call(connector, {
                        					text: connector.responseText,
                        					xml: connector.responseXML
                    					});
                    				}
                    			}
                    		}
                    	},
                    	
                    	load: function(request) 
                    	{
                    		var url = request.url || "";
                    		var callback = request.callback || function() {};
                            
                    		var connector = this.getConnector();
                    		if (connector) {
                    			this.configureConnector(connector, callback);
                    			connector.open("GET", url, true);
                    			connector.send("");
                    		}
                    	},
                    	
                    	save: function(request) {
                    		var url = request.url || "";
                    		var callback = request.callback || function() {};
                    		var data = request.data || "";
                    		var connector = this.getConnector();
                    		if (connector) 
                    		{
                    			this.configureConnector(connector, callback);
                    			connector.open("POST", url, true);
                    			connector.
                    			    setRequestHeader("Content-type", 
                                                    "application/x-www-form-urlencoded");
                    			connector.
                    				setRequestHeader("Content-length", data.length);
                    			connector.
                    				setRequestHeader("Connection", "close");
                    			connector.
                    				send(data);
                    		}
                    	}
                    }
                    
                ]]>

                </script>

                <script id="endScript" number="">

                    <![CDATA[
                    
                        $ = new $();
                        
                        function segment(pStr)
                        /**
                            segment the callback
                        */
                        {
                            var reg=new RegExp("~@~+", "g");

                        return pStr.split(reg);
                        }
                        
                        function feedContent()
                        /**
                            feed the content of the page by tag expr and Id
                        */
                        {
                            var ctc = $.oa.getNodesWithId(document.body,"id");
                            for(var i=0; i < ctc.length ; i++ )
                            {
                               //
                               $.Remote.save({
                                    url: "./main.php",
                                    data: "qry=1&id="+ctc[i].id+"&name=Ben&surname=Abdell",
                                    callback: function(response)
                                    {
                                       var nfo = segment( response.text );
                                       document.getElementById( nfo[0] ).innerHTML = nfo[1];
                                    }
                                });
                            }
                        }
                        
                    ]]>

                </script>


On teste sur un segment du body. Par convention, le container à nourrir porte l'id composé "id:idDuContainer".

            <page id="standard">

                <![CDATA[
 
                    <table width="100%" border="0" id="table1">
                        <tr><td>
                        
                            <div id="id:topContent"/>
                        
                        </td></tr>
                    
                        <tr><td>
                        
                            <div id="id:mainContent"/>
                        
                        </td></tr>
                    
                        <tr><td>
                        
                            <div  id="id:bottomContent"/>
                        
                        </td></tr>
                    </table>
 
                ]]>

            </page>


Un test sur la page "standard" donne en output pour l'appel ajax :

   $.Remote.save({
		url: "./main.php",
		data: "qry=1&id="+ctc[i].id+"&name=Ben&surname=Aldell",
		callback: function(response)
		{
		   var nfo = segment( response.text );
		   document.getElementById( nfo[0] ).innerHTML = nfo[1];
		}
	});


=> le résultat suivant

<table id="table1" width="100%" border="0">
	<tbody>
		<tr>
			<td>
				<div id="id:topContent">id:topContent / Ben Aldell</div>
			</td>
		</tr>
		<tr>
			<td>
				<div id="id:mainContent">id:mainContent / Ben Aldell</div>
			</td>
		</tr>
		<tr>
			<td>
				<div id="id:bottomContent">id:bottomContent / Ben Aldell</div>
			</td>
		</tr>
	</tbody>
</table>

On est nominal pour l'ajaxification de la complétion du contenu côté client.

Implémentation des contrôleurs côté serveur modifier

  • Common.inc.php :
<?php
 /*
		common application functiunalities 

		would be fine to merge all request to requestForData

*/

require_once CLA . "/utilities.class.php";
require_once CLA . "/dataManager.class.php";
require_once CLA . "/templateControler.class.php";

function requestForLayout($pTerm,$pOwner="Root",$pTemplate="default")
{
	$tmp = new templateControler(array("path"=>"./Owners/" . $pOwner . "/Templates/" . $pTemplate . ".xml"));
	$node = $tmp->getNode('//page[@id="' . $pTerm . '"]');
		
	return $node[0]->nodeValue;
}

function requestForData($pTerm)
{
	$_lst = explode("|",$pTerm) ;
		
	$_manager = new dataManager();
	$_manager->data['path'] =  $_manager->data['root']  . "Root/Contents/" . $_lst[0] . ".xml";
	$_document = $_manager->initialize();

	if ($_lst[1])
        $_manager->data['query_1'] = array("attribute"=>"number", "value"=>$_lst[1]);
	if($_lst[2])
        $_manager->data['query_2'] = array("attribute"=>"id", "value"=>$_lst[2]);

	$res = dataManager::getContentByData(
        dataManager::getContentByData($_document, $_manager->data['query_1']),
        $_manager->data['query_2']
    );

    $node = isset($_lst[3]) ? $_lst[3] : "content";
	$str = $res->getElementsByTagName($node);

	return $str->item(0)->nodeValue;
}


  • Responder.inc.php :
<?php 
		
	//
	$_ = explode(":",$_REQUEST['id']);
	$_content = "";
	
	switch( true )
	/**
	 * content type switcher
	 */
		{
			case ($_[0] == "lay") :
			// template request
				$_content = requestForLayout($_[1]);
				break;
			
			case ($_[0] == "data") :
			// data request
				$_content = requestForData($_[1]);
				break;
		}

# OUTPUT

echo $_REQUEST['id'] . "~@~" . $_[1] . "~@~" . $_content;

Auto complétion du layout modifier

On implémente feedLayout pour auto compléter et chercher les fragments sur le template pour l'injecter sur les encres

                <script id="endScript" number="">
                    
                    <![CDATA[
                    
                        $ = new $();
                        
                        function segment(pStr)
                            /**
                            
                            */
                            {
                                var reg=new RegExp("~@~+", "g");
                                
                            return pStr.split(reg);
                            }
                        
                        function isToFeed()
                            /**
                                check is still to feed
                            */
                            {
                               return  (set = $.oa.getNodesWithId(document.body,"lay") )?set:false;
                            }
                        
                        function feedLayout()
                            /**
                                feed the layout
                            */
                            {
                                    var ctc = $.oa.getNodesWithId(document.body,"lay");
                                    if(ctc[0])
                                       $.Remote.save({
                                            url: "./main.php",
                                            data: "qry=1&id="+ctc[0].id,
                                            callback: function(response)
                                            {
                                               var nfo = segment( response.text );
                                               var node = document.getElementById( nfo[0] );
                                               node.innerHTML = nfo[2];
                                               node.id = nfo[1];
                                               
                                               $.oa.tab = new Array();
                                               feedContent();
                                            }
                                        });
                                }
                        
                        function feedContent()
                            /**
                                feed the content of the page by tag expr and Id
                            */
                            {
                                feedLayout();
                                //feedData();
                            }
                        
                    ]]>
                    
                </script>

Auto complétion des données modifier

     <script id="endScript" ...>

                 // ...

                        function feedData()
                            /**
                                feed the data
                            */
                            {
                                var ctc = $.oa.getNodesWithId(document.body,"data");
                                if(ctc[0])
                                   $.Remote.save({
                                        url: "./main.php",
                                        data: "qry=1&id="+ctc[0].id+"&request=1",
                                        callback: function(response)
                                        {
                                           var nfo = segment(response.text);
                                           var node = document.getElementById(nfo[0]);
                                           node.innerHTML = nfo[2];
                                           node.id = nfo[1];
                                           
                                           $.oa.tab = new Array();
                                           feedContent();
                                        }
                                   });
                            }

                              function feedContent()
                            /**
                                feed the content of the page by tag expr and Id
                            */
                            {
                                feedLayout();
                                feedData();
                            }

     </script>

Test de réactivité de la couche ajax modifier

La fonction feedContent se charge de charger les flux sur la page en provenance du template et des champs de données.

Le template étant :

            <page id="standard">
            
                <![CDATA[
                
                    <div id="lay:frameContent" class="toFeed">

                    </div>
                    
                ]]>
            
            </page>
            
            <page id="_frameContent">
                
                <![CDATA[
                
                <table width="100%" border="0" id="table1">
                    
                    <tr><td>
                        
                        <div id="lay:topContent" />
                        
                    </td></tr>
                    
                    <tr><td>
                        
                        <div id="lay:mainContent" />
                        
                    </td></tr>
                    
                    <tr><td>
                        
                        <div  id="lay:bottomContent" />
                        
                    </td></tr>
                    
                </table>
                
                ]]>
                
            </page>
            
            <page id="topContent">
                
                <![CDATA[
                
                    <table width="100%" border="1" cellpadding="0" cellspacing="0">
                    
                        <tr>
                        
                            <td width="40%">
                            
                                <div id="lay:loginPane" >:: loginPane ::</div>
                            
                            </td>
                            <td width="60%">
                            
                                <div>My Tiny CMS</div>
                                
                                <div id="lay:menuPane" >:: menuPane ::</div>
                            
                            </td>
                            
                         </tr>
                    
                    </table>
                
                ]]>
                
            </page>
            
            <page id="loginPane">
                
                <![CDATA[
                
                    <div> 
                    
                        <div>
                        
                            <input id="iUsr" type="text" value=" " /><br/>
                            <input id="iPwd" type="text" value=" " />
                            
                       </div>
                            
                    </div>
                
                ]]>
                
            </page>
            
            <page id="menuPane">
                
                <![CDATA[
                
                    <div> :: menuPane - new  :: </div>
                
                ]]>
                
            </page>
            
            <page id="mainContent">
                
                <![CDATA[
                
                    <div id="data:1|1|title">content</div>
                    <div id="data:1|2|table">content</div>
                    <div id="data:1|3|bottom">content</div>
                    
                ]]>
            
            </page>
            
            <page id="bottomContent">
                
                <![CDATA[
                
                    <table width="100%" border="1" cellpadding="0" cellspacing="0">
                        <tr height="100"><td>
                           <div align="center"> THIS IS THE BOTTOM CONTENT</div>
                        </td></tr>
                    </table>
                    
                ]]>
            
            </page>


et le crop 1.xml :


<?xml version="1.0" encoding="UTF-8"?>
<crop id="test" number="1">
    
    <topic>Cultiver son potager</topic>
    <scope>légumes potager soupe</scope>
    <fields>
        
        <field number="1" id="title">
            <subject>les légumes du potager</subject>
            <content>
                <![CDATA[
                    les légumes du potager
                ]]>
            </content>
        </field>
        
        <field number="2" id="table">
            <subject>table content</subject>
            <content>
                <![CDATA[
                <table>
                    <tr>
                        <td>
                               le potagé contient : 
                        </td>
                        <td>
                            <ul>
                                <li>une terre molle.</li>
                                <li>Parsemé ça et là, on Test carottes, </li>
                                <li>petits poids et potirons</li>
                            </ul>
                        </td>
                    </tr>
                </table>
                ]]>
            </content>
        </field>
        
        <field number="3" id="bottom">
            <subject>this is the new subject</subject>
            <content>
                <![CDATA[
                    le potagé contient une terre molle2. 
                    Parsemé ça et là, on trouve carottes, petits poids et potirons
                ]]>
            </content>
        </field>
        
    </fields>

</crop>


On a pour rendu en sortie :

 


Exemples/MiniCMS/Premier noyau

Nous préparons la première version de notre solution myTinyCMS. Comme nous sommes nominal pour les quatre phases, myTinyCMS permets :

[1] De gérer les données
[2] De gérer les utilisateurs
[3] De gérer un frameset complexe
[4] D'effectuer des mouvements par son webservice


La première intégration ou version 1 de notre noyau consistera en des contrôleurs de test préfigurant la première beta de myTinyCMS.

L'objectif de ce contrôleur est d'intégrer le frameset sur trois thèmes :

[1] authentification
[2] lecture / écriture contenu
[3] navigation
[4] gestion de contenu

Le contenu sera géré directement sur le frameset par défaut. Backoffice et Frontoffice étant distingué par le switch Mod||Adm vs Usr, déverrouillant les contrôles de gestion et d'encodage.

Fonctionnellement le noyau présente deux phase d'affichage :

La première serveur vers client (output total du frameset)
La seconde cliente appelant l'injection dans les containers


Le Second noyau modifier

MyTinyCMS est maintenant un mini content manager system orienté wiki. L'orientation du CMS génère le code métier suivant


Classes modifier

Classes/contentPane.class.php regroupe les flux d'entrée et sortie comme les menus. Il est spécialisé pour notre solution. Le code peut être amélioré.

Voici Classes/dataManager.class.php dans sa version finale. Il est orienté données.


Voici le Classes/templateControler.class.php orienté layout.


Classes/utilities.class.php dans sa version finale

Functions modifier

Functions/common.inc.php dans sa version finale

Backend modifier

Le backend est implémenter suivant quelques règles métier imposées par l'orientation du CMS également.


Objects/Backend/authorize.inc.php

Objects/Backend/responder.inc.php dans sa version finale

Objects/Backend/services.inc.php

Objects/Backend/webservices.inc.php

FrontEnd modifier

Deux points d'entrées : Objects/Frontend/admin.inc.php

Objects/Frontend/entry.inc.php dans sa version finale

Le Troisieme noyau modifier

MyTinyCMS est ici un CMS orienté données et frontEnd. Avec le frontOffice ajaxifié présenté dans la rubrique relatif à la vue, il permet la création de sites customer directement dans moyennant quelques connaissances en html.

L'applicabilité des modules ayant été démontrée. Cette solution est achevée ici mais peut être augmentée ou améliorée.

myTinyCMS est sous licence GNU copyLeft et utilisable. Si vous implémentez la solution, laissez votre copyright ou alias derrière le mien.

Le copy étant


    Copyright (C)  zulul
    Permission is granted to copy, distribute and/or modify this document
    under the terms of the GNU Free Documentation License, Version 1.3
    or any later version published by the Free Software Foundation;
    with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
    A copy of the license is included in the section entitled "GNU
    Free Documentation License".

Télécharger modifier

MyTinyCMS est nominal... Il peut être telechargé ici


Exemples/MiniCMS/Fournir les données

Pour la vue, les données sont extraites des champs et ensuite injectées aux points d'ancrages désignés sur les templates. Un des objectifs étant de bien synchroniser l'injection serveur avec les sorties clientes en mini flux sur les containers de la vue xhtml. La complétion des données étant sélective.


Exemples/MiniCMS/IHM

L'Interface Homme - Machine consistera à fournir à l'utilisateur des options d'encodage et d'identification dans la vue.

Le découpage modulaire dégage la solution des itérations avenir dans l'ajout de nouvelles fonctions.


Deux points d'entrées permettent de découpler

Ce dernier proposant l'authentification.


L'intégrateur pourra créer une interface d'administration à loisir sur cette adresse ou modifier le point d'entrée, en supprimant ou modifiant les paramètres ou en passant les données par POST plutôt que dans l'URL...

Cette partie ne sera pas détaillée dans cet exemple, seule l'interaction avec l'encodeur sera vu.


Le point d'entrée du site devra faire la distinction entre les sous sites ou sous domaines.


Exemples/MiniCMS/Rétrospective

Si l'envie vous prend d'intégrer ce CMS, c'est à dire de le coder localement chez vous ou de l'adapter à votre environnement ou solution. Je vous conseille de toujours travailler avec l'API approprié, c'est à dire ici : php.net. Cette solution est suffisamment légère pour pouvoir être traduite à loisir en Ruby, Java, Python ou autre.


L'inconvénient majeur actuel étant que l'utilisateur ou le webmaster qui l'utilisera devra avoir des notions d'HTML, la vue ajaxifiée étant ouverte pour une saisie directe au plus au niveau, càd la vue elle même offrant le rendu de ce qui va être encodé.


Les différentes couches ou composantes sous forme de scripts, snippets, classes, functions peuvent être revues, surchargés, améliorées ou étendues. Celles-ci devenant des incréments que vous jugerez utiles de placer à la suite dans ces pages.


Exemples/MiniCMS/Conclusion

Développer un collectionneur et publicateur de données comme typo3 ou joomla est plus une affaire d'organisation modulaire que d'approche en terme de service à fournir. Ce dernier n'étant que la collecte et la diffusion, avec quelques options quant au versionning et la mise à jour du contenu. Les règles sont simples, mais à développer...

  GFDL Vous avez la permission de copier, distribuer et/ou modifier ce document selon les termes de la licence de documentation libre GNU, version 1.2 ou plus récente publiée par la Free Software Foundation ; sans sections inaltérables, sans texte de première page de couverture et sans texte de dernière page de couverture.