PHP


Accès aux bases de données



BoTTom


Bases de données

Le principal intérêt d'un langage Web dynamique est sa capacité à la gestion de bases de données SQL.

A cet effet, le langage PHP propose de nombreux outils permettant de travailler avec la plupart des SGBDR (Système de Gestion de Bases de données Relationnelles) tels que Oracle, Sybase, Microsoft SQL Server, PostgreSQL ou encore MySQL, son système de gestion de prédilection.

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


la connexion ODBC se fait avec les attributs suivants :


IBM, propose le code suivant pour définir un accès universel (plateforme Windows/Linux et OS/400)

 // If we report the OS as AIX or OS400, assume we're running under PASE
$isPase = (PHP_OS == "AIX" || PHP_OS == "OS400");                      


if (!$isPase) {
  $dsn = "DRIVER=iSeries Access ODBC Driver;SYSTEM=$isdb_system;DBQ=$isdb_database";
   $db = odbc_pconnect($dsn, $user, $pwd);
} else {                                                                         
   $db = odbc_connect($isdb_system, $user, $pwd);
   odbc_setoption($db, 1, SQL_ATTR_DBC_DEFAULT_LIB, $isdb_database)                                     
}                                                                                

Vous remarquerez l'utilisation de odbc_pconnect dans le cas d'un serveur intel et odbc_connect quand les pages php sont sur iSeries.

cet extrait de code est proposé avec des exemples d'accès à DB2/400 en php sous linux, sur la page
http://publib.boulder.ibm.com/infocenter/iseries/v5r4/topic/rzatv/rzatvexodbc.htm
 

ATTENTION.

