Tải bản đầy đủ (.pdf) (5 trang)

Pratique de MySQL et PHP- P52 pps

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (146.02 KB, 5 trang )

5.3 Portabilité multi-SGBD 233
5.3 PORTABILITÉ MULTI-SGBD
Nous abordons maintenant la question de la portabilité sur plusieurs systèmes de
bases de données. Le présent livre est principalement orienté vers MySQL, mais ce
produit lui-même s’attache à respecter la norme SQL, ce qui ouvre la perspective de
pouvoir porter une application sur d’autres SGBD. Pour un site spécifique, installé
en un seul exemplaire, avec le choix définitif d’utiliser MySQL, la question de la
portabilité ne se pose pas. À l’autre extrême un logiciel généraliste que l’on souhaite
diffuser le plus largement possible gagnera à être compatible avec des systèmes
répandus comme PostgreSQL, ORACLE, voire SQLite. SQLite est une interface
SQL pour stocker et rechercher des données dans un fichier, sans passer par un
serveur de bases de données. SQLite est fourni avec PHP 5 et ne nécessite donc
aucune installation autre que celle de PHP.
Le site W
EBSCOPE est conçu (et testé) pour être portable, ce qui impose quelques
précautions initiales discutées ici.
MySQL est un SGBD relationnel. Il appartient à une famille de systèmes très
répandus – ORACLE, PostgreSQL, SQL Server, SYBASE, DB2, le récent SQLite –
qui tous s’appuient sur le modèle relationnel de représentation et d’interrogation des
données, modèle dont la principale concrétisation est le langage SQL.
En théorie, toute application s’appuyant sur un SGBD relationnel est portable sur
les autres. En pratique, chaque système ajoute à la norme SQL ses propres spécificités,
ce qui nécessite, quand on veut concevoir une application réellement portable, de
bien distinguer ce qui relève de la norme et ce qui relève des extensions propriétaires.
Cette section décrit les écueils à éviter et donne quelques recommandations. Le
site proposé dans les chapitres qui suivent s’appuie sur ces recommandations pour
proposer un code entièrement portable. La seule modification à effectuer pour passer
d’un système à un autre est un simple changement de paramètre de configuration.
Comme nous allons le voir, le développement d’une application portable n’est pas
plus difficile que celle d’une application dédiée, à condition de mettre en place
quelques précautions initiales simples.


Cette section peut être omise sans dommage par ceux qui n’envisagent pas
d’utiliser un autre système que MySQL.
5.3.1 Précautions syntaxiques
Il faut bien distinguer deux parties dans SQL. Le langage de définition de données, ou
LDD, permet de créer tous les composants du schéma – principalement les tables. Les
commandes sont les CREATE, ALTER,etDROP. Le langage de manipulation de données
(LMD) comprend les commandes SELECT, UPDATE, INSERT et DELETE.
MySQL est très proche de la norme SQL, et tout ce que nous avons présenté
jusqu’ici, à quelques exceptions près, relève de cette norme et peut fonctionner sous
un autre SGBD. Ces exceptions sont :
1. certains types de données, dont, principalement, TEXT ;
234 Chapitre 5. Organisation du développement
2. des constructions comme ENUM et SET ;
3. l’auto-incrémentation des clés (option AUTO_INCREMENT de CREATE TABLE).
Il suffit d’ignorer ENUM et SET. Pour les types de données, MySQL propose un
ensemble plus riche que celui de la norme SQL. Le tableau 2.1, page 462, donne la
liste des types disponibles et précise ceux qui appartiennent à la norme SQL ANSI :
il faut se limiter à ces derniers pour une application portable.
Cela étant, certains types très pratiques, comme TEXT, ne sont pas normalisés (ou,
plus précisément, la norme SQL qui préconise BIT VARYING n’est pas suivie). Il est
souvent nécessaire d’utiliser ce type car les attributs de type VARCHAR sont limités à
une taille maximale de 255 caractères. Le type TEXT existe dans PostgreSQL, mais
pas dans ORACLE où son équivalent est le type LONG. Le script de création de notre
schéma,
Films.sql, page 202, est entièrement compatible avec la norme, à l’exception
du résumé du film, de type TEXT, qu’il faut donc remplacer par LONG si l’on souhaite
utiliser ORACLE. Ce genre de modification affecte l’installation, et pas l’utilisation
du site, ce qui limite les inconvénients.
Un type normalisé en SQL, mais assez difficile d’utilisation est le type DATE.Dans
le cadre d’une application PHP, le plus simple est de stocker les dates au format dit

