Classe PHP pour gérer une connexion MySQL

Voici un exemple de classe PHP simple permettant de gérer la connexion à une base de données. Cette classe s’assure d’initier une instance de connexion unique via un design pattern singleton.

Les différents fichiers

La connexion est gérée par un seul fichier PHP contenant une classe PHP, mais dans un soucis d’organisation du code, l’exemple proposé ici se composera de trois fichiers :

  1. Un fichier de configuration avec les paramètres de connexion à la base de données ;
  2. La class PHP de connexion à la base de données (le plus important) ;
  3. Un fichier d’exemple pour l’utilisation de la classe.

Le fichier de configuration

Le fichier de configuration est un simple fichier contenant quatre constantes permettant de stocker les paramètres de connexion à la base de données : l’adresse de la base, le nom de la base, le nom d’utilisateur et le mot de passe.

php config.inc.php
<?php

// Paramètres de connexion à la base de données
define('DB_HOST','localhost'); // Adresse de la base, généralement localhost
define('DB_NAME','ma_bdd');    // Nom de la base de données
define('DB_USER','jcrego');    // Nom de l'utilisateur MySQL
define('DB_PASS','Xu78s5');    // Mot de passe de l'utilisateur

La class MySQL

Ce fichier est évidemment le plus important puisqu’il s’agit du sujet de cet article. Cette classe se compose essentiellement de méthodes magiques, mais elle contient également la méthode getInstance() déclarée en statique qui va nous permettre de récupérer l’instance de connexion à la base de données.

La méthode __construct() est destinée à initier la connexion à la base de données en utilisant l’extension PDO.

php MySQL.class.php
<?php
/**
 * Classe MySQL utilisant un design pattern Singleton
 * 
 * @author  Julien Crego <prenomnom@gmail.com>
 * @version 1.0
 */

class MySQL {

    static private $oPDO = NULL;
    static private $oInstance = NULL;
    
    /**
     * Constructeur défini en privé pour le rendre inaccessible
     */
    private function __construct() {    
        self::$oPDO = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
        self::$oPDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
        self::$oPDO->query("SET NAMES 'utf8'");
    }

    /**
     * Methode magique de destruction de l'instance MySQL
     */
    public function __destruct() {
        self::$oPDO = null ;
    }

    /**
     * Methode magique clone verrouillée via une exception
     */
    public function __clone() {
        throw new Exception('Impossible de cloner une connexion SQL protégée par un singleton');
    }

   /**
    * Méthode magique pour rétablir toute connexion de base de données 
    * qui aurait été perdue durant la linéarisation
    */
    public function __wakeUp( ) {
        // Vérification de la connexion
        if(self::$oInstance instanceof self) {
                throw new MySQLException();
        }
        // Correction de la reference
        self::$oInstance = $this;
    }
    
    /**
     * Méthode magique pour l'appel des fonctions de l'objet PDO quand 
     * elles ne sont pas définies dans la classe
     * 
     * @param type $method
     * @param type $params
     */
    public function __call($method, $params) {
        if(self::$oPDO == NULL){
            self::__construct();
        }
        
        return call_user_func_array(array(self::$oPDO, $method), $params);
    }
    
   /**
    * Fournit l'unique instance du Singleton
    *
    * @return	MySQL
    */
    static public function getInstance(){
        // Verification que l'instance n'a pas déja ete initialisée
        if(!(self::$oInstance instanceof self)){
            self::$oInstance = new self();
        }
        // Retour de l'instance unique
        return self::$oInstance;
    }  
}

Le fichier de test

Le fichier de test est uniquement destiné à intégrer les différents fichiers et à faire en sorte que les erreurs PHP éventuelles soient bien visibles.

php demo.php
<?php
// Affichage des messages d'erreurs
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);

// Inclusion des paramètres de configuration
require 'config.inc.php';

// Inclusion de la classe MySQL
require 'MySQL.class.php';

echo "Exemples à venir" ;

Mise en place des conditions de test

Afin de pouvoir vous donner des exemples, je pars du principe que les points suivants sont réglés :

  1. Vous avez créé les trois fichiers.
  2. Vous avez un serveur et une base de données à disposition.
  3. Vous avez personnalisé le fichier config.inc.php avec vos paramètres de connexion MySQL.
  4. Vous avez testé le fichier demo.php dans un navigateur et vous avez obtenu le texte « Exemples à venir » à l’écran sans aucun message d’erreur.

Les différents exemples proposés maintenant peuvent être testés en étant intégré à la suite du fichier demo.php. Les lignes de code de base de ce fichier ne seront pas réécrites à chaque fois.

La base de données utilisée pour les exemples est une base extrêmement simple composée d’une seule table et de quatre enregistrements dont voici le code de création.

--
-- Table structure for table `exemple`
--