Beaucoup de paramètres et de comportements sont dépendant de la convention d'appellation (Sql ou système)

 

  • Convention d'appellation SQL

    Sur Windows/Linux
     vous pouvez fournir une liste de bibliothèques, celle ci sera utilisée pour la recherche des types de données utilisateur (UDT), des fonctions (UDF) et des procédures cataloguées. La première de ces bibliothèques sera considérée comme la bibliothèque par défaut.

    Sur I5
    vous ne pouvez fournir qu'un seul nom de bibliothèque qui sera la bibliothèque par défaut

    La bibliothèque par défaut est automatiquement utilisée lors d'une requête SQL non qualifiée pour l'accès aux tables

    Si vous n'indiquez pas de bibliothèque par défaut, c'est le AUTHORIZATION ID qui est utilisé

    l'authorization ID est par défaut le nom de l'utilisateur en cours, peut être modifié par SET AUTHORIZATION ID et doit être un nom de profil.


  • Convention d'appellation système

    Vous utilisez *LIBL.

    La liste de bibliothèques est celle de l'utilisateur connecté (application de la JOBD incluse) ou par défaut de l'utilisateur du serveur Apache.

    Vous pouvez la compléter en passant les ordres suivants :
       $addlible = "call qcmdexc ('addlible bdvin9' , 0000000015.00000)";
       $result = odbc_exec($db, $addlible);


  • Dans tous les cas, vous pouvez passer en plus l'ordre SQL SET PATH pour fournir une liste de bibliothèques à utiliser pour vos références à :
    •vos UDT (types de données définis par l'utilisateur)
    •vos procédures cataloguées
    •vos fonctions (UDF), si la liste des paramètres est correcte


Voyez la liste des propriétés pouvant être renseignées dans la chaîne de connexion (Dsn) à l'adresse suivante

http://publib.boulder.ibm.com/infocenter/iseries/v7r1m0/topic/rzaik/connectkeywords.htm


et la liste des options de connexion CLI (odbc_setoption() ,  quand ODBC est sur l'AS400) à

http://publib.boulder.ibm.com/infocenter/iseries/v7r1m0/index.jsp?topic=%2Fcli%2Frzadpfnsenva.htm

Liste partielle :

Clé signification Choix dft odbc_setoption()
DBQ  (voir plus haut) Bibliothèque "QGPL" SQL_ATTR_DBC_DEFAULT_LIB
NAM  Convention d'appellation. 0 = "sql" ( schema.table)
1 = "system" ( schema/table)
0 SQL_ATTR_DBC_SYS_NAMING
CMT Niveau de transaction
0 = Commit immediate (*NONE)
1 = Read committed (*CS)
2 = Read uncommitted (*CHG)
3 = Repeatable read (*ALL)
4 = Serializable (*RR)
2 SQL_ATTR_COMMIT

Attention les valeurs sont  différentes
*NONE 1
*CHG 2
*cs 3
*ALL 4
*RR 5

DFT  Format de la Date
0 = yy/ddd   (*JUL)
1 = mm/dd/yy  (*MDY)
2 = dd/mm/yy  (*DMY) 
3 = yy/mm/dd  (*YMD)
4 = mm/dd/yyyy  (*USA)
5 = yyyy-mm-dd  (*ISO)
6 = dd.mm.yyyy  (*EUR)
7 = yyyy-mm-dd  (*JIS)
5 SQL_ATTR_DATE_FMT

*ISO 1
*USA 2
*EUR 3
*JIS 4
*MDY 5
*DMY 6
*YMD 7
DSP Séparateur de la Date
0 = "/" 
1 = "-"  
2 = "."  
3 = ","  
4 = " "  
1 SQL_ATTR_DATE_SEP

/ 1
- 2
. 3
, 4
espace 5
format du job 7
DEC Séparateur décimal

0 = "." (point)
1 = "," (virgule)
0 SQL_ATTR_DECIMAL_SEP
. 3
, 4
format du job 7
TFT Format des Heures
0 = hh:mm:ss  (*HMS) 
1 = hh:mm AM/PM  (*USA)
2 = hh.mm.ss  (*ISO)
3 = hh.mm.ss  (*EUR) 
4 = hh:mm:ss  (*JIS)
0 SQL_ATTR_TIME_FMT
*ISO 1
*USA 2
*EUR 3
*HMS 9
TSP Séparateur des Heures 0 = ":" 
1 = "." 
2 = "," 
3 = " " 
0 SQL_ATTR_TIME_SEP

                       (voir la date)


Exemple :

le code suivant accède à des fichiers DB2/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", $user, $pwd);                             
  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 = 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 (sur le site volubis.fr)


Voyez la liste des fonctions ODBC à http://fr.php.net/manual/fr/ref.uodbc.php

Extrait :


ces deux fonctions n'ont de sens que si vous êtes sous contrôle de validation

Remarques concernant les mises à jour (update) et les insertions (insert)

  1. Si vous travaillez sur des fichiers (Tables) non journalisé, vous devez être hors commitment control (CMT à 0 ou SQL_ATTR_COMMIT à 1) ce qui n'est jamais la valeur par défaut.Si vous travaillez sur des tables journalisées et sous commitment control, pensez à valider (Commit)

  2. Si vous êtes sur une version récente de Linux, elle fonctionne (ainsi que Apache) en UTF-8, (basé sur Unicode) et le driver ODBC de Client Access traduit les données en utilisant la routine iconv(). Cette dernière pose un problème avec nos caractères (Latin-1, soit ISO-8859-1 ou 8859-15 avec l'Euro)

    Vous recevez alors le message
    [unixODBC][IBM][iSeries Access ODBC Driver][DB2 UDB]SQL0104 - Error message text unavailable. Message can not be translated successfully.


    pour résoudre ce problème, passez la locale en Français, par setlocale(LC_ALL, 'fr_FR') lors de la connexion.


    Nous vous proposons donc, comme code de connexion universel et implémentant nos règles habituelles de fonctionnement :
    <?
    $isPase = (PHP_OS == "AIX" || PHP_OS == "OS400");              
    $system = "AS400";
    $bib = "MABIBPF";
    $user = "USER";
    $pwd = "PWD";
    //connexion, sans commitment control, convention d'appellation système(==> *libl) et date au format DMY
    // à adapter en fonction du contexte (fichiers journalisés ou pas, etc ...)
    if (!$isPase) {
        // sur PC 
        $dsn = "DRIVER=iSeries Access ODBC Driver;SYSTEM=$system;CMT=0;NAM=1;DFT=2";
        $db = odbc_connect($dsn, $user, $pwd);
        if ($db == 0){
            echo ("<BR><B>Erreur Connexion " . odbc_error() . ": " . odbc_errormsg() . "</B>");
        }
        if (PHP_OS == "Linux") {
            setlocale(LC_ALL, 'fr_FR') ;
        }
    } else { 
        // sur I5 
        $db = odbc_connect($system, $user, $pwd);
        odbc_setoption($db, 1, SQL_ATTR_COMMIT, 1); 
        odbc_setoption($db, 1, SQL_ATTR_DBC_SYS_NAMING, 1); 
        odbc_setoption($db, 1, SQL_ATTR_DATE_FMT, 6); 
    } 
    //dans tous les cas
    // mise en place *LIBL
    $lg = '00000000' . number_format(strlen($bib) + 9, 5 , ".", ""); $addlible = "call qsys/qcmdexc ('ADDLIBLE " . $bib . "' , $lg)"; $result = odbc_exec($db, $addlible); if ($result == 0){ echo "Erreur mise en place *LIBL " . $addlible; echo ("<BR><B>Erreur " . odbc_error() . ": " . odbc_errormsg() . "</B>"); } // mise en place des bibliothèques pour les fonctions et les procédures $setpath = "set path='QSYS,QSYS2,BIBFCT1,BIBFCT2'"; $result = odbc_exec($db, $setpath); if ($result == 0){ echo "Erreur SETPATH " . $setpath; echo ("<BR><B>Erreur " . odbc_error() . ": " . odbc_errormsg() . "</B>"); } ?>

  3. Mise à jour de données contenant une apostrophe (quote) comme dans 'pomme d'api'

    • Si vous avez activé l'option magic_quotes_runtime (dans php.ini) l'apostrophe est protégée par un \ ('pomme d\'api') Ca marche avec certaines bases, mais pas avec DB2, sauf à ajouter magic_quotes_sybase qui ajoute ' (donc, double la quote) et non \.

    la fonction get_magic_quotes_runtime() permet de connaitre la valeur en cours, set_magic_quotes_runtime() de la modifier.

    • Doublez la quote manuellement, par :
    str_replace("'" , "''" , variableAtraiter);
  4. Si vos données sont en Unicode sur le system i

remplacez SetEnv="CCSID=819" par SetEnv="CCSID=1208" dans le fichier fastcgi.conf (ZendServer uniquement)

ajoutez :

odbc_setoption($link, 1, SQL_ATTR_UTF8, 1);
 

6.Autres accès base de données


	MONPF de type *FILE dans MABIB non trouvé. SQLCODE=-204

Sur IBM i, il est préférable d'utilisier db2_fetch_array() au lieu de db2_fetch_row()/db2_result().

En général db2_fetch_row()/db2_result() a plus de problèmes avec des types de colonne variés dans la traduction de EBCDIC/ASCII, en incluant de possibles troncatures dans les applications DBCS. db2_result ne fonctionne pas, non plus avec des BLOB. Vous pourriez enfin, aussi trouver de meilleures performances à utiliser db2_fetch_array().


 

il s'agit d'un objet, masquant la complexité et les disparités des différents drivers. 

il offre le grand avantage de proposer un accès transparent aux base de données y compris l'appel aux procédures cataloguées.

Création d'une nouvelle instance de l'objet PDO (cela créé la connexion)


<?php
 try {
   // connexion via le driver
DB2_CONNECT
   
// ibm:AS400 aurait utilisé DB2 connect

   // on peut fournir un tableau d'options propres au driver
   $db = new PDO('ibm:AS400', 'user', 'pwd' );
   echo "Connecté !"
 } catch (Exception $e) {
   echo "Erreur : " . $e->getMessage();
 }


?>

 

Exécution d' une requête et affichage du résultat


$req = $db->prepare("select * from fichier ");

if ($req->execute()) {
while (($row = $req->fetch()) !== false) {
print_r($row);
}
}

Deuxième exemple plus détaillé

<?php
 // Ouverture d'une connection PDO
 $bdd = new PDO('ibm:AS400', "user", "pwd");
 // Requete
 $query= "select vin_code, vin_nom from bdvinA.vins order by vin_nom fetch first 10 rows only";
           
 $request = $bdd->query($query);
           
?>
<p>Résultat : </p>
<table border ="1">
<tr>
<th>ID</th>
<th><b>Nom du vin</th>
</tr>
<?php
 while ($ligne = $request->fetch()) {
           echo("<TR>");
           echo("<TD>" . $ligne['VIN_CODE'] . "</TD>");
           echo("<TD>" . $ligne['VIN_NOM'] . "</TD>");
           echo("</TR>");
   
 }
$request->closeCursor();
?>

Gestion d'une transaction (commit/rollback)


try {
   // connexion via DB2 Connect
   $db = new PDO('ibm:AS400', 'user', 'pwd', array(PDO_ATTR_PERSISTENT => true));
   $db->setAttribute(PDO_ATTR_ERRMODE, PDO_ERRMODE_EXCEPTION);


    $db->beginTransaction();
    $db->exec("insert into BDVIN1.appellations values (998 , 'Priorat')");
    $db->exec("insert into BDVIN1.appellations values (999 , 'DAO Penedes')");
    $db->commit();

 } catch (Exception $e) {
   $db->rollBack();
   echo "Erreur : " . $e->getMessage();
}

 

Appel à une procédure cataloguée (le paramètre a été déclaré en sortie, c.a.d retourné par la procédure)

$req = $db->prepare("CALL BDVIN1.procedure1(?)");
$req->bindParam(1, $retour, PDO_PARAM_STR, 4000); // N° , $variable, type, lg

// exécution
$req->execute();

print "valeur retourn&eacute;e $retour\n";

 


Voyez la liste des méthodes à http://php.net/manual/fr/book.pdo.php

et cette page IBM expliquant comment utiliser PDO avec DB2

 

BoTTom

 

©AF400