« Unix », soit un entier représentant le nombre de secondes depuis le premier janvier
1970. Des fonctions PHP (notamment getDate()) permettent ensuite de manipuler
et d’afficher cette valeur à volonté.
Pour les mots-clés de SQL et les identificateurs, il n’y a pas de problème si on se
limite aux caractères ASCII (mieux vaut éviter les lettres accentuées). L’utilisation
des majuscules et minuscules est en revanche un point délicat. Les mots-clés SQL ne
sont pas sensibles à la casse, et il en va de même des identificateurs. Pour un système
relationnel, toutes les syntaxes suivantes seront donc acceptées, quelle que soit la
casse employée pour créer le schéma :

SELECT TITRE FROM FILM ;

select titre from film ;

Select Titre From Film.
Attention cependant à MySQL qui stocke chaque table dans un fichier dont le
nom est celui donné à la table dans la commande CREATE TABLE. Sous un système
UNIX où les noms de fichiers sont sensibles à la casse, MySQL ne trouvera ni la
table FILM ni la table film si le fichier s’appelle
Film. Il faut donc toujours nommer les
tables de la même manière dans la clause FROM, ce qui est facilité par l’emploi d’une
convention uniforme comme – par exemple – une majuscule pour la première lettre
et des minuscules ensuite.
Il faut de plus prendre en compte PHP qui, lui, est sensible à la casse dans les noms
des variables. Les variables $TITRE, $titre et $Titre sont donc considérées comme
différentes. Ces noms de variables sont attribués automatiquement par les fonctions
PHP d’accès aux bases de données comme mysql_fetch_object() (MySQL),
pg_fetch_object() (PostgreSQL), oci_fetch_object() (ORACLE), etc. Tout
5.3 Portabilité multi-SGBD 235
dépend de la manière dont ces fonctions nomment les attributs dans les résultats. Or

les systèmes appliquent des règles très différentes :

MySQL utilise la même casse que celle de la clause SELECT : après un SELECT
Titre FROM Film on récupèrera donc une variable $Titre ;

PostgreSQL utilise toujours les minuscules, quelle que soit la casse employée :
après un SELECT Titre FROM Film on récupèrera donc une variable
$titre ;

ORACLE utilise toujours les majuscules, quelle que soit la casse employée :
après un SELECT Titre FROM Film on récupèrera donc une variable
$TITRE.
Ces différentes conventions sont dangereuses car elle influent directement sur la
correction du code PHP. Avec l’apparition de la couche PDO qui uniformise l’accès
aux bases de données depuis la version PHP 5.1, le problème est plus facile à résoudre,
mais il est préférable dès le départ d’adopter des noms dattributs loù la casse n’est pas
significative : nous avons choisi d’utiliser uniquement les minuscules.
Dernier point auquel il faut être attentif : l’échappement des chaînes de caractères
pour traiter les caractères gênants (typiquement, les apostrophes) avant une insertion
ou une mise à jour. On utilise traditionnellement la fonction addSlashes() qui
convient pour MySQL et PostgreSQL, mais par pour ORACLE, SQLite ou SYBASE
qui utilisent le doublement des apostrophes. Il faut donc encapsuler la technique
d’échappement dans une fonction qui se charge d’appliquer la méthode appropriée
en fonction du SGBD utilisé (c’est la méthode prepareChaine() de notre classe
BD).
5.3.2 Le problème des séquences
Voyons maintenant le problème de l’incrémentation automatique des identifiants. Il
est très fréquent d’utiliser comme clé primaire d’une table un numéro qui doit donc
être incrémenté chaque fois que l’on insère une nouvelle ligne. En l’absence d’un
mécanisme spécifique pour gérer ce numéro, on peut penser à prendre le numéro

maximal existant et à lui ajouter 1. En SQL cela s’exprime facilement comme ceci :
SELECT MAX(id) + 1 FROM < table>
−− puis insertion dans la table avec le numéro obtenu
Cette solution n’est pas très satisfaisante. Il faut en effet s’assurer que deux sessions
utilisateur ne vont pas simultanément effectuer la requête donnant le nouvel id, sous
peine de se retrouver avec deux commandes INSERT utilisant le même identifiant.
On peut verrouiller la table avant d’effectuer la requête SELECT,auprixd’un
blocage temporaire mais général, y compris pour les sessions qui ne cherchent pas
à créer d’identifiant. Enfin, dernier inconvénient, cela peut soulever des problèmes
de performances.
Tous les systèmes fournissent donc des générateurs d’identifiants, ou séquences.
Malheureusement aucun n’applique la même méthode. Dans MySQL, on peut asso-
cier une option AUTO_INCREMENT à une clé primaire (voir par exemple page 199).
236 Chapitre 5. Organisation du développement
Si on n’indique pas cette clé dans une commande INSERT, MySQL se charge auto-
matiquement d’attribuer un nouvel identifiant. De plus il est possible de récupérer
l’identifiant précédemment attribué avec la fonction last_insert_id().SQLite
emploie la même méthode, sans spécifier AUTO_INCREMENT.
Sous ORACLE et PostgreSQL, on utilise des séquences
5
qui sont des composants
du schéma dédiés à la génération d’identifiants. Une séquence est créée par la
commande DDL suivante :
CREATE SEQUENCE <nomS´equence>;
Il existe, pour chaque système, de nombreuses options permettant d’indiquer la
valeur initiale, la valeur maximale, le pas d’incrémentation, etc. Sous PostgreSQL,
on peut obtenir un nouvel identifiant en appliquant la fonction NEXTVAL() àla
séquence. Ensuite, dans la même session, on obtient l’identifiant qui vient d’être
attribué avec la fonction CURRVAL(). Voici un exemple de session sous PostgreSQL.
On crée la séquence, on appelle deux fois NEXTVAL() puis une fois CURRVAL().

Films=# CREATE SEQUENCE ma_sequence;
CREATE SEQUENCE
Films=# SELECT NEXTVAL(’ma_sequence’);
nextval

1
Films=# SELECT NEXTVAL(’ma_sequence’);
nextval

2
Films=# SELECT CURRVAL(’ma_sequence’);
currval

2
Le fonctionnement est pratiquement identique sous ORACLE. Pour obtenir, dans
une application PHP, un générateur d’identifiants qui fonctionne sur tous les SGBD,
il faut donc écrire une fonction (ou une méthode dans une classe) qui fait appel,
selon le système utilisé, à la méthode appropriée. En ce qui concerne MySQL, si on
souhaite que l’application soit portable, on ne peut pas utiliser l’auto-incrémentation
des lignes de la table ; il faut donc se ramener aux séquences trouvées dans les autres
systèmes. On y arrive aisément en créant une table spéciale, avec un seul attribut
auto-incrémenté. Chaque insertion dans cette table génère un nouvel identifiant que
l’on peut alors obtenir avec la fonction last_insert_id(). Voici, sous MySQL,
une session équivalente à celle de PostgreSQL.
5. PostgreSQL fournit également un type non standard SERIAL qui fonctionne comme l’auto-
incrémentation de MySQL.
5.3 Portabilité multi-SGBD 237
mysql> CREATE TABLE SequenceArtiste
-> (id INTEGER NOT NULL AUTO_INCREMENT,
-> PRIMARY KEY (id));

mysql>
mysql> insert into SequenceArtiste values();
Query OK, 1 row affected (0,01 sec)
mysql> insert into SequenceArtiste values();
Query OK, 1 row affected (0,00 sec)
mysql> select last_insert_id();
+ +
| last_insert_id() |
+ +
|2|
+ +
La classe BD, décrite dans le chapitre 3, est enrichie d’une méthode abstraite
genereID(), déclarée comme suit :
// G´en´eration d’un identifiant
abstract public function genereID($nomSequence);
Cette méthode est ensuite déclinée dans chaque sous-classe correspondant à
chaque système. Voici la méthode pour MySQL.
// G´en´eration d’un identifiant
public function genereID($nomSequence)
{
// Insertion d’un ligne pour obtenir l’auto-incr´ementation
$this->execRequete("INSERT INTO $nomSequence VALUES()");
// Si quelque chose s’est mal pass´e, on a lev´e une exception,
// sinon on retourne l’identifiant
return mysql_insert_id();
}
Et la voici pour PostgreSQL.
// G´en´eration d’un identifiant
public function genereID($nomSequence)
{

// Appel `alas´equence
$res = $this->execRequete("SELECT NextVal(’$nomSequence’) AS id");
$seq = $this->objetSuivant($res);
return $seq->id;
}
La gestion des séquences est le seul aspect pour lequel la programmation d’une
application PHP/MySQL s’écarte légèrement des techniques que l’on emploierait si

×