Accès à une base de données
Créez site11 en copiant site10.
- /cms
- ...
- site10
- site11
Dans ce chapitre, nous allons programmer l'accès à une base de données, la configuration du connecteur et l'initialisation de la connexion au démarrage du programme, puis un jeu de fonctions permettant d'enregistrer des variables dans la BD.
Pour tester le résultat en ligne, entrez http://www.frasq.org/cms/site11 dans la barre d'adresse de votre navigateur.
Créez un utilisateur appelé frasqdb2
dans la table user
de la base de données mysql
:
(Host, `User`, Password, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv, Drop_priv, Reload_priv, Shutdown_priv, Process_priv, File_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Show_db_priv, Super_priv, Create_tmp_table_priv, Lock_tables_priv, Execute_priv, Repl_slave_priv, Repl_client_priv, Create_view_priv, Show_view_priv, Create_routine_priv, Alter_routine_priv, Create_user_priv, Event_priv, Trigger_priv, ssl_type, max_questions, max_updates, max_connections, max_user_connections)
VALUES ('localhost', 'frasqdb2', PASSWORD('Fch9Xw4k'), 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', '', '0', '0', '0', '0')
IMPORTANT : Choisissez un mot de passe aléatoire et notez-le. Pensez à sélectionner la fonction PASSWORD pour le champ password
sinon la valeur ne sera pas chiffrée et le mot de passe transmis par PHP, qui sera chiffré, ne correspondra pas.
Ajoutez les droits d'accès de l'utilisateur frasqdb2
à la BD frasqdb2
dans la table db
:
(Host, Db, USER, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv, Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv, Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv)
VALUES ('localhost', 'frasqdb2', 'frasqdb2', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N', 'Y', 'Y', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N', 'N')
Quittez MySQL :
mysql> quit
Lisez l'article Les outils du développeur web pour apprendre à installer MySQL et plus généralement un environnement LAMP.
Un connecteur à une BD a 4 paramètres : le nom de l'utilisateur de la BD et son mot de passe, le nom ou l'adresse IP du serveur et le nom de la BD.
Ajoutez le fichier de configuration db.inc dans le dossier includes avec le contenu suivant :
- /cms/site11
- includes
- db.inc
- includes
- $db_url = 'mysql://username:password@localhost/databasename';
- $db_prefix = false;
- $db_debug = false;
$db_url
définit le connecteur à la BD. Remplacez databasename et username par frasqdb2, password par le mot de passe que vous avez noté quand vous avez créé l'utilisateur de la BD.
$db_prefix
permet de changer les noms de toutes les tables afin d'éviter des conflits avec d'autres composants de la BD.
$db_debug
à true
trace tous les ordres SQL.
IMPORTANT : Assurez-vous que ce fichier est protégé d'un accès extérieur mais toujours lisible par Apache :
$ chmod 600 db.inc
$ sudo chown www-data db.inc
Initialisez la connexion à la BD au démarrage du programme dans la fonction bootstrap
définie dans le fichier bootstrap.php du dossier library :
- @include 'db.inc';
Charge la configuration de la connexion à la base de données.
- if (isset($db_url) && $db_url == 'mysql://username:password@localhost/databasename') {
- $db_url = false;
- }
- if ($db_url) {
- require_once 'pdo.php';
- db_connect($db_url);
- }
Met $db_url
à false
si $db_url
est à la valeur qui rappelle les paramètres du connecteur.
Si $db_url
n'est pas false
, charge le fichier des fonctions d'accès à la BD puis ouvre la connexion.
Ajoutez le fichier pdo.php dans le dossier library avec le contenu suivant :
- /cms/site11
- library
- pdo.php
- library
- $db_conn=false;
- $db_scheme=false;
- function db_connect($url, $persistent=true) {
- global $db_conn, $db_scheme;
- $url = parse_url($url);
- $scheme = $url['scheme'];
- $host = urldecode($url['host']);
- if (isset($url['port'])) {
- $host = $host . ':' . $url['port'];
- }
- $user = urldecode($url['user']);
- $pass = isset($url['pass']) ? urldecode($url['pass']) : '';
- $path = urldecode($url['path']);
- if ($path[0] == '/') {
- $path = substr($path, 1);
- }
- $dsn = "$scheme:host=$host;dbname=$path";
- $options = array(PDO::ATTR_PERSISTENT => $persistent ? true : false);
- try {
- $db_conn = new PDO($dsn, $user, $pass, $options);
- $db_conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
- $db_conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
- $db_conn->exec("SET NAMES 'utf8'");
- if ($scheme == 'mysql') {
- $db_conn->exec("SET SQL_MODE='ANSI_QUOTES'");
- }
- $db_scheme=$scheme;
- }
- catch (PDOException $e) {
- die($e->getMessage());
- }
- return $db_conn;
- }
db_connect
analyse $url
pour en extraire les paramètres de connexion à la BD puis ouvre une connexion permanente avec le serveur et sélectionne la BD demandée.
En cas de problème, db_connect
remonte l'erreur MySQL et déclenche une erreur PHP.
Avant de retourner le connecteur, db_connect
force l'encodage des caractères en UTF-8.
Notez que $db_conn
est une variable globale.
- function db_query($sql) {
- global $db_debug;
- global $db_conn;
- if ($db_debug) {
- dump($sql);
- }
- try {
- $r = $db_conn->query($sql);
- }
- catch (PDOException $e) {
- die($e->getMessage());
- }
- $rows = $r->fetchAll(PDO::FETCH_ASSOC);
- if (!$rows) {
- return false;
- }
- return $rows;
- }
db_query
exécute la requête $sql
en appelant la fonction privée _db_sql_query
et retourne toutes les lignes de réponse dans un tableau ou false
si la requête n'a rien renvoyé.
- function db_query($sql) {
- global $db_debug;
- global $db_conn;
- if ($db_debug) {
- dump($sql);
- }
- try {
- $r = $db_conn->query($sql);
- }
- catch (PDOException $e) {
- die($e->getMessage());
- }
- $rows = $r->fetchAll(PDO::FETCH_ASSOC);
- if (!$rows) {
- return false;
- }
- if (get_magic_quotes_runtime()) {
- foreach ($rows as $row) {
- foreach ($row as $k => &$v) {
- $v = stripslashes($v);
- }
- }
- }
- return $rows;
- }
Dans cette version utilisée avant PHP 7, db_query
retire les \ (BACKSLASH) des valeurs retournées ajoutés automatiquement par PHP si le paramètre de configuration magic_quotes_gpc
est à true
.
- function db_insert($sql) {
- return _db_sql_exec($sql);
- }
- function db_update($sql) {
- return _db_sql_exec($sql);
- }
- function db_delete($sql) {
- return _db_sql_exec($sql);
- }
- function db_exec($sql) {
- return _db_sql_exec($sql);
- }
- function db_insert_id($id=null) {
- global $db_conn;
- $r = $db_conn->lastInsertId($id);
- return $r;
- }
db_insert
, db_update
et db_delete
retourne le résultat de l'exécution de la requête $sql
par la fonction privée _db_sql_query
.
db_insert_id
retourne la clé primaire créée par le dernier appel à db_insert
.
- function db_sql_arg($s, $escape=true, $optional=false) {
- global $db_conn;
- if ($s === NULL or $s === false or $s === '') {
- return $optional ? 'NULL' : "''";
- }
- return $escape ? $db_conn->quote($s) : "'$s'";
- }
db_sql_arg
prépare un argument pour une requête SQL en ajoutant une ' (QUOTE) avant et après une valeur littérale.
Mettez $escape
à false
seulement si $s
ne peut pas contenir de caractères spéciaux pour SQL.
Mettez $optional
à true
pour un champ dont la valeur est optionnelle.
IMPORTANT : PHP doit être connecté à une BD pour que la fonction mysql_real_escape_string
fonctionne.
- function db_prefix_table($table) {
- global $db_prefix;
- return $db_prefix ? $db_prefix . $table : $table;
- }
db_prefix_table
ajoute $db_prefix
au début du nom d'une table.
$db_prefix
est défini dans db.inc.
IMPORTANT : Utilisez systématiquement db_prefix_table
et db_sql_arg
pour fabriquer les noms des tables et les valeurs des arguments dans une requête SQL. EXEMPLE :
- $sqlname=db_sql_arg($name, false);
- $tabregistry=db_prefix_table('registry');
- db_delete("DELETE FROM $tabregistry WHERE name=$sqlname LIMIT 1");
$sqlname
contient la valeur de $name
entourée par des guillemets simples, sans caractères d'échappement. Si $db_prefix
vaut fq_
, $tabregistry
vaut fq_registry
.
- function _db_sql_exec($sql) {
- global $db_debug;
- global $db_conn;
- if ($db_debug) {
- dump($sql);
- }
- try {
- $r = $db_conn->exec($sql);
- }
- catch (PDOException $e) {
- die($e->getMessage());
- }
- return $r;
- }
_db_sql_exec
trace la requête $sql
si $db_debug
est true
puis l'exécute.
En cas d'erreur, PHP est arrêté.
_db_sql_exec
est une fonction privée appelée par toutes les fonctions qui exécutent une requête SQL. Elle permet de centraliser les appels à la méthode exec
de la classe PDO.
$db_debug
est défini dans db.inc.
Modifiez la page d'accueil, en français et en anglais, pour afficher les numéros de version de PHP, de MySQL et du système du serveur web :
- /cms/site11
- views
- en
- home.phtml
- fr
- home.phtml
- en
- views
- <h3>Bienvenue</h3>
- <p>PHP <?php echo phpversion(); ?><br />
- MySQL <?php echo db_version(); ?><br />
- <?php echo php_uname('s'); ?> <?php echo php_uname('r'); ?></p>
- <h3>Welcome</h3>
- <p>PHP <?php echo phpversion(); ?><br />
- MySQL <?php echo db_version(); ?><br />
- <?php echo php_uname('s'); ?> <?php echo php_uname('r'); ?></p>
Entrez http://localhost/cms/site11 dans la barre d'adresse de votre navigateur pour vérifier que le site est connecté à la BD.
Mettez $db_url
à false
dans le fichier includes/db.inc pour ne plus connecter le programme à la BD. Rechargez la page d'accueil. Une erreur est générée.
La fonction db_version
n'est pas définie. Le fichier pdo.php n'a pas été chargé par la fonction bootstrap
.
Pour illustrer les fonctions d'accès à une BD, nous allons programmer un registre permanent de sauvegarde de variables et de leurs valeurs :
$ mysql -u root -p
Entrez dans la BD du site que vous venez de créer :
mysql> USE frasqdb2;
Créez la table registry
avec les champs name
et value
:
mysql> CREATE TABLE registry (
name varchar(100) NOT NULL,
value longtext NOT NULL,
PRIMARY KEY (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Ajoutez le fichier registry.php dans le dossier library avec le contenu suivant :
- /cms/site11
- library
- registry.php
- library
- function registry_get($name, $default=false) {
- $sqlname=db_sql_arg($name, false);
- $tabregistry=db_prefix_table('registry');
- $r = db_query("SELECT value FROM $tabregistry WHERE name=$sqlname LIMIT 1");
- return $r ? unserialize($r[0]['value']) : $default;
- }
registry_get
retourne la valeur de la variable dont le nom est donné par $name
.
Les valeurs enregistrées dans la BD sont sérialisées.
registry_get
fabrique une requête SQL qui extrait la valeur de la variable demandée et la désérialise avant de la retourner.
Notez l'emploi des fonctions db_sql_arg
et db_prefix_table
.
Si la variable $name
n'existe pas, registry_get
retourne la valeur donnée par $default
qui vaut false
par défaut.
- function registry_set($name, $value) {
- $sqlname=db_sql_arg($name, false);
- $sqlvalue=db_sql_arg(serialize($value), true);
- $tabregistry=db_prefix_table('registry');
- db_insert("INSERT $tabregistry SET name=$sqlname, value=$sqlvalue ON DUPLICATE KEY UPDATE name=VALUES(name), value=VALUES(value)");
- }
registry_set
enregistre la variable dont le nom est donné par $name
avec la valeur donnée par $value
.
Les valeurs enregistrées dans la BD sont sérialisées.
Si la variable est déjà enregistrée, sa valeur est remplacée.
- function registry_delete($name) {
- $sqlname=db_sql_arg($name, false);
- $tabregistry=db_prefix_table('registry');
- db_delete("DELETE FROM $tabregistry WHERE name=$sqlname LIMIT 1");
- }
registry_delete
supprime la variable dont le nom est donné par $name
.
Commentaires