CREATE TABLE `exemple` (
  `id` int(11) NOT NULL,
  `label` varchar(50) NOT NULL,
  `type` varchar(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

--
-- Dumping data for table `exemple`
--

INSERT INTO `exemple` (`id`, `label`, `type`) VALUES
(1, 'grand', 'adjectif'),
(2, 'petit', 'adjectif'),
(3, 'marcher', 'verbe'),
(4, 'boire', 'verbe');

--
-- Indexes for table `exemple`
--
ALTER TABLE `exemple`
  ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT for table `exemple`
--
ALTER TABLE `exemple`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
COMMIT;

Exemples d’interrogation de la base de données

Requête SELECT basique

Pour ce premier exemple, nous effectuons une simple requête SELECT.

// La requête est enregistrée dans une variable nommée $req (request)
$req = "SELECT * FROM exemple WHERE id = 1";

// La requête est exécutée en faisant appel à l'instance de connexion à MySQL via $stmt (statement)
$stmt = Mysql::getInstance()->query($req);

// La variable $res (result) stocke un tableau contenant toutes les lignes de résultats
$res = $stmt->fetchAll();

// On affiche les résultats
var_dump($res);

Le résultat de la requête est :

array(1) {
  [0]=>
  array(6) {
    ["id"]=>
    string(1) "1"
    [0]=>
    string(1) "1"
    ["label"]=>
    string(5) "grand"
    [1]=>
    string(5) "grand"
    ["type"]=>
    string(8) "adjectif"
    [2]=>
    string(8) "adjectif"
  }
}

Requête préparée

Dans cette exemple, nous allons sécuriser notre requête en utilisant le principe de requête préparée qui permettent de se protéger des injections SQL.

// La valeur de l'id est remplacé par le symbole ?
$req = "SELECT * FROM exemple WHERE id = ?";

// La requête est préparée sans les valeurs associées
$stmt = Mysql::getInstance()->prepare($req);

// La requête est exécutée avec le tableau [3] comme argument
// Le ? de la requête préparée est donc remplacé par la valeur 3
$stmt->execute([3]);

$res = $stmt->fetchAll();
var_dump($res);

Le résultat de la requête est :

array(1) {
  [0]=>
  array(6) {
    ["id"]=>
    string(1) "3"
    [0]=>
    string(1) "3"
    ["label"]=>
    string(7) "marcher"
    [1]=>
    string(7) "marcher"
    ["type"]=>
    string(5) "verbe"
    [2]=>
    string(5) "verbe"
  }
}

Requête préparée avec plusieurs paramètres

Pour cet exemple, nous avons besoin de deux paramètres dans notre requête.

// Les deux paramètres sont remplacés par des ?
$req = "SELECT * FROM exemple WHERE id = ? AND label = ?";

// La requête est préparée
$stmt = Mysql::getInstance()->prepare($req);

// La requête est exécutée avec le tableau [4, 'boire'] comme argument
// Le premier ? sera remplacé par 4
// Le second ? sera remplacé par 'boire'
$stmt->execute([4, 'boire']);

$res = $stmt->fetchAll();
var_dump($res);

Le résultat de la requête est :

array(1) {
  [0]=>
  array(6) {
    ["id"]=>
    string(1) "4"
    [0]=>
    string(1) "4"
    ["label"]=>
    string(5) "boire"
    [1]=>
    string(5) "boire"
    ["type"]=>
    string(5) "verbe"
    [2]=>
    string(5) "verbe"
  }
}

Utilisation de la fonction bindValue() de PDO

La fonction bindValue() permet d’associer une valeur à un paramètre qui peut être identifié via un ? ou via un marqueur nommé.

// Les paramètres sont identifiés via les marqueurs :id et :type
$req = "SELECT * FROM exemple WHERE id = :id AND type = :type";

// La requête est préparée
$stmt = Mysql::getInstance()->prepare($req );

// La valeur 4 est associée au marqueur id
$stmt->bindValue('id', 4);

// La valeur 'verbe' est associée au marqueur type
$stmt->bindValue('type', 'verbe');

$stmt->execute();
$res = $stmt->fetchAll();
var_dump($res);

Le résultat de la requête est :

array(1) {
  [0]=>
  array(6) {
    ["id"]=>
    string(1) "4"
    [0]=>
    string(1) "4"
    ["label"]=>
    string(5) "boire"
    [1]=>
    string(5) "boire"
    ["type"]=>
    string(5) "verbe"
    [2]=>
    string(5) "verbe"
  }
}

Utilisation de la fonction lastInsertId() de PDO

La fonction lastInsertId() de PDO permet de récupérer l’identifiant de la dernière ligne insérée dans la base de données.

$req = "INSERT INTO exemple SET label = :label, type = :type";
$stmt = Mysql::getInstance()->prepare($req );
$stmt->bindValue('label', 'courir');
$stmt->bindValue('type', 'verbe');
$stmt->execute();

// On utilise lastInsertId() pour récupérer l'id d'enregistrement
echo "Insertion de l'enregistrement ".Mysql::getInstance()->lastInsertId();

Le résultat de la requête est :

Insertion de l'enregistrement 5

Sauf mention contraire*, l'article Classe PHP pour gérer une connexion MySQL et son contenu par Julien Crego sont mis à disposition selon les termes de la licence Creative Commons

Licence Creative Commons

Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International

Cette licence vous permet de remixer, arranger, et adapter cette œuvre à des fins non commerciales tant que vous créditez la source en citant le nom des auteurs et que les nouvelles œuvres sont diffusées selon les mêmes conditions.

* Cette notice est intégrée automatiquement à la fin de chaque article de ce site.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.