Site Web dynamique reposant sur une base de données
Les Sites Web dynamiques reposent généralement sur des bases de données qui sont interrogées par les scripts CGI utilisés pour leur développement (comme PHP). Le contenu ainsi visible sur la page provient partiellement (voir entièrement) de la base de données. Donc, le fait de modifier les données stoquées dans celle-ci revient à modifier le contenu du site Web.
Le SGBD le plus connu pour accompagner le langage PHP est MySQL. Cependant, PHP peut aussi dialoguer avec plusieurs autres SGBD comme PostgreSQL, Oracle, SQL Server...
Jusqu'à la version 5 de PHP, la connexion à une base de données (en particulier MySQL) se faisait d'une manière transparente grâce à des fonctions du genre mysql_connect(), mysql_select_db(), mysql_query()... Ces fonctions marchent encore sur les dernières versions PHP5.x (y compris PHP5.6), mais elles sont rendues obsolètes à partir de PHP5.5. A la sortie de PHP7 elles ont été supprimées.
Bien que ces fonctions marchaient très bien, leurs limites n'ont pas mis beaucoup de temps pour se manifester, en particulier quand on souhaite migrer vers un SBGD autre que MySQL. En effet, toutes ces fonctions préfixées par mysql_ n'auraient pas marché. Il faut par conséquent repasser en vue une grande partie du code déjà développé.
Heureusement l'objet PDO a été développé et intégré sous forme d'extension au langage PHP.
Objet PDO (PHP Data Objects)
PDO c'est quoi?
PDO signifie PHP Data Objects. Il s'agit d'une interface qui permet au scripts PHP d’interroger une base de données via des requêtes SQL.
PDO est une extension qui s'ajoute au PHP pour que ses différentes fonctionnalités soient disponibles dans le langage. Il constitue une interface d'abstraction de la base de données, c'est à dire qu'on peut utiliser l'ensemble de ses fonctions pour executer des requêtes SQL quelque soit le SGBD. Autrement dit, si l'application Web repose sur le SGBD MySQL, on peut migrer vers le SGBD PostgreSQL sans modifier le code source (quelques modifications mineurs sont requises).
L'abstraction de la base de données constitue un point fort par rapport aux anciennes méthodes d'accès à celles-ci. D'ailleurs, il constitue l'ultime avantage du PDO, sans en être le seul.
Le bon fonctionnement de PDO repose sur la disponibilité du pilote de la base de données. Il faut que celui-ci soit pris en charge pour pouvoir interroger le SGBD souhaité.
Pour déclarer le pilote du SBGD MySQL par exemple, il faut aller dans le fichier php.ini et ajouter la ligne suivante (si elle n'est pas déjà déclarée):
extension = php_pdo_mysql.dll
Notez que la ligne suivante doit aussi figurer:
extension = php_pdo.dll
Cette étape est nécessaire pour les version PHP inférieures à 5.3.
Utilisation de l'objet PDO (Connexion à une base de données)
Pour simplifier l'explication, je vais me baser sur un exemple:
Supposons que le serveur de base de données est le serveur local (localhost) et que la base données (MySQL) que l'on souhaite interroger s'appelle "mabase". Pour arriver à interroger cette base de données on utilisera le nom d'utilisateur "user" et le mot de passe "1234". Bien entendu, l'utilisateur "user" dispose juste d'assez de privilèges pour interroger la base de données via les scripts PHP.
On suppose maintenant que dans la base de données "mabase" on a créé une table nommée "utilisateurs" dont la structure est la suivante:
CREATE TABLE `utilisateurs` (
`id` int(10) unsigned NOT NULL auto_increment,
`date` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
`nom` varchar(40) NOT NULL,
`prenom` varchar(40) NOT NULL,
`login` varchar(40) NOT NULL,
`pass` varchar(40) NOT NULL,
PRIMARY KEY (`id`)
);
Je suppose que vous êtes à l'aise avec le langage SQL. Sinon, exécutez juste ce code via l'outil PHPMyAdmin sur votre serveur.
Maintenant que tous les paramètres de notre base de données sont là. On va voir comment l'interroger à l'aide de PHP via l'objet PDO.
Chaîne de connexion à la base de données (Instance PDO)
La Chaîne de connexion est la représentation textuelle des informations de connexion à la base de données installée sur un serveur. La déclaration de la chaîne de connexion dans ce cas revient à créer une instance PDO comme ceci:
<?php
$pdo = new PDO("mysql:host=localhost;dbname=mabase","user","1234");
?>
$pdo correspond à l'instance PDO créée en passant au constructeur les paramètres qui constituent la chaîne de connexion. Trois paramètres sont renseignés:
Le premier paramètre correspont à la chaîne de caractères "mysql:host=localhost;dbname=1234". Le mot clé mysql s'appelle préfixe DSN (pour Data Source Name). Il indique qu'on a affaire à une base de données MySQL. Si vous utilisez un autre SGBD vous renseignez le DSN correspondant. D'ailleurs, c'est pratiquement le seul paramètre qui change quand on passe d'un SGBD à un autre (d'où le nom abstraction de la base de données).
Après le préfixe DSN on met deux point suivis des mots clés host et dbname auxquels on affecte respectivement le nom du serveur de base de données (localhost dans ce cas) et le nom de la base de données (mabase).
Il est possible d'ajouter d'autres mots clés comme port pour désigner un numéro de port particulier (sur lequel le SBGD écoute les requêtes) ou charset pour spécifier l'encodage (ou jeux de caractères). Ces différents paramètres sont espacés par point virgule.
Le deuxième paramètre correspond au nom d'utilisateur qui doit être préalablement créé sur le serveur de base de données. Cet utilisateur détient un certains nombre de privilèges et il ne peut passer au serveur que les requêtes définies par les privilèges qui lui sont attribués.
Le troisième paramètre correspond au mot de passe de l'utilisateur.
Gestion d'erreurs éventuelles lors de l'instanciation (Exception PDOException)
Si les paramètres renseignés au constructeur de la classe PDO sont corrects et que le serveur de base de données fonctionne normalement alors l'objet $pdo sera créé sans encombre. Par contre si les paramètres ne son pas bien déclarés ou que le serveur de base de données n'est pas accessible alors une exception de type PDOException (vue dans la page précédente) est lancée automatiquement. Il faut donc la rattraper comme ceci:
On imagine que si notre projet Web est composés de plusieurs pages, alors la plupart d'entre elles aura besoin de dialoguer avec la base de données. Le code précédent sera donc déclaré dans chacune d'elles. Il faut alors songer à mettre ce code dans un fichier à part qu'on inclura au besoin (à l'aide de include() ou require()...).
Préparation et exécution des requêtes (Requêtes préparées)
L'un des points fort de PDO est les requêtes préparées. En effet, une requête préparée est plus rapide et, aussi, plus sûre (surtout contre les tentatives d'injections SQL).
Pour préparer une requête, il est recommandé de ne pas y spécifier les valeurs, mais plutôt remplacer celle-ci par des marqueurs interrogatifs (?) ou des paramètres nommées.
Pour mieux comprendre, supposons que l'on veut insérer dans la table "utilisateurs" les valeurs suivantes:
- Nom: Einstein
- Prénom: Albert
- Login: a.einstein
- Mot de passe: 2020
Nous allons donc préparer la requête puis l’exécuter comme ceci:
La méthode prepare() de l'objet PDO permet de préparer une requête. Elle accepte comme paramètre une chaîne de caractères qui correspond à la requête souhaitée. Les marqueurs interrogatifs sont là pour désigner les valeurs à passer à la requête. Ces valeurs seront ensuite passées lors de l’exécution de la requête. $ins est l'objet correspondant à la requête préparée.
La méthode execute() est appelée par l'objet $ins qui correspond à la requête préparée. Elle accepte comme paramètre une variable de type tableau qui contient les valeurs qui remplaceront, dans l'ordre, les marqueurs interrogatif. Première entrée pour le premier marqueur, deuxième entrée pour le deuxième marqueur et ainsi de suite.
Si la requête préparée ne contient aucun marqueur interrogatif alors la méthode execute() est laissée vide.
Vous avez sans doute remarqué que le mot de passe a été haché par la fonction md5(). Il ne faut jamais stocker les mots de passe en claire (ni dans un fichier, ni dans la base de données).
Le traitement précédent peut être fait à l'aide des paramètres nommés au lieux des marqueurs interrogatifs comme ceci:
Dans la requête préparée, on déclare à la place des valeurs des séquences qui commencent par deux points (:nom ou :prenom par exemple). Lors de l’exécution, on passe en paramètre un tableau associatif dont les clés sont les paramètres nommés (en tant que chaînes de caractères). Les valeurs du tableau seront les valeurs que l'on veut passer à la requête.
Cette technique ne nous oblige pas de déclarer les valeurs au sein du tableau passé lors de l’exécution dans l'ordre établi dans la requête. C'est utile quand la requête contient un nombre important de paramètres, ce qui rend le respect de l'ordre peu compliqué.
De la même manière on peut passer des requêtes de modification, suppression, vidage de la table...
Exemple de suppression de l'enregistrement inséré:
<?php
$ins = $pdo->prepare("delete from utilisateurs where id=? limit 1");
$ins->execute(array(1));
?>
Nous avons supposé que le id (qui constitue la clé primaire de la table) de cet enregistrement vaut 1.
Requête de sélection
Pour passer une requête de sélection il y a un petit traitement à ajouter à ce que nous avons vu précédemment. Pour mieux comprendre, nous allons alimenter notre table avec des enregistrements à l'aide de la requête SQL suivante (que vous pouvez passer directement via l'outil PHPMyAdmin):
Supposons maintenant que nous souhaitons afficher toutes ces lignes dans une pages Web. On va donc procéder ainsi:
Tout d'abord on va préparer puis executer la requête de sélection:
<?php
$ins = $pdo->prepare("select * from utilisateurs order by id");
$ins->setFetchMode(PDO::FETCH_ASSOC);
$ins->execute();
?>
Maintenant, il faut récupérer les enregistrements dans une variable PHP. En fait, on va récupérer toute la table dans une seule variable. Alors, il est évident que la variable sera un tableau à deux dimensions. Une pour désigner la ligne et l'autre pour désigner la colonne.
Revenons à la nouvelle méthode setFetchMode(). Il s'agit d'une méthode facultative appelée par l'objet requête et elle accepte comme paramètre une constante de classe. On utilise souvent PDO::FETCH_ASSOC pour dire que l'on veut récupérer les résultats dans un tableau associatif. Dans ce cas, le tableau récupéré aura dans le premier crochet le numéro de la ligne (commençant de 0) et dans le deuxième crochet une clé qui n'est rien d'autre que la valeur de l'attribut de la table (id, date, nom...).
Le code qui permet de verser la table dans la variable tableau est le suivant:
<?php
$tab = $ins->fetchAll();
?>
La méthode fetchAll() appelée par l'objet requête retourne un tableau contenant toutes les lignes retournées par la requête.
Désormais que nous disposons de la variable $tab, on peut en faire ce qu'on veut. Nous avons choisi d'afficher toutes les lignes sur la page. Le code ressemblerait donc à ceci: