[Plan du site]
Vous êtes ici ---
> Le Site du Zéro
> Les tutoriels
> Non-Officiels
> Site Web
> PHP
> Base de données
> Lecture du tutoriel
SQL en POO sans prise de tête
Vous vous apprêtez à lire un tutoriel rédigé par un membre de ce site. Malgré tout le soin que ce membre a pu apporter au tutoriel, nous ne pouvons pas garantir que les informations contenues sur cette page sont exactes à 100%. Merci de garder cela en tête lorsque vous lirez cette page ;o)
Vous avez sans doute déjà aperçu un certain nombre de tutos & sites qui vous disaient en gros caractères alléchants :
« Simplifiez SQL en utilisant la POO ! ».
Dans 95 % des cas, ces tutos & sites vous proposaient de créer une classe gérant les principales fonctions dont vous avez besoin avec SQL.
Mais saviez-vous que, depuis PHP 4.1.3, tous ces tutos sont obsolètes ? Car il existe
une extension de PHP qui, une fois activée, fait déjà tout ça - et même des trucs auxquels vous n'avez même pas pensé !
C'est cette extension et ses avantages (et inconvénients

) que nous allons voir dans ce tuto.
Ici, je pars du principe que vous désirez utiliser MySQL, et que vous voulez utiliser la Programmation Orientée Objet (POO).
D'aucuns dirons qu'il y a d'autres méthodes, qu'elle n'est pas optimale, etc. Ce n'est pas le problème ici. Ce tutoriel présente une solution, et n'a pas la prétention de présenter la meilleure solution (qui, de toutes façons, n'existe pas dans l'absolu).
Bonne question, ça :
De quoi ai-je besoin pour pouvoir utiliser cette extension qui, d'après toi, va me faire tout mon boulot tout seul ?
Heu... D'abord, j'ai pas dit que ça allait faire le boulot tout seul, hein, j'ai dit que ça allait le simplifier. Nuance.
De quoi parle-t-on ?
D'abord, il faut comprendre le titre, ce qui implique deux choses.
1- Savoir ce qu'est SQL et comment on s'en sert d'habitude
Je ne peux que vous renvoyer à
l'excellent tuto de M@teo21.
2- Savoir ce qu'est la Programmation Orientée Objet (POO)
En fait, on ne va pas avoir besoin de toute la puissance de la programmation orientée objet ici. Pour comprendre ce tuto, il faut que vous sachiez juste :
- les notions de base (attribut, méthode) ;
- ce qu'est l'héritage (et les notions associées) ;
- ce qu'est un constructeur.
Je rappellerai comment on fait tout ça en PHP ; en cas de doute, vous pouvez consulter
la partie sur la POO du cours de C++. La théorie est exactement la même !
Comment utiliser MySQLi ?
Maintenant qu'on sait de quoi on parle, il faut
pouvoir utiliser cette fameuse extension. La doc nous dit qu'il faut l'installer, mais, pas de panique ! C'est déjà fait sur tout serveur sérieux !
Pour vous rassurer, voici les hébergeurs et programmes qui l'ont activée :
Mon hébergeur ou programme n'est pas dans la liste, ça veut dire que je ne peux pas l'utiliser ?
Absolument pas ! Simplement, je n'ai pas cette information.
D'ailleurs, voici sa cousine, la liste des hébergeurs / programmes qui
ne permettent pas l'utilisation de MySQLi :
- Free
- Sans doute les très vieilles versions de EasyPHP, mais il faudrait se mettre à jour !
Si vous connaissez un serveur qui n'est pas dans l'une de ces listes, communiquez-le moi (forum, MP) et je l'ajouterai.
Comment faire pour savoir si je peux utiliser MySQLi ?
Le plus simple est de créer un fichier, appelé (par exemple)
version.php, et qui contient juste ça :
Code : PHP
Mettez-le sur le serveur et allez voir avec votre navigateur internet préféré. Vous devriez obtenir un gros tas de tableaux ; cherchez un peu (Ctrl + F ?

) ; si vous trouvez un gros titre disant « MySQLi » suivi d'un tableau, c'est bon. Sinon, vous ne pouvez pas utiliser MySQLi en l'état.
Que faire si je ne peux pas utiliser MySQLi ?
Il faut demander au responsable du serveur de l'activer :
Si c'est vous (donc que vous développez sur votre PC), mettez vos logiciels à jour (EasyPHP, Wamp, PHP seul éventuellement, etc.).
Si vous ne pouvez toujours pas l'utiliser, c'est sans doute qu'elle n'est pas activée, mais là on sort du cadre du tuto.
Si vous êtes sur un serveur hébergé à Pétaouschnock et que vous n'en avez pas les droits d'administration, il faut demander à l'administrateur. Mais bon, là j'y crois pas trop, m'enfin, vous pouvez toujours essayer...
Si vous ne pouvez toujours pas utiliser MySQLi, vous pouvez essayer l'une des nombreuses classes qui émulent son fonctionnement, mais vous perdez l'intérêt de la classe interne à PHP.
Celle-ci n'a pas l'air mal, mais je ne l'ai pas testée.
Notez que si ces classes fonctionnent de la même manière que MySQLi, elles ne sont pas immédiatement compatibles (i.e. les classes, méthodes et attributs ont des noms différents).
La programmation orientée objet en PHP
Comme je ne vais pas refaire ce qui a déjà été fait, je vous renvoie
au tuto de Keeper.
Bien, maintenant qu'on sait ce qu'on va faire et avec quoi, on va pouvoir attaquer les choses sérieuses !
J'utilise des guillemets simples (') pour délimiter mes chaînes de caractères, mais rien ne vous empêche d'utiliser les guillemets doubles (") si vous préférez. Dans mes exemples ça ne change rien, mais faites gaffe dans vos scripts.
Voyez ici pour la différence.
Les objets utilisés
En utilisant MySQLi, on utilise en fait deux types d'objets :
1- l'objet de type MySQLi, qui représente
la connexion à la base de données. C'est grâce à lui qu'on va pouvoir faire nos requêtes et récupérer plein d'informations utiles ;
2- l'objet de type result, qui représente
le résultat de notre action sur la connexion. Typiquement, on s'en sert pour deux choses :
- savoir si la requête s'est bien déroulée ;
- récupérer les résultats d'un select.
Bien sûr, il y a d'autres objets, mais ils ne nous intéressent pas à notre niveau.
L'utilisation de base
Créer une connexion
Ben oui, ça n'a pas changé : si on ne se connecte pas à la base de données, on ne peut strictement rien faire.
Il s'agit tout simplement de créer un nouvel objet
MySQLi qui représente ? Oui, une connexion !
Ça se fait très simplement, de la manière suivante :
Code : PHP1
2
3 | <?php
$connexion = new mysqli('adresse_du_serveur', 'login', 'mot_de_passe', 'nom_de_la_base_de_donnees');
?>
|
On peut évidemment sélectionner la base de données à un autre moment :
Code : PHP1
2
3
4 | <?php
$connexion = new mysqli('adresse_du_serveur', 'login', 'mot_de_passe');
$connexion->select_db('nom_de_la_base_de_donnees');
?>
|
Faire une requête
Bon, maintenant qu'on a une connexion, on aimerait bien l'utiliser !
Rien de plus simple !
Code : PHP1
2
3 | <?php
$resultat = $connexion->query('MA REQUETE SQL');
?>
|
Ici la variable
$resultat contient ce qu'a retourné la requête SQL : un objet
result, ou encore
false si la requête n'a pas marché, ou tout un tas de trucs en fonction de ladite requête.
Fermer la connexion
Citation : Lao-TseuUne connexion inutile, jamais ne doit être gardée.
En conséquence de quoi, les programmeurs ont prévu une fonction permettant de fermer une connexion :
Code : PHP1
2
3 | <?php
$connexion->close(); // close() est une méthode, donc n'oubliez pas les parenthèses !
?>
|
Traiter un résultat
Bon, maintenant on a (sans doute) récupéré le résultat de notre requête, mais il faut bien en faire quelque chose...
Défiler une liste de résultats
C'est ce qu'on fait le plus souvent, et si vous savez faire ça, vous pouvez récupérer un seul résultat.
Code : PHP1
2
3
4
5
6
7 | <?php
while ($ligne = $resultat->fetch_assoc()) {
/* Traitement du résultat
On accède aux différentes colonnes par : */
$ligne['nomDeLaColonne'];
}
?>
|
Ou, en renvoyant un objet :
Code : PHP1
2
3
4
5
6
7 | <?php
while ($ligne = $resultat->fetch_object()) {
/* Traitement du résultat
On accède aux différentes colonnes par : */
$ligne->nomDeLaColonne;
}
?>
|
Vous devez avoir des noms de colonnes uniques pour ces fonctions. Si vous avez une requête qui renvoie deux noms de colonnes identiques, seule la première sera prise en compte. Dans ce cas, utilisez fetch_row().
PHP nous informe que la méthode
fetch_row() n'est pas significativement plus rapide que
fetch_array() ou
fetch_object(). Utilisez ces deux dernières, elles sont plus claires et permettent au code d'être relu plus facilement...
Compter le nombre de lignes envoyées
C'est très simple, l'objet
$resultat a une propriété qui donne directement cette valeur :
Code : PHP1
2
3 | <?php
$resultat->num_rows
?>
|
Fermer un résultat
Comme la connexion, il est inutile de garder en mémoire un résultat qui ne sert plus à rien, et ça peut prendre beaucoup de place dans la mémoire !
Vous pouvez utiliser n'importe laquelle des méthodes suivantes, seul le nom change :
Code : PHP1
2
3
4
5 | <?php
$resultat->close();
$resultat->free();
$resultat->free_result();
?>
|
Comme pour la connexion, n'oubliez pas les parenthèses () : c'est une méthode.
Il existe plein d'autres fonctions de traitement de résultat, mais elles sont assez spécifiques ou ne nous intéressent pas. N'hésitez pas à
aller voir la doc pour plus d'infos.
Et ? C'est tout sur le traitement des résultats ?
Oui. Le reste se fait exactement comme vous le faisiez avec la bibliothèque SQL « standard ».
Maintenant que vous avez les bases, je vais vous présenter ce qu'on peut facilement faire grâce à MySQLi.
Gestion d'erreurs
Par défaut, MySQLi reste muet sur les erreurs qui peuvent survenir dans vos requêtes. Nous allons le faire parler

