3.1 Tour d’horizon de la programmation objet 133 if (!$this−>connexion = @mysql_pconnect ($serveur , $login , $mot_de_passe)) return 0; // Connexion à la base if (!@mysql_select_db ($this−>nom_base , $this −>connexion)) return 0; return $this−>connexion ; } // Méthode d ’ exécution d ’une requête . protected function exec ($requete) { return @mysql_query ($requete , $this−>connexion) ; } // Partie publique: implantation des méthodes abstraites // Accès à la ligne suivante , sous forme d’ objet public function objetSuivant ($resultat ) { return mysql_fetch_object ($resultat); } // Accès à la ligne suivante , sous forme de tableau associatif public function ligneSuivante ($resultat) {return mysql_fetch_assoc ($resultat); } // Accès à la ligne suivante , sous forme de tableau indicé public function tableauSuivant ($resultat ) {return mysql_fetch_row ($resultat); } // Echappement des apostrophes et autres préparation à // l ’insertion public function prepareChaine($chaine) { return mysql_real_escape_string($chaine); } // Retour du message d’erreur public function messageSGBD () {returnmysql_error ($this−>connexion) ;} // Méthode ajoutée : renvoie le schéma d’une table public function schemaTable($nom_table) { // Recherche de la liste des attributs de la table $liste_attr = @mysql_list_fields($this−>nom_base , $nom_table , $this−>connexion) ; if (! $liste_attr ) throw new Exception ("Pb d’ analyse de $nom_table") ; // Recherche des attributs et stockage dans le tableau for ($i = 0; $i < mysql_num_fields($liste_attr); $i++) { $nom = mysql_field_name($liste_attr , $i); $schema[$nom][ ’longueur ’ ] = mysql_field_len( $liste_attr , $i ); $schema [$nom][ ’ type ’ ] = mysql_field_type($liste_attr , $i); $schema [$nom][ ’ cle_primaire ’ ] = 134 Chapitre 3. Programmation objet substr_count ( mysql_field_flags ($liste_attr , $i) , " primary_key") ; $schema[$nom][ ’not_null ’] = substr_count (mysql_field_flags ($liste_attr , $i) , "not_null"); } return $schema; } // Destructeur de la classe : on se déconnecte function __destruct () { if ($this−>connexion) @mysql_close ( $this−>connexion) ; } // Fin de la classe } ?> On peut noter que la redéfinition du constructeur est inutile puisqu’il est déjà fourni au niveau de la classe parente. En revanche, il faut en définir la partie spécifique, soit les méthodes connect() et exec(). Au moment où on effectuera une instanciation d’un objet de la classe BDMySQL, l’exécution se déroulera comme suit : • le constructeur défini dans la classe BD sera appelé, puisqu’il est hérité, et non surchargé ; • ce constructeur appelle à son tour la méthode connect() qui, elle, est définie au niveau de la classe BDMySQL. Le constructeur lèvera une exception si la méthode connect() échoue. On a bien l’interaction souhaitée entre le code générique de la classe parente et le code spécifique de la classe dérivée. Le même mécanisme s’applique à l’exécution de requêtes, avec la méthode générique execRequete() appelant la méthode spéci- fique exec() (ainsi, éventuellement, que la méthode messageSGBD()), et levant une exception si nécessaire en fonction du retour de cette dernière. Cela étant, une classe publique de la super-classe peut toujours être surchargée. Si on souhaite par exemple lever deux exceptions différentes, une pour l’erreur de connexion au serveur et l’autre pour l’erreur d’accès à une base, on peut redéfinir un constructeur pour la classe BDMySQL comme suit : function __construct ($login , $mot_de_dasse , $base , $serveur) { // On conserve le nom de la base $this−>nom_base = $base ; // Connexion au serveur MySQL if (!$this−>connexion = @mysql_pconnect ($serveur , $login , $mot_de_dasse)) throw new Exception (" Erreur de connexion au serveur . ") ; // Connexion à la base if (! @mysql_select_db ($this−>nom_base , $this−>connexion)) 3.1 Tour d’horizon de la programmation objet 135 throw new Exception ("Erreur de connexion à la base . ") ; } Attention : quand une méthode est surchargée (donc redéfinie dans une classe dérivée), la méthode de la classe parente n’est plus appelée. La surcharge est donc bien un remplacement de la méthode héritée. C’est valable également pour le constructeur : la définition d’un constructeur pour la classe BDMySQL implique que le constructeur de la super-classe BD ne sera plus appelé au moment de l’instanciation d’un objet BDMySQL. Il est cependant possible de faire l’appel explicitement grâce à la syntaxe parent::BD() : voir l’exemple de la classe R´epertoire, page 128. Toutes les autres méthodes abstraites sont ensuite implantées par un simple appel à la fonction correspondante de l’interface de programmation (API) MySQL. Il est bien entendu possible d’étendre la puissance de la classe dérivée en lui ajoutant d’autres fonctionnalités de MySQL. Ces méthodes seraient alors spécifiques aux instances de la classe BDMySQL et ne pourraient donc pas être appelées dans une application souhaitant pouvoir accéder à des SGBD différents et se fondant sur l’interface définie dans la super-classe BD. Regardons de plus près la méthode schemaTable. Si tout se passe bien, elle renvoieun tableau associatif à deux dimensions décrivant pour chaque attribut (pre- mière dimension du tableau) les options de création de la table passée en paramètre (seconde dimension). Il s’agit à peu de choses près des informations du CREATE TABLE : longueur et type d’un attribut donné, et booléen indiquant si cet attribut fait partie de la clé primaire identifiant une ligne de la table. Cette fonction renvoie une valeur dont la taille peut être importante. Cette valeur, initialement stockée dans une variable locale de la méthode, schemaTable, doit ensuite être copiée vers une variable du script appelant. Ce code est correct mais on peut se poser la question de l’impact négatif sur les performances en cas d’appels intensifs à cette méthode pour des tables contenant beaucoup d’attributs. L’utilisation d’un passage par référence peut alors s’envisager (voir la discussion page 61). On aurait le simple changement : function schemaTable($nom_table , &$schema) { // Comme avant } et la fonction alimenterait directement la variable du script appelant, dont on obtient ici une référence. La méthode schemaTable() est une méthode ajoutée (elle sera utilisée pour une autre classe, page 167). La déclarer sous forme de méthode abstraite au niveau de la classe BD enrichirait la spécification des interactions, mais imposerait l’implantation de cette méthode dans toutes les sous-classes. Il reste à définir autant de sous-classes que de SGBD, soit ORACLE, ou Post- greSQL, ou encore SQLite, un moteur SQL directement intégré à PHP depuis la version 5, etc. La classe ci-dessous correspond à PostgreSQL. 136 Chapitre 3. Programmation objet Exemple 3.5 exemples/BDPostgreSQL.php : La classe dérivée BDPostgreSQL <?php // Sous−classe de la classe abstraite BD, implantant l ’accès à // PostgreSQL require_once("BD.php"); class BDPostgreSQL extends BD { // Pas de propriétés: elles sont héritées de la classe BD // Pas de constructeur : lui aussi est hérité // Méthode connect : connexion à PostgreSQL protected function connect ($login , $mot_de_passe , $base , $serveur) { // Quelques ajustements PostgreSQL $login = strToLower($login) ; $base = strToLower($base) ; if ($serveur == ’ localhost ’) $serveur ="" ; // Création de la chaîne de connexion $chaineC = " user=$login dbname=$base password=$mot_de_passe host=$serveur"; // Connexion au serveur et à la base return $this−>connexion = pg_connect ($chaineC); } // Méthode d ’ exécution d ’une requête protected function exec ($requete) { return @pg_exec ( $this −>connexion , $requete ) ; } // −−−− Partie publique −−−−−−−−−−−−−−−−−−−−−−−−− // Accès à la ligne suivante , sous forme d’ objet function objetSuivant ( $resultat ) {return pg_fetch_object ($resultat); } // Accès à la ligne suivante , sous forme de tableau associatif function ligneSuivante ($resultat) { return pg_fetch_assoc ( $resultat) ; } // Accès à la ligne suivante , sous forme de tableau indicé function tableauSuivant ($resultat ) {return pg_fetch_row ($resultat); } // Echappement des apostrophes et autres préparations à // l ’insertion public function prepareChaine($chaine) { return addSlashes($chaine) ; } // Retour du message d ’erreur public function messageSGBD () { return pg_last_error($this−>connexion) ;} // Destructeur de la classe : on se déconnecte 3.1 Tour d’horizon de la programmation objet 137 function __destruct () { @pg_close ($this−>connexion) ; } // Fin de la classe } ?> On retrouve la même structure que pour BDMySQL, avec l’appel aux fonctions cor- respondantes de PostgreSQL, et la prise en compte de quelques spécificités. Caracté- ristique (assez désagréable ) de l’interface PHP/PostgreSQL : tous les identificateurs (noms de tables, d’attributs, de base, etc.) sont systématiquement traduits en minus- cules, ce qui impose quelques conversions avec la fonction PHP strToLower() (voir la méthode connect() ci-dessus). De plus, pour la connexion au serveur localhost, PostgreSQL demande que le nom du serveur soit la chaîne vide. Ces particularités peuvent être prises en compte au moment de l’implantation des méthodes abstraites. On peut maintenant considérer qu’un objet instance de la classe BDMySQL ou un objet instance de la classe BDPostgreSQL sont tous deux conformes au comporte- ment décrit dans la super-classe commune, BD. On peut donc les utiliser exactement de la même manière si on se limite au comportement commun défini dans cette super-classe. Le script suivant montre un code qui, hormis le choix initial de la classe à instancier, fonctionne aussi bien pour accéder à MySQL que pour accéder à PostgreSQL (ou SQLite, ou ORACLE, ou tout autre système pour lequel on définira une sous-classe de BD). Exemple 3.6 exemples/ApplClasseBD.php : Accès générique à un SGBD. <?xml version=" 1.0 " encoding=" iso −8959−1"?> <!DOCTYPE ht ml PUBLIC " −/ /W3C / / DTD XHTML 1 . 0 S t r i c t / / EN " "http ://www.w3. org/TR/xhtml1/DTD/xhtml1− strict.dtd"> <html xmlns="http ://www.w3.org/1999/xhtml" xml: lang=" fr " > <head> <title >Application de la classe BD</title > < link rel=’stylesheet ’ href="films .css" type="text/ css"/> </head> <body> <h1>Application de la classe BD</h1> <?php require_once ("Connect.php"); // La sous−classe pour MySQL require_once ("BDMySQL. class .php"); // La sous−classe pour PostgreSQL require_once ("BDPostgreSQL. class .php"); // La sous−classe pour SQLite require_once ("BDSQLite. class .php"); . code générique de la classe parente et le code spécifique de la classe dérivée. Le même mécanisme s’applique à l’exécution de requêtes, avec la méthode générique execRequete() appelant la méthode. Partie publique: implantation des méthodes abstraites // Accès à la ligne suivante , sous forme d’ objet public function objetSuivant ($resultat ) { return mysql_ fetch_object ($resultat); } //. tableauSuivant ($resultat ) {return mysql_ fetch_row ($resultat); } // Echappement des apostrophes et autres préparation à // l ’insertion public function prepareChaine($chaine) { return mysql_ real_escape_string($chaine);