Pause-Café Volubis

pause-café

rendez-vous technique
Pause-Café est une réunion technique
destinée aux informaticiens sur plateforme IBM i.
Elle a lieu 3 à 4 fois par an : en Bretagne et sur internet.

Pause-café #37

Avril 2005


BoTTom

PHP

 

commencons par quelques rappels :

Serveur WEB et architecture N-tiers

L'origine de l'informatique est basée sur une Architecture 1 tiers

Le terminal
L'ordinateur central



A l'arrivée de la micro-informatique ce shéma a pu être reproduit en créant des applications autonomes.

(problèmes de duplication des données)

Pour conserver la convivialité de l'interface graphique tout en centralisant les données, on utilise l'architecture 2 tiers (ou client/serveur)



revoyons la fameux schéma du gartner's group lié à cette architecture :

Données
Données
Données
Données
Données
Traitement
Traitement
 
Traitement
présentation
 
Données
Traitement
Traitement
Traitement
Présentation
Présentation
Présentation
Présentation
Présentation
présentation distribuée
(rewamping)
présentation distante (X11) Gestion distante des données
(client/serveur)
traitement distribué
(proc. cataloguées)
bases de données distribuées



puis vint le réseau  et particulièrement l'Internet implémentant le couple  HTTP / HTML

HTTP est un serveur de fichier (proche de FTP en fait) le plus connu étant APACHE

Le but est de fournir au clients (Internet Explorer, Netscape, Opera etc...) des pages HTML à afficher.


Le standard HTML est un langage SGML (comme UIM sur l'AS/400) qui est interprété complétement par la navigateur sur le poste client:

par exemple

voici un texte en <b>GRAS</b> dont une partie est <font color="#FF0000">
rouge</font><br>(suivi d'un passage &agrave; la ligne)

s'affiche :

voici un texte en GRAS dont une partie est rouge
(suivi d'un passage à la ligne)

Une page WEB sous WDS client

l'onglet source affiche le source HTML



Sur l'AS400, HTTP Server for AS/400 (powered by Apache) est une implémentation d'apache v2

  • la configuration est stockée dans IFS (fichier httpd.conf)
    pour éditer la configuration, utilisez la commande EDTF ( wrklnk, puis option 2)
Mais vous pouvez aussi utiliser l'administration graphique du(des) serveur(s) WEB
(il s'agit du serveur *ADMIN, accessible sur le port 2001, à lancer par STRTCPSVR)

pour créer votre premier serveur Apache, tapez comme url ":http://votreas400:2001/HTTPAdmin",
et dans le menu de gauche, choisissez (Create New HTTP server)


Très vite il a fallut insérer dans ces pages HTML des données entreprise
(commandes en attente, stock disponible etc...).

le langage HTML contient des balises permettant de déclarer des zones de saisie dans des formulaires


  
Exemples de zones de type text, textarea, checkbox(cases à cocher) :

Saisissez votre nom <INPUT TYPE="text" NAME="nom" SIZE=30> ==> Saisissez votre nom
Saisissez ici vos remarques :
<TEXTAREA NAME = "remarque" ROWS=6 COLS=50> ==>
</TEXTAREA>



Saisissez ici vos remarques


    
Région
<UL>
        <LI> France <INPUT TYPE="checkbox" NAME="pays" VALUE="FRA" CHECKED>
        <LI> Ile de France<INPUT TYPE="checkbox" NAME="pays" VALUE="ILE"> ==>
        <LI> Paris<INPUT TYPE="checkbox" NAME="pays" VALUE="PAR">
</UL>
Région
  • France
  • Ile de France
  • Paris


HTML propose aussi des zones cachées (hidden), des listes déroulantes et des boutons radio.


Sur le serveur, la norme initiale fut CGI, qui permet l'appel d'un programme "natif",  chargé de générer du HTML en réponse. La technique est assez rugueuse mais cela fonctionne (sur l'AS/400 avec des programmes C ou RPG).

Pour Utiliser CGI en RPG, vous disposez des API suivantes (GAP4 Uniquement)

  • QtmhGetEnv lire le contenu d'une variable d'environnement
  • QtmhRdStin   lire STDIN (le flot en entré)
  • QtmhCvtDb découper STDIN suivant le format d'un fichier BD
  • QtmhWrStout écrire dans STDOUT (le flot en sortie)
    vous devez gnérer un flot commencant par :

    - "content-type : text/html", suivi par du flot HTML à afficher.
    - "location : http://uuuuuuu", uuuuu étant l'adresse du fichier à afficher (l'url)

 

Pour alléger l'écriture de pgm CGI, la plupart des plates-formes proposent des langages de script ou l'on mélange dans un fichier texte, du HTML, du code (propriétaire) et des ordres d'accès à la base.

Le serveur contenant un pgm CGI déjà écrit, traitant ces fichiers "scripts" et retournant au navigateur la page HTML après avoir remplacé les ordres d'accès aux données par les données elle même.

Cela s'appelle PHP dans le monde linux (souvent associé à la base MYSQL), ASP ou ASP.NET pour Microsoft (avec IIS) et Net.Data avec les bases DB2 chez IBM.

Net.Data est basé sur la notion de section, une section représentant soit un page HTML
(un fichier Net.Data contenant donc plusieurs pages) ou une fonction à exécuter (SQL principalement)

Exemple :

%{================================================================%}
%{= MACRO NET.DATA =%}
%{= =%}
%{= affichage d'une liste sans sélection (sans page d'appel) =%}
%{= =%}
%{= But général : afficher la liste des appellations (BDVIN1) =%}
%{================================================================%}

%{******************************************************************************%}
%{* SQL1: génère la liste des appellations dans un tableau HTML *%}
%{******************************************************************************%}
%function
(DTW_SQL) Fsql1() {
%{* Requête SQL *%}
%{*--------------*%}
select Appellation, Region_code from BDVIN1.Appellations
order by Appellation
fetch first 50 rows only
%{* traitement du résultat *%}
%{*------------------------*%}
%report{
<table border ="1">
<tr>
<th><b>Appellation</th>
<th>Region_code</th>
</tr>
{* pour chaque ligne retournée *%}
%{*-----------------------------*%}
%row{
<tr>
<td>$(V1)</td>
<td>$(V2)</td>
</tr>
%}
</table>
%}
%}
%{*************************************************************************%}
%{* PAGE1: page HTML liste des appellations (utilise SQL1) *%}
%{*************************************************************************%}
%html (page1) {
<html>
<body>
<p align="center">
<font size="5" face = "Arial"><b>LISTE des APPELLATIONS<br></b></font>
</p>
@Fsql1()
<BR>
</body>
</html>
%}

Affiche la liste des 50 premieres appellations vinicoles, trièes par nom.

  • page1 est la page HTML à afficher, (@Fsql1 sera remplacé par le résultat produit, c'est à dire la liste elle même.
  • Fsql1 est la fonction SQL, lancant l'ordre Select et mettant en page le résultat.

 Malheureusement, l'avenir de ce langage semble bien sombre (arrêt de support de la part d'IBM sur toutes les plateformes sauf I5/OS et Z/OS, fermeture des différents forums et autres sites web, etc...)


Pour terminer, l'état de l'art est aujourd'hui de travailler dans une architecture dite 3 tiers, c'est à dire en découplant le serveur de traitement (les programmes souvent placés avec le serveur WEB) des données (pouvant être situées sur un serveur éloigné).

Cette technique est implémentée avec les serveurs d'application (Websphere Application Server ou TOMCAT)

Qu'est-ce qu'un serveur d'applications ?

•L'API Servlet 2.2 a été développée afin de permettreà un serveur de traiter des requêtes dans un environnement distribué et plus particulièrement des requêtes HTTP. Les servlet sont ansi un remplacement idéal (basé sur Java) des script CGI.

•L'API java Server Pages 1.1 apporte une solution à l'un des problèmes soulevé par les servlets HTTP: l'imbrication du code HTM dans le code JAVA. (une page JSP est transformée en servlet à la volée)

•le troisème volet souvent associé à ces deux premiers est JDBC, pemettant un accès universel aux bases de données hétérogènes.

Tomcat est un conteneur de servlet (ou serveur d'application) et implémente ces APIs (et quelques autres) comme le produit commercial IBM : Webspshere Application Server.

Ils peuvent offrir en plus, suivant la version, de nombreux avantages comme les pools de connexion (mutualisation des accès base de données), la gestion de la montée en charge, etc...

Il s'agit d'écrire des programmes JAVA s'exécutant sur le serveur et non sur le poste client , le serveur d'application assurant le lien entre le serveur WEB et la JVM (machine virtuelle java).Ces programmes java pouvant être des classes autonomes (servlet) générant du HTML :


ou contenus dans des pages JSP : pages HTML faisant références à des objets externes [des beans].

Les pages JSP permettant d'intégrer du HTML (conçu par un graphiste) et du code JAVA (écrit par un développeur) dans un même fichier.

Dans la pratique, il ne faut pas opposer Servlet et JSP et nous rencontrerons souvent l'architecture Modèle/Vue/Controleur ou MVC qui consiste à invoquer une servlet créant un objet (un client par exemple) puis redirigeant l'affichage vers une page JSP. La page JSP affiche ensuite du HTML qui utilise le bean client
(<% client.raison_sociale %> , <% client.solde %> , etc...)


ET PHP dans tout cela, et bien c'est un langage de script, comme nous l'avons vu plus haut, mais :
  • puissant, il intègre des fonctions et la possibilité de programmer "objet"
  • universel, il est disponible sur toutes les plateformes(Linux, Unix, WIndows et même OS400)
  • simple à apprendre (même si la logique imposée par le couple HTTP/HTML oblige parfoit à réinventer la roue)
  • Open Source (téléchagrement gratuit, une communauté de développeur impressionnante)
» Quelques règles concernant la syntaxe PHP

Le code PHP est toujours encadré par des balises le signalant. Les balises possibles sont :
  • <?php      ?>
  • <?      ?>
  • <%      %>
  • <script language="php">      </script>

Les plus couramment utilisés sont <?    ?> 

l'énorme intéret de cette syntaxe c'est que tout ce qui n'est pas encadré de <? ..  ?> est du HTML, la plupart des éditeurs HTML (Dreamweaver, Nvu) sont capables de manipuler le html en ignorant le PHP.


La première chose à savoir c'est qu'une syntaxe se termine TOUJOURS (sauf quelques exceptions) par un point-virgule , en cas d'oubli vous verrez apparaître PARSE ERROR  lors de l'exécution du  fichier.


le code suivant Affiche
<?
echo "Bonjour le monde !" ;
?>
Bonjour le monde !

comme tous les langages :
  1. vous pouvez déclarer des variables (le nom commence toujours par $ )
  2. vous pouvez déclarer des tableaux  : $montableau[ ]
  3. le langage possède de nombreuses fonctions intégrées (date par exemple pour récupérer date/heure)
  4. vous pouvez créér vos propres fonctions : function mafonction(param1, param2 ) {      .../...    }

le code suivant                        (le point concatène)
Affiche
<?
$date = date("d-m-Y");
$
heure = date("H:i");
echo(
"Nous sommes le" .  $date ."et il est" .  $heure);
?>
Nous sommes le 13-04-2005 et il est 11:10

» interaction avec un formulaire

dans la balise form, vous devez mettre les coordonnées de votre fichier php

dans le fichier php, vous retrouvez les données saisies par $variablephp= $_POST['nom_dans_leformulaire']

le code suivant affiche votre prénom
<html>                                                                           
  <body>                                                                         
     <p align="center">                                                          
     <font size="5" face = "Arial"><b>Entrez votre pr&eacute;nom :<br></b></font>
     <form method="POST" action ="tp0.php">                                      
     <p>Entrez un pr&eacute;nom                                                  
       <input type=text size=30 name="prenom">                                   
                 <p>                                                             
                   <input type=submit value="Valider">                           
                   &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                          
                   <input type=reset value="R&eacute;initialiser">               
                 </p>                                                            
     </form>                                                                     
 </body>                                                                         
 </html>                                                                                   
<html>                                                             
  <body>                                                           
     <p align="center">                                            
     <font size="5" face = "Arial"><b>BONJOUR ! <br></b></font>    
      </p>                                                         
<?php                                                              
  // recuperation de la variable du formulaire                      
  $qui= $_POST['prenom'];                                          
  // Affichage                                                     
  echo("Bonjour " . $qui . "!");                                   
?>                                                     
           
 </body>                                                           
</html>                                                           

Voyez ici le résultat (l'identification se fait avec l'id php, même mot de passe)
» Accès aux bases de données

avec MYSQL (extrait) :
  • mysql_connect() : connexion à un serveur MYSQL
  • mysql_select_db() : choix de la base de données
  • mysql_query() : exécution d'une requête
  • mysql_num_rows : nombre de lignes retournées
  • mysql_num_fileds : nombre de colonnes retournées
  • mysql_fetch_row : lecture d'une ligne le résultat est placé dans un tableau indicé (accès par n°)
  • mysql_fetch_assoc : lecture d'une ligne, le résultat est placé dans un tableau associé (accès par le nom)
  • mysql_close : fermeture de la connexion au serveur
le code suivant liste le contenu d'une table suivant un critère :
<html>                                                                           
  <body>                                                                         
     <p align="center">                                                          
     <font size="5" face = "Arial"><b>Entrez un code appellation :<br></b></font>
     <form method="POST" action ="tp2MYSQL.php">                                 
     <p>Entrez un code (num&eacute;rique)                                        
       <input type=text size=30 maxlength=6 name="appel">                        
       <em>par exemple 13,144,...</em></p>                                       
                 <p>                                                             
                   <input type=submit value="Valider">                           
                   &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                          
                   <input type=reset value="R&eacute;initialiser">               
                 </p>                                                            
     </form>                                                                     
 </body>                                                                         
 </html>                                                                         
<html>                                                                       
  <body>                                                                     
     <p align="center">                                                      
     <font size="5" face = "Arial"><b>LISTE des APPELLATIONS<br></b></font>  
      </p>                                                                   
<?php                                                                        
// connexion à MYSQL                                                         
  $link = mysql_connect("localhost", "root", "") or die("erreur connexion"); 
// récupération de la saisie (variable du formulaire)                                                
$appel= $_POST['appel']; 
// construction de la requete                                                      
$query = "select pr_nom, pr_commune from producteurs                         
            where appel_code = ".$appel." order by pr_nom";                                                                                 
$base  = "vins";                                                             
// on sélectionne la base                                                    
mysql_select_db($base , $link);                
?>                                                                                               
<hr>                                                                                        
<?php                                                                                       
  $result = mysql_query($query) or die("Erreur SQL !<br>".mysql_errno()." - ".mysql_error());
?>                                                                                          
                                                                                            
  R&eacute;sultat:<br>                                                                      
<?php                                                                                       
 if (mysql_num_rows($result) == 0):                                                         
    echo("<B>Requete ex&eacute;cut&eacute;e, mais vide</B>");                                                  
else:                                                   
?>                                                      
 </div>                                                 
 <TABLE BORDER=1>                                       
<TR>                                                    
<?php                                                   
  // boucle sur les colonnes de la requête              
  for ($i = 0; $i < mysql_num_fields($result); $i++) {  
   echo("<TH>" . mysql_field_name($result,$i) . "</TH>");
  }                                                                                                                                                ?>                                                             
</TR>                                                       
 <?php                                                         
  echo(mysql_num_rows($result) . " lignes");                   
                                                               
 while($prod = mysql_fetch_row($result))                       
 {                                                             
  echo("<TR>");                                                
   // boucle sur les valeurs d'une ligne.                      
   for ($j = 0; $j < mysql_num_fields($result); $j++) { 
     echo("<TD>" . $prod[$j] . "</TD>"); 
    }                                    
   echo("</TR>");                        
    }                                    
                                         
  ?>                                     
</TABLE>                              
<?php                                  
    endif;                               
   mysql_close();  
 ?>      
 </body> 
</html>                                                                                                  

Voyez ici le résultat


» AS/400, vous avez dit AS/400  ?

avez vous en tête une annonce IBM concernant un produit nommé PASE ?



 PASE (option 33 de l'OS)

  était un produit facturable en V5R10 et devient intégré à l'OS et dont
   l'installation est conseillée par IBM (pour java, DNS V5, etc...)

   il s'agit de tous les binaires AIX (5L) sur l'AS/400 rendant celui-ci
    très compatible avec une machine UNIX.

   Cette fonctionnalité s'appuie sur le processeur commun à la gamme
   Iseries et Pseries (POWER4 et 5, 64 bits) et la capacité de ce dernier
   à "switcher" d'un mode, d'un environnement à l'autre

   (le processeur possède des instructions propres à l'OS/400)



  pour lancer un "shell" PASE  :

       > CALL QP2TERM   (pour une saisie utilisateur)
       > CALL QP2SHELL  (pour exécuter un script)
       > l'API Qp2RunPase, pour lancer Pase depuis un pgm ILE.

  + le répertoire de PASE est /QOpenSys/usr/bin

    Si un exécutable n'est pas trouvé dans le chemin indiqué, PASE essai
     en ajoutant "/QOpenSys" devant la racine ("/") du chemin,
      sauf à renseigner la variable d'env. "PASE_EXEC_QOPENSYS" à "N"

    Les fonctions PASE qui retournent les utilisateurs et les groupes,
     retournent les noms en minuscules sauf "PASE_USRGRP_LOWERCASE" à "N"

 Le shell lancé par PASE est le Khorn shell (/QOpenSys/usr/bin/sh)



  pour voir la liste des commandes placez vous dans /QOpenSys/usr/bin
   par cd (vérifiez par pwd ,que vous n'y êtes pas déja) et lancez ls .





  A cette liste, vous devez ajouter les commandes "internes" comme cd, ...

  un certain nombre d'utilitaires ne pourront pas s'exécuter à partir
   d'une session 5250.(type de terminal non compatible)

   vous devrez peut-être recourir à un environnement X11 (inclus dans PASE)
    depuis un serveur X (non fourni) à installer sur PC (ou Linux) .

  Pour lancer une application X, démarrer un serveur X11/Xwindow
  (XthinPro ou Hummingbird Exceed) ou bien un poste sous Linux

  -> renseignez la variable d'environnement DISPLAY avec l'adresse IP
      du serveur X, puis lancez ( xterm par exemple) depuis Qp2Term.

  sinon, regardez les commandes suivantes : (extrait, bien sûr ...)

              cat   - affiche le contenu d'un fichier
              ln    - créé un lien symbolique sur un fichier existant
              mkdir - création d'un répertoire
              rm    - destruction de fichier(s)
              mv    - déplace ou renomme un fichier


 Ne confondez pas QSH (le shell de l'OS/400) et PASE (compatible AIX)


  PASE possède plus de fonctionnalités, et quelques différences :

   il peut tenir compte des retours chariot à l'affichage (F11)
   il est sensible à la casse (différence minuscules/MAJUSCULES), pas QSH.
   il ne transforme pas EBCDIC/ASCII sauf pour stdin,stdoutet stderr
    si vous fixez QIBM_PASE_DESCRIPTOR_STDIO à T (QSh utilise les CCSID)





 Exemples d'utilisation de pase :

  Aller chercher des binaires compilés pour AIX et les faire tourner
   sur votre machine.

  voyez http://aixpdslib.seas.ucla.edu/index.html , par exemple.

   vous trouverez sur ce site zip.2.3.tar.Z et unzip.5.50.tar.Z

  Placez ces fichiers dans /QopenSys, puis lancez un terminal PASE

    call Qp2term

    décompressez le fichier par uncompress zip.2.3.tar.Z

    puis restaurez par tar -cvf zip_2_3.tar

    cela doit vous créer un répertoire user et des sous répertoires

     placez vous dans /QopenSys/usr/local/bin (par cd)

 tapez zip -h

  cela doit vous afficher l'aide

  #
 > zip -h
   Copyright (C) 1990-1999 Info-ZIP
   Type 'zip "-L"' for software license.
   Zip 2.3 (November 29th 1999). Usage:
   zip [-options] [-b path] [-t mmddyyyy] [-n suffixes] [zipfile list] [-xi
   list]

 vous pouvez maintenant zipper des fichiers par :

   zip fichier-zip-à-créer fichier-à-zipper

 ou bien par  : (depuis un CL ou une ligne de commande)

    CALL QP2SHELL PARM('/QopenSys/usr/local/bin/zip'  +
                       '/chemin/fichier.zip'          +
                       '/chemin/fichier-à-zipper/')



 Deuxième exemple : MYSQL et PHP (il y a un Redbook dédié à PHP sur Iseries)

   a/ téléchargez les sources depuis http://www.php.net
       et compilez avec un compilateur pour AIX (voyez avec GCC par exemple) ,

ou

   b/ téléchargez une version déja compilée sur http://www.i5php.net

pour MYSQL, décompressez les fichiers dans /QOpenSys/usr/local/var/

- créez un utilisateur (MYSQL) et donnez lui tous les droits sur le répertoire
/QOpenSys/usr/local/var/mysql419/data

- lancez le script mysql_install_db

- lancez MYSQL par /QOpenSys/usr/local/var/mysql419/bin/mysqld_safe

- vous pouver utiliser MYSQL en mode commande :

 



PHP


récupérer une version de php, avec les options dont vous avez besoin.



  décompressez et restaurez les fichiers dans /QopenSys/php, par exemple.
   (les exécutables seront dans /QOpenSys/php/bin)

  paramétrez votre serveur Apache avec les directives suivantes

   ScriptAlias /php-bin/ /QOpenSys/php/bin/
   # définition d'un nouveau type mime 
   AddType application/x-httpd-php .php
   # forcer exécution pour un type donné (php-bin remplacé par ScriptAlias) 
   Action application/x-httpd-php /php-bin/php
   <Directory /QOpenSys/php/bin>
     Options +ExecCGI
     order allow,deny
     allow from all
   </Directory>

vous pouvez donc maintenant utiliser des produits écrits en php et utilisant mysql

PHPMyAdmin

SPIP


mais vous pouvez, bien sur, accèder à votre base de données DB2/400 en utilisant les routines ODBC
(elles sont présentes en natif sous OS/400 sous le nom de CLI)

le code suivant accède à des fichiers 400 :
<html>                                                                       
  <body>                                                                     
     <p align="center">                                                      
     <font size="5" face = "Arial"><b>LISTE des APPELLATIONS<br></b></font>  
      </p>                                                                   
<?php                                                                        
// connexion à L'AS/400                                                         
  $link = odbc_connect("AS400", "", "");                             
  if(!odbc_setoption($link, 1, SQL_ATTR_DBC_DEFAULT_LIB, "BDVIN1")) {
       echo "ERREUR : impossible de travailler avec BDVIN1" ;      
    }
// récupération de la saisie (variable du formulaire)                                                
$appel= $_POST['appel']; 
// construction de la requete                                                      
$query = "select pr_nom, pr_commune from producteurs                         
            where appel_code = ".$appel." order by pr_nom";                                                                                 
?>                                                                                               
<hr>                                                                                        
<?php                                                                                       
  $result =  $result = odbc_exec($link, $query);
?>                                                                                          
                                                                                            
  R&eacute;sultat:<br>                                                                      
<?php                                                                                       
  if ($result == 0):                                                      
   echo ("<B>Error " . odbc_error() . ": " . odbc_errormsg() . "</B>");
 elseif (odbc_num_rows($result) == 0):                                                                                      
    echo("<B>Requete ex&eacute;cut&eacute;e, mais vide</B>");                                                  
else:                                                   
?>                                                      
 </div>                                                 
 <TABLE BORDER=1>                                       
<TR>                                                    
<?php                                                   
  // boucle sur les colonnes de la requête              
  for ($i = 0; $i < odbc_num_fields($result); $i++) {  
   echo("<TH>" . odbc_field_name($result,$i) . "</TH>");
  }                                                                                                                                                ?>                                                             
</TR>                                                       
 <?php                                                                         
                                                               
  while(odbc_fetch_into($result , $prod) != FALSE) {                    
 {                                                             
  echo("<TR>");                                                
   // boucle sur les valeurs d'une ligne.                      
   for ($j = 0; $j < odbc_num_fields($result); $j++) { 
     echo("<TD>" . $prod[$j] . "</TD>"); 
    }                                    
   echo("</TR>");                        
    }                                    
                                         
  ?>                                     
</TABLE>                              
<?php                                  
    endif;                               
   odbc_close();  
 ?>      
 </body> 
</html>                                                                                                  

Voyez ici le résultat (la connexion se fait avec l'id php, même mot de passe)


ces même routines sont disponibles sous WIN32 (Client Access) sous Linux (toujours Client Access)
et même en version 64 bits pour une partition Linux sur I5


Copyright © 1995,2005 VOLUBIS