(mais sans violence, je vous rassure).
Le code de cette partie fonctionne avec PHP5, je ne l'ai pas testé avec PHP4, et je crois qu'il ne fonctionne pas (la gestion objet est très différente).
Une nouvelle classe
Donc, nous voulons une classe qui fait la même chose que MySQLi
et en plus qui nous affiche les erreurs, pour nous aider à débugger.
Rien de plus simple, il suffit de faire une classe
qui hérite de MySQLi (c'est une classe, donc on peut l'hériter) :
Code : PHP1
2
3
4
5 | <?php
class Sql extends mysqli {
// Le code de la classe, qu'on va remplir au fur et à mesure.
}
?>
|
Voilà, on peut utiliser notre classe
Sql en lieu et place de notre classe MySQLi, et ce en changeant
une seule ligne de code : la création !
Code : PHP1
2
3 | <?php
$connexion = new mysqli('adresse_du_serveur', 'login', 'mot_de_passe', 'nom_de_la_base_de_donnees');
?>
|
devient
Code : PHP1
2
3 | <?php
$connexion = new Sql('adresse_du_serveur', 'login', 'mot_de_passe', 'nom_de_la_base_de_donnees');
?>
|
Notez que contrairement à PHP, je respecte ici la convention qui dit qu'un nom de classe commence par une majuscule.
Erreurs à la connexion
Les premières erreurs peuvent survenir quand on crée la connexion à la base de données (erreur de login / mot de passe, etc.).
Avec MySQLi, on se connecte à la création de l'objet : il faut donc gérer cette erreur dans le constructeur.
Je vais considérer qu'on sélectionne la base de données directement à l'instanciation.
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | <?php
public function __construct($hote, $login, $pass, $base) { // On surcharge le constructeur
/* On appelle le constructeur de la classe parent.
Ici, c'est comme si on faisait un « $retour = new mysqli($hote, $login, $pass, $base) », sauf que comme on est dans une classe héritée de MySQLi, on doit utiliser cette syntaxe pour accéder aux méthodes de la classe parent.
*/
$retour = parent::__construct($hote, $login, $pass, $base);
// On regarde si MySQLi a renvoyé un numéro d'erreur à la connexion :
if (mysqli_connect_errno()) {
echo '<p class = "erreur">Échec de la connexion : '.mysqli_connect_error().'</p>'; // Si oui, on affiche le message d'erreur généré
exit(); // Puis on termine le script, rien ne sert de continuer sans la connexion SQL, tout va bugger.
}
return $retour; // On renvoie ce qu'a renvoyé le constructeur de MySQLi
}
?>
|
Erreurs de requêtes
Même concept, cette fois on surcharge la méthode
query(), qui permet d'exécuter les requêtes.
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | <?php
public function query($requete) {
// On exécute la requête via la méthode de la classe parent
$retour = parent::query($requete);
// Si la requête a renvoyé "false", c'est qu'elle a échouée
if (!$retour) {
// On affiche alors le numéro et la description de l'erreur
echo '<p class = "erreur">Erreur SQL n°',$this->errno,' : ',$this->error,'</p>';
// Ainsi que la requête qui a raté
echo '<p class = "erreur">Requête concernée : « ',$query,' »</p>';
$retour = false;
}
return $retour;
}
?>
|
Quel est l'intérêt de ce genre de choses par rapport à un habituel « or die() » placé après les exécutions de requêtes ?
C'est simple : quand vous voulez passer votre site « en production », il est très conseillé de
ne pas afficher les erreurs SQL, surtout parce que les requêtes peuvent donner des indications importantes pour hacker votre site.
Ici, notre classe est entièrement compatible avec MySQLi : si vous vous contentez de créer une connexion avec MySQLi et non la classe SQL, votre code fonctionnera exactement de la même manière - il n'affichera aucune erreur, c'est tout.
Tracer les requêtes
Quand on débugge un site, c'est souvent pratique de savoir si on a des requêtes lentes, et si oui lesquelles.
On va ajouter quelques lignes de code à notre classe pour savoir ça automatiquement !
Ne jamais laisser les traces complètes sur un site accessible au public, à moins que vous ne teniez absolument à vous faire hacker !
On voudrait savoir facilement :
- combien de requêtes ont été effectuées ;
- combien de temps elles ont mis au total ;
- combien de temps chacune d'elle a mis, pour trouver s'il y en a une particulièrement lente.
On va donc ajouter trois attributs à notre classe :
Code : PHP1
2
3
4
5
6
7 | <?php
class Sql extends mysqli {
public $tempsExecution = 0; // Temps d'exécution total
public $nbRequetes = 0; // Nombre de requêtes exécutées dans le script
public $traceRequetes = null; // Tableau associant chaque requête à son temps d'exécution, en millisecondes
}
?>
|
Puis, on va modifier la surcharge de la méthode
query() :
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 | <?php
public function query($requete) {
// On enregistre la requête dans le tableau, à l'emplacement n° $nbRequete.
$this->traceRequetes[$this->nbRequetes]['requete'] = $requete;
// On enregistre le temps serveur avant l'exécution de la requête
$tmpTemps = microtime(true);
// On exécute la requête
$retour = parent::query($requete);
// La différence entre le temps serveur avant et après la requête nous donne le temps d'exécution
$tmpTempsRequete = microtime(true) - $tmpTemps;
// On ajoute ce temps au temps total
$this->tempsExecution += $tmpTempsRequete;
// On enregistre le temps d'exécution de la requête, à l'emplacement n° $nbRequete requête (en millisecondes, avec 2 chiffres après la virgule).
$this->traceRequetes[$this->nbRequetes]['temps'] = round($tmpTempsRequete * 1000, 2);
// On incrémente le nombre de requêtes effectuées
$this->nbRequetes++;
// On renvoie le résultat
return $retour;
}
?>
|
Puis, à la fin du script, il suffit d'afficher l'attribut
traceRequetes pour avoir une liste complète des requêtes exécutées, dans l'ordre, avec leur temps d'exécution.
Par exemple :
Code : PHP1
2
3 | <?php
echo '<pre>',htmlentities(print_r($connexion->traceRequetes, true)),'</pre>';
?>
|
«
<pre> ...
</pre> » affiche le texte tel qu'il est formaté dans la source ;
«
htmlentities( ..., true) » est nécessaire pour ne pas faire bugger le navigateur s'il y a des « < », « > » ou « & » dans la requête ;
et «
print_r ($variable) » formate et affiche le contenu d'une variable.
Ce bout de code affiche quelque chose comme :
Code : Autre1
2
3
4
5
6
7
8
9
10
11
12
13
14
| Array
(
[0] => Array
(
[requete] => SELECT 'Ma requête SQL' FROM machin
[temps] => 33,29
)
[1] => Array
(
[requete] => SELECT 'Une autre requête SQL' FROM truc
[temps] => 1,62
)
) |
Si vous voulez tracer les requêtes sur un site en production, vous pouvez tout simplement enregistrer l'attribut $connexion->traceRequetes dans un fichier, à la fin du script !
La classe complète
Sans les commentaires, pour ne pas surcharger. Tout le code est décortiqué juste ci-dessus, remontez un peu si vous ne comprenez pas quelque chose.
Code : PHP 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 | <?php
class Sql extends mysqli {
public $tempsExecution = 0;
public $nbRequetes = 0;
public $traceRequetes = null;
public function __construct($hote, $login, $pass, $base) {
$retour = parent::__construct($hote, $login, $pass, $base);
if (mysqli_connect_errno()) {
echo '<p class = "erreur">Échec de la connexion : '.mysqli_connect_error().'</p>';
exit();
}
return $retour;
}
public function query($requete) {
$this->traceRequetes[$this->nbRequetes]['requete'] = $requete;
$tmpTemps = microtime(true);
$retour = parent::query($requete);
$tmpTempsRequete = microtime(true) - $tmpTemps;
$this->tempsExecution += $tmpTempsRequete;
$this->traceRequetes[$this->nbRequetes]['temps'] = round($tmpTempsRequete * 1000, 2);
$this->nbRequetes++;
if (!$retour) {
echo '<p class = "erreur">Erreur SQL n°',$this->errno,' : ',$this->error,'</p>';
echo '<p class = "erreur">Requête concernée : "',$query,'"</p>';
$retour = false;
}
return $retour;
}
}
?>
|
Voilà, comme vous avez pu le voir, le grand intérêt de MySQLi est de pouvoir étendre très facilement ses possibilités, et ce en modifiant très peu le code.
Si vous avez un code qui fonctionne déjà avec MySQLi, vous n'aurez qu'à changer la ligne de création de l'objet pour profiter de la classe présentée, par exemple.
Après, à vous de voir ce dont vous avez besoin, laissez courir votre imagination !
Cette partie va être très courte, puisque très simple.
Principe de base
Toutes les fonctions de l'extension MySQL de PHP sont de la forme :
mysql_nom_fonction(), et s'utilisent ainsi :
Code : PHP1
2
3 | <?php
$retour = mysql_nom_fonction($connexion);
?>
|
Où
$connexion est le nom de la variable de connexion, créé au début du script.
Pour migrer votre script, il suffit :
1) de modifier «
$connexion », qui devient un objet de type MySQLi (comme montré ci-dessus) ;
2) de modifier vos appels de fonctions MySQL, qui deviennent tous du type :
Code : PHP1
2
3 | <?php
$retour = $connexion->nom_fonction();
?>
|
Où «
$connexion » ne change pas, et «
nom_fonction() » est le même qu'avant !
Donc,
même pas besoin de réfléchir ! Il suffit de lancer une recherche sur «
mysql_ » et de remplacer !
Pièges à éviter
... Parce que n'est pas si simple que ça !
Le type de l'objet
Dans certains cas, il ne faut pas utiliser :
Code : PHP1
2
3 | <?php
$retour = $connexion->nom_fonction();
?>
|
mais quelque chose du style :
Code : PHP1
2
3 | <?php
$retour = $resultat->nom_fonction();
?>
|
Où «
$resultat » est l'objet renvoyé par la méthode
query().
Mais comment je sais si je dois utiliser $connexion ou $resultat ?
Il suffit de réfléchir 30 secondes : si vous voulez utiliser la connexion, c'est
$connexion ; si vous voulez utiliser un résultat, c'est
$resultat ! En général, c'était déjà le cas avec l'ancien système.
Les attributs
Dans certains cas, ce qui était une fonction devient
un attribut, et non pas une méthode. Donc, si vous laissez les parenthèses à la fin du nom, ça buggera, et le message d'erreur n'est pas très clair.
Le plus utilisé, c'est
Code : PHP1
2
3 | <?php
$retour = mysql_num_rows($resultat);
?>
|
qui devient
Code : PHP1
2
3 | <?php
$retour = $resultat->num_rows; // Sans les parenthèses !
?>
|
Voilà, vous savez (presque) tout de MySQLi. En fait, non. Vous savez faire ce que vous saviez faire avant, plus quelques trucs.
MySQLi permet de faire la même chose que l'extension MySQL (ça, vous savez le faire, maintenant), plus plein d'autres trucs. Les plus intéressants sont la gestion des transactions et surtout la possibilité de « grouper » les requêtes en un seul appel. Mais là, c'est l'objet d'un autre tuto !
N'hésitez pas à
aller consulter et reconsulter la doc, vous y trouverez toujours quelque chose ; d'autant plus qu'elle est claire et